From a690fa4387cedd474ccf2ae4a54e9fe3deee4110 Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Fri, 11 Dec 2020 22:48:01 +0200 Subject: [PATCH 01/15] Cleanup current code and tests. --- README.md | 2 +- composer.json | 7 +++ src/Enum.php | 75 +++++++++++++------------------- src/EnumFieldServiceProvider.php | 34 +++++++++++++++ tests/IntegerEnumTest.php | 9 ++-- tests/ModelTest.php | 8 ++-- tests/StringEnumTest.php | 3 +- tests/TestCase.php | 12 +++-- 8 files changed, 86 insertions(+), 64 deletions(-) create mode 100644 src/EnumFieldServiceProvider.php diff --git a/README.md b/README.md index 46f44d0..9e619ac 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ class Example extends Resource return [ // ... - Enum::make('User Type')->attachEnum(UserType::class), + Enum::make('User Type')->attach(UserType::class), // ... ]; diff --git a/composer.json b/composer.json index e849199..9848fe7 100644 --- a/composer.json +++ b/composer.json @@ -47,6 +47,13 @@ "SimpleSquid\\Nova\\Fields\\Enum\\Tests\\": "tests" } }, + "extra": { + "laravel": { + "providers": [ + "SimpleSquid\\Nova\\Fields\\Enum\\EnumFieldServiceProvider" + ] + } + }, "scripts": { "test": "vendor/bin/phpunit --colors=always" }, diff --git a/src/Enum.php b/src/Enum.php index b28f5a8..3c1ff9a 100644 --- a/src/Enum.php +++ b/src/Enum.php @@ -1,64 +1,49 @@ options($this->getEnumOptions($enumClass)) - ->rules('required', new EnumValue($enumClass, false)) - ->resolveUsing( - function ($enum) { - return $enum instanceof \BenSampo\Enum\Enum ? $enum->value : $enum; - } - ) - ->displayUsing( - function ($enum) { - return $enum instanceof \BenSampo\Enum\Enum ? $enum->description : $enum; - } - ); + parent::__construct($name, $attribute, $resolveCallback); + + $this->resolveUsing( + function ($value) { + return $value instanceof \BenSampo\Enum\Enum ? $value->value : $value; + } + ); + + $this->displayUsing( + function ($value) { + return $value instanceof \BenSampo\Enum\Enum ? $value->description : $value; + } + ); + + $this->fillUsing( + function (NovaRequest $request, $model, $attribute, $requestAttribute) { + if ($request->exists($requestAttribute)) { + $model->{$attribute} = $request[$requestAttribute]; + } + } + ); } - /** - * Hydrate the given attribute on the model based on the incoming request. - * - * @param NovaRequest $request - * @param string $requestAttribute - * @param object $model - * @param string $attribute - * - * @return void - */ - protected function fillAttributeFromRequest(NovaRequest $request, $requestAttribute, $model, $attribute) + public function attach($class) { - if ($request->exists($requestAttribute)) { - $model->{$attribute} = $request[$requestAttribute]; - } + return $this->options($class::asSelectArray()) + ->rules('required', new EnumValue($class, false)); } - protected function getEnumOptions(string $enumClass): array + /** + * @deprecated deprecated since version 2.3 + */ + public function attachEnum($class) { - // Since laravel-enum v2.2.0, the method has been named 'asSelectArray' - if (in_array(Arrayable::class, class_implements($enumClass))) { - return $enumClass::asSelectArray(); - } - - return $enumClass::toSelectArray(); + return $this->attach($class); } } diff --git a/src/EnumFieldServiceProvider.php b/src/EnumFieldServiceProvider.php new file mode 100644 index 0000000..32fa8f2 --- /dev/null +++ b/src/EnumFieldServiceProvider.php @@ -0,0 +1,34 @@ +field = Enum::make('Enum'); - $this->field->attachEnum(ExampleIntegerEnum::class); + $this->field->attach(ExampleIntegerEnum::class); } /** @test */ - public function field_starts_with_zero_config() + public function field_starts_with_no_options_and_rules() { $field = Enum::make('Enum'); - $this->assertEmpty($field->meta); + $this->assertArrayNotHasKey('options', $field->meta); $this->assertEmpty($field->rules); - $this->assertNull($field->resolveCallback); - $this->assertNull($field->displayCallback); } /** @test */ diff --git a/tests/ModelTest.php b/tests/ModelTest.php index 135aa4f..c1501c0 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -16,13 +16,15 @@ protected function setUp(): void { parent::setUp(); + $this->setUpDatabase($this->app); + $this->model = ExampleModel::create(['enum' => ExampleIntegerEnum::Moderator()]); } /** @test */ public function field_resolves_correct_value() { - $field = Enum::make('Enum')->attachEnum(ExampleIntegerEnum::class); + $field = Enum::make('Enum')->attach(ExampleIntegerEnum::class); $field->resolve($this->model); @@ -32,7 +34,7 @@ public function field_resolves_correct_value() /** @test */ public function field_displays_correct_description() { - $field = Enum::make('Enum')->attachEnum(ExampleIntegerEnum::class); + $field = Enum::make('Enum')->attach(ExampleIntegerEnum::class); $field->resolveForDisplay($this->model); @@ -42,7 +44,7 @@ public function field_displays_correct_description() /** @test */ public function field_fills_database_with_enum_value() { - $field = Enum::make('Enum')->attachEnum(ExampleIntegerEnum::class); + $field = Enum::make('Enum')->attach(ExampleIntegerEnum::class); $request = new NovaRequest(); $request->query->add(['enum' => ExampleIntegerEnum::Subscriber()]); diff --git a/tests/StringEnumTest.php b/tests/StringEnumTest.php index 25ab8d6..4171975 100644 --- a/tests/StringEnumTest.php +++ b/tests/StringEnumTest.php @@ -2,7 +2,6 @@ namespace SimpleSquid\Nova\Fields\Enum\Tests; -use PHPUnit\Framework\TestCase; use SimpleSquid\Nova\Fields\Enum\Enum; use SimpleSquid\Nova\Fields\Enum\Tests\Examples\ExampleStringEnum; @@ -17,7 +16,7 @@ protected function setUp(): void $this->field = Enum::make('Enum'); - $this->field->attachEnum(ExampleStringEnum::class); + $this->field->attach(ExampleStringEnum::class); } /** @test */ diff --git a/tests/TestCase.php b/tests/TestCase.php index 4187242..ed0dbe5 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,19 +4,17 @@ use Illuminate\Database\Schema\Blueprint; use Orchestra\Testbench\TestCase as Orchestra; +use SimpleSquid\Nova\Fields\Enum\EnumFieldServiceProvider; abstract class TestCase extends Orchestra { - protected function setUp(): void + protected function getPackageProviders($app) { - parent::setUp(); - - $this->setUpDatabase($this->app); + return [ + EnumFieldServiceProvider::class, + ]; } - /** - * @param \Illuminate\Foundation\Application $app - */ protected function setUpDatabase($app) { $this->artisan('migrate:fresh'); From 1ed82d024c295b550763bee3df55289c06bbdc91 Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Sat, 12 Dec 2020 00:36:35 +0200 Subject: [PATCH 02/15] Add select filter. --- README.md | 35 +++++++++++++++++++++++++++++++---- src/EnumFilter.php | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 src/EnumFilter.php diff --git a/README.md b/README.md index 9e619ac..7dbaec4 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ [![MIT License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) [![Total Downloads](https://img.shields.io/packagist/dt/simplesquid/nova-enum-field.svg?style=flat-square)](https://packagist.org/packages/simplesquid/nova-enum-field) -Laravel Nova field to add enums to resources. This field uses the [BenSampo/laravel-enum](https://github.com/BenSampo/laravel-enum) package, so make sure to check out the installation instructions there first. +Laravel Nova field to add enums to resources. This field uses +the [BenSampo/laravel-enum](https://github.com/BenSampo/laravel-enum) package, so make sure to check out the +installation instructions there first. ![Screenshot of the enum field](https://github.com/simplesquid/nova-enum-field/raw/master/docs/screenshot.png) @@ -19,11 +21,12 @@ composer require simplesquid/nova-enum-field ## Setup -This package requires that you use Attribute Casting in your models. From the docs at [BenSampo/laravel-enum](https://github.com/BenSampo/laravel-enum#attribute-casting), this can be done like so: +This package requires that you use Attribute Casting in your models. From the docs +at [BenSampo/laravel-enum](https://github.com/BenSampo/laravel-enum#attribute-casting), this can be done like so: ```php +use App\Enums\UserType; use BenSampo\Enum\Traits\CastsEnums; -use BenSampo\Enum\Tests\Enums\UserType; use Illuminate\Database\Eloquent\Model; class Example extends Model @@ -43,7 +46,7 @@ You can use the `Enum` field in your Nova resource like so: ```php namespace App\Nova; -use BenSampo\Enum\Tests\Enums\UserType; +use App\Enums\UserType; use SimpleSquid\Nova\Fields\Enum\Enum; class Example extends Resource @@ -63,6 +66,30 @@ class Example extends Resource } ``` +If you would like to use the provided Nova filter, you can include it as such: + +```php +namespace App\Nova; + +use App\Enums\UserType; +use SimpleSquid\Nova\Fields\Enum\EnumFilter; + +class Example extends Resource +{ + // ... + + public function filters(Request $request) + { + return [ + new EnumFilter('user_type', UserType::class), + + // Or with optional filter name: + // new EnumFilter('user_type', UserType::class, 'Type of user'), + ]; + } +} +``` + ## Testing ``` bash diff --git a/src/EnumFilter.php b/src/EnumFilter.php new file mode 100644 index 0000000..b444de8 --- /dev/null +++ b/src/EnumFilter.php @@ -0,0 +1,41 @@ +column = $column; + $this->class = $class; + + if (! is_null($name)) { + $this->name = $name; + } + } + + public function name() + { + return $this->name ?: Nova::humanize($this->column); + } + + public function apply(Request $request, $query, $value) + { + return $query->where($this->column, $value); + } + + public function options(Request $request) + { + return array_flip($this->class::asSelectArray()); + } +} From 2788983816787d475b006ded8467ac81528ace71 Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Fri, 22 Jan 2021 12:55:59 +0200 Subject: [PATCH 03/15] Add select filter tests and refactor older tests. --- composer.json | 2 + phpunit.xml.dist | 1 + ...ExampleIntegerEnum.php => IntegerEnum.php} | 2 +- .../{ExampleModel.php => IntegerModel.php} | 4 +- .../{ExampleStringEnum.php => StringEnum.php} | 2 +- tests/Examples/StringModel.php | 21 ++++++ tests/{ModelTest.php => FieldTest.php} | 24 +++--- tests/FilterTest.php | 23 ++++++ ...tegerEnumTest.php => IntegerFieldTest.php} | 20 ++--- tests/IntegerFilterTest.php | 73 +++++++++++++++++++ ...StringEnumTest.php => StringFieldTest.php} | 14 ++-- tests/StringFilterTest.php | 73 +++++++++++++++++++ tests/TestCase.php | 6 +- 13 files changed, 229 insertions(+), 36 deletions(-) rename tests/Examples/{ExampleIntegerEnum.php => IntegerEnum.php} (87%) rename tests/Examples/{ExampleModel.php => IntegerModel.php} (79%) rename tests/Examples/{ExampleStringEnum.php => StringEnum.php} (89%) create mode 100644 tests/Examples/StringModel.php rename tests/{ModelTest.php => FieldTest.php} (51%) create mode 100644 tests/FilterTest.php rename tests/{IntegerEnumTest.php => IntegerFieldTest.php} (70%) create mode 100644 tests/IntegerFilterTest.php rename tests/{StringEnumTest.php => StringFieldTest.php} (52%) create mode 100644 tests/StringFilterTest.php diff --git a/composer.json b/composer.json index 9848fe7..ced968a 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,8 @@ "laravel/nova": "^3.0" }, "require-dev": { + "joshgaber/novaunit": "^2.2", + "nunomaduro/collision": "^5.2", "orchestra/testbench": "^5.0|^6.0", "phpunit/phpunit": "^8.2|^9.0", "symfony/var-dumper": "^5.0" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1318ca3..78d3969 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,6 +10,7 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" + printerClass="NunoMaduro\Collision\Adapters\Phpunit\Printer" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"> diff --git a/tests/Examples/ExampleIntegerEnum.php b/tests/Examples/IntegerEnum.php similarity index 87% rename from tests/Examples/ExampleIntegerEnum.php rename to tests/Examples/IntegerEnum.php index beefdf1..4b25f67 100644 --- a/tests/Examples/ExampleIntegerEnum.php +++ b/tests/Examples/IntegerEnum.php @@ -9,7 +9,7 @@ * @method static Moderator() * @method static Subscriber() */ -class ExampleIntegerEnum extends Enum +class IntegerEnum extends Enum { const Administrator = 0; diff --git a/tests/Examples/ExampleModel.php b/tests/Examples/IntegerModel.php similarity index 79% rename from tests/Examples/ExampleModel.php rename to tests/Examples/IntegerModel.php index 5a7ac2d..cc9f928 100644 --- a/tests/Examples/ExampleModel.php +++ b/tests/Examples/IntegerModel.php @@ -5,7 +5,7 @@ use BenSampo\Enum\Traits\CastsEnums; use Illuminate\Database\Eloquent\Model; -class ExampleModel extends Model +class IntegerModel extends Model { use CastsEnums; @@ -16,6 +16,6 @@ class ExampleModel extends Model public $timestamps = false; protected $casts = [ - 'enum' => ExampleIntegerEnum::class, + 'enum' => IntegerEnum::class, ]; } diff --git a/tests/Examples/ExampleStringEnum.php b/tests/Examples/StringEnum.php similarity index 89% rename from tests/Examples/ExampleStringEnum.php rename to tests/Examples/StringEnum.php index f662e04..f540801 100644 --- a/tests/Examples/ExampleStringEnum.php +++ b/tests/Examples/StringEnum.php @@ -9,7 +9,7 @@ * @method static Moderator() * @method static Subscriber() */ -class ExampleStringEnum extends Enum +class StringEnum extends Enum { const Administrator = 'administrator'; diff --git a/tests/Examples/StringModel.php b/tests/Examples/StringModel.php new file mode 100644 index 0000000..2a4d524 --- /dev/null +++ b/tests/Examples/StringModel.php @@ -0,0 +1,21 @@ + StringEnum::class, + ]; +} diff --git a/tests/ModelTest.php b/tests/FieldTest.php similarity index 51% rename from tests/ModelTest.php rename to tests/FieldTest.php index c1501c0..04179ea 100644 --- a/tests/ModelTest.php +++ b/tests/FieldTest.php @@ -4,12 +4,12 @@ use Laravel\Nova\Http\Requests\NovaRequest; use SimpleSquid\Nova\Fields\Enum\Enum; -use SimpleSquid\Nova\Fields\Enum\Tests\Examples\ExampleIntegerEnum; -use SimpleSquid\Nova\Fields\Enum\Tests\Examples\ExampleModel; +use SimpleSquid\Nova\Fields\Enum\Tests\Examples\IntegerEnum; +use SimpleSquid\Nova\Fields\Enum\Tests\Examples\IntegerModel; -class ModelTest extends TestCase +class FieldTest extends TestCase { - /** @var \SimpleSquid\Nova\Fields\Enum\Tests\Examples\ExampleModel */ + /** @var \SimpleSquid\Nova\Fields\Enum\Tests\Examples\IntegerModel */ private $model; protected function setUp(): void @@ -18,13 +18,13 @@ protected function setUp(): void $this->setUpDatabase($this->app); - $this->model = ExampleModel::create(['enum' => ExampleIntegerEnum::Moderator()]); + $this->model = IntegerModel::create(['enum' => IntegerEnum::Moderator()]); } /** @test */ - public function field_resolves_correct_value() + public function it_resolves_correct_value() { - $field = Enum::make('Enum')->attach(ExampleIntegerEnum::class); + $field = Enum::make('Enum')->attach(IntegerEnum::class); $field->resolve($this->model); @@ -32,9 +32,9 @@ public function field_resolves_correct_value() } /** @test */ - public function field_displays_correct_description() + public function it_displays_correct_description() { - $field = Enum::make('Enum')->attach(ExampleIntegerEnum::class); + $field = Enum::make('Enum')->attach(IntegerEnum::class); $field->resolveForDisplay($this->model); @@ -42,12 +42,12 @@ public function field_displays_correct_description() } /** @test */ - public function field_fills_database_with_enum_value() + public function it_fills_database_with_enum_value() { - $field = Enum::make('Enum')->attach(ExampleIntegerEnum::class); + $field = Enum::make('Enum')->attach(IntegerEnum::class); $request = new NovaRequest(); - $request->query->add(['enum' => ExampleIntegerEnum::Subscriber()]); + $request->query->add(['enum' => IntegerEnum::Subscriber()]); $field->fill($request, $this->model); diff --git a/tests/FilterTest.php b/tests/FilterTest.php new file mode 100644 index 0000000..1761385 --- /dev/null +++ b/tests/FilterTest.php @@ -0,0 +1,23 @@ +filter = new MockFilter(new EnumFilter('enum', IntegerEnum::class)); + } + + /** @test */ + public function it_is_a_select_filter() + { + $this->filter->assertSelectFilter(); + } +} diff --git a/tests/IntegerEnumTest.php b/tests/IntegerFieldTest.php similarity index 70% rename from tests/IntegerEnumTest.php rename to tests/IntegerFieldTest.php index 2bd3549..50abded 100644 --- a/tests/IntegerEnumTest.php +++ b/tests/IntegerFieldTest.php @@ -4,9 +4,9 @@ use BenSampo\Enum\Rules\EnumValue; use SimpleSquid\Nova\Fields\Enum\Enum; -use SimpleSquid\Nova\Fields\Enum\Tests\Examples\ExampleIntegerEnum; +use SimpleSquid\Nova\Fields\Enum\Tests\Examples\IntegerEnum; -class IntegerEnumTest extends TestCase +class IntegerFieldTest extends TestCase { /** @var \SimpleSquid\Nova\Fields\Enum\Enum */ private $field; @@ -17,11 +17,11 @@ protected function setUp(): void $this->field = Enum::make('Enum'); - $this->field->attach(ExampleIntegerEnum::class); + $this->field->attach(IntegerEnum::class); } /** @test */ - public function field_starts_with_no_options_and_rules() + public function it_starts_with_no_options_and_rules() { $field = Enum::make('Enum'); @@ -30,7 +30,7 @@ public function field_starts_with_no_options_and_rules() } /** @test */ - public function an_enum_can_be_attached_to_the_field() + public function an_enum_can_be_attached_to_it() { $this->assertArrayHasKey('options', $this->field->meta); @@ -55,21 +55,21 @@ public function attaching_an_enum_adds_correct_rules() { $this->assertContains('required', $this->field->rules); - $this->assertContainsEquals(new EnumValue(ExampleIntegerEnum::class, false), $this->field->rules); + $this->assertContainsEquals(new EnumValue(IntegerEnum::class, false), $this->field->rules); } /** @test */ - public function field_resolves_correct_value() + public function it_resolves_correct_value() { - $this->field->resolve(['enum' => ExampleIntegerEnum::Moderator()]); + $this->field->resolve(['enum' => IntegerEnum::Moderator()]); $this->assertSame(1, $this->field->value); } /** @test */ - public function field_displays_correct_description() + public function it_displays_correct_description() { - $this->field->resolveForDisplay(['enum' => ExampleIntegerEnum::Moderator()]); + $this->field->resolveForDisplay(['enum' => IntegerEnum::Moderator()]); $this->assertSame('Moderator', $this->field->value); } diff --git a/tests/IntegerFilterTest.php b/tests/IntegerFilterTest.php new file mode 100644 index 0000000..5c53f33 --- /dev/null +++ b/tests/IntegerFilterTest.php @@ -0,0 +1,73 @@ +setUpDatabase($this->app); + + IntegerModel::create(['enum' => IntegerEnum::Moderator()]); + IntegerModel::create(['enum' => IntegerEnum::Moderator()]); + + IntegerModel::create(['enum' => IntegerEnum::Administrator()]); + + $this->filter = new MockFilter(new EnumFilter('enum', IntegerEnum::class)); + } + + /** @test */ + public function it_contains_all_the_filter_values() + { + $this->filter->assertHasOption(IntegerEnum::Subscriber()); + + $this->filter->assertHasOption(IntegerEnum::Moderator()); + + $this->filter->assertHasOption(IntegerEnum::Administrator()); + } + + /** @test */ + public function it_returns_the_correct_number_of_results() + { + $response = $this->filter->apply(IntegerModel::class, IntegerEnum::Subscriber()); + + $response->assertCount(0); + + $response = $this->filter->apply(IntegerModel::class, IntegerEnum::Moderator()); + + $response->assertCount(2); + + $response = $this->filter->apply(IntegerModel::class, IntegerEnum::Administrator()); + + $response->assertCount(1); + } + + /** @test */ + public function it_returns_the_correct_results() + { + $response1 = $this->filter->apply(IntegerModel::class, IntegerEnum::Moderator()); + + $response2 = $this->filter->apply(IntegerModel::class, IntegerEnum::Administrator()); + + IntegerModel::whereEnum(IntegerEnum::Moderator())->each(function ($model) use ($response1, $response2) { + $response1->assertContains($model); + + $response2->assertMissing($model); + }); + + IntegerModel::whereEnum(IntegerEnum::Administrator())->each(function ($model) use ($response1, $response2) { + $response1->assertMissing($model); + + $response2->assertContains($model); + }); + } +} diff --git a/tests/StringEnumTest.php b/tests/StringFieldTest.php similarity index 52% rename from tests/StringEnumTest.php rename to tests/StringFieldTest.php index 4171975..0dd42c1 100644 --- a/tests/StringEnumTest.php +++ b/tests/StringFieldTest.php @@ -3,9 +3,9 @@ namespace SimpleSquid\Nova\Fields\Enum\Tests; use SimpleSquid\Nova\Fields\Enum\Enum; -use SimpleSquid\Nova\Fields\Enum\Tests\Examples\ExampleStringEnum; +use SimpleSquid\Nova\Fields\Enum\Tests\Examples\StringEnum; -class StringEnumTest extends TestCase +class StringFieldTest extends TestCase { /** @var \SimpleSquid\Nova\Fields\Enum\Enum */ private $field; @@ -16,21 +16,21 @@ protected function setUp(): void $this->field = Enum::make('Enum'); - $this->field->attach(ExampleStringEnum::class); + $this->field->attach(StringEnum::class); } /** @test */ - public function field_resolves_correct_value() + public function it_resolves_correct_value() { - $this->field->resolve(['enum' => ExampleStringEnum::Moderator()]); + $this->field->resolve(['enum' => StringEnum::Moderator()]); $this->assertSame('moderator', $this->field->value); } /** @test */ - public function field_displays_correct_description() + public function it_displays_correct_description() { - $this->field->resolveForDisplay(['enum' => ExampleStringEnum::Moderator()]); + $this->field->resolveForDisplay(['enum' => StringEnum::Moderator()]); $this->assertSame('Moderator', $this->field->value); } diff --git a/tests/StringFilterTest.php b/tests/StringFilterTest.php new file mode 100644 index 0000000..1760d15 --- /dev/null +++ b/tests/StringFilterTest.php @@ -0,0 +1,73 @@ +setUpDatabase($this->app, 'string'); + + StringModel::create(['enum' => StringEnum::Moderator()]); + StringModel::create(['enum' => StringEnum::Moderator()]); + + StringModel::create(['enum' => StringEnum::Administrator()]); + + $this->filter = new MockFilter(new EnumFilter('enum', StringEnum::class)); + } + + /** @test */ + public function it_contains_all_the_filter_values() + { + $this->filter->assertHasOption(StringEnum::Subscriber()); + + $this->filter->assertHasOption(StringEnum::Moderator()); + + $this->filter->assertHasOption(StringEnum::Administrator()); + } + + /** @test */ + public function it_returns_the_correct_number_of_results() + { + $response = $this->filter->apply(StringModel::class, StringEnum::Subscriber()); + + $response->assertCount(0); + + $response = $this->filter->apply(StringModel::class, StringEnum::Moderator()); + + $response->assertCount(2); + + $response = $this->filter->apply(StringModel::class, StringEnum::Administrator()); + + $response->assertCount(1); + } + + /** @test */ + public function it_returns_the_correct_results() + { + $response1 = $this->filter->apply(StringModel::class, StringEnum::Moderator()); + + $response2 = $this->filter->apply(StringModel::class, StringEnum::Administrator()); + + StringModel::whereEnum(StringEnum::Moderator())->each(function ($model) use ($response1, $response2) { + $response1->assertContains($model); + + $response2->assertMissing($model); + }); + + StringModel::whereEnum(StringEnum::Administrator())->each(function ($model) use ($response1, $response2) { + $response1->assertMissing($model); + + $response2->assertContains($model); + }); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index ed0dbe5..cb15825 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -15,13 +15,13 @@ protected function getPackageProviders($app) ]; } - protected function setUpDatabase($app) + protected function setUpDatabase($app, $type = 'integer') { $this->artisan('migrate:fresh'); - $app['db']->connection()->getSchemaBuilder()->create('example_models', function (Blueprint $table) { + $app['db']->connection()->getSchemaBuilder()->create('example_models', function (Blueprint $table) use ($type) { $table->increments('id'); - $table->integer('enum'); + $table->$type('enum'); }); } } From 110efa7b727a3f810fcdd1e158b76f068d593a84 Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Fri, 22 Jan 2021 13:48:17 +0200 Subject: [PATCH 04/15] Drop support for Laravel 7. --- .github/workflows/run-tests.yml | 6 ++---- composer.json | 10 +++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f064804..ca52647 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,12 +9,10 @@ jobs: fail-fast: true matrix: os: [ ubuntu-latest ] - php: [ 7.3, 7.4 ] - laravel: [ 7.*, 8.* ] + php: [ 7.3, 7.4, 8.0 ] + laravel: [ 8.* ] stability: [ prefer-lowest, prefer-stable ] include: - - laravel: 7.* - testbench: 5.* - laravel: 8.* testbench: 6.* diff --git a/composer.json b/composer.json index ced968a..2dc86a9 100644 --- a/composer.json +++ b/composer.json @@ -27,16 +27,16 @@ ], "require": { "php": "^7.3|^8.0", - "bensampo/laravel-enum": "^2.0|^3.0", - "cakephp/chronos": "^1.2.3|^2.0", - "illuminate/support": "^7.0|^8.0", + "bensampo/laravel-enum": "^3.0", + "illuminate/support": "^8.0", "laravel/nova": "^3.0" }, "require-dev": { "joshgaber/novaunit": "^2.2", + "mockery/mockery": "^1.3.3", "nunomaduro/collision": "^5.2", - "orchestra/testbench": "^5.0|^6.0", - "phpunit/phpunit": "^8.2|^9.0", + "orchestra/testbench": "^6.0", + "phpunit/phpunit": "^9.3.3", "symfony/var-dumper": "^5.0" }, "autoload": { From 62a83470b438f3de621af189d62f860c44d45aaa Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Fri, 22 Jan 2021 20:38:37 +0200 Subject: [PATCH 05/15] Add Boolean Filter and tests. --- src/EnumBooleanFilter.php | 49 ++++++++++++ src/EnumFilter.php | 2 - tests/BooleanFilterTest.php | 24 ++++++ tests/IntegerBooleanFilterTest.php | 120 +++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 src/EnumBooleanFilter.php create mode 100644 tests/BooleanFilterTest.php create mode 100644 tests/IntegerBooleanFilterTest.php diff --git a/src/EnumBooleanFilter.php b/src/EnumBooleanFilter.php new file mode 100644 index 0000000..789b743 --- /dev/null +++ b/src/EnumBooleanFilter.php @@ -0,0 +1,49 @@ +column = $column; + $this->class = $class; + + if (!is_null($name)) { + $this->name = $name; + } + } + + public function name() + { + return $this->name ?: Nova::humanize($this->column); + } + + public function apply(Request $request, $query, $value) + { + $enums = array_keys(array_filter($value)); + + return empty($enums) ? $query : $query->where( + function ($query) use ($enums) { + $query->where($this->column, array_shift($enums)); + + foreach ($enums as $enum) { + $query->orWhere($this->column, $enum); + } + } + ); + } + + public function options(Request $request) + { + return array_flip($this->class::asSelectArray()); + } +} diff --git a/src/EnumFilter.php b/src/EnumFilter.php index b444de8..823e8f5 100644 --- a/src/EnumFilter.php +++ b/src/EnumFilter.php @@ -8,8 +8,6 @@ class EnumFilter extends Filter { - public $component = 'select-filter'; - protected $column; protected $class; diff --git a/tests/BooleanFilterTest.php b/tests/BooleanFilterTest.php new file mode 100644 index 0000000..d4665c0 --- /dev/null +++ b/tests/BooleanFilterTest.php @@ -0,0 +1,24 @@ +filter = new MockFilter(new EnumBooleanFilter('enum', IntegerEnum::class)); + } + + /** @test */ + public function it_is_a_boolean_filter() + { + $this->filter->assertBooleanFilter(); + } +} diff --git a/tests/IntegerBooleanFilterTest.php b/tests/IntegerBooleanFilterTest.php new file mode 100644 index 0000000..ec7239c --- /dev/null +++ b/tests/IntegerBooleanFilterTest.php @@ -0,0 +1,120 @@ +setUpDatabase($this->app); + + IntegerModel::create(['enum' => IntegerEnum::Moderator()]); + IntegerModel::create(['enum' => IntegerEnum::Moderator()]); + + IntegerModel::create(['enum' => IntegerEnum::Administrator()]); + + $this->filter = new MockFilter(new EnumBooleanFilter('enum', IntegerEnum::class)); + } + + /** @test */ + public function it_contains_all_the_filter_values() + { + $this->filter->assertHasOption(IntegerEnum::Subscriber()); + + $this->filter->assertHasOption(IntegerEnum::Moderator()); + + $this->filter->assertHasOption(IntegerEnum::Administrator()); + } + + public function optionsProvider(): array + { + return [ + [true, false, false, 0], + [true, true, false, 2], + [true, false, true, 1], + [true, true, true, 3], + [false, false, false, 3], + [false, true, false, 2], + [false, false, true, 1], + [false, true, true, 3], + ]; + } + + /** + * @dataProvider optionsProvider + * @test + */ + public function it_returns_the_correct_number_of_results( + bool $subscriber, + bool $moderator, + bool $administrator, + int $count + ) { + $value = [ + IntegerEnum::Subscriber => $subscriber, + IntegerEnum::Moderator => $moderator, + IntegerEnum::Administrator => $administrator, + ]; + + $response = $this->filter->apply(IntegerModel::class, $value); + + $response->assertCount($count); + } + + /** + * @dataProvider optionsProvider + * @test + */ + public function it_returns_the_correct_results( + bool $subscriber, + bool $moderator, + bool $administrator, + int $count + ) { + $value = [ + IntegerEnum::Subscriber => $subscriber, + IntegerEnum::Moderator => $moderator, + IntegerEnum::Administrator => $administrator, + ]; + + $response = $this->filter->apply(IntegerModel::class, $value); + + // None selected should show all models + if (count(array_unique($value)) === 1 && $subscriber === false) { + $subscriber = $moderator = $administrator = true; + } + + IntegerModel::whereEnum(IntegerEnum::Subscriber())->each(function ($model) use ($response, $subscriber) { + if ($subscriber) { + return $response->assertContains($model); + } + + $response->assertMissing($model); + }); + + IntegerModel::whereEnum(IntegerEnum::Moderator())->each(function ($model) use ($response, $moderator) { + if ($moderator) { + return $response->assertContains($model); + } + + $response->assertMissing($model); + }); + + IntegerModel::whereEnum(IntegerEnum::Administrator())->each(function ($model) use ($response, $administrator) { + if ($administrator) { + return $response->assertContains($model); + } + + $response->assertMissing($model); + }); + } +} From bd31cd8255a9d1065d19cb7fc5b9c46f94773257 Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Fri, 22 Jan 2021 18:39:19 +0000 Subject: [PATCH 06/15] Fix styling. --- src/EnumBooleanFilter.php | 2 +- tests/BooleanFilterTest.php | 1 - tests/IntegerBooleanFilterTest.php | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/EnumBooleanFilter.php b/src/EnumBooleanFilter.php index 789b743..6575c7c 100644 --- a/src/EnumBooleanFilter.php +++ b/src/EnumBooleanFilter.php @@ -17,7 +17,7 @@ public function __construct($column, $class, $name = null) $this->column = $column; $this->class = $class; - if (!is_null($name)) { + if (! is_null($name)) { $this->name = $name; } } diff --git a/tests/BooleanFilterTest.php b/tests/BooleanFilterTest.php index d4665c0..bf494da 100644 --- a/tests/BooleanFilterTest.php +++ b/tests/BooleanFilterTest.php @@ -4,7 +4,6 @@ use JoshGaber\NovaUnit\Filters\MockFilter; use SimpleSquid\Nova\Fields\Enum\EnumBooleanFilter; -use SimpleSquid\Nova\Fields\Enum\EnumFilter; use SimpleSquid\Nova\Fields\Enum\Tests\Examples\IntegerEnum; class BooleanFilterTest extends TestCase diff --git a/tests/IntegerBooleanFilterTest.php b/tests/IntegerBooleanFilterTest.php index ec7239c..af21645 100644 --- a/tests/IntegerBooleanFilterTest.php +++ b/tests/IntegerBooleanFilterTest.php @@ -60,8 +60,8 @@ public function it_returns_the_correct_number_of_results( int $count ) { $value = [ - IntegerEnum::Subscriber => $subscriber, - IntegerEnum::Moderator => $moderator, + IntegerEnum::Subscriber => $subscriber, + IntegerEnum::Moderator => $moderator, IntegerEnum::Administrator => $administrator, ]; @@ -81,8 +81,8 @@ public function it_returns_the_correct_results( int $count ) { $value = [ - IntegerEnum::Subscriber => $subscriber, - IntegerEnum::Moderator => $moderator, + IntegerEnum::Subscriber => $subscriber, + IntegerEnum::Moderator => $moderator, IntegerEnum::Administrator => $administrator, ]; From ae591b5a329350d7f2154c47902fddb8869a7df4 Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Fri, 22 Jan 2021 21:36:49 +0200 Subject: [PATCH 07/15] Update README to include the new Boolean filter. --- README.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7dbaec4..f73f431 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,9 @@ class Example extends Resource } ``` -If you would like to use the provided Nova filter, you can include it as such: +### Filters + +If you would like to use the provided Nova Select filter, you can include it as such: ```php namespace App\Nova; @@ -90,6 +92,30 @@ class Example extends Resource } ``` +Alternatively, you may wish to use the provided Nova Boolean filter: + +```php +namespace App\Nova; + +use App\Enums\UserType; +use SimpleSquid\Nova\Fields\Enum\EnumBooleanFilter; + +class Example extends Resource +{ + // ... + + public function filters(Request $request) + { + return [ + new EnumBooleanFilter('user_type', UserType::class), + + // Or with optional filter name: + // new EnumBooleanFilter('user_type', UserType::class, 'Type of user'), + ]; + } +} +``` + ## Testing ``` bash From 07e47d8945aabf2ec9810bcffd5fef5bc15c60e3 Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Mon, 25 Jan 2021 14:59:25 +0200 Subject: [PATCH 08/15] Add new Flagged Enum field and relevant tests. --- src/EnumBooleanFilter.php | 59 +++++++++-- src/EnumFilter.php | 24 +++-- src/FlaggedEnum.php | 44 ++++++++ tests/Examples/FlaggedEnum.php | 18 ++++ tests/Examples/FlaggedModel.php | 21 ++++ tests/FlaggedBooleanFilterTest.php | 162 +++++++++++++++++++++++++++++ tests/FlaggedFieldTest.php | 109 +++++++++++++++++++ tests/FlaggedFilterTest.php | 79 ++++++++++++++ 8 files changed, 502 insertions(+), 14 deletions(-) create mode 100644 src/FlaggedEnum.php create mode 100644 tests/Examples/FlaggedEnum.php create mode 100644 tests/Examples/FlaggedModel.php create mode 100644 tests/FlaggedBooleanFilterTest.php create mode 100644 tests/FlaggedFieldTest.php create mode 100644 tests/FlaggedFilterTest.php diff --git a/src/EnumBooleanFilter.php b/src/EnumBooleanFilter.php index 6575c7c..ade68c3 100644 --- a/src/EnumBooleanFilter.php +++ b/src/EnumBooleanFilter.php @@ -2,38 +2,77 @@ namespace SimpleSquid\Nova\Fields\Enum; +use BenSampo\Enum\Traits\QueriesFlaggedEnums; use Illuminate\Http\Request; +use Illuminate\Support\Arr; use Laravel\Nova\Filters\BooleanFilter; use Laravel\Nova\Nova; class EnumBooleanFilter extends BooleanFilter { + use QueriesFlaggedEnums; + protected $column; protected $class; - public function __construct($column, $class, $name = null) + protected $flagged; + + protected $scope = 'any'; + + public function __construct($column, $class) { $this->column = $column; $this->class = $class; - if (! is_null($name)) { - $this->name = $name; - } + $this->flagged = is_subclass_of($this->class, \BenSampo\Enum\FlaggedEnum::class); + } + + public function filterAnyFlags() + { + $this->scope = 'any'; + + return $this; + } + + public function filterAllFlags() + { + $this->scope = 'all'; + + return $this; } - public function name() + public function name($name = null) { - return $this->name ?: Nova::humanize($this->column); + if (is_null($name)) { + return $this->name ?: Nova::humanize($this->column); + } + + $this->name = $name; + return $this; } public function apply(Request $request, $query, $value) { $enums = array_keys(array_filter($value)); - return empty($enums) ? $query : $query->where( + if (empty($enums)) { + return $query; + } + + if ($this->flagged && $this->scope === 'all') { + return $this->scopeHasAllFlags($query, $this->column, $enums); + } + + return $query->where( function ($query) use ($enums) { - $query->where($this->column, array_shift($enums)); + if ($this->flagged) { + $query = $this->scopeHasAnyFlags($query, $this->column, $enums); + + $enums = in_array($this->class::None, $enums) ? [$this->class::None] : []; + } else { + $query->where($this->column, array_shift($enums)); + } foreach ($enums as $enum) { $query->orWhere($this->column, $enum); @@ -44,6 +83,10 @@ function ($query) use ($enums) { public function options(Request $request) { + if ($this->flagged && $this->scope === 'all') { + return array_flip(Arr::except($this->class::asSelectArray(), $this->class::None)); + } + return array_flip($this->class::asSelectArray()); } } diff --git a/src/EnumFilter.php b/src/EnumFilter.php index 823e8f5..9bba576 100644 --- a/src/EnumFilter.php +++ b/src/EnumFilter.php @@ -2,33 +2,45 @@ namespace SimpleSquid\Nova\Fields\Enum; +use BenSampo\Enum\Traits\QueriesFlaggedEnums; use Illuminate\Http\Request; use Laravel\Nova\Filters\Filter; use Laravel\Nova\Nova; class EnumFilter extends Filter { + use QueriesFlaggedEnums; + protected $column; protected $class; - public function __construct($column, $class, $name = null) + protected $flagged; + + public function __construct($column, $class) { $this->column = $column; $this->class = $class; - if (! is_null($name)) { - $this->name = $name; - } + $this->flagged = is_subclass_of($this->class, \BenSampo\Enum\FlaggedEnum::class); } - public function name() + public function name($name = null) { - return $this->name ?: Nova::humanize($this->column); + if (is_null($name)) { + return $this->name ?: Nova::humanize($this->column); + } + + $this->name = $name; + return $this; } public function apply(Request $request, $query, $value) { + if ($this->flagged && $value != $this->class::None) { + return $this->scopeHasFlag($query, $this->column, $value); + } + return $query->where($this->column, $value); } diff --git a/src/FlaggedEnum.php b/src/FlaggedEnum.php new file mode 100644 index 0000000..563aa1c --- /dev/null +++ b/src/FlaggedEnum.php @@ -0,0 +1,44 @@ +noValueText('None'); + } + + public function attach($class) + { + $this->resolveUsing( + function ($value) use ($class) { + if (!$value instanceof \BenSampo\Enum\FlaggedEnum) { + return $value; + } + + return collect($value->getValues())->mapWithKeys(function ($flag) use ($value) { + return [$flag => $value->hasFlag($flag)]; + })->except($class::None)->all(); + } + ); + + $this->fillUsing( + function (NovaRequest $request, $model, $attribute, $requestAttribute) use ($class) { + if ($request->exists($requestAttribute)) { + $value = json_decode($request[$requestAttribute], true); + + $model->{$attribute} = $class::flags(array_keys(array_filter($value))); + } + } + ); + + return $this->options(Arr::except($class::asSelectArray(), $class::None)); + } +} diff --git a/tests/Examples/FlaggedEnum.php b/tests/Examples/FlaggedEnum.php new file mode 100644 index 0000000..79c191f --- /dev/null +++ b/tests/Examples/FlaggedEnum.php @@ -0,0 +1,18 @@ + FlaggedEnum::class, + ]; +} diff --git a/tests/FlaggedBooleanFilterTest.php b/tests/FlaggedBooleanFilterTest.php new file mode 100644 index 0000000..9f7ba4c --- /dev/null +++ b/tests/FlaggedBooleanFilterTest.php @@ -0,0 +1,162 @@ + [0], + FlaggedEnum::ReadComments => [1, 2], + FlaggedEnum::WriteComments => [2], + FlaggedEnum::EditComments => [], + ]; + + protected function setUp(): void + { + parent::setUp(); + $this->setUpDatabase($this->app); + + $this->models[0] = FlaggedModel::create(['enum' => FlaggedEnum::None]); + $this->models[1] = FlaggedModel::create(['enum' => FlaggedEnum::ReadComments]); + $this->models[2] = FlaggedModel::create([ + 'enum' => array_sum([ + FlaggedEnum::ReadComments, + FlaggedEnum::WriteComments + ]) + ]); + + $this->filter = new EnumBooleanFilter('enum', FlaggedEnum::class); + $this->mockFilter = new MockFilter($this->filter); + } + + private function getOptions(array $keys, array $options = [[]]): array + { + if (empty($keys)) { + return $options; + } + + $current = array_shift($keys); + $newOptions = []; + + foreach ($options as $option) { + $newOptions[] = $option + [$current => true]; + $newOptions[] = $option + [$current => false]; + } + + return $this->getOptions($keys, $newOptions); + } + + /** @test */ + public function it_contains_all_the_filter_values() + { + foreach (array_keys($this->results) as $enum) { + $this->mockFilter->assertHasOption($enum); + } + } + + /** @test */ + public function it_can_filter_by_all_selected_options() + { + $this->filter->filterAllFlags(); + + foreach (array_keys($this->results) as $enum) { + if ($enum === FlaggedEnum::None) { + continue; + } + + $this->mockFilter->assertHasOption($enum); + } + } + + /** @test */ + public function it_returns_the_correct_number_of_results_when_filtering_by_any_flag() + { + foreach ($options = $this->getOptions(FlaggedEnum::getValues()) as $option) { + $response = $this->mockFilter->apply(IntegerModel::class, $option); + + // None selected should show all models + if (count(array_filter($option)) === 0) { + $response->assertCount(3); + } else { + $response->assertCount(count(array_unique(array_merge(...array_intersect_key($this->results, array_filter($option)))))); + } + } + } + + /** @test */ + public function it_returns_the_correct_results_when_filtering_by_any_flag() + { + foreach ($options = $this->getOptions(FlaggedEnum::getValues()) as $option) { + $response = $this->mockFilter->apply(IntegerModel::class, $option); + + // None selected should show all models + if (count(array_filter($option)) === 0) { + $models = array_keys($this->models); + } else { + $models = array_unique(array_merge(...array_intersect_key($this->results, array_filter($option)))); + } + + foreach ($models as $contain) { + $response->assertContains($this->models[$contain]); + } + + foreach (array_diff(array_keys($this->models), $models) as $missing) { + $response->assertMissing($this->models[$missing]); + } + } + } + + /** @test */ + public function it_returns_the_correct_number_of_results_when_filtering_by_all_flags() + { + $this->filter->filterAllFlags(); + + foreach ($options = $this->getOptions(array_diff(FlaggedEnum::getValues(), [FlaggedEnum::None])) as $option) { + $response = $this->mockFilter->apply(IntegerModel::class, $option); + + // None selected should show all models + if (count(array_filter($option)) === 0) { + $response->assertCount(3); + } else { + $response->assertCount(count(array_intersect(array_keys($this->models), ...array_intersect_key($this->results, array_filter($option))))); + } + } + } + + /** @test */ + public function it_returns_the_correct_results_when_filtering_by_all_flags() + { + $this->filter->filterAllFlags(); + + foreach ($options = $this->getOptions(array_diff(FlaggedEnum::getValues(), [FlaggedEnum::None])) as $option) { + $response = $this->mockFilter->apply(IntegerModel::class, $option); + + // None selected should show all models + if (count(array_filter($option)) === 0) { + $models = array_keys($this->models); + } else { + $models = array_intersect(array_keys($this->models), ...array_intersect_key($this->results, array_filter($option))); + } + + foreach ($models as $contain) { + $response->assertContains($this->models[$contain]); + } + + foreach (array_diff(array_keys($this->models), $models) as $missing) { + $response->assertMissing($this->models[$missing]); + } + } + } +} diff --git a/tests/FlaggedFieldTest.php b/tests/FlaggedFieldTest.php new file mode 100644 index 0000000..129119d --- /dev/null +++ b/tests/FlaggedFieldTest.php @@ -0,0 +1,109 @@ + true, + FlaggedEnum::WriteComments => true, + FlaggedEnum::EditComments => false, + ]; + + protected function setUp(): void + { + parent::setUp(); + + app()->register(NovaServiceProvider::class); + + $this->setUpDatabase($this->app); + + $this->field = FlaggedEnumField::make('Enum')->attach(FlaggedEnum::class); + + $this->model = FlaggedModel::create(['enum' => FlaggedEnum::None]); + } + + /** @test */ + public function it_starts_with_no_options() + { + $field = FlaggedEnumField::make('Enum'); + + $this->assertEmpty($field->options); + } + + /** @test */ + public function it_allows_an_enum_to_be_attached() + { + $this->assertNotEmpty($this->field->options); + } + + /** @test */ + public function it_has_no_value_text() + { + $this->assertSame('None', $this->field->noValueText); + } + + /** @test */ + public function it_displays_enum_options() + { + $this->assertCount(3, $this->field->options); + + foreach (array_keys($this->values) as $enum) { + $this->assertContains([ + 'label' => FlaggedEnum::getDescription($enum), + 'name' => $enum + ], $this->field->options); + } + } + + /** @test */ + public function it_resolves_enum_values() + { + $this->field->resolve($this->model); + + $this->assertCount(3, $this->field->value); + + foreach (array_keys($this->values) as $enum) { + $this->assertEquals(false, $this->field->value[$enum]); + } + + $this->model->enum = array_keys(array_filter($this->values)); + + $this->field->resolve($this->model); + + $this->assertCount(3, $this->field->value); + + foreach ($this->values as $enum => $value) { + $this->assertEquals($value, $this->field->value[$enum]); + } + } + + /** @test */ + public function it_fills_database_with_flagged_enum_value() + { + $request = new NovaRequest(); + $request->query->add(['enum' => json_encode($this->values)]); + + $this->field->fill($request, $this->model); + + $this->assertDatabaseHas('example_models', ['enum' => FlaggedEnum::None]); + + $this->model->save(); + + $this->assertDatabaseHas('example_models', [ + 'enum' => array_sum(array_keys(array_filter($this->values))) + ]); + + $this->assertDatabaseMissing('example_models', ['enum' => FlaggedEnum::None]); + } +} diff --git a/tests/FlaggedFilterTest.php b/tests/FlaggedFilterTest.php new file mode 100644 index 0000000..ab78d67 --- /dev/null +++ b/tests/FlaggedFilterTest.php @@ -0,0 +1,79 @@ + [0], + FlaggedEnum::ReadComments => [1, 2], + FlaggedEnum::WriteComments => [2], + FlaggedEnum::EditComments => [], + ]; + + protected function setUp(): void + { + parent::setUp(); + + app()->register(NovaServiceProvider::class); + + $this->setUpDatabase($this->app); + + $this->models[0] = FlaggedModel::create(['enum' => FlaggedEnum::None]); + + $this->models[1] = FlaggedModel::create(['enum' => FlaggedEnum::ReadComments]); + + $this->models[2] = FlaggedModel::create([ + 'enum' => array_sum([ + FlaggedEnum::ReadComments, + FlaggedEnum::WriteComments + ]) + ]); + + $this->filter = new MockFilter(new EnumFilter('enum', FlaggedEnum::class)); + } + + /** @test */ + public function it_contains_all_the_filter_values() + { + foreach (array_keys($this->results) as $enum) { + $this->filter->assertHasOption($enum); + } + } + + /** @test */ + public function it_returns_the_correct_number_of_results() + { + foreach ($this->results as $enum => $models) { + $response = $this->filter->apply(FlaggedModel::class, $enum); + + $response->assertCount(count($models)); + } + } + + /** @test */ + public function it_returns_the_correct_results() + { + foreach ($this->results as $enum => $models) { + $response = $this->filter->apply(FlaggedModel::class, $enum); + + foreach ($models as $contain) { + $response->assertContains($this->models[$contain]); + } + + foreach (array_diff([0, 1, 2], $models) as $missing) { + $response->assertMissing($this->models[$missing]); + } + } + } +} From 2f5eaff473e8272039ed1807819982a6e0f2538b Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Mon, 25 Jan 2021 16:05:02 +0200 Subject: [PATCH 09/15] Refactor and simplify tests. --- phpunit.xml.dist | 3 +- tests/BooleanFilterTest.php | 23 ---- tests/Examples/FlaggedEnum.php | 8 +- tests/FieldTest.php | 59 --------- tests/Fields/FieldTest.php | 59 +++++++++ tests/{ => Fields}/FlaggedFieldTest.php | 18 +-- tests/Fields/IntegerFieldTest.php | 60 +++++++++ tests/Fields/StringFieldTest.php | 60 +++++++++ tests/FilterTest.php | 23 ---- tests/Filters/BooleanFilterTest.php | 42 ++++++ tests/Filters/FilterTest.php | 42 ++++++ .../FlaggedBooleanFilterTest.php | 62 +++------ tests/{ => Filters}/FlaggedFilterTest.php | 36 ++---- tests/Filters/IntegerBooleanFilterTest.php | 89 +++++++++++++ tests/Filters/IntegerFilterTest.php | 65 ++++++++++ tests/Filters/StringBooleanFilterTest.php | 89 +++++++++++++ tests/Filters/StringFilterTest.php | 65 ++++++++++ tests/IntegerBooleanFilterTest.php | 120 ------------------ tests/IntegerFieldTest.php | 76 ----------- tests/IntegerFilterTest.php | 73 ----------- tests/StringFieldTest.php | 37 ------ tests/StringFilterTest.php | 73 ----------- tests/TestCase.php | 8 ++ 23 files changed, 631 insertions(+), 559 deletions(-) delete mode 100644 tests/BooleanFilterTest.php delete mode 100644 tests/FieldTest.php create mode 100644 tests/Fields/FieldTest.php rename tests/{ => Fields}/FlaggedFieldTest.php (84%) create mode 100644 tests/Fields/IntegerFieldTest.php create mode 100644 tests/Fields/StringFieldTest.php delete mode 100644 tests/FilterTest.php create mode 100644 tests/Filters/BooleanFilterTest.php create mode 100644 tests/Filters/FilterTest.php rename tests/{ => Filters}/FlaggedBooleanFilterTest.php (72%) rename tests/{ => Filters}/FlaggedFilterTest.php (71%) create mode 100644 tests/Filters/IntegerBooleanFilterTest.php create mode 100644 tests/Filters/IntegerFilterTest.php create mode 100644 tests/Filters/StringBooleanFilterTest.php create mode 100644 tests/Filters/StringFilterTest.php delete mode 100644 tests/IntegerBooleanFilterTest.php delete mode 100644 tests/IntegerFieldTest.php delete mode 100644 tests/IntegerFilterTest.php delete mode 100644 tests/StringFieldTest.php delete mode 100644 tests/StringFilterTest.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 78d3969..ba6889a 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -19,7 +19,8 @@ - tests + tests/Fields + tests/Filters diff --git a/tests/BooleanFilterTest.php b/tests/BooleanFilterTest.php deleted file mode 100644 index bf494da..0000000 --- a/tests/BooleanFilterTest.php +++ /dev/null @@ -1,23 +0,0 @@ -filter = new MockFilter(new EnumBooleanFilter('enum', IntegerEnum::class)); - } - - /** @test */ - public function it_is_a_boolean_filter() - { - $this->filter->assertBooleanFilter(); - } -} diff --git a/tests/Examples/FlaggedEnum.php b/tests/Examples/FlaggedEnum.php index 79c191f..8bec39e 100644 --- a/tests/Examples/FlaggedEnum.php +++ b/tests/Examples/FlaggedEnum.php @@ -12,7 +12,9 @@ */ final class FlaggedEnum extends BaseFlaggedEnum { - const ReadComments = 1 << 0; - const WriteComments = 1 << 1; - const EditComments = 1 << 2; + const ReadComments = 1 << 0; + + const WriteComments = 1 << 1; + + const EditComments = 1 << 2; } diff --git a/tests/FieldTest.php b/tests/FieldTest.php deleted file mode 100644 index 04179ea..0000000 --- a/tests/FieldTest.php +++ /dev/null @@ -1,59 +0,0 @@ -setUpDatabase($this->app); - - $this->model = IntegerModel::create(['enum' => IntegerEnum::Moderator()]); - } - - /** @test */ - public function it_resolves_correct_value() - { - $field = Enum::make('Enum')->attach(IntegerEnum::class); - - $field->resolve($this->model); - - $this->assertSame(1, $field->value); - } - - /** @test */ - public function it_displays_correct_description() - { - $field = Enum::make('Enum')->attach(IntegerEnum::class); - - $field->resolveForDisplay($this->model); - - $this->assertSame('Moderator', $field->value); - } - - /** @test */ - public function it_fills_database_with_enum_value() - { - $field = Enum::make('Enum')->attach(IntegerEnum::class); - - $request = new NovaRequest(); - $request->query->add(['enum' => IntegerEnum::Subscriber()]); - - $field->fill($request, $this->model); - - $this->model->save(); - - $this->assertDatabaseHas('example_models', ['enum' => 2]); - $this->assertDatabaseMissing('example_models', ['enum' => 1]); - } -} diff --git a/tests/Fields/FieldTest.php b/tests/Fields/FieldTest.php new file mode 100644 index 0000000..6f6c5a9 --- /dev/null +++ b/tests/Fields/FieldTest.php @@ -0,0 +1,59 @@ +setUpDatabase($this->app); + + $this->field = Enum::make('Enum')->attach(IntegerEnum::class); + } + + /** @test */ + public function it_starts_with_no_options_and_rules() + { + $field = Enum::make('Enum'); + + $this->assertArrayNotHasKey('options', $field->meta); + + $this->assertEmpty($field->rules); + } + + /** @test */ + public function it_allows_an_enum_to_be_attached() + { + $this->assertArrayHasKey('options', $this->field->meta); + } + + /** @test */ + public function it_adds_correct_rules() + { + $this->assertContains('required', $this->field->rules); + + $this->assertContainsEquals(new EnumValue(IntegerEnum::class, false), $this->field->rules); + } + + /** @test */ + public function it_displays_enum_options() + { + $this->assertCount(count(IntegerEnum::getValues()), $this->field->meta['options']); + + foreach (IntegerEnum::getValues() as $enum) { + $this->assertContains([ + 'label' => IntegerEnum::getDescription($enum), + 'value' => $enum + ], $this->field->meta['options']); + } + } +} diff --git a/tests/FlaggedFieldTest.php b/tests/Fields/FlaggedFieldTest.php similarity index 84% rename from tests/FlaggedFieldTest.php rename to tests/Fields/FlaggedFieldTest.php index 129119d..329e385 100644 --- a/tests/FlaggedFieldTest.php +++ b/tests/Fields/FlaggedFieldTest.php @@ -1,12 +1,12 @@ register(NovaServiceProvider::class); - $this->setUpDatabase($this->app); $this->field = FlaggedEnumField::make('Enum')->attach(FlaggedEnum::class); @@ -56,9 +54,13 @@ public function it_has_no_value_text() /** @test */ public function it_displays_enum_options() { - $this->assertCount(3, $this->field->options); + $this->assertCount(count(FlaggedEnum::getValues()) - 1, $this->field->options); + + foreach (FlaggedEnum::getValues() as $enum) { + if ($enum === FlaggedEnum::None) { + continue; + } - foreach (array_keys($this->values) as $enum) { $this->assertContains([ 'label' => FlaggedEnum::getDescription($enum), 'name' => $enum @@ -71,7 +73,7 @@ public function it_resolves_enum_values() { $this->field->resolve($this->model); - $this->assertCount(3, $this->field->value); + $this->assertCount(count(FlaggedEnum::getValues()) - 1, $this->field->value); foreach (array_keys($this->values) as $enum) { $this->assertEquals(false, $this->field->value[$enum]); @@ -81,7 +83,7 @@ public function it_resolves_enum_values() $this->field->resolve($this->model); - $this->assertCount(3, $this->field->value); + $this->assertCount(count(FlaggedEnum::getValues()) - 1, $this->field->value); foreach ($this->values as $enum => $value) { $this->assertEquals($value, $this->field->value[$enum]); diff --git a/tests/Fields/IntegerFieldTest.php b/tests/Fields/IntegerFieldTest.php new file mode 100644 index 0000000..78010fb --- /dev/null +++ b/tests/Fields/IntegerFieldTest.php @@ -0,0 +1,60 @@ +setUpDatabase($this->app); + + $this->field = Enum::make('Enum')->attach(IntegerEnum::class); + + $this->model = IntegerModel::create(['enum' => IntegerEnum::Moderator]); + } + + /** @test */ + public function it_resolves_enum_value() + { + $this->field->resolve($this->model); + + $this->assertSame(IntegerEnum::Moderator, $this->field->value); + } + + /** @test */ + public function it_displays_enum_description() + { + $this->field->resolveForDisplay($this->model); + + $this->assertSame(IntegerEnum::Moderator()->description, $this->field->value); + } + + /** @test */ + public function it_fills_database_with_enum_value() + { + $request = new NovaRequest(); + $request->query->add(['enum' => IntegerEnum::Subscriber]); + + $this->field->fill($request, $this->model); + + $this->assertDatabaseHas('example_models', ['enum' => IntegerEnum::Moderator]); + + $this->model->save(); + + $this->assertDatabaseHas('example_models', ['enum' => IntegerEnum::Subscriber]); + + $this->assertDatabaseMissing('example_models', ['enum' => IntegerEnum::Moderator]); + } +} diff --git a/tests/Fields/StringFieldTest.php b/tests/Fields/StringFieldTest.php new file mode 100644 index 0000000..4ade608 --- /dev/null +++ b/tests/Fields/StringFieldTest.php @@ -0,0 +1,60 @@ +setUpDatabase($this->app, 'string'); + + $this->field = Enum::make('Enum')->attach(StringEnum::class); + + $this->model = StringModel::create(['enum' => StringEnum::Moderator]); + } + + /** @test */ + public function it_resolves_enum_value() + { + $this->field->resolve($this->model); + + $this->assertSame(StringEnum::Moderator, $this->field->value); + } + + /** @test */ + public function it_displays_enum_description() + { + $this->field->resolveForDisplay($this->model); + + $this->assertSame(StringEnum::Moderator()->description, $this->field->value); + } + + /** @test */ + public function it_fills_database_with_enum_value() + { + $request = new NovaRequest(); + $request->query->add(['enum' => StringEnum::Subscriber]); + + $this->field->fill($request, $this->model); + + $this->assertDatabaseHas('example_models', ['enum' => StringEnum::Moderator]); + + $this->model->save(); + + $this->assertDatabaseHas('example_models', ['enum' => StringEnum::Subscriber]); + + $this->assertDatabaseMissing('example_models', ['enum' => StringEnum::Moderator]); + } +} diff --git a/tests/FilterTest.php b/tests/FilterTest.php deleted file mode 100644 index 1761385..0000000 --- a/tests/FilterTest.php +++ /dev/null @@ -1,23 +0,0 @@ -filter = new MockFilter(new EnumFilter('enum', IntegerEnum::class)); - } - - /** @test */ - public function it_is_a_select_filter() - { - $this->filter->assertSelectFilter(); - } -} diff --git a/tests/Filters/BooleanFilterTest.php b/tests/Filters/BooleanFilterTest.php new file mode 100644 index 0000000..3bcc989 --- /dev/null +++ b/tests/Filters/BooleanFilterTest.php @@ -0,0 +1,42 @@ +filter = new EnumBooleanFilter('enum', IntegerEnum::class); + + $this->mockFilter = new MockFilter($this->filter); + } + + /** @test */ + public function it_is_a_boolean_filter() + { + $this->mockFilter->assertBooleanFilter(); + } + + /** @test */ + public function it_has_a_default_name() + { + $this->assertEquals('Enum', $this->filter->name()); + } + + /** @test */ + public function it_can_have_a_different_name() + { + $this->assertInstanceOf(EnumBooleanFilter::class, $this->filter->name('Different name')); + + $this->assertEquals('Different name', $this->filter->name()); + } +} diff --git a/tests/Filters/FilterTest.php b/tests/Filters/FilterTest.php new file mode 100644 index 0000000..1d16ad9 --- /dev/null +++ b/tests/Filters/FilterTest.php @@ -0,0 +1,42 @@ +filter = new EnumFilter('enum', IntegerEnum::class); + + $this->mockFilter = new MockFilter($this->filter); + } + + /** @test */ + public function it_is_a_select_filter() + { + $this->mockFilter->assertSelectFilter(); + } + + /** @test */ + public function it_has_a_default_name() + { + $this->assertEquals('Enum', $this->filter->name()); + } + + /** @test */ + public function it_can_have_a_different_name() + { + $this->assertInstanceOf(EnumFilter::class, $this->filter->name('Different name')); + + $this->assertEquals('Different name', $this->filter->name()); + } +} diff --git a/tests/FlaggedBooleanFilterTest.php b/tests/Filters/FlaggedBooleanFilterTest.php similarity index 72% rename from tests/FlaggedBooleanFilterTest.php rename to tests/Filters/FlaggedBooleanFilterTest.php index 9f7ba4c..1ccee84 100644 --- a/tests/FlaggedBooleanFilterTest.php +++ b/tests/Filters/FlaggedBooleanFilterTest.php @@ -1,12 +1,13 @@ [0], - FlaggedEnum::ReadComments => [1, 2], - FlaggedEnum::WriteComments => [2], - FlaggedEnum::EditComments => [], - ]; + private $results = []; protected function setUp(): void { parent::setUp(); + $this->setUpDatabase($this->app); + $this->filter = new EnumBooleanFilter('enum', FlaggedEnum::class); + + $this->mockFilter = new MockFilter($this->filter); + $this->models[0] = FlaggedModel::create(['enum' => FlaggedEnum::None]); + $this->models[1] = FlaggedModel::create(['enum' => FlaggedEnum::ReadComments]); + $this->models[2] = FlaggedModel::create([ 'enum' => array_sum([ FlaggedEnum::ReadComments, @@ -37,8 +40,12 @@ protected function setUp(): void ]) ]); - $this->filter = new EnumBooleanFilter('enum', FlaggedEnum::class); - $this->mockFilter = new MockFilter($this->filter); + $this->results = [ + FlaggedEnum::None => [0], + FlaggedEnum::ReadComments => [1, 2], + FlaggedEnum::WriteComments => [2], + FlaggedEnum::EditComments => [], + ]; } private function getOptions(array $keys, array $options = [[]]): array @@ -73,6 +80,7 @@ public function it_can_filter_by_all_selected_options() foreach (array_keys($this->results) as $enum) { if ($enum === FlaggedEnum::None) { + $this->mockFilter->assertOptionMissing($enum); continue; } @@ -80,21 +88,6 @@ public function it_can_filter_by_all_selected_options() } } - /** @test */ - public function it_returns_the_correct_number_of_results_when_filtering_by_any_flag() - { - foreach ($options = $this->getOptions(FlaggedEnum::getValues()) as $option) { - $response = $this->mockFilter->apply(IntegerModel::class, $option); - - // None selected should show all models - if (count(array_filter($option)) === 0) { - $response->assertCount(3); - } else { - $response->assertCount(count(array_unique(array_merge(...array_intersect_key($this->results, array_filter($option)))))); - } - } - } - /** @test */ public function it_returns_the_correct_results_when_filtering_by_any_flag() { @@ -108,6 +101,8 @@ public function it_returns_the_correct_results_when_filtering_by_any_flag() $models = array_unique(array_merge(...array_intersect_key($this->results, array_filter($option)))); } + $response->assertCount(count($models)); + foreach ($models as $contain) { $response->assertContains($this->models[$contain]); } @@ -118,23 +113,6 @@ public function it_returns_the_correct_results_when_filtering_by_any_flag() } } - /** @test */ - public function it_returns_the_correct_number_of_results_when_filtering_by_all_flags() - { - $this->filter->filterAllFlags(); - - foreach ($options = $this->getOptions(array_diff(FlaggedEnum::getValues(), [FlaggedEnum::None])) as $option) { - $response = $this->mockFilter->apply(IntegerModel::class, $option); - - // None selected should show all models - if (count(array_filter($option)) === 0) { - $response->assertCount(3); - } else { - $response->assertCount(count(array_intersect(array_keys($this->models), ...array_intersect_key($this->results, array_filter($option))))); - } - } - } - /** @test */ public function it_returns_the_correct_results_when_filtering_by_all_flags() { @@ -150,6 +128,8 @@ public function it_returns_the_correct_results_when_filtering_by_all_flags() $models = array_intersect(array_keys($this->models), ...array_intersect_key($this->results, array_filter($option))); } + $response->assertCount(count($models)); + foreach ($models as $contain) { $response->assertContains($this->models[$contain]); } diff --git a/tests/FlaggedFilterTest.php b/tests/Filters/FlaggedFilterTest.php similarity index 71% rename from tests/FlaggedFilterTest.php rename to tests/Filters/FlaggedFilterTest.php index ab78d67..ee9b707 100644 --- a/tests/FlaggedFilterTest.php +++ b/tests/Filters/FlaggedFilterTest.php @@ -1,12 +1,12 @@ [0], - FlaggedEnum::ReadComments => [1, 2], - FlaggedEnum::WriteComments => [2], - FlaggedEnum::EditComments => [], - ]; + private $results = []; protected function setUp(): void { parent::setUp(); - app()->register(NovaServiceProvider::class); - $this->setUpDatabase($this->app); + $this->filter = new MockFilter(new EnumFilter('enum', FlaggedEnum::class)); + $this->models[0] = FlaggedModel::create(['enum' => FlaggedEnum::None]); $this->models[1] = FlaggedModel::create(['enum' => FlaggedEnum::ReadComments]); @@ -40,38 +35,35 @@ protected function setUp(): void ]) ]); - $this->filter = new MockFilter(new EnumFilter('enum', FlaggedEnum::class)); + $this->results = [ + FlaggedEnum::None => [0], + FlaggedEnum::ReadComments => [1, 2], + FlaggedEnum::WriteComments => [2], + FlaggedEnum::EditComments => [], + ]; } /** @test */ public function it_contains_all_the_filter_values() { - foreach (array_keys($this->results) as $enum) { + foreach (FlaggedEnum::getValues() as $enum) { $this->filter->assertHasOption($enum); } } /** @test */ - public function it_returns_the_correct_number_of_results() + public function it_returns_the_correct_results() { foreach ($this->results as $enum => $models) { $response = $this->filter->apply(FlaggedModel::class, $enum); $response->assertCount(count($models)); - } - } - - /** @test */ - public function it_returns_the_correct_results() - { - foreach ($this->results as $enum => $models) { - $response = $this->filter->apply(FlaggedModel::class, $enum); foreach ($models as $contain) { $response->assertContains($this->models[$contain]); } - foreach (array_diff([0, 1, 2], $models) as $missing) { + foreach (array_diff(array_keys($this->models), $models) as $missing) { $response->assertMissing($this->models[$missing]); } } diff --git a/tests/Filters/IntegerBooleanFilterTest.php b/tests/Filters/IntegerBooleanFilterTest.php new file mode 100644 index 0000000..16e27f0 --- /dev/null +++ b/tests/Filters/IntegerBooleanFilterTest.php @@ -0,0 +1,89 @@ +setUpDatabase($this->app); + + $this->filter = new MockFilter(new EnumBooleanFilter('enum', IntegerEnum::class)); + + $this->models[0] = IntegerModel::create(['enum' => IntegerEnum::Moderator]); + + $this->models[1] = IntegerModel::create(['enum' => IntegerEnum::Moderator]); + + $this->models[2] = IntegerModel::create(['enum' => IntegerEnum::Administrator]); + + $this->results = [ + IntegerEnum::Moderator => [0, 1], + IntegerEnum::Administrator => [2], + IntegerEnum::Subscriber => [], + ]; + } + + private function getOptions(array $keys, array $options = [[]]): array + { + if (empty($keys)) { + return $options; + } + + $current = array_shift($keys); + $newOptions = []; + + foreach ($options as $option) { + $newOptions[] = $option + [$current => true]; + $newOptions[] = $option + [$current => false]; + } + + return $this->getOptions($keys, $newOptions); + } + + /** @test */ + public function it_contains_all_the_filter_values() + { + foreach (IntegerEnum::getValues() as $enum) { + $this->filter->assertHasOption($enum); + } + } + + /** @test */ + public function it_returns_the_correct_results() + { + foreach ($options = $this->getOptions(IntegerEnum::getValues()) as $option) { + $response = $this->filter->apply(IntegerModel::class, $option); + + // None selected should show all models + if (count(array_filter($option)) === 0) { + $models = array_keys($this->models); + } else { + $models = array_unique(array_merge(...array_intersect_key($this->results, array_filter($option)))); + } + + $response->assertCount(count($models)); + + foreach ($models as $contain) { + $response->assertContains($this->models[$contain]); + } + + foreach (array_diff(array_keys($this->models), $models) as $missing) { + $response->assertMissing($this->models[$missing]); + } + } + } +} diff --git a/tests/Filters/IntegerFilterTest.php b/tests/Filters/IntegerFilterTest.php new file mode 100644 index 0000000..0646279 --- /dev/null +++ b/tests/Filters/IntegerFilterTest.php @@ -0,0 +1,65 @@ +setUpDatabase($this->app); + + $this->filter = new MockFilter(new EnumFilter('enum', IntegerEnum::class)); + + $this->models[0] = IntegerModel::create(['enum' => IntegerEnum::Moderator]); + + $this->models[1] = IntegerModel::create(['enum' => IntegerEnum::Moderator]); + + $this->models[2] = IntegerModel::create(['enum' => IntegerEnum::Administrator]); + + $this->results = [ + IntegerEnum::Moderator => [0, 1], + IntegerEnum::Administrator => [2], + IntegerEnum::Subscriber => [], + ]; + } + + /** @test */ + public function it_contains_all_the_filter_values() + { + foreach (IntegerEnum::getValues() as $enum) { + $this->filter->assertHasOption($enum); + } + } + + /** @test */ + public function it_returns_the_correct_results() + { + foreach ($this->results as $enum => $models) { + $response = $this->filter->apply(IntegerModel::class, $enum); + + $response->assertCount(count($models)); + + foreach ($models as $contain) { + $response->assertContains($this->models[$contain]); + } + + foreach (array_diff(array_keys($this->models), $models) as $missing) { + $response->assertMissing($this->models[$missing]); + } + } + } +} diff --git a/tests/Filters/StringBooleanFilterTest.php b/tests/Filters/StringBooleanFilterTest.php new file mode 100644 index 0000000..0d8e7ae --- /dev/null +++ b/tests/Filters/StringBooleanFilterTest.php @@ -0,0 +1,89 @@ +setUpDatabase($this->app, 'string'); + + $this->filter = new MockFilter(new EnumBooleanFilter('enum', StringEnum::class)); + + $this->models[0] = StringModel::create(['enum' => StringEnum::Moderator]); + + $this->models[1] = StringModel::create(['enum' => StringEnum::Moderator]); + + $this->models[2] = StringModel::create(['enum' => StringEnum::Administrator]); + + $this->results = [ + StringEnum::Moderator => [0, 1], + StringEnum::Administrator => [2], + StringEnum::Subscriber => [], + ]; + } + + private function getOptions(array $keys, array $options = [[]]): array + { + if (empty($keys)) { + return $options; + } + + $current = array_shift($keys); + $newOptions = []; + + foreach ($options as $option) { + $newOptions[] = $option + [$current => true]; + $newOptions[] = $option + [$current => false]; + } + + return $this->getOptions($keys, $newOptions); + } + + /** @test */ + public function it_contains_all_the_filter_values() + { + foreach (StringEnum::getValues() as $enum) { + $this->filter->assertHasOption($enum); + } + } + + /** @test */ + public function it_returns_the_correct_results() + { + foreach ($options = $this->getOptions(StringEnum::getValues()) as $option) { + $response = $this->filter->apply(StringModel::class, $option); + + // None selected should show all models + if (count(array_filter($option)) === 0) { + $models = array_keys($this->models); + } else { + $models = array_unique(array_merge(...array_values(array_intersect_key($this->results, array_filter($option))))); + } + + $response->assertCount(count($models)); + + foreach ($models as $contain) { + $response->assertContains($this->models[$contain]); + } + + foreach (array_diff(array_keys($this->models), $models) as $missing) { + $response->assertMissing($this->models[$missing]); + } + } + } +} diff --git a/tests/Filters/StringFilterTest.php b/tests/Filters/StringFilterTest.php new file mode 100644 index 0000000..37d1de4 --- /dev/null +++ b/tests/Filters/StringFilterTest.php @@ -0,0 +1,65 @@ +setUpDatabase($this->app, 'string'); + + $this->filter = new MockFilter(new EnumFilter('enum', StringEnum::class)); + + $this->models[0] = StringModel::create(['enum' => StringEnum::Moderator]); + + $this->models[1] = StringModel::create(['enum' => StringEnum::Moderator]); + + $this->models[2] = StringModel::create(['enum' => StringEnum::Administrator]); + + $this->results = [ + StringEnum::Moderator => [0, 1], + StringEnum::Administrator => [2], + StringEnum::Subscriber => [], + ]; + } + + /** @test */ + public function it_contains_all_the_filter_values() + { + foreach (StringEnum::getValues() as $enum) { + $this->filter->assertHasOption($enum); + } + } + + /** @test */ + public function it_returns_the_correct_results() + { + foreach ($this->results as $enum => $models) { + $response = $this->filter->apply(StringModel::class, $enum); + + $response->assertCount(count($models)); + + foreach ($models as $contain) { + $response->assertContains($this->models[$contain]); + } + + foreach (array_diff(array_keys($this->models), $models) as $missing) { + $response->assertMissing($this->models[$missing]); + } + } + } +} diff --git a/tests/IntegerBooleanFilterTest.php b/tests/IntegerBooleanFilterTest.php deleted file mode 100644 index af21645..0000000 --- a/tests/IntegerBooleanFilterTest.php +++ /dev/null @@ -1,120 +0,0 @@ -setUpDatabase($this->app); - - IntegerModel::create(['enum' => IntegerEnum::Moderator()]); - IntegerModel::create(['enum' => IntegerEnum::Moderator()]); - - IntegerModel::create(['enum' => IntegerEnum::Administrator()]); - - $this->filter = new MockFilter(new EnumBooleanFilter('enum', IntegerEnum::class)); - } - - /** @test */ - public function it_contains_all_the_filter_values() - { - $this->filter->assertHasOption(IntegerEnum::Subscriber()); - - $this->filter->assertHasOption(IntegerEnum::Moderator()); - - $this->filter->assertHasOption(IntegerEnum::Administrator()); - } - - public function optionsProvider(): array - { - return [ - [true, false, false, 0], - [true, true, false, 2], - [true, false, true, 1], - [true, true, true, 3], - [false, false, false, 3], - [false, true, false, 2], - [false, false, true, 1], - [false, true, true, 3], - ]; - } - - /** - * @dataProvider optionsProvider - * @test - */ - public function it_returns_the_correct_number_of_results( - bool $subscriber, - bool $moderator, - bool $administrator, - int $count - ) { - $value = [ - IntegerEnum::Subscriber => $subscriber, - IntegerEnum::Moderator => $moderator, - IntegerEnum::Administrator => $administrator, - ]; - - $response = $this->filter->apply(IntegerModel::class, $value); - - $response->assertCount($count); - } - - /** - * @dataProvider optionsProvider - * @test - */ - public function it_returns_the_correct_results( - bool $subscriber, - bool $moderator, - bool $administrator, - int $count - ) { - $value = [ - IntegerEnum::Subscriber => $subscriber, - IntegerEnum::Moderator => $moderator, - IntegerEnum::Administrator => $administrator, - ]; - - $response = $this->filter->apply(IntegerModel::class, $value); - - // None selected should show all models - if (count(array_unique($value)) === 1 && $subscriber === false) { - $subscriber = $moderator = $administrator = true; - } - - IntegerModel::whereEnum(IntegerEnum::Subscriber())->each(function ($model) use ($response, $subscriber) { - if ($subscriber) { - return $response->assertContains($model); - } - - $response->assertMissing($model); - }); - - IntegerModel::whereEnum(IntegerEnum::Moderator())->each(function ($model) use ($response, $moderator) { - if ($moderator) { - return $response->assertContains($model); - } - - $response->assertMissing($model); - }); - - IntegerModel::whereEnum(IntegerEnum::Administrator())->each(function ($model) use ($response, $administrator) { - if ($administrator) { - return $response->assertContains($model); - } - - $response->assertMissing($model); - }); - } -} diff --git a/tests/IntegerFieldTest.php b/tests/IntegerFieldTest.php deleted file mode 100644 index 50abded..0000000 --- a/tests/IntegerFieldTest.php +++ /dev/null @@ -1,76 +0,0 @@ -field = Enum::make('Enum'); - - $this->field->attach(IntegerEnum::class); - } - - /** @test */ - public function it_starts_with_no_options_and_rules() - { - $field = Enum::make('Enum'); - - $this->assertArrayNotHasKey('options', $field->meta); - $this->assertEmpty($field->rules); - } - - /** @test */ - public function an_enum_can_be_attached_to_it() - { - $this->assertArrayHasKey('options', $this->field->meta); - - $this->assertEquals([ - [ - 'label' => 'Administrator', - 'value' => 0, - ], - [ - 'label' => 'Moderator', - 'value' => 1, - ], - [ - 'label' => 'Subscriber', - 'value' => 2, - ], - ], $this->field->meta['options']); - } - - /** @test */ - public function attaching_an_enum_adds_correct_rules() - { - $this->assertContains('required', $this->field->rules); - - $this->assertContainsEquals(new EnumValue(IntegerEnum::class, false), $this->field->rules); - } - - /** @test */ - public function it_resolves_correct_value() - { - $this->field->resolve(['enum' => IntegerEnum::Moderator()]); - - $this->assertSame(1, $this->field->value); - } - - /** @test */ - public function it_displays_correct_description() - { - $this->field->resolveForDisplay(['enum' => IntegerEnum::Moderator()]); - - $this->assertSame('Moderator', $this->field->value); - } -} diff --git a/tests/IntegerFilterTest.php b/tests/IntegerFilterTest.php deleted file mode 100644 index 5c53f33..0000000 --- a/tests/IntegerFilterTest.php +++ /dev/null @@ -1,73 +0,0 @@ -setUpDatabase($this->app); - - IntegerModel::create(['enum' => IntegerEnum::Moderator()]); - IntegerModel::create(['enum' => IntegerEnum::Moderator()]); - - IntegerModel::create(['enum' => IntegerEnum::Administrator()]); - - $this->filter = new MockFilter(new EnumFilter('enum', IntegerEnum::class)); - } - - /** @test */ - public function it_contains_all_the_filter_values() - { - $this->filter->assertHasOption(IntegerEnum::Subscriber()); - - $this->filter->assertHasOption(IntegerEnum::Moderator()); - - $this->filter->assertHasOption(IntegerEnum::Administrator()); - } - - /** @test */ - public function it_returns_the_correct_number_of_results() - { - $response = $this->filter->apply(IntegerModel::class, IntegerEnum::Subscriber()); - - $response->assertCount(0); - - $response = $this->filter->apply(IntegerModel::class, IntegerEnum::Moderator()); - - $response->assertCount(2); - - $response = $this->filter->apply(IntegerModel::class, IntegerEnum::Administrator()); - - $response->assertCount(1); - } - - /** @test */ - public function it_returns_the_correct_results() - { - $response1 = $this->filter->apply(IntegerModel::class, IntegerEnum::Moderator()); - - $response2 = $this->filter->apply(IntegerModel::class, IntegerEnum::Administrator()); - - IntegerModel::whereEnum(IntegerEnum::Moderator())->each(function ($model) use ($response1, $response2) { - $response1->assertContains($model); - - $response2->assertMissing($model); - }); - - IntegerModel::whereEnum(IntegerEnum::Administrator())->each(function ($model) use ($response1, $response2) { - $response1->assertMissing($model); - - $response2->assertContains($model); - }); - } -} diff --git a/tests/StringFieldTest.php b/tests/StringFieldTest.php deleted file mode 100644 index 0dd42c1..0000000 --- a/tests/StringFieldTest.php +++ /dev/null @@ -1,37 +0,0 @@ -field = Enum::make('Enum'); - - $this->field->attach(StringEnum::class); - } - - /** @test */ - public function it_resolves_correct_value() - { - $this->field->resolve(['enum' => StringEnum::Moderator()]); - - $this->assertSame('moderator', $this->field->value); - } - - /** @test */ - public function it_displays_correct_description() - { - $this->field->resolveForDisplay(['enum' => StringEnum::Moderator()]); - - $this->assertSame('Moderator', $this->field->value); - } -} diff --git a/tests/StringFilterTest.php b/tests/StringFilterTest.php deleted file mode 100644 index 1760d15..0000000 --- a/tests/StringFilterTest.php +++ /dev/null @@ -1,73 +0,0 @@ -setUpDatabase($this->app, 'string'); - - StringModel::create(['enum' => StringEnum::Moderator()]); - StringModel::create(['enum' => StringEnum::Moderator()]); - - StringModel::create(['enum' => StringEnum::Administrator()]); - - $this->filter = new MockFilter(new EnumFilter('enum', StringEnum::class)); - } - - /** @test */ - public function it_contains_all_the_filter_values() - { - $this->filter->assertHasOption(StringEnum::Subscriber()); - - $this->filter->assertHasOption(StringEnum::Moderator()); - - $this->filter->assertHasOption(StringEnum::Administrator()); - } - - /** @test */ - public function it_returns_the_correct_number_of_results() - { - $response = $this->filter->apply(StringModel::class, StringEnum::Subscriber()); - - $response->assertCount(0); - - $response = $this->filter->apply(StringModel::class, StringEnum::Moderator()); - - $response->assertCount(2); - - $response = $this->filter->apply(StringModel::class, StringEnum::Administrator()); - - $response->assertCount(1); - } - - /** @test */ - public function it_returns_the_correct_results() - { - $response1 = $this->filter->apply(StringModel::class, StringEnum::Moderator()); - - $response2 = $this->filter->apply(StringModel::class, StringEnum::Administrator()); - - StringModel::whereEnum(StringEnum::Moderator())->each(function ($model) use ($response1, $response2) { - $response1->assertContains($model); - - $response2->assertMissing($model); - }); - - StringModel::whereEnum(StringEnum::Administrator())->each(function ($model) use ($response1, $response2) { - $response1->assertMissing($model); - - $response2->assertContains($model); - }); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php index cb15825..5e97c7a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,6 +3,7 @@ namespace SimpleSquid\Nova\Fields\Enum\Tests; use Illuminate\Database\Schema\Blueprint; +use Laravel\Nova\NovaServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; use SimpleSquid\Nova\Fields\Enum\EnumFieldServiceProvider; @@ -15,6 +16,13 @@ protected function getPackageProviders($app) ]; } + protected function setUp(): void + { + parent::setUp(); + + app()->register(NovaServiceProvider::class); + } + protected function setUpDatabase($app, $type = 'integer') { $this->artisan('migrate:fresh'); From 977313701a079a4835ffb6d190f3f8f276e1d99c Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Mon, 25 Jan 2021 14:05:47 +0000 Subject: [PATCH 10/15] Fix styling. --- src/EnumBooleanFilter.php | 1 + src/EnumFilter.php | 1 + src/FlaggedEnum.php | 2 +- tests/Fields/FieldTest.php | 2 +- tests/Fields/FlaggedFieldTest.php | 8 ++++---- tests/Filters/FlaggedBooleanFilterTest.php | 11 ++++++----- tests/Filters/FlaggedFilterTest.php | 10 +++++----- tests/Filters/IntegerBooleanFilterTest.php | 4 ++-- tests/Filters/IntegerFilterTest.php | 4 ++-- tests/Filters/StringBooleanFilterTest.php | 4 ++-- tests/Filters/StringFilterTest.php | 4 ++-- 11 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/EnumBooleanFilter.php b/src/EnumBooleanFilter.php index ade68c3..1d7f800 100644 --- a/src/EnumBooleanFilter.php +++ b/src/EnumBooleanFilter.php @@ -49,6 +49,7 @@ public function name($name = null) } $this->name = $name; + return $this; } diff --git a/src/EnumFilter.php b/src/EnumFilter.php index 9bba576..d363104 100644 --- a/src/EnumFilter.php +++ b/src/EnumFilter.php @@ -32,6 +32,7 @@ public function name($name = null) } $this->name = $name; + return $this; } diff --git a/src/FlaggedEnum.php b/src/FlaggedEnum.php index 563aa1c..fb855a2 100644 --- a/src/FlaggedEnum.php +++ b/src/FlaggedEnum.php @@ -19,7 +19,7 @@ public function attach($class) { $this->resolveUsing( function ($value) use ($class) { - if (!$value instanceof \BenSampo\Enum\FlaggedEnum) { + if (! $value instanceof \BenSampo\Enum\FlaggedEnum) { return $value; } diff --git a/tests/Fields/FieldTest.php b/tests/Fields/FieldTest.php index 6f6c5a9..8d180fd 100644 --- a/tests/Fields/FieldTest.php +++ b/tests/Fields/FieldTest.php @@ -52,7 +52,7 @@ public function it_displays_enum_options() foreach (IntegerEnum::getValues() as $enum) { $this->assertContains([ 'label' => IntegerEnum::getDescription($enum), - 'value' => $enum + 'value' => $enum, ], $this->field->meta['options']); } } diff --git a/tests/Fields/FlaggedFieldTest.php b/tests/Fields/FlaggedFieldTest.php index 329e385..61d2ade 100644 --- a/tests/Fields/FlaggedFieldTest.php +++ b/tests/Fields/FlaggedFieldTest.php @@ -15,9 +15,9 @@ class FlaggedFieldTest extends TestCase private $model; private $values = [ - FlaggedEnum::ReadComments => true, + FlaggedEnum::ReadComments => true, FlaggedEnum::WriteComments => true, - FlaggedEnum::EditComments => false, + FlaggedEnum::EditComments => false, ]; protected function setUp(): void @@ -63,7 +63,7 @@ public function it_displays_enum_options() $this->assertContains([ 'label' => FlaggedEnum::getDescription($enum), - 'name' => $enum + 'name' => $enum, ], $this->field->options); } } @@ -103,7 +103,7 @@ public function it_fills_database_with_flagged_enum_value() $this->model->save(); $this->assertDatabaseHas('example_models', [ - 'enum' => array_sum(array_keys(array_filter($this->values))) + 'enum' => array_sum(array_keys(array_filter($this->values))), ]); $this->assertDatabaseMissing('example_models', ['enum' => FlaggedEnum::None]); diff --git a/tests/Filters/FlaggedBooleanFilterTest.php b/tests/Filters/FlaggedBooleanFilterTest.php index 1ccee84..0b3ca7f 100644 --- a/tests/Filters/FlaggedBooleanFilterTest.php +++ b/tests/Filters/FlaggedBooleanFilterTest.php @@ -36,15 +36,15 @@ protected function setUp(): void $this->models[2] = FlaggedModel::create([ 'enum' => array_sum([ FlaggedEnum::ReadComments, - FlaggedEnum::WriteComments - ]) + FlaggedEnum::WriteComments, + ]), ]); $this->results = [ - FlaggedEnum::None => [0], - FlaggedEnum::ReadComments => [1, 2], + FlaggedEnum::None => [0], + FlaggedEnum::ReadComments => [1, 2], FlaggedEnum::WriteComments => [2], - FlaggedEnum::EditComments => [], + FlaggedEnum::EditComments => [], ]; } @@ -81,6 +81,7 @@ public function it_can_filter_by_all_selected_options() foreach (array_keys($this->results) as $enum) { if ($enum === FlaggedEnum::None) { $this->mockFilter->assertOptionMissing($enum); + continue; } diff --git a/tests/Filters/FlaggedFilterTest.php b/tests/Filters/FlaggedFilterTest.php index ee9b707..7d185eb 100644 --- a/tests/Filters/FlaggedFilterTest.php +++ b/tests/Filters/FlaggedFilterTest.php @@ -31,15 +31,15 @@ protected function setUp(): void $this->models[2] = FlaggedModel::create([ 'enum' => array_sum([ FlaggedEnum::ReadComments, - FlaggedEnum::WriteComments - ]) + FlaggedEnum::WriteComments, + ]), ]); $this->results = [ - FlaggedEnum::None => [0], - FlaggedEnum::ReadComments => [1, 2], + FlaggedEnum::None => [0], + FlaggedEnum::ReadComments => [1, 2], FlaggedEnum::WriteComments => [2], - FlaggedEnum::EditComments => [], + FlaggedEnum::EditComments => [], ]; } diff --git a/tests/Filters/IntegerBooleanFilterTest.php b/tests/Filters/IntegerBooleanFilterTest.php index 16e27f0..f87fa1a 100644 --- a/tests/Filters/IntegerBooleanFilterTest.php +++ b/tests/Filters/IntegerBooleanFilterTest.php @@ -31,9 +31,9 @@ protected function setUp(): void $this->models[2] = IntegerModel::create(['enum' => IntegerEnum::Administrator]); $this->results = [ - IntegerEnum::Moderator => [0, 1], + IntegerEnum::Moderator => [0, 1], IntegerEnum::Administrator => [2], - IntegerEnum::Subscriber => [], + IntegerEnum::Subscriber => [], ]; } diff --git a/tests/Filters/IntegerFilterTest.php b/tests/Filters/IntegerFilterTest.php index 0646279..510775a 100644 --- a/tests/Filters/IntegerFilterTest.php +++ b/tests/Filters/IntegerFilterTest.php @@ -31,9 +31,9 @@ protected function setUp(): void $this->models[2] = IntegerModel::create(['enum' => IntegerEnum::Administrator]); $this->results = [ - IntegerEnum::Moderator => [0, 1], + IntegerEnum::Moderator => [0, 1], IntegerEnum::Administrator => [2], - IntegerEnum::Subscriber => [], + IntegerEnum::Subscriber => [], ]; } diff --git a/tests/Filters/StringBooleanFilterTest.php b/tests/Filters/StringBooleanFilterTest.php index 0d8e7ae..1a81819 100644 --- a/tests/Filters/StringBooleanFilterTest.php +++ b/tests/Filters/StringBooleanFilterTest.php @@ -31,9 +31,9 @@ protected function setUp(): void $this->models[2] = StringModel::create(['enum' => StringEnum::Administrator]); $this->results = [ - StringEnum::Moderator => [0, 1], + StringEnum::Moderator => [0, 1], StringEnum::Administrator => [2], - StringEnum::Subscriber => [], + StringEnum::Subscriber => [], ]; } diff --git a/tests/Filters/StringFilterTest.php b/tests/Filters/StringFilterTest.php index 37d1de4..aaa07b9 100644 --- a/tests/Filters/StringFilterTest.php +++ b/tests/Filters/StringFilterTest.php @@ -31,9 +31,9 @@ protected function setUp(): void $this->models[2] = StringModel::create(['enum' => StringEnum::Administrator]); $this->results = [ - StringEnum::Moderator => [0, 1], + StringEnum::Moderator => [0, 1], StringEnum::Administrator => [2], - StringEnum::Subscriber => [], + StringEnum::Subscriber => [], ]; } From 3a7848f687f0981ba76e9d4a4a576a570af58423 Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Mon, 25 Jan 2021 16:14:22 +0200 Subject: [PATCH 11/15] Update minimum dependency. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2dc86a9..2839a33 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ ], "require": { "php": "^7.3|^8.0", - "bensampo/laravel-enum": "^3.0", + "bensampo/laravel-enum": "^3.1", "illuminate/support": "^8.0", "laravel/nova": "^3.0" }, From 64d52a9861a28e3077f66d8755ca9ed66cb9b20b Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Mon, 25 Jan 2021 16:44:15 +0200 Subject: [PATCH 12/15] Update minimum dependency. --- composer.json | 4 +--- src/EnumFieldServiceProvider.php | 34 -------------------------------- tests/TestCase.php | 10 +--------- 3 files changed, 2 insertions(+), 46 deletions(-) delete mode 100644 src/EnumFieldServiceProvider.php diff --git a/composer.json b/composer.json index 2839a33..1968b2f 100644 --- a/composer.json +++ b/composer.json @@ -51,9 +51,7 @@ }, "extra": { "laravel": { - "providers": [ - "SimpleSquid\\Nova\\Fields\\Enum\\EnumFieldServiceProvider" - ] + "providers": [] } }, "scripts": { diff --git a/src/EnumFieldServiceProvider.php b/src/EnumFieldServiceProvider.php deleted file mode 100644 index 32fa8f2..0000000 --- a/src/EnumFieldServiceProvider.php +++ /dev/null @@ -1,34 +0,0 @@ -register(NovaServiceProvider::class); - } - protected function setUpDatabase($app, $type = 'integer') { $this->artisan('migrate:fresh'); From 9fb6a0f83d0fa9201d6a7f174e3de25bc9fc9f2a Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Mon, 25 Jan 2021 16:50:24 +0200 Subject: [PATCH 13/15] Add tests for lack of casts. --- tests/Examples/NoCastsModel.php | 14 ++++ tests/Fields/NoCastsFieldTest.php | 60 +++++++++++++++ tests/Filters/NoCastsBooleanFilterTest.php | 89 ++++++++++++++++++++++ tests/Filters/NoCastsFilterTest.php | 65 ++++++++++++++++ 4 files changed, 228 insertions(+) create mode 100644 tests/Examples/NoCastsModel.php create mode 100644 tests/Fields/NoCastsFieldTest.php create mode 100644 tests/Filters/NoCastsBooleanFilterTest.php create mode 100644 tests/Filters/NoCastsFilterTest.php diff --git a/tests/Examples/NoCastsModel.php b/tests/Examples/NoCastsModel.php new file mode 100644 index 0000000..dcd8100 --- /dev/null +++ b/tests/Examples/NoCastsModel.php @@ -0,0 +1,14 @@ +setUpDatabase($this->app, 'string'); + + $this->field = Enum::make('Enum')->attach(StringEnum::class); + + $this->model = NoCastsModel::create(['enum' => StringEnum::Moderator]); + } + + /** @test */ + public function it_resolves_enum_value() + { + $this->field->resolve($this->model); + + $this->assertSame(StringEnum::Moderator, $this->field->value); + } + + /** @test */ + public function it_displays_enum_description() + { + $this->field->resolveForDisplay($this->model); + + $this->assertSame(StringEnum::Moderator, $this->field->value); + } + + /** @test */ + public function it_fills_database_with_enum_value() + { + $request = new NovaRequest(); + $request->query->add(['enum' => StringEnum::Subscriber]); + + $this->field->fill($request, $this->model); + + $this->assertDatabaseHas('example_models', ['enum' => StringEnum::Moderator]); + + $this->model->save(); + + $this->assertDatabaseHas('example_models', ['enum' => StringEnum::Subscriber]); + + $this->assertDatabaseMissing('example_models', ['enum' => StringEnum::Moderator]); + } +} diff --git a/tests/Filters/NoCastsBooleanFilterTest.php b/tests/Filters/NoCastsBooleanFilterTest.php new file mode 100644 index 0000000..050797e --- /dev/null +++ b/tests/Filters/NoCastsBooleanFilterTest.php @@ -0,0 +1,89 @@ +setUpDatabase($this->app, 'string'); + + $this->filter = new MockFilter(new EnumBooleanFilter('enum', StringEnum::class)); + + $this->models[0] = NoCastsModel::create(['enum' => StringEnum::Moderator]); + + $this->models[1] = NoCastsModel::create(['enum' => StringEnum::Moderator]); + + $this->models[2] = NoCastsModel::create(['enum' => StringEnum::Administrator]); + + $this->results = [ + StringEnum::Moderator => [0, 1], + StringEnum::Administrator => [2], + StringEnum::Subscriber => [], + ]; + } + + private function getOptions(array $keys, array $options = [[]]): array + { + if (empty($keys)) { + return $options; + } + + $current = array_shift($keys); + $newOptions = []; + + foreach ($options as $option) { + $newOptions[] = $option + [$current => true]; + $newOptions[] = $option + [$current => false]; + } + + return $this->getOptions($keys, $newOptions); + } + + /** @test */ + public function it_contains_all_the_filter_values() + { + foreach (StringEnum::getValues() as $enum) { + $this->filter->assertHasOption($enum); + } + } + + /** @test */ + public function it_returns_the_correct_results() + { + foreach ($options = $this->getOptions(StringEnum::getValues()) as $option) { + $response = $this->filter->apply(NoCastsModel::class, $option); + + // None selected should show all models + if (count(array_filter($option)) === 0) { + $models = array_keys($this->models); + } else { + $models = array_unique(array_merge(...array_values(array_intersect_key($this->results, array_filter($option))))); + } + + $response->assertCount(count($models)); + + foreach ($models as $contain) { + $response->assertContains($this->models[$contain]); + } + + foreach (array_diff(array_keys($this->models), $models) as $missing) { + $response->assertMissing($this->models[$missing]); + } + } + } +} diff --git a/tests/Filters/NoCastsFilterTest.php b/tests/Filters/NoCastsFilterTest.php new file mode 100644 index 0000000..8d1e288 --- /dev/null +++ b/tests/Filters/NoCastsFilterTest.php @@ -0,0 +1,65 @@ +setUpDatabase($this->app, 'string'); + + $this->filter = new MockFilter(new EnumFilter('enum', StringEnum::class)); + + $this->models[0] = NoCastsModel::create(['enum' => StringEnum::Moderator]); + + $this->models[1] = NoCastsModel::create(['enum' => StringEnum::Moderator]); + + $this->models[2] = NoCastsModel::create(['enum' => StringEnum::Administrator]); + + $this->results = [ + StringEnum::Moderator => [0, 1], + StringEnum::Administrator => [2], + StringEnum::Subscriber => [], + ]; + } + + /** @test */ + public function it_contains_all_the_filter_values() + { + foreach (StringEnum::getValues() as $enum) { + $this->filter->assertHasOption($enum); + } + } + + /** @test */ + public function it_returns_the_correct_results() + { + foreach ($this->results as $enum => $models) { + $response = $this->filter->apply(NoCastsModel::class, $enum); + + $response->assertCount(count($models)); + + foreach ($models as $contain) { + $response->assertContains($this->models[$contain]); + } + + foreach (array_diff(array_keys($this->models), $models) as $missing) { + $response->assertMissing($this->models[$missing]); + } + } + } +} From 795b25b1131a1bc11d0e38b3c72bcc30e071f4cb Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Mon, 25 Jan 2021 14:51:02 +0000 Subject: [PATCH 14/15] Fix styling. --- tests/Filters/NoCastsBooleanFilterTest.php | 4 ++-- tests/Filters/NoCastsFilterTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Filters/NoCastsBooleanFilterTest.php b/tests/Filters/NoCastsBooleanFilterTest.php index 050797e..cbde178 100644 --- a/tests/Filters/NoCastsBooleanFilterTest.php +++ b/tests/Filters/NoCastsBooleanFilterTest.php @@ -31,9 +31,9 @@ protected function setUp(): void $this->models[2] = NoCastsModel::create(['enum' => StringEnum::Administrator]); $this->results = [ - StringEnum::Moderator => [0, 1], + StringEnum::Moderator => [0, 1], StringEnum::Administrator => [2], - StringEnum::Subscriber => [], + StringEnum::Subscriber => [], ]; } diff --git a/tests/Filters/NoCastsFilterTest.php b/tests/Filters/NoCastsFilterTest.php index 8d1e288..e2b7db6 100644 --- a/tests/Filters/NoCastsFilterTest.php +++ b/tests/Filters/NoCastsFilterTest.php @@ -31,9 +31,9 @@ protected function setUp(): void $this->models[2] = NoCastsModel::create(['enum' => StringEnum::Administrator]); $this->results = [ - StringEnum::Moderator => [0, 1], + StringEnum::Moderator => [0, 1], StringEnum::Administrator => [2], - StringEnum::Subscriber => [], + StringEnum::Subscriber => [], ]; } From ec5fc8becb509028d933243e0dbbb473d8e6a97d Mon Sep 17 00:00:00 2001 From: mdpoulter Date: Mon, 25 Jan 2021 16:53:45 +0200 Subject: [PATCH 15/15] Update README and CHANGELOG. --- CHANGELOG.md | 25 ++++++++++++++++++++--- README.md | 57 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85f9b3b..f96ef86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,29 @@ All notable changes to `nova-enum-field` will be documented in this file. +## 2.3.0 - 2021-01-25 + +- Drop support for Laravel `7.x` +- Drop support for `laravel-enum < 3.1`. +- Add a customisable select filter. +- Add a customisable boolean filter. +- Add a flagged enum field. +- Refactor and simplify tests. + +## 2.2.0 - 2020-09-25 + +- Add support for Laravel `8.x`. +- Add support for PHP `8.0`. +- Allow editing enums without casts. + +## 2.1.0 - 2020-09-01 + +- Add support for `laravel-enum 2.2.0`. + ## 2.0.0 - 2020-07-08 -- Update `larave-enum` to `2.x` -- Drop support for laravel < `7.x` +- Add support for `laravel-enum 2.x`. +- Drop support for Laravel `< 7.x`. ## 1.1.0 - 2019-09-30 @@ -16,7 +35,7 @@ All notable changes to `nova-enum-field` will be documented in this file. ## 1.0.4 - 2019-09-27 - Add documentation. -- Refactor field code. +- Refactor `Enum` field code. ## 1.0.3 - 2019-09-27 diff --git a/README.md b/README.md index f73f431..50c888a 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,7 @@ [![MIT License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) [![Total Downloads](https://img.shields.io/packagist/dt/simplesquid/nova-enum-field.svg?style=flat-square)](https://packagist.org/packages/simplesquid/nova-enum-field) -Laravel Nova field to add enums to resources. This field uses -the [BenSampo/laravel-enum](https://github.com/BenSampo/laravel-enum) package, so make sure to check out the -installation instructions there first. +Laravel Nova field to add enums to resources. This field uses the [BenSampo/laravel-enum](https://github.com/BenSampo/laravel-enum) package, so make sure to check out the installation instructions there first. ![Screenshot of the enum field](https://github.com/simplesquid/nova-enum-field/raw/master/docs/screenshot.png) @@ -21,8 +19,7 @@ composer require simplesquid/nova-enum-field ## Setup -This package requires that you use Attribute Casting in your models. From the docs -at [BenSampo/laravel-enum](https://github.com/BenSampo/laravel-enum#attribute-casting), this can be done like so: +It is strongly recommended that you use Attribute Casting in your models. From the docs at [BenSampo/laravel-enum](https://github.com/BenSampo/laravel-enum#attribute-casting), this can be done like this: ```php use App\Enums\UserType; @@ -41,7 +38,7 @@ class Example extends Model ## Usage -You can use the `Enum` field in your Nova resource like so: +You can use the `Enum` field in your Nova resource like this: ```php namespace App\Nova; @@ -66,13 +63,41 @@ class Example extends Resource } ``` +### Flagged Enums + +You can use the `FlaggedEnum` field in your Nova resource like this (see [Flagged/Bitwise Enum](https://github.com/BenSampo/laravel-enum#flaggedbitwise-enum) setup): + +```php +namespace App\Nova; + +use App\Enums\UserPermissions; +use SimpleSquid\Nova\Fields\Enum\FlaggedEnum; + +class Example extends Resource +{ + // ... + + public function fields(Request $request) + { + return [ + // ... + + FlaggedEnum::make('User Permissions')->attach(UserPermissions::class), + + // ... + ]; + } +} +``` + ### Filters -If you would like to use the provided Nova Select filter, you can include it as such: +If you would like to use the provided Nova Select filter (which is compatible with both the `Enum` and `FlaggedEnum` fields), you can include it like this: ```php namespace App\Nova; +use App\Enums\UserPermissions; use App\Enums\UserType; use SimpleSquid\Nova\Fields\Enum\EnumFilter; @@ -85,18 +110,22 @@ class Example extends Resource return [ new EnumFilter('user_type', UserType::class), + new EnumFilter('user_permissions', UserPermissions::class), + // Or with optional filter name: - // new EnumFilter('user_type', UserType::class, 'Type of user'), + (new EnumFilter('user_type', UserType::class)) + ->name('Type of user'), ]; } } ``` -Alternatively, you may wish to use the provided Nova Boolean filter: +Alternatively, you may wish to use the provided Nova Boolean filter (which is also compatible with both the `Enum` and `FlaggedEnum` fields): ```php namespace App\Nova; +use App\Enums\UserPermissions; use App\Enums\UserType; use SimpleSquid\Nova\Fields\Enum\EnumBooleanFilter; @@ -109,8 +138,16 @@ class Example extends Resource return [ new EnumBooleanFilter('user_type', UserType::class), + new EnumBooleanFilter('user_permissions', UserPermissions::class), + // Or with optional filter name: - // new EnumBooleanFilter('user_type', UserType::class, 'Type of user'), + (new EnumBooleanFilter('user_type', UserType::class)) + ->name('Type of user'), + + // When filtering a FlaggedEnum, it will default to filtering + // by ANY flags, however you may wish to filter by ALL flags: + (new EnumBooleanFilter('user_permissions', UserPermissions::class)) + ->filterAllFlags(), ]; } }