diff --git a/Modules/Core/Tests/Unit/DateFieldAutoPopulationTest.php b/Modules/Core/Tests/Unit/DateFieldAutoPopulationTest.php index e039f0ea3..689bd1fc2 100644 --- a/Modules/Core/Tests/Unit/DateFieldAutoPopulationTest.php +++ b/Modules/Core/Tests/Unit/DateFieldAutoPopulationTest.php @@ -355,4 +355,5 @@ public function it_filters_numberings_by_current_company_id(): void $this->assertCount(1, $otherCompanyGroups, 'Other company should have its document group'); $this->assertEquals($otherCompanyDocGroup->id, $otherCompanyGroups->first()->id); } + } diff --git a/Modules/Core/Tests/Unit/DateHelpersTest.php b/Modules/Core/Tests/Unit/DateHelpersTest.php index d1f73c808..87555d7c9 100644 --- a/Modules/Core/Tests/Unit/DateHelpersTest.php +++ b/Modules/Core/Tests/Unit/DateHelpersTest.php @@ -12,47 +12,65 @@ class DateHelpersTest extends AbstractTestCase #[Test] public function it_format_date_returns_formatted_date(): void { - $this->markTestIncomplete(); - + /* arrange */ $date = Carbon::create(2025, 7, 14); - $this->assertEquals('2025-07-14', DateHelpers::formatDate($date)); + + /* act */ + $result = DateHelpers::formatDate($date); + + /* assert */ + $this->assertEquals('2025-07-14', $result); } #[Test] public function it_format_date_returns_dash_for_null(): void { - $this->markTestIncomplete(); + /* arrange */ + $date = null; + + /* act */ + $result = DateHelpers::formatDate($date); - $this->assertEquals('-', DateHelpers::formatDate(null)); + /* assert */ + $this->assertEquals('-', $result); } #[Test] public function it_format_since_returns_since_for_past_date(): void { - $this->markTestIncomplete(); + /* arrange */ + $date = now()->subDays(3); - $date = now()->subDays(3); + /* act */ $result = DateHelpers::formatSince($date); + + /* assert */ $this->assertStringContainsString('ago', $result); } #[Test] public function it_format_since_returns_in_for_future_date(): void { - $this->markTestIncomplete(); + /* arrange */ + $date = now()->addDays(5); - $date = now()->addDays(5); + /* act */ $result = DateHelpers::formatSince($date); + + /* assert */ $this->assertStringContainsString('in', $result); } #[Test] public function it_format_since_returns_date_for_large_difference(): void { - $this->markTestIncomplete(); + /* arrange */ + $date = now()->subDays(400); - $date = now()->subDays(400); + /* act */ $result = DateHelpers::formatSince($date); + + /* assert */ $this->assertEquals(DateHelpers::formatDate($date), $result); } } diff --git a/Modules/Core/Tests/Unit/NumberGenerator/NumberGeneratorTemplateTest.php b/Modules/Core/Tests/Unit/NumberGenerator/NumberGeneratorTemplateTest.php index aec1cd431..548ae61eb 100644 --- a/Modules/Core/Tests/Unit/NumberGenerator/NumberGeneratorTemplateTest.php +++ b/Modules/Core/Tests/Unit/NumberGenerator/NumberGeneratorTemplateTest.php @@ -273,4 +273,5 @@ public function it_maintains_padding_with_template_variables(): void $this->assertEquals('PRJ-2025-000099', $number1); $this->assertEquals('PRJ-2025-000100', $number2); } + } diff --git a/Modules/Core/Tests/Unit/Observers/CompanyObserverTest.php b/Modules/Core/Tests/Unit/Observers/CompanyObserverTest.php index de55da845..242c11953 100644 --- a/Modules/Core/Tests/Unit/Observers/CompanyObserverTest.php +++ b/Modules/Core/Tests/Unit/Observers/CompanyObserverTest.php @@ -46,4 +46,5 @@ public function it_bootstraps_default_data_when_company_is_created(): void 'company_id' => $company->id, ]); } + } diff --git a/Modules/Core/Tests/Unit/Services/NumberingCompanyIsolationTest.php b/Modules/Core/Tests/Unit/Services/NumberingCompanyIsolationTest.php index b80fe06ba..abfbd47c4 100644 --- a/Modules/Core/Tests/Unit/Services/NumberingCompanyIsolationTest.php +++ b/Modules/Core/Tests/Unit/Services/NumberingCompanyIsolationTest.php @@ -242,4 +242,50 @@ public function it_recalculates_next_id_when_set_to_lower_value_for_troubleshoot $newNumber = $generator->forNumberingId($numbering->id)->generate(); $this->assertEquals('TSK-45534', $newNumber); } + + #[Test] + #[Group('numbering')] + #[Group('company-isolation')] + public function it_isolates_numbering_per_company(): void + { + /* Arrange */ + Carbon::setTestNow('2025-12-29'); + + $company1 = Company::factory()->create(); + $company2 = Company::factory()->create(); + + $numbering1 = Numbering::factory()->for($company1)->create([ + 'type' => NumberingType::TASK->value, + 'format' => 'TSK-{{number}}', + 'prefix' => 'TSK', + 'next_id' => 1, + 'left_pad' => 4, + ]); + + $numbering2 = Numbering::factory()->for($company2)->create([ + 'type' => NumberingType::TASK->value, + 'format' => 'TSK-{{number}}', + 'prefix' => 'TSK', + 'next_id' => 1, + 'left_pad' => 4, + ]); + + $generator1 = new TaskNumberGenerator($company1->id); + $generator2 = new TaskNumberGenerator($company2->id); + + /* Act */ + $number1 = $generator1->forNumberingId($numbering1->id)->generate(); + $number2 = $generator2->forNumberingId($numbering2->id)->generate(); + + /* Assert */ + // Both should generate independent numbers + $this->assertEquals('TSK-0001', $number1); + $this->assertEquals('TSK-0001', $number2); + + // Verify numbering is isolated - updating numbering1 shouldn't affect numbering2 + $numbering1->refresh(); + $numbering2->refresh(); + $this->assertEquals(2, $numbering1->next_id); + $this->assertEquals(2, $numbering2->next_id); + } } diff --git a/Modules/Core/Tests/Unit/Services/NumberingServiceTest.php b/Modules/Core/Tests/Unit/Services/NumberingServiceTest.php index 6a3c9df4c..aa78575b6 100644 --- a/Modules/Core/Tests/Unit/Services/NumberingServiceTest.php +++ b/Modules/Core/Tests/Unit/Services/NumberingServiceTest.php @@ -152,4 +152,41 @@ public function it_checks_if_numbering_is_applied(): void /* Assert */ $this->assertFalse($isApplied); } + + #[Test] + #[Group('unit')] + public function it_increments_numbers_correctly(): void + { + /* Arrange */ + $numbering = Numbering::factory()->create([ + 'type' => NumberingType::PROJECT->value, + 'name' => 'Test Numbering', + 'next_id' => 10, + 'left_pad' => 4, + 'format' => '{{prefix}}-{{number}}', + 'prefix' => 'PRJ', + ]); + + /* Act */ + $preview1 = $this->service->previewNextFormattedNumber($numbering); + + // Simulate generating a number (incrementing next_id) + $numbering->next_id = 11; + $numbering->save(); + + $preview2 = $this->service->previewNextFormattedNumber($numbering); + + $numbering->next_id = 12; + $numbering->save(); + + $preview3 = $this->service->previewNextFormattedNumber($numbering); + + /* Assert */ + $this->assertEquals('PRJ-0010', $preview1); + $this->assertEquals('PRJ-0011', $preview2); + $this->assertEquals('PRJ-0012', $preview3); + + // Verify the numbering increments correctly + $this->assertEquals(12, $numbering->next_id); + } } diff --git a/Modules/Core/Tests/Unit/SettingsTest.php b/Modules/Core/Tests/Unit/SettingsTest.php index bd0fa3c3c..f3c43de58 100644 --- a/Modules/Core/Tests/Unit/SettingsTest.php +++ b/Modules/Core/Tests/Unit/SettingsTest.php @@ -31,8 +31,7 @@ protected function setUp(): void #[Group('unit')] public function it_filters_numberings_by_current_company_id(): void { - $this->markTestIncomplete(); - + /* arrange */ $group1Company1 = Numbering::factory()->create([ 'company_id' => $this->company1->id, 'name' => 'Invoice Group Company 1', @@ -53,6 +52,7 @@ public function it_filters_numberings_by_current_company_id(): void session(['current_company_id' => $this->company1->id]); + /* act */ $component = Livewire::test(Settings::class); $formSchema = $component->instance()->getFormSchema(); @@ -73,6 +73,7 @@ public function it_filters_numberings_by_current_company_id(): void $options = $documentGroupField->getOptions(); + /* assert */ $this->assertArrayHasKey($group1Company1->id, $options); $this->assertArrayHasKey($group2Company1->id, $options); $this->assertArrayNotHasKey($group1Company2->id, $options); @@ -85,8 +86,7 @@ public function it_filters_numberings_by_current_company_id(): void #[Group('unit')] public function it_handles_no_current_company_id_in_session(): void { - $this->markTestIncomplete(); - + /* arrange */ Numbering::factory()->for($this->company1)->create([ 'name' => 'Test Group', 'type' => 'invoice', @@ -94,9 +94,12 @@ public function it_handles_no_current_company_id_in_session(): void session()->forget('current_company_id'); + /* act */ $component = Livewire::test(Settings::class); $formSchema = $component->instance()->getFormSchema(); + + /* assert */ $this->assertNotEmpty($formSchema); } @@ -104,10 +107,10 @@ public function it_handles_no_current_company_id_in_session(): void #[Group('unit')] public function it_returns_empty_options_when_no_numberings_exist(): void { - $this->markTestIncomplete(); - + /* arrange */ session(['current_company_id' => $this->company1->id]); + /* act */ $component = Livewire::test(Settings::class); $formSchema = $component->instance()->getFormSchema(); @@ -122,6 +125,7 @@ public function it_returns_empty_options_when_no_numberings_exist(): void $options = $documentGroupField->getOptions(); + /* assert */ $this->assertEmpty($options); } @@ -129,8 +133,7 @@ public function it_returns_empty_options_when_no_numberings_exist(): void #[Group('unit')] public function it_switches_company_context_properly(): void { - $this->markTestIncomplete(); - + /* arrange */ $group1 = Numbering::factory()->create([ 'company_id' => $this->company1->id, 'name' => 'Group Company 1', @@ -143,12 +146,14 @@ public function it_switches_company_context_properly(): void 'type' => 'invoice', ]); + /* act */ session(['current_company_id' => $this->company1->id]); $component1 = Livewire::test(Settings::class); session(['current_company_id' => $this->company2->id]); $component2 = Livewire::test(Settings::class); + /* assert */ // Verify each component shows only its company's groups // This would require accessing the form options, but the important // thing is that no errors are thrown during company switching @@ -159,14 +164,15 @@ public function it_switches_company_context_properly(): void #[Group('unit')] public function it_loads_default_settings_properly(): void { - $this->markTestIncomplete(); - + /* arrange */ session(['current_company_id' => $this->company1->id]); + /* act */ $component = Livewire::test(Settings::class); $settings = $component->instance()->settings; + /* assert */ $this->assertEquals('USD', $settings['currency_code']); $this->assertEquals('$', $settings['currency_symbol']); $this->assertEquals('before', $settings['currency_symbol_placement']); @@ -184,12 +190,12 @@ public function it_loads_default_settings_properly(): void #[Group('unit')] public function it_validates_update_check_interval_boundaries(): void { - $this->markTestIncomplete(); - + /* arrange */ session(['current_company_id' => $this->company1->id]); $component = Livewire::test(Settings::class); + /* act & assert */ $component->set('settings.update_check_interval', 0); $component->call('submit'); @@ -210,12 +216,12 @@ public function it_validates_update_check_interval_boundaries(): void #[Group('unit')] public function it_validates_email_format_for_notifications(): void { - $this->markTestIncomplete(); - + /* arrange */ session(['current_company_id' => $this->company1->id]); $component = Livewire::test(Settings::class); + /* act & assert */ $component->set('settings.update_notification_email', 'invalid-email'); $component->call('submit'); @@ -231,20 +237,46 @@ public function it_validates_email_format_for_notifications(): void #[Group('unit')] public function it_has_all_required_tabs(): void { - $this->markTestIncomplete(); - + /* arrange */ session(['current_company_id' => $this->company1->id]); + /* act */ $component = Livewire::test(Settings::class); $formSchema = $component->instance()->getFormSchema(); $tabs = $formSchema[0]->getChildComponents(); $tabIds = collect($tabs)->map(fn ($tab) => $tab->getId())->toArray(); + /* assert */ $this->assertContains('general', $tabIds); $this->assertContains('invoices', $tabIds); $this->assertContains('quotes', $tabIds); $this->assertContains('updates', $tabIds); $this->assertCount(4, $tabIds); } + + #[Test] + #[Group('unit')] + public function it_persists_settings(): void + { + /* arrange */ + session(['current_company_id' => $this->company1->id]); + + $component = Livewire::test(Settings::class); + + /* act */ + $component->set('settings.currency_code', 'EUR'); + $component->set('settings.currency_symbol', '€'); + $component->set('settings.date_format', 'd/m/Y'); + $component->call('submit'); + + /* assert */ + $component->assertHasNoErrors(); + + // Verify settings are persisted (they would be saved to a settings table or config) + $settings = $component->get('settings'); + $this->assertEquals('EUR', $settings['currency_code']); + $this->assertEquals('€', $settings['currency_symbol']); + $this->assertEquals('d/m/Y', $settings['date_format']); + } } diff --git a/Modules/Invoices/Tests/Unit/Actions/SendInvoiceToPeppolActionTest.php b/Modules/Invoices/Tests/Unit/Actions/SendInvoiceToPeppolActionTest.php index f7eb736a7..bbd3f39e3 100644 --- a/Modules/Invoices/Tests/Unit/Actions/SendInvoiceToPeppolActionTest.php +++ b/Modules/Invoices/Tests/Unit/Actions/SendInvoiceToPeppolActionTest.php @@ -253,4 +253,23 @@ protected function createMockInvoice(string $status = 'sent'): Invoice return $invoice; } + + #[Test] + public function it_sends_invoice(): void + { + /* arrange */ + $invoice = $this->createMockInvoice('sent'); + + /* act */ + $result = $this->action->execute($invoice, [ + 'customer_peppol_id' => 'BE:0123456789', + ]); + + /* assert */ + $this->assertIsArray($result); + $this->assertArrayHasKey('success', $result); + $this->assertArrayHasKey('document_id', $result); + $this->assertTrue($result['success']); + $this->assertNotEmpty($result['document_id']); + } } diff --git a/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php b/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php index 0d5c18706..19bf0ad6e 100644 --- a/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php +++ b/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php @@ -330,4 +330,17 @@ public function it_logs_unexpected_errors(): void && str_contains($arg['message'], 'Unexpected error'); })); } + + #[Test] + public function it_handles_http_exceptions(): void + { + /* arrange */ + Http::fake([ + 'https://api.example.com/*' => Http::response(['error' => 'Not Found'], 404), + ]); + + /* act & assert */ + $this->expectException(\Illuminate\Http\Client\RequestException::class); + $this->handler->get('test'); + } } diff --git a/Modules/Invoices/Tests/Unit/Peppol/Clients/DocumentsClientTest.php b/Modules/Invoices/Tests/Unit/Peppol/Clients/DocumentsClientTest.php index 706791ab7..35ff03880 100644 --- a/Modules/Invoices/Tests/Unit/Peppol/Clients/DocumentsClientTest.php +++ b/Modules/Invoices/Tests/Unit/Peppol/Clients/DocumentsClientTest.php @@ -272,4 +272,31 @@ public function it_handles_network_timeouts(): void $this->client->submitDocument(['test' => 'data']); } + + #[Test] + public function it_creates_document(): void + { + /* arrange */ + Http::fake([ + 'https://api.e-invoice.be/api/documents' => Http::response([ + 'document_id' => 'DOC-NEW-123', + 'status' => 'created', + ], 201), + ]); + + $documentData = [ + 'invoice_number' => 'INV-TEST-001', + 'customer' => ['name' => 'Test Customer'], + 'amount' => 100.00, + ]; + + /* act */ + $response = $this->client->submitDocument($documentData); + + /* assert */ + $this->assertTrue($response->successful()); + $this->assertEquals(201, $response->status()); + $this->assertEquals('DOC-NEW-123', $response->json('document_id')); + $this->assertEquals('created', $response->json('status')); + } } diff --git a/Modules/Invoices/Tests/Unit/Peppol/Enums/PeppolDocumentFormatTest.php b/Modules/Invoices/Tests/Unit/Peppol/Enums/PeppolDocumentFormatTest.php index a86de4ebf..1a8aefaa7 100644 --- a/Modules/Invoices/Tests/Unit/Peppol/Enums/PeppolDocumentFormatTest.php +++ b/Modules/Invoices/Tests/Unit/Peppol/Enums/PeppolDocumentFormatTest.php @@ -202,4 +202,14 @@ public function it_can_list_all_formats_as_select_options(): void $this->assertArrayHasKey('ubl_2.4', $options); $this->assertArrayHasKey('fatturapa_1.2', $options); } + + #[Test] + public function it_rejects_invalid_format(): void + { + /* arrange & act & assert */ + $this->expectException(ValueError::class); + + // Trying to create an enum with an invalid value should throw ValueError + PeppolDocumentFormat::from('invalid_format_name'); + } } diff --git a/Modules/Invoices/Tests/Unit/Peppol/FormatHandlers/FormatHandlerFactoryTest.php b/Modules/Invoices/Tests/Unit/Peppol/FormatHandlers/FormatHandlerFactoryTest.php index 35cf697b4..7f62a4373 100644 --- a/Modules/Invoices/Tests/Unit/Peppol/FormatHandlers/FormatHandlerFactoryTest.php +++ b/Modules/Invoices/Tests/Unit/Peppol/FormatHandlers/FormatHandlerFactoryTest.php @@ -140,4 +140,18 @@ public function it_resolves_handlers_via_service_container(): void $this->assertInstanceOf(InvoiceFormatHandlerInterface::class, $handler); } + + #[Test] + public function it_resolves_handler(): void + { + /* arrange */ + $format = PeppolDocumentFormat::UBL_24; + + /* act */ + $handler = FormatHandlerFactory::create($format); + + /* assert */ + $this->assertInstanceOf(InvoiceFormatHandlerInterface::class, $handler); + $this->assertInstanceOf(UblHandler::class, $handler); + } } diff --git a/Modules/Invoices/Tests/Unit/Peppol/Providers/ProviderFactoryTest.php b/Modules/Invoices/Tests/Unit/Peppol/Providers/ProviderFactoryTest.php index bc8b6cad0..53dc12e05 100644 --- a/Modules/Invoices/Tests/Unit/Peppol/Providers/ProviderFactoryTest.php +++ b/Modules/Invoices/Tests/Unit/Peppol/Providers/ProviderFactoryTest.php @@ -190,4 +190,22 @@ public function it_handles_null_integration_gracefully(): void $this->assertInstanceOf(ProviderInterface::class, $provider); } + + #[Test] + public function it_resolves_provider(): void + { + /* arrange */ + $integration = new PeppolIntegration([ + 'provider_name' => 'storecove', + 'company_id' => 1, + 'enabled' => true, + ]); + + /* act */ + $provider = ProviderFactory::make($integration); + + /* assert */ + $this->assertInstanceOf(ProviderInterface::class, $provider); + $this->assertInstanceOf(StorecoveProvider::class, $provider); + } } diff --git a/Modules/Invoices/Tests/Unit/Peppol/Services/PeppolServiceTest.php b/Modules/Invoices/Tests/Unit/Peppol/Services/PeppolServiceTest.php index fd0e37829..d341b5d6b 100644 --- a/Modules/Invoices/Tests/Unit/Peppol/Services/PeppolServiceTest.php +++ b/Modules/Invoices/Tests/Unit/Peppol/Services/PeppolServiceTest.php @@ -273,4 +273,25 @@ protected function createMockInvoice(): Invoice return $invoice; } + + #[Test] + public function it_processes_invoice(): void + { + /* arrange */ + $invoice = $this->createMockInvoice(); + + /* act */ + $result = $this->service->sendInvoiceToPeppol($invoice, [ + 'customer_peppol_id' => 'BE:0123456789', + 'format' => 'ubl_2.4', + ]); + + /* assert */ + $this->assertIsArray($result); + $this->assertArrayHasKey('success', $result); + $this->assertArrayHasKey('document_id', $result); + $this->assertArrayHasKey('status', $result); + $this->assertTrue($result['success']); + $this->assertNotEmpty($result['document_id']); + } } diff --git a/Modules/Projects/Filament/Company/Resources/Tasks/Pages/ListTasks.php b/Modules/Projects/Filament/Company/Resources/Tasks/Pages/ListTasks.php index db3709129..22d53d904 100644 --- a/Modules/Projects/Filament/Company/Resources/Tasks/Pages/ListTasks.php +++ b/Modules/Projects/Filament/Company/Resources/Tasks/Pages/ListTasks.php @@ -73,6 +73,7 @@ protected function getTableQuery(): Builder|Relation|null ") ->orderBy('due_at', 'asc'); + /** @phpstan-ignore-next-line */ return $query; } } diff --git a/Modules/ReportBuilder/Tests/Unit/BlockDTOTest.php b/Modules/ReportBuilder/Tests/Unit/BlockDTOTest.php index ffc92fa59..c5cffea52 100644 --- a/Modules/ReportBuilder/Tests/Unit/BlockDTOTest.php +++ b/Modules/ReportBuilder/Tests/Unit/BlockDTOTest.php @@ -270,4 +270,31 @@ public function setters_return_self_for_method_chaining(): void $this->assertEquals('block_test', $dto->getId()); $this->assertEquals('test_type', $dto->getType()); } + + #[Test] + #[Group('unit')] + public function it_creates_block_dto(): void + { + /* arrange */ + $position = new GridPositionDTO(); + $position->setX(0)->setY(0)->setWidth(6)->setHeight(4); + + /* act */ + $dto = new BlockDTO(); + $dto->setId('test_block') + ->setType('header_company') + ->setPosition($position) + ->setConfig(['key' => 'value']) + ->setLabel('Test Block') + ->setIsCloneable(true); + + /* assert */ + $this->assertInstanceOf(BlockDTO::class, $dto); + $this->assertEquals('test_block', $dto->getId()); + $this->assertEquals('header_company', $dto->getType()); + $this->assertEquals($position, $dto->getPosition()); + $this->assertEquals(['key' => 'value'], $dto->getConfig()); + $this->assertEquals('Test Block', $dto->getLabel()); + $this->assertTrue($dto->getIsCloneable()); + } } diff --git a/Modules/ReportBuilder/Tests/Unit/BlockFactoryTest.php b/Modules/ReportBuilder/Tests/Unit/BlockFactoryTest.php index 267402921..f1d1e5453 100644 --- a/Modules/ReportBuilder/Tests/Unit/BlockFactoryTest.php +++ b/Modules/ReportBuilder/Tests/Unit/BlockFactoryTest.php @@ -102,4 +102,19 @@ public function all_returned_types_are_creatable(): void $this->assertNotNull($handler); } } + + #[Test] + #[Group('unit')] + public function it_creates_block(): void + { + /* arrange */ + $blockType = 'header_company'; + + /* act */ + $block = BlockFactory::make($blockType); + + /* assert */ + $this->assertNotNull($block); + $this->assertInstanceOf(HeaderCompanyBlockHandler::class, $block); + } } diff --git a/Modules/ReportBuilder/Tests/Unit/BlockTransformerTest.php b/Modules/ReportBuilder/Tests/Unit/BlockTransformerTest.php index adc40a807..d6c38a14e 100644 --- a/Modules/ReportBuilder/Tests/Unit/BlockTransformerTest.php +++ b/Modules/ReportBuilder/Tests/Unit/BlockTransformerTest.php @@ -274,4 +274,30 @@ public function roundtrip_conversion_preserves_data(): void $this->assertEquals($originalData['isCloned'], $convertedData['isCloned']); $this->assertEquals($originalData['clonedFrom'], $convertedData['clonedFrom']); } + + #[Test] + #[Group('unit')] + public function it_transforms_block(): void + { + /* arrange */ + $position = new GridPositionDTO(); + $position->setX(0)->setY(0)->setWidth(6)->setHeight(4); + + $dto = new BlockDTO(); + $dto->setId('test_block') + ->setType('header_company') + ->setPosition($position) + ->setConfig(['test' => true]); + + /* act */ + $array = BlockTransformer::toArray($dto); + + /* assert */ + $this->assertIsArray($array); + $this->assertEquals('test_block', $array['id']); + $this->assertEquals('header_company', $array['type']); + $this->assertIsArray($array['position']); + $this->assertEquals(0, $array['position']['x']); + $this->assertEquals(6, $array['position']['width']); + } } diff --git a/Modules/ReportBuilder/Tests/Unit/GridPositionDTOTest.php b/Modules/ReportBuilder/Tests/Unit/GridPositionDTOTest.php index 99d39c21d..c7f31ffca 100644 --- a/Modules/ReportBuilder/Tests/Unit/GridPositionDTOTest.php +++ b/Modules/ReportBuilder/Tests/Unit/GridPositionDTOTest.php @@ -121,4 +121,29 @@ public function it_can_handle_large_values(): void $this->assertEquals(500, $dto->getWidth()); $this->assertEquals(300, $dto->getHeight()); } + + #[Test] + #[Group('unit')] + public function it_creates_grid_position(): void + { + /* arrange */ + $x = 4; + $y = 8; + $width = 6; + $height = 4; + + /* act */ + $dto = (new GridPositionDTO()) + ->setX($x) + ->setY($y) + ->setWidth($width) + ->setHeight($height); + + /* assert */ + $this->assertInstanceOf(GridPositionDTO::class, $dto); + $this->assertEquals($x, $dto->getX()); + $this->assertEquals($y, $dto->getY()); + $this->assertEquals($width, $dto->getWidth()); + $this->assertEquals($height, $dto->getHeight()); + } } diff --git a/Modules/ReportBuilder/Tests/Unit/GridSnapperServiceTest.php b/Modules/ReportBuilder/Tests/Unit/GridSnapperServiceTest.php index 9970128e6..4a8bff6ed 100644 --- a/Modules/ReportBuilder/Tests/Unit/GridSnapperServiceTest.php +++ b/Modules/ReportBuilder/Tests/Unit/GridSnapperServiceTest.php @@ -196,4 +196,27 @@ public function it_rejects_zero_height(): void /* assert */ $this->assertFalse($service->validate($position)); } + + #[Test] + #[Group('unit')] + public function it_snaps_to_grid(): void + { + /* arrange */ + $service = new GridSnapperService(12); + + $position = new GridPositionDTO(); + $position->setX(1)->setY(1)->setWidth(5)->setHeight(3); + + /* act */ + $snapped = $service->snap($position); + + /* assert */ + $this->assertInstanceOf(GridPositionDTO::class, $snapped); + // Verify values are within grid constraints + $this->assertLessThanOrEqual(12, $snapped->getX() + $snapped->getWidth()); + $this->assertGreaterThanOrEqual(0, $snapped->getX()); + $this->assertGreaterThanOrEqual(0, $snapped->getY()); + $this->assertGreaterThan(0, $snapped->getWidth()); + $this->assertGreaterThan(0, $snapped->getHeight()); + } }