diff --git a/tests/Column/Base/HeaderContextTest.php b/tests/Column/Base/HeaderContextTest.php new file mode 100644 index 000000000..d5dbaaa06 --- /dev/null +++ b/tests/Column/Base/HeaderContextTest.php @@ -0,0 +1,237 @@ +createHeaderContext(translator: $translator); + + $result = $headerContext->translate('test.message'); + + $this->assertSame('test.message', $result); + } + + public function testTranslateWithStringable(): void + { + $translator = Mock::translator('en'); + + $headerContext = $this->createHeaderContext(translator: $translator); + + $stringable = new class () { + public function __toString(): string + { + return 'Stringable Message'; + } + }; + + $result = $headerContext->translate($stringable); + + $this->assertSame('Stringable Message', $result); + } + + public function testPrepareSortableWithEmptyProperty(): void + { + $cell = new Cell(); + $headerContext = $this->createHeaderContext(); + + $result = $headerContext->prepareSortable($cell, 'nonexistent'); + + $this->assertSame($cell, $result[0]); + $this->assertNull($result[1]); + $this->assertSame('', $result[2]); + $this->assertSame('', $result[3]); + } + + public function testPrepareSortableWithNullSort(): void + { + $cell = new Cell(); + $headerContext = $this->createHeaderContext( + sort: null, + originalSort: null + ); + + $result = $headerContext->prepareSortable($cell, 'name'); + + $this->assertSame($cell, $result[0]); + $this->assertNull($result[1]); + $this->assertSame('', $result[2]); + $this->assertSame('', $result[3]); + } + + public function testPrepareSortableWithPropertyNotInConfig(): void + { + $sort = Sort::any(); + $cell = new Cell(); + $headerContext = $this->createHeaderContext( + sort: $sort, + originalSort: $sort, + orderProperties: ['name' => 'name'] + ); + + $result = $headerContext->prepareSortable($cell, 'age'); + + $this->assertSame($cell, $result[0]); + $this->assertNull($result[1]); + $this->assertSame('', $result[2]); + $this->assertSame('', $result[3]); + } + + public function testPrepareSortableWithNoOrder(): void + { + $sort = Sort::any(['name' => []]); + $cell = new Cell(); + $headerContext = $this->createHeaderContext( + sort: $sort, + originalSort: $sort, + orderProperties: ['name' => 'name'], + sortableHeaderClass: 'sortable', + sortableHeaderPrepend: '↕', + sortableHeaderAppend: '!' + ); + + $result = $headerContext->prepareSortable($cell, 'name'); + + $this->assertInstanceOf(Cell::class, $result[0]); + $this->assertStringContainsString('sortable', $result[0]->getAttributes()['class']); + $this->assertInstanceOf(A::class, $result[1]); + $this->assertSame('↕', $result[2]); + $this->assertSame('!', $result[3]); + } + + public function testPrepareSortableWithAscOrder(): void + { + $sort = Sort::any(['name' => []])->withOrder(['name' => 'asc']); + $cell = new Cell(); + $headerContext = $this->createHeaderContext( + sort: $sort, + originalSort: $sort, + orderProperties: ['name' => 'name'], + sortableHeaderAscClass: 'asc', + sortableHeaderAscPrepend: '↑', + sortableHeaderAscAppend: '!', + sortableLinkAscClass: 'link-asc' + ); + + $result = $headerContext->prepareSortable($cell, 'name'); + + $this->assertInstanceOf(Cell::class, $result[0]); + $this->assertStringContainsString('asc', $result[0]->getAttributes()['class']); + $this->assertInstanceOf(A::class, $result[1]); + $this->assertSame('↑', $result[2]); + $this->assertSame('!', $result[3]); + } + + public function testPrepareSortableWithDescOrder(): void + { + $sort = Sort::any(['name' => []])->withOrder(['name' => 'desc']); + $cell = new Cell(); + $headerContext = $this->createHeaderContext( + sort: $sort, + originalSort: $sort, + orderProperties: ['name' => 'name'], + sortableHeaderDescClass: 'desc', + sortableHeaderDescPrepend: '↓', + sortableHeaderDescAppend: '!', + sortableLinkDescClass: 'link-desc' + ); + + $result = $headerContext->prepareSortable($cell, 'name'); + + $this->assertInstanceOf(Cell::class, $result[0]); + $this->assertStringContainsString('desc', $result[0]->getAttributes()['class']); + $this->assertInstanceOf(A::class, $result[1]); + $this->assertSame('↓', $result[2]); + $this->assertSame('!', $result[3]); + } + + private function createHeaderContext( + ?Sort $sort = null, + ?Sort $originalSort = null, + array $orderProperties = ['name' => 'name'], + ?string $sortableHeaderClass = null, + string|Stringable $sortableHeaderPrepend = '', + string|Stringable $sortableHeaderAppend = '', + ?string $sortableHeaderAscClass = null, + string|Stringable $sortableHeaderAscPrepend = '', + string|Stringable $sortableHeaderAscAppend = '', + ?string $sortableHeaderDescClass = null, + string|Stringable $sortableHeaderDescPrepend = '', + string|Stringable $sortableHeaderDescAppend = '', + array $sortableLinkAttributes = [], + ?string $sortableLinkAscClass = null, + ?string $sortableLinkDescClass = null, + ?PageToken $pageToken = null, + ?int $pageSize = null, + bool $multiSort = false, + ?TranslatorInterface $translator = null + ): HeaderContext { + if ($sort === null) { + $sort = Sort::any(); + } + + if ($originalSort === null) { + $originalSort = Sort::any(); + } + + if ($translator === null) { + $translator = Mock::translator('en'); + } + + $urlConfig = new UrlConfig( + pageParameterName: 'page', + previousPageParameterName: 'prev', + pageSizeParameterName: 'per-page', + sortParameterName: 'sort' + ); + + $urlCreator = fn(): string => '#'; + + return new HeaderContext( + originalSort: $originalSort, + sort: $sort, + orderProperties: $orderProperties, + sortableHeaderClass: $sortableHeaderClass, + sortableHeaderPrepend: $sortableHeaderPrepend, + sortableHeaderAppend: $sortableHeaderAppend, + sortableHeaderAscClass: $sortableHeaderAscClass, + sortableHeaderAscPrepend: $sortableHeaderAscPrepend, + sortableHeaderAscAppend: $sortableHeaderAscAppend, + sortableHeaderDescClass: $sortableHeaderDescClass, + sortableHeaderDescPrepend: $sortableHeaderDescPrepend, + sortableHeaderDescAppend: $sortableHeaderDescAppend, + sortableLinkAttributes: $sortableLinkAttributes, + sortableLinkAscClass: $sortableLinkAscClass, + sortableLinkDescClass: $sortableLinkDescClass, + pageToken: $pageToken, + pageSize: $pageSize, + multiSort: $multiSort, + urlConfig: $urlConfig, + urlCreator: $urlCreator, + translator: $translator, + translationCategory: 'grid' + ); + } +} diff --git a/tests/Column/DataColumnRendererTest.php b/tests/Column/DataColumnRendererTest.php new file mode 100644 index 000000000..47709bcb0 --- /dev/null +++ b/tests/Column/DataColumnRendererTest.php @@ -0,0 +1,147 @@ +filterFactoryContainer = new Container(ContainerConfig::create()); + + $this->dataReader = new IterableDataReader([ + ['id' => 1, 'name' => 'John', 'age' => 20], + ['id' => 2, 'name' => 'Mary', 'age' => 21], + ]); + } + + public function testRenderColumn(): void + { + $this->expectNotToPerformAssertions(); + + $column = new DataColumn('test'); + $cell = new Cell(); + $translator = Mock::translator('en'); + + $context = new GlobalContext( + $this->dataReader, + [], + [], + $translator, + 'test' + ); + + $renderer = new DataColumnRenderer( + $this->filterFactoryContainer, + new Validator() + ); + + $renderer->renderColumn($column, $cell, $context); + } + + public function testRenderHeader(): void + { + $column = new DataColumn('test', 'Test Header'); + $cell = new Cell(); + $translator = Mock::translator('en'); + + $sort = Sort::any(); + + $context = new HeaderContext( + $sort, + $sort, + ['test' => 'test'], + 'sortable', + '', + '', + 'asc', + '', + '', + 'desc', + '', + '', + [], + 'asc-link', + 'desc-link', + null, + 10, + false, + new UrlConfig(), + null, + $translator, + 'test' + ); + + $renderer = new DataColumnRenderer( + $this->filterFactoryContainer, + new Validator() + ); + + $result = $renderer->renderHeader($column, $cell, $context); + + $this->assertNotEmpty($result->getContent()); + } + + public function testRenderBody(): void + { + $column = new DataColumn('name'); + $cell = new Cell(); + $data = ['id' => 1, 'name' => 'John Doe', 'age' => 20]; + + $context = new DataContext( + $this->dataReader, + $column, + $data, + 1, + 0 + ); + + $renderer = new DataColumnRenderer( + $this->filterFactoryContainer, + new Validator() + ); + + $result = $renderer->renderBody($column, $cell, $context); + + $content = $result->getContent(); + $this->assertNotEmpty($content); + $this->assertStringContainsString('John Doe', (string)$content[0]); + } + + public function testGetOrderProperties(): void + { + $column = new DataColumn('test'); + $renderer = new DataColumnRenderer( + $this->filterFactoryContainer, + new Validator() + ); + + $result = $renderer->getOrderProperties($column); + $this->assertEquals(['test' => 'test'], $result); + } +} diff --git a/tests/Filter/Factory/EqualsFilterFactoryTest.php b/tests/Filter/Factory/EqualsFilterFactoryTest.php new file mode 100644 index 000000000..8f6f3ddc1 --- /dev/null +++ b/tests/Filter/Factory/EqualsFilterFactoryTest.php @@ -0,0 +1,57 @@ +create('name', 'John'); + + $this->assertInstanceOf(Equals::class, $filter); + + $this->assertSame('name', $filter->getField()); + $this->assertSame('John', $filter->getValue()); + } + + public function testCreateWithEmptyValue(): void + { + $factory = new EqualsFilterFactory(); + $filter = $factory->create('name', ''); + + $this->assertNull($filter); + } + + public function testCreateWithZeroValue(): void + { + $factory = new EqualsFilterFactory(); + $filter = $factory->create('age', '0'); + + $this->assertNull($filter); + } + + public function testCreateWithNonEmptyNumericValue(): void + { + $factory = new EqualsFilterFactory(); + + /** @var Equals $filter */ + $filter = $factory->create('quantity', '42'); + + $this->assertInstanceOf(Equals::class, $filter); + + $this->assertSame('quantity', $filter->getField()); + $this->assertSame('42', $filter->getValue()); + } +} diff --git a/tests/Filter/Factory/LikeFilterFactoryTest.php b/tests/Filter/Factory/LikeFilterFactoryTest.php index 3bb936ca7..55e161f83 100644 --- a/tests/Filter/Factory/LikeFilterFactoryTest.php +++ b/tests/Filter/Factory/LikeFilterFactoryTest.php @@ -8,6 +8,9 @@ use PHPUnit\Framework\TestCase; use Yiisoft\Yii\DataView\Filter\Factory\LikeFilterFactory; +/** + * @covers \Yiisoft\Yii\DataView\Filter\Factory\LikeFilterFactory + */ final class LikeFilterFactoryTest extends TestCase { public function testBase(): void @@ -21,6 +24,22 @@ public function testBase(): void $this->assertNull($filter->isCaseSensitive()); } + public function testCreateWithEmptyValue(): void + { + $factory = new LikeFilterFactory(); + $filter = $factory->create('name', ''); + + $this->assertNull($filter); + } + + public function testCreateWithZeroValue(): void + { + $factory = new LikeFilterFactory(); + $filter = $factory->create('name', '0'); + + $this->assertNull($filter); + } + public static function dataCaseSensitive(): iterable { yield [null]; diff --git a/tests/Filter/Widget/DropdownFilterTest.php b/tests/Filter/Widget/DropdownFilterTest.php new file mode 100644 index 000000000..be531cdca --- /dev/null +++ b/tests/Filter/Widget/DropdownFilterTest.php @@ -0,0 +1,91 @@ +optionsData([ + 'active' => 'Active', + 'inactive' => 'Inactive', + ]); + $context = new Context('status', 'active', 'filter-form'); + + $html = $filter->renderFilter($context); + + $this->assertStringContainsString('name="status"', $html); + $this->assertStringContainsString('form="filter-form"', $html); + $this->assertStringContainsString('onChange="this.form.submit()"', $html); + $this->assertStringContainsString('value="active"', $html); + $this->assertStringContainsString('selected', $html); + } + + public function testRenderFilterWithoutValue(): void + { + $filter = new DropdownFilter(); + $context = new Context('status', null, 'filter-form'); + + $html = $filter->renderFilter($context); + + $this->assertStringContainsString('name="status"', $html); + $this->assertStringContainsString('form="filter-form"', $html); + $this->assertStringContainsString('onChange="this.form.submit()"', $html); + $this->assertStringNotContainsString('value="', $html); + } + + public function testOptionsData(): void + { + $filter = new DropdownFilter(); + $options = [ + 'active' => 'Active', + 'inactive' => 'Inactive', + ]; + + $filter = $filter->optionsData($options); + $context = new Context('status', null, 'filter-form'); + + $html = $filter->renderFilter($context); + + $this->assertStringContainsString('>Active<', $html); + $this->assertStringContainsString('>Inactive<', $html); + $this->assertStringContainsString('value="active"', $html); + $this->assertStringContainsString('value="inactive"', $html); + } + + public function testAddAttributes(): void + { + $filter = new DropdownFilter(); + $filter = $filter->addAttributes(['class' => 'custom-select', 'data-test' => 'value']); + + $context = new Context('status', null, 'filter-form'); + $html = $filter->renderFilter($context); + + $this->assertStringContainsString('class="custom-select"', $html); + $this->assertStringContainsString('data-test="value"', $html); + } + + public function testAttributes(): void + { + $filter = new DropdownFilter(); + $filter = $filter->attributes(['class' => 'new-select', 'required' => true]); + + $context = new Context('status', null, 'filter-form'); + $html = $filter->renderFilter($context); + + $this->assertStringContainsString('class="new-select"', $html); + $this->assertStringContainsString('required', $html); + } +} diff --git a/tests/Filter/Widget/TextInputFilterTest.php b/tests/Filter/Widget/TextInputFilterTest.php new file mode 100644 index 000000000..ccbbdf40f --- /dev/null +++ b/tests/Filter/Widget/TextInputFilterTest.php @@ -0,0 +1,85 @@ +renderFilter($context); + + $this->assertStringContainsString('name="username"', $result); + $this->assertStringContainsString('value="john"', $result); + $this->assertStringContainsString('form="filter-form"', $result); + $this->assertStringContainsString('type="text"', $result); + } + + public function testRenderWithNullValue(): void + { + $filter = new TextInputFilter(); + $context = new Context('username', null, 'filter-form'); + + $result = $filter->renderFilter($context); + + $this->assertStringContainsString('name="username"', $result); + $this->assertStringNotContainsString('value=', $result); + $this->assertStringContainsString('form="filter-form"', $result); + } + + public function testAddAttributes(): void + { + $filter = new TextInputFilter(); + $newFilter = $filter->addAttributes(['class' => 'form-control', 'placeholder' => 'Enter username']); + + $this->assertNotSame($filter, $newFilter); + + $context = new Context('username', 'john', 'filter-form'); + $result = $newFilter->renderFilter($context); + + $this->assertStringContainsString('class="form-control"', $result); + $this->assertStringContainsString('placeholder="Enter username"', $result); + } + + public function testAttributes(): void + { + $filter = new TextInputFilter(); + $filter = $filter->addAttributes(['data-test' => 'original']); + + $newFilter = $filter->attributes(['class' => 'form-control', 'id' => 'username-filter']); + + $this->assertNotSame($filter, $newFilter); + + $context = new Context('username', 'john', 'filter-form'); + $result = $newFilter->renderFilter($context); + + $this->assertStringContainsString('class="form-control"', $result); + $this->assertStringContainsString('id="username-filter"', $result); + $this->assertStringNotContainsString('data-test="original"', $result); + } + + public function testRender(): void + { + $context = new Context('username', 'john', 'filter-form'); + $result = (new TextInputFilter()) + ->withContext($context) + ->render(); + + $this->assertStringContainsString('name="username"', $result); + $this->assertStringContainsString('value="john"', $result); + $this->assertStringContainsString('form="filter-form"', $result); + } +} diff --git a/tests/PageSize/InputPageSizeTest.php b/tests/PageSize/InputPageSizeTest.php new file mode 100644 index 000000000..efd4bc8b9 --- /dev/null +++ b/tests/PageSize/InputPageSizeTest.php @@ -0,0 +1,86 @@ +withContext($context); + + $html = $widget->render(); + + $this->assertStringContainsString('value="10"', $html); + $this->assertStringContainsString('data-default-page-size="20"', $html); + $this->assertStringContainsString('data-url-pattern="/test?pagesize=YII-DATAVIEW-PAGE-SIZE-PLACEHOLDER"', $html); + $this->assertStringContainsString('data-default-url="/test"', $html); + $this->assertStringContainsString('onchange=', $html); + } + + public function testAddAttributes(): void + { + $context = new PageSizeContext( + currentValue: 10, + defaultValue: 20, + constraint: false, + urlPattern: '/test?pagesize=YII-DATAVIEW-PAGE-SIZE-PLACEHOLDER', + defaultUrl: '/test' + ); + + $widget = new InputPageSize(); + $widget = $widget->withContext($context); + $widget = $widget->addAttributes(['class' => 'form-control', 'id' => 'page-size-input']); + + $html = $widget->render(); + + $this->assertStringContainsString('class="form-control"', $html); + $this->assertStringContainsString('id="page-size-input"', $html); + } + + public function testAttributes(): void + { + $context = new PageSizeContext( + currentValue: 10, + defaultValue: 20, + constraint: false, + urlPattern: '/test?pagesize=YII-DATAVIEW-PAGE-SIZE-PLACEHOLDER', + defaultUrl: '/test' + ); + + $widget = new InputPageSize(); + $widget = $widget->withContext($context); + $widget = $widget->attributes(['class' => 'custom-input', 'data-test' => 'value']); + + $html = $widget->render(); + + $this->assertStringContainsString('class="custom-input"', $html); + $this->assertStringContainsString('data-test="value"', $html); + } + + public function testGetContextWithoutSettingContext(): void + { + $widget = new InputPageSize(); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Context is not set.'); + + // This will trigger the getContext() method internally + $widget->render(); + } +} diff --git a/tests/PageSize/SelectPageSizeTest.php b/tests/PageSize/SelectPageSizeTest.php new file mode 100644 index 000000000..7b2e74bfb --- /dev/null +++ b/tests/PageSize/SelectPageSizeTest.php @@ -0,0 +1,108 @@ +withContext($context); + + $html = $widget->render(); + + $this->assertStringContainsString('