diff --git a/tests/Column/DataColumnTest.php b/tests/Column/DataColumnTest.php index 1bfe6117f..6137a9f6b 100644 --- a/tests/Column/DataColumnTest.php +++ b/tests/Column/DataColumnTest.php @@ -10,7 +10,9 @@ use Yiisoft\Definitions\Exception\InvalidConfigException; use Yiisoft\Definitions\Exception\NotInstantiableException; use Yiisoft\Factory\NotFoundException; +use Yiisoft\Validator\Rule\Email; use Yiisoft\Yii\DataView\Column\DataColumn; +use Yiisoft\Yii\DataView\Filter\Factory\EqualsFilterFactory; use Yiisoft\Yii\DataView\GridView; use Yiisoft\Yii\DataView\Tests\Support\Assert; use Yiisoft\Yii\DataView\Tests\Support\TestTrait; @@ -698,7 +700,7 @@ public function testFilterWithValidationError(): void new DataColumn( 'email', filter: true, - filterValidation: new \Yiisoft\Validator\Rule\Email() + filterValidation: new Email() ), ) ->id('w1-grid') @@ -745,7 +747,7 @@ public function testCustomFilterFactory(): void new DataColumn( 'status', filter: true, - filterFactory: new \Yiisoft\Yii\DataView\Filter\Factory\EqualsFilterFactory() + filterFactory: new EqualsFilterFactory() ), ) ->id('w1-grid') diff --git a/tests/Column/RadioColumnRendererTest.php b/tests/Column/RadioColumnRendererTest.php index f36847bd8..b39a7bc0a 100644 --- a/tests/Column/RadioColumnRendererTest.php +++ b/tests/Column/RadioColumnRendererTest.php @@ -241,53 +241,6 @@ public function testRenderFooterWithContent(): void $this->assertSame('Footer content', $result->getContent()[0]); } - public function testRenderFooterWithoutContent(): void - { - $column = new RadioColumn(); - $cell = new Cell(); - $translator = Mock::translator('en'); - - $context = new GlobalContext( - dataReader: $this->dataReader, - pathArguments: [], - queryParameters: [], - translator: $translator, - translationCategory: 'test' - ); - - $renderer = new RadioColumnRenderer(); - $result = $renderer->renderFooter($column, $cell, $context); - - $this->assertEmpty($result->getContent()); - } - - public function testRenderBodyWithCustomContentAndEmptyAttributes(): void - { - $column = new RadioColumn( - bodyAttributes: [], - inputAttributes: [], - content: static fn($input) => Html::div($input)->class('custom-wrapper') - ); - $cell = new Cell(); - $data = ['id' => 1]; - - $context = new DataContext( - preparedDataReader: $this->dataReader, - column: $column, - data: $data, - key: 1, - index: 0 - ); - - $renderer = new RadioColumnRenderer(); - $result = $renderer->renderBody($column, $cell, $context); - - $content = $result->getContent(); - $this->assertNotEmpty($content); - $this->assertStringContainsString('
', (string)$content[0]); - $this->assertEmpty($result->getAttributes()); - } - public function testRenderBodyWithCustomName(): void { $column = new RadioColumn( @@ -648,12 +601,9 @@ public function testRenderBodyWithCustomHtmlContent(): void content: static fn($input) => Html::div() ->class('input-group') ->content( - Html::label() - ->content( - $input, - Html::span('*')->class('required'), - 'Select user' - ) + $input, + Html::span('*')->class('required'), + 'Select user' ) ); $cell = new Cell(); @@ -811,7 +761,6 @@ public function testRenderBodyWithCustomContent(): void public function testDefaultNameIsUsedWhenNotProvided(): void { - // Arrange $column = new RadioColumn(); $cell = new Cell(); $data = ['id' => 42]; @@ -824,10 +773,8 @@ public function testDefaultNameIsUsedWhenNotProvided(): void ); $renderer = new RadioColumnRenderer(); - // Act $result = $renderer->renderBody($column, $cell, $context); - // Assert $content = $result->getContent(); $this->assertStringContainsString('name="radio-selection"', (string)$content[0]); } @@ -892,7 +839,7 @@ public function testRenderBodyWithEmptyContentClosure(): void public function testRenderBodyWithContextInContentClosure(): void { $column = new RadioColumn( - content: static fn($input, $context) => (string)$input . ' Item ' . $context->index + content: static fn($input, $context) => $input . ' Item ' . $context->index ); $cell = new Cell(); $data = ['id' => 42]; @@ -1115,7 +1062,6 @@ public function testRenderBodyWithCustomValueAndChecked(): void public function testEmptyNameAttributeIsPreserved(): void { - // Arrange $column = new RadioColumn( inputAttributes: [ 'name' => '', @@ -1135,17 +1081,14 @@ public function testEmptyNameAttributeIsPreserved(): void ); $renderer = new RadioColumnRenderer(); - // Act $result = $renderer->renderBody($column, $cell, $context); - // Assert $content = $result->getContent(); $this->assertStringContainsString('name value="1"', (string)$content[0]); } public function testEmptyValueAttributeIsPreserved(): void { - // Arrange $column = new RadioColumn( inputAttributes: [ 'value' => '', @@ -1165,10 +1108,8 @@ public function testEmptyValueAttributeIsPreserved(): void ); $renderer = new RadioColumnRenderer(); - // Act $result = $renderer->renderBody($column, $cell, $context); - // Assert $content = $result->getContent(); $this->assertStringContainsString('name="radio-selection" value', (string)$content[0]); } @@ -1253,4 +1194,230 @@ public function testRenderBodyWithInactiveStatus(): void $this->assertStringNotContainsString('checked', (string)$content[0]); $this->assertFalse($result->shouldEncode()); } + + public function testContentClosureReceivesInputAndContext(): void + { + $expectedInput = null; + $expectedContext = null; + $column = new RadioColumn( + inputAttributes: [ + 'class' => 'form-check-input', + 'name' => 'selection', + ], + content: static function($input, $context) use (&$expectedInput, &$expectedContext) { + $expectedInput = $input; + $expectedContext = $context; + return $input; + } + ); + $cell = new Cell(); + $data = ['id' => 42, 'name' => 'Test']; + $context = new DataContext( + preparedDataReader: $this->dataReader, + column: $column, + data: $data, + key: 1, + index: 0 + ); + $renderer = new RadioColumnRenderer(); + + $result = $renderer->renderBody($column, $cell, $context); + + $this->assertNotNull($expectedInput); + $this->assertStringContainsString('type="radio"', (string)$expectedInput); + $this->assertSame($context, $expectedContext); + } + + public function testContentClosureCanModifyInput(): void + { + $column = new RadioColumn( + inputAttributes: [ + 'class' => 'form-check-input', + 'name' => 'selection', + ], + content: static function($input) { + return Html::div() + ->class('input-wrapper') + ->content( + $input->class('modified-input'), + Html::label('Select item') + ); + } + ); + $cell = new Cell(); + $data = ['id' => 42]; + $context = new DataContext( + preparedDataReader: $this->dataReader, + column: $column, + data: $data, + key: 1, + index: 0 + ); + $renderer = new RadioColumnRenderer(); + + $result = $renderer->renderBody($column, $cell, $context); + + $content = $result->getContent(); + $this->assertStringContainsString('class="modified-input"', (string)$content[0]); + $this->assertStringContainsString('class="input-wrapper"', (string)$content[0]); + } + + public function testRenderBodyPreservesExistingInputAttributes(): void + { + $column = new RadioColumn( + inputAttributes: [ + 'class' => 'custom-radio', + 'data-test' => 'test-value', + 'aria-label' => 'Select option', + 'name' => 'custom-name', + 'value' => 'custom-value', + ] + ); + $cell = new Cell(); + $data = ['id' => 42]; + $context = new DataContext( + preparedDataReader: $this->dataReader, + column: $column, + data: $data, + key: 1, + index: 0 + ); + $renderer = new RadioColumnRenderer(); + + $result = $renderer->renderBody($column, $cell, $context); + + $content = $result->getContent(); + $this->assertStringContainsString('class="custom-radio"', (string)$content[0]); + $this->assertStringContainsString('data-test="test-value"', (string)$content[0]); + $this->assertStringContainsString('aria-label="Select option"', (string)$content[0]); + $this->assertStringContainsString('name="custom-name"', (string)$content[0]); + $this->assertStringContainsString('value="custom-value"', (string)$content[0]); + } + + public function testRenderBodyWithCustomizedHtmlStructure(): void + { + $column = new RadioColumn( + inputAttributes: [ + 'class' => 'form-check-input custom-radio', + 'id' => 'radio-42', + 'aria-label' => 'Select option', + ], + content: static function($input) { + return Html::div() + ->class('form-check custom-control') + ->attributes(['data-test' => 'radio-wrapper']) + ->content( + $input, + Html::span('✓')->class('check-mark'), + Html::label() + ->class('form-check-label') + ->attributes(['for' => 'radio-42']) + ->content('Select option') + ); + } + ); + $cell = new Cell(); + $data = ['id' => 42]; + $context = new DataContext( + preparedDataReader: $this->dataReader, + column: $column, + data: $data, + key: 1, + index: 0 + ); + $renderer = new RadioColumnRenderer(); + + $result = $renderer->renderBody($column, $cell, $context); + + $content = $result->getContent(); + $this->assertStringContainsString('form-check-input', (string)$content[0]); + $this->assertStringContainsString('custom-radio', (string)$content[0]); + $this->assertStringContainsString('for="radio-42"', (string)$content[0]); + $this->assertStringContainsString('id="radio-42"', (string)$content[0]); + $this->assertStringContainsString('check-mark', (string)$content[0]); + $this->assertStringContainsString('Select option', (string)$content[0]); + } + + public function testRenderBodyWithCustomizedHtmlStructureAndAttributes(): void + { + $column = new RadioColumn( + inputAttributes: [ + 'class' => 'form-check-input custom-radio', + 'id' => 'radio-42', + 'aria-label' => 'Select option', + 'data-test' => 'test-value', + ], + content: static function($input) { + return Html::div() + ->class('form-check custom-control') + ->attributes([ + 'data-test' => 'radio-wrapper', + 'data-user-id' => '42', + ]) + ->content( + $input, + Html::span('✓')->class('check-mark'), + Html::label() + ->class('form-check-label') + ->attributes(['for' => 'radio-42']) + ->content('Select option') + ); + } + ); + $cell = new Cell(); + $data = ['id' => 42]; + $context = new DataContext( + preparedDataReader: $this->dataReader, + column: $column, + data: $data, + key: 1, + index: 0 + ); + $renderer = new RadioColumnRenderer(); + + $result = $renderer->renderBody($column, $cell, $context); + + $content = $result->getContent(); + $this->assertStringContainsString('form-check-input', (string)$content[0]); + $this->assertStringContainsString('custom-radio', (string)$content[0]); + $this->assertStringContainsString('data-test="test-value"', (string)$content[0]); + $this->assertStringContainsString('data-user-id="42"', (string)$content[0]); + $this->assertStringContainsString('for="radio-42"', (string)$content[0]); + $this->assertStringContainsString('id="radio-42"', (string)$content[0]); + $this->assertStringContainsString('check-mark', (string)$content[0]); + $this->assertStringContainsString('Select option', (string)$content[0]); + } + + public function testRenderBodyWithDataFromContext(): void + { + $column = new RadioColumn( + inputAttributes: ['class' => 'form-check-input'], + content: static function($input, $context) { + return Html::div() + ->class('form-check') + ->content( + $input, + Html::label() + ->class('form-check-label') + ->content($context->data['name']) + ); + } + ); + $cell = new Cell(); + $data = ['id' => 42, 'name' => 'Test User']; + $context = new DataContext( + preparedDataReader: $this->dataReader, + column: $column, + data: $data, + key: 1, + index: 0 + ); + $renderer = new RadioColumnRenderer(); + + $result = $renderer->renderBody($column, $cell, $context); + + $content = $result->getContent(); + $this->assertStringContainsString('Test User', (string)$content[0]); + $this->assertStringContainsString('class="form-check-label"', (string)$content[0]); + } } diff --git a/tests/GridView/ImmutableTest.php b/tests/GridView/ImmutableTest.php index 13adbc815..dfe2e5ba1 100644 --- a/tests/GridView/ImmutableTest.php +++ b/tests/GridView/ImmutableTest.php @@ -10,6 +10,7 @@ use Yiisoft\Definitions\Exception\InvalidConfigException; use Yiisoft\Definitions\Exception\NotInstantiableException; use Yiisoft\Factory\NotFoundException; +use Yiisoft\Validator\Result; use Yiisoft\Yii\DataView; use Yiisoft\Yii\DataView\Column\DataColumn; use Yiisoft\Yii\DataView\Pagination\OffsetPagination; @@ -106,7 +107,7 @@ private function createBaseListView(): DataView\BaseListView return new class () extends DataView\BaseListView { public function renderItems( array $items, - \Yiisoft\Validator\Result $filterValidationResult, + Result $filterValidationResult, ?ReadableDataInterface $preparedDataReader, ): string { return ''; diff --git a/tests/GridView/TranslatorTest.php b/tests/GridView/TranslatorTest.php index ab540ec17..5d362c2a1 100644 --- a/tests/GridView/TranslatorTest.php +++ b/tests/GridView/TranslatorTest.php @@ -11,6 +11,7 @@ use Yiisoft\Definitions\Reference; use Yiisoft\Di\Container; use Yiisoft\Di\ContainerConfig; +use Yiisoft\Factory\NotFoundException; use Yiisoft\Translator\Translator; use Yiisoft\Translator\TranslatorInterface; use Yiisoft\Validator\Validator; @@ -153,7 +154,7 @@ public function testEmptyTextTranslatorWithLocaleRussian(): void /** * @throws InvalidConfigException - * @throws \Yiisoft\Factory\NotFoundException + * @throws NotFoundException * @throws NotInstantiableException * @throws CircularReferenceException */ @@ -204,7 +205,7 @@ public function testSummaryTranslatorWithLocaleDefault(): void /** * @throws InvalidConfigException - * @throws \Yiisoft\Factory\NotFoundException + * @throws NotFoundException * @throws NotInstantiableException * @throws CircularReferenceException */ @@ -257,7 +258,7 @@ public function testSummaryTranslatorWithLocaleSpanish(): void /** * @throws InvalidConfigException - * @throws \Yiisoft\Factory\NotFoundException + * @throws NotFoundException * @throws NotInstantiableException * @throws CircularReferenceException */ diff --git a/tests/Pagination/OffsetPaginationTest.php b/tests/Pagination/OffsetPaginationTest.php index 53d85dc0a..327b032f7 100644 --- a/tests/Pagination/OffsetPaginationTest.php +++ b/tests/Pagination/OffsetPaginationTest.php @@ -4,30 +4,65 @@ namespace Yiisoft\Yii\DataView\Tests\Pagination; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Yiisoft\Definitions\Exception\CircularReferenceException; use Yiisoft\Definitions\Exception\InvalidConfigException; use Yiisoft\Definitions\Exception\NotInstantiableException; use Yiisoft\Factory\NotFoundException; -use Yiisoft\Yii\DataView\Pagination\PaginatorNotSetException; +use Yiisoft\Yii\DataView\Column\DataColumn; use Yiisoft\Yii\DataView\GridView; use Yiisoft\Yii\DataView\Pagination\OffsetPagination; +use Yiisoft\Yii\DataView\Pagination\PaginatorNotSetException; use Yiisoft\Yii\DataView\Tests\Support\Assert; use Yiisoft\Yii\DataView\Tests\Support\TestTrait; +/** + * @psalm-suppress PropertyNotSetInConstructor + */ final class OffsetPaginationTest extends TestCase { use TestTrait; + private const PAGE_SIZE = 10; + private const TOTAL_ITEMS = 30; + + /** + * @return array + */ + private function createTestData(): array + { + return array_map( + static fn (int $i): array => ['id' => $i, 'value' => "Item $i"], + range(1, self::TOTAL_ITEMS) + ); + } + /** + * @param array $data + */ + private function createGridView(array $data, OffsetPagination $pagination): string + { + return GridView::widget() + ->id('w1-grid') + ->dataReader($this->createOffsetPaginator($data, self::PAGE_SIZE)) + ->columns( + new DataColumn(property: 'id'), + new DataColumn(property: 'value') + ) + ->paginationWidget($pagination) + ->render(); + } + + /** + * @throws CircularReferenceException * @throws InvalidConfigException - * @throws NotFoundException * @throws NotInstantiableException - * @throws CircularReferenceException + * @throws NotFoundException */ - public function testRenderPaginatorEmptyData(): void + public function testEmptyData(): void { - $offsetPaginator = $this->createOffsetPaginator([], 10); + $offsetPaginator = $this->createOffsetPaginator([], self::PAGE_SIZE); Assert::equalsWithoutLE( <<expectException(PaginatorNotSetException::class); - $this->expectExceptionMessage('Failed to create widget because "paginator" is not set.'); - Assert::invokeMethod($pagination, 'getPaginator'); + $pagination->render(); + } + + public function testEmptyContainerTag(): void + { + $pagination = OffsetPagination::widget(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Tag name cannot be empty.'); + $pagination->containerTag(''); + } + + public function testEmptyListTag(): void + { + $pagination = OffsetPagination::widget(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Tag name cannot be empty.'); + $pagination->listTag(''); + } + + public function testEmptyItemTag(): void + { + $pagination = OffsetPagination::widget(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Tag name cannot be empty.'); + $pagination->itemTag(''); + } + + /** + * @throws CircularReferenceException + * @throws InvalidConfigException + * @throws NotInstantiableException + * @throws NotFoundException + */ + public function testCustomAttributes(): void + { + $pagination = OffsetPagination::widget() + ->containerAttributes(['class' => 'container']) + ->linkAttributes(['class' => 'link']) + ->currentLinkClass('current') + ->disabledLinkClass('inactive') + ->maxNavLinkCount(5); + + $html = $this->createGridView($this->createTestData(), $pagination); + + $this->assertStringContainsString('class="container"', $html); + $this->assertStringContainsString('class="link inactive"', $html); + $this->assertStringContainsString('class="link current"', $html); + } + + /** + * @throws CircularReferenceException + * @throws InvalidConfigException + * @throws NotInstantiableException + * @throws NotFoundException + */ + public function testCustomLabels(): void + { + $pagination = OffsetPagination::widget() + ->labelFirst('First') + ->labelLast('Last') + ->labelPrevious('Prev') + ->labelNext('Next'); + + $html = $this->createGridView($this->createTestData(), $pagination); + + $this->assertStringContainsString('First', $html); + $this->assertStringContainsString('Last', $html); + $this->assertStringContainsString('Prev', $html); + $this->assertStringContainsString('Next', $html); + } + + /** + * @throws CircularReferenceException + * @throws InvalidConfigException + * @throws NotInstantiableException + * @throws NotFoundException + */ + public function testCustomTags(): void + { + $pagination = OffsetPagination::widget() + ->containerTag('div') + ->listTag('ul') + ->itemTag('li'); + + $html = $this->createGridView($this->createTestData(), $pagination); + + $this->assertStringContainsString('assertStringContainsString('assertStringContainsString('