diff --git a/tests/database/migrations/create_posts_table.php b/tests/database/migrations/create_posts_table.php index 361458285f..a03ae7439d 100644 --- a/tests/database/migrations/create_posts_table.php +++ b/tests/database/migrations/create_posts_table.php @@ -16,6 +16,7 @@ public function up(): void $table->unsignedTinyInteger('rating')->default(0); $table->json('tags')->nullable(); $table->string('title'); + $table->json('json')->nullable(); $table->json('json_array_of_objects')->nullable(); $table->timestamps(); $table->softDeletes(); diff --git a/tests/database/migrations/modify_users_table.php b/tests/database/migrations/modify_users_table.php index 51c96cd3e3..a97d33f158 100644 --- a/tests/database/migrations/modify_users_table.php +++ b/tests/database/migrations/modify_users_table.php @@ -13,6 +13,7 @@ public function up(): void { Schema::table('users', function (Blueprint $table) { $table->after('password', function (Blueprint $table) { + $table->json('json')->nullable(); $table->string('email_code_authentication_secret')->nullable(); $table->string('google_two_factor_authentication_secret')->nullable(); $table->text('google_two_factor_authentication_recovery_codes')->nullable(); diff --git a/tests/src/Fixtures/Livewire/PostsTable.php b/tests/src/Fixtures/Livewire/PostsTable.php index 3598ec9151..ea6d19762b 100644 --- a/tests/src/Fixtures/Livewire/PostsTable.php +++ b/tests/src/Fixtures/Livewire/PostsTable.php @@ -90,7 +90,19 @@ public function table(Table $table): Table ->state('correct state'), Tables\Columns\TextColumn::make('formatted_state') ->formatStateUsing(fn () => 'formatted state'), + Tables\Columns\TextColumn::make('json.foo') + ->searchable() + ->sortable(), + Tables\Columns\TextColumn::make('json.bar.baz') + ->searchable() + ->sortable(), Tables\Columns\TextColumn::make('json_array_of_objects.*.value'), + Tables\Columns\TextColumn::make('author.json.foo') + ->searchable() + ->sortable(), + Tables\Columns\TextColumn::make('author.json.bar.baz') + ->searchable() + ->sortable(), Tables\Columns\TextColumn::make('extra_attributes') ->extraAttributes([ 'class' => 'text-danger-500', diff --git a/tests/src/Fixtures/Models/Post.php b/tests/src/Fixtures/Models/Post.php index 2c76ff9489..3890135284 100644 --- a/tests/src/Fixtures/Models/Post.php +++ b/tests/src/Fixtures/Models/Post.php @@ -16,6 +16,7 @@ class Post extends Model protected $casts = [ 'is_published' => 'boolean', 'tags' => 'array', + 'json' => 'array', 'json_array_of_objects' => 'array', ]; diff --git a/tests/src/Fixtures/Models/User.php b/tests/src/Fixtures/Models/User.php index e0d3edafa8..6e00c21b59 100644 --- a/tests/src/Fixtures/Models/User.php +++ b/tests/src/Fixtures/Models/User.php @@ -37,6 +37,7 @@ class User extends Authenticatable implements FilamentUser, HasEmailCodeAuthenti * @var array */ protected $casts = [ + 'json' => 'array', 'email_verified_at' => 'datetime', 'google_two_factor_authentication_secret' => 'encrypted', 'google_two_factor_authentication_recovery_codes' => 'encrypted:array', diff --git a/tests/src/Tables/ColumnTest.php b/tests/src/Tables/ColumnTest.php index 4643006bb3..1e9b1ac2d5 100644 --- a/tests/src/Tables/ColumnTest.php +++ b/tests/src/Tables/ColumnTest.php @@ -4,7 +4,9 @@ use Filament\Tables\Columns\TextColumn; use Filament\Tests\Fixtures\Livewire\PostsTable; use Filament\Tests\Fixtures\Models\Post; +use Filament\Tests\Fixtures\Models\User; use Filament\Tests\Tables\TestCase; +use Illuminate\Support\Str; use function Filament\Tests\livewire; @@ -44,6 +46,54 @@ ->assertCanSeeTableRecords($posts->sortByDesc('author.name'), inOrder: true); }); +it('can sort records with JSON column', function () { + $posts = Post::factory()->count(10)->state(fn (): array => [ + 'json' => ['foo' => Str::random()], + ])->create(); + + livewire(PostsTable::class) + ->sortTable('json.foo') + ->assertCanSeeTableRecords($posts->sortBy('json.foo'), inOrder: true) + ->sortTable('json.foo', 'desc') + ->assertCanSeeTableRecords($posts->sortByDesc('json.foo'), inOrder: true); +}); + +it('can sort records with nested JSON column', function () { + $posts = Post::factory()->count(10)->state(fn (): array => [ + 'json' => ['bar' => ['baz' => Str::random()]], + ])->create(); + + livewire(PostsTable::class) + ->sortTable('json.bar.baz') + ->assertCanSeeTableRecords($posts->sortBy('json.bar.baz'), inOrder: true) + ->sortTable('json.bar.baz', 'desc') + ->assertCanSeeTableRecords($posts->sortByDesc('json.bar.baz'), inOrder: true); +}); + +it('can sort records with relationship JSON column', function () { + $posts = Post::factory()->count(10)->state(fn (): array => [ + 'author_id' => User::factory()->state(['json' => ['foo' => Str::random()]]), + ])->create(); + + livewire(PostsTable::class) + ->sortTable('author.json.foo') + ->assertCanSeeTableRecords($posts->sortBy('author.json.foo'), inOrder: true) + ->sortTable('author.json.foo', 'desc') + ->assertCanSeeTableRecords($posts->sortByDesc('author.json.foo'), inOrder: true); +}); + +it('can sort records with relationship nested JSON column', function () { + $posts = Post::factory()->count(10)->state(fn (): array => [ + 'author_id' => User::factory()->state(['json' => ['bar' => ['baz' => Str::random()]]]), + ])->create(); + + livewire(PostsTable::class) + ->sortTable('author.json.bar.baz') + ->assertCanSeeTableRecords($posts->sortBy('author.json.bar.baz'), inOrder: true) + ->sortTable('author.json.bar.baz', 'desc') + ->assertCanSeeTableRecords($posts->sortByDesc('author.json.bar.baz'), inOrder: true); +}); + it('can search records', function () { $posts = Post::factory()->count(10)->create(); @@ -77,6 +127,82 @@ ->assertCanNotSeeTableRecords($posts->where('author.name', '!=', $author)); }); +it('can search posts with JSON column', function () { + $search = Str::random(); + + $matchingPosts = Post::factory()->count(5)->create([ + 'json' => ['foo' => $search], + ]); + + $notMatchingPosts = Post::factory()->count(5)->create([ + 'json' => ['foo' => Str::random()], + ]); + + livewire(PostsTable::class) + ->searchTable($search) + ->assertCanSeeTableRecords($matchingPosts) + ->assertCanNotSeeTableRecords($notMatchingPosts); +}); + +it('can search posts with nested JSON column', function () { + $search = Str::random(); + + $matchingPosts = Post::factory()->count(5)->create([ + 'json' => ['bar' => ['baz' => $search]], + ]); + + $notMatchingPosts = Post::factory()->count(5)->create([ + 'json' => ['bar' => ['baz' => Str::random()]], + ]); + + livewire(PostsTable::class) + ->searchTable($search) + ->assertCanSeeTableRecords($matchingPosts) + ->assertCanNotSeeTableRecords($notMatchingPosts); +}); + +it('can search posts with relationship JSON column', function () { + $search = Str::random(); + + $matchingAuthor = User::factory() + ->create(['json' => ['foo' => $search]]); + + $matchingPosts = Post::factory() + ->for($matchingAuthor, 'author') + ->count(5) + ->create(); + + $notMatchingPosts = Post::factory() + ->count(5) + ->create(); + + livewire(PostsTable::class) + ->searchTable($search) + ->assertCanSeeTableRecords($matchingPosts) + ->assertCanNotSeeTableRecords($notMatchingPosts); +}); + +it('can search posts with relationship nested JSON column', function () { + $search = Str::random(); + + $matchingAuthor = User::factory() + ->create(['json' => ['bar' => ['baz' => $search]]]); + + $matchingPosts = Post::factory() + ->for($matchingAuthor, 'author') + ->count(5) + ->create(); + + $notMatchingPosts = Post::factory() + ->count(5) + ->create(); + + livewire(PostsTable::class) + ->searchTable($search) + ->assertCanSeeTableRecords($matchingPosts) + ->assertCanNotSeeTableRecords($notMatchingPosts); +}); + it('can search individual column records with relationship', function () { $posts = Post::factory()->count(10)->create(); @@ -141,6 +267,46 @@ ->assertTableColumnFormattedStateNotSet('formatted_state', 'incorrect formatted state', $post); }); +it('can output JSON values', function () { + $post = Post::factory()->create([ + 'json' => ['foo' => 'bar'], + ]); + + livewire(PostsTable::class) + ->assertTableColumnStateSet('json.foo', 'bar', $post); +}); + +it('can output nested JSON values', function () { + $post = Post::factory()->create([ + 'json' => ['bar' => ['baz' => 'qux']], + ]); + + livewire(PostsTable::class) + ->assertTableColumnStateSet('json.bar.baz', 'qux', $post); +}); + +it('can output relationship JSON values', function () { + $post = Post::factory()->create([ + 'author_id' => User::factory()->state([ + 'json' => ['foo' => 'bar'], + ]), + ]); + + livewire(PostsTable::class) + ->assertTableColumnStateSet('author.json.foo', 'bar', $post); +}); + +it('can output relationship nested JSON values', function () { + $post = Post::factory()->create([ + 'author_id' => User::factory()->state([ + 'json' => ['bar' => ['baz' => 'qux']], + ]), + ]); + + livewire(PostsTable::class) + ->assertTableColumnStateSet('author.json.bar.baz', 'qux', $post); +}); + it('can output values in a JSON array column of objects', function () { $post = Post::factory()->create([ 'json_array_of_objects' => [