2308 lines
93 KiB
PHP
2308 lines
93 KiB
PHP
<?php
|
|
|
|
use Filament\Tables\Columns\IconColumn;
|
|
use Filament\Tables\Columns\TextColumn;
|
|
use Filament\Tests\Fixtures\Livewire\CustomDataTable;
|
|
use Filament\Tests\Fixtures\Livewire\ImagesTable;
|
|
use Filament\Tests\Fixtures\Livewire\PostsTable;
|
|
use Filament\Tests\Fixtures\Livewire\PostsTableWithColumnIndividualSearchTermSplittingDisabled;
|
|
use Filament\Tests\Fixtures\Livewire\PostsTableWithColumnIndividualSearchTermSplittingEnabled;
|
|
use Filament\Tests\Fixtures\Livewire\PostsTableWithNonSplitMultiColumnIndividualSearch;
|
|
use Filament\Tests\Fixtures\Livewire\PostsTableWithQualifiedColumns;
|
|
use Filament\Tests\Fixtures\Livewire\PostsTableWithReservedJsPropertyColumnSearch;
|
|
use Filament\Tests\Fixtures\Livewire\PostsTableWithTableSearchableColumns;
|
|
use Filament\Tests\Fixtures\Livewire\UsersTable;
|
|
use Filament\Tests\Fixtures\Livewire\UsersWithTeamTable;
|
|
use Filament\Tests\Fixtures\Models\Company;
|
|
use Filament\Tests\Fixtures\Models\Image;
|
|
use Filament\Tests\Fixtures\Models\Language;
|
|
use Filament\Tests\Fixtures\Models\Post;
|
|
use Filament\Tests\Fixtures\Models\Profile;
|
|
use Filament\Tests\Fixtures\Models\Setting;
|
|
use Filament\Tests\Fixtures\Models\Team;
|
|
use Filament\Tests\Fixtures\Models\User;
|
|
use Filament\Tests\Tables\TestCase;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Str;
|
|
|
|
use function Filament\Tests\livewire;
|
|
|
|
uses(TestCase::class);
|
|
|
|
describe('rendering and sorting', function (): void {
|
|
it('can render text column', function (): void {
|
|
Post::factory()->count(10)->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->assertCanRenderTableColumn('title');
|
|
});
|
|
|
|
it('can render text column with relationship', function (): void {
|
|
Post::factory()->count(10)->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->assertCanRenderTableColumn('author.name');
|
|
});
|
|
|
|
it('can sort records', function (): void {
|
|
Post::factory()->count(10)->create();
|
|
|
|
$sortedAsc = Post::query()->orderBy('title')->orderBy('id')->get();
|
|
$sortedDesc = Post::query()->orderByDesc('title')->orderBy('id')->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('title')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('title', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with relationship', function (): void {
|
|
Post::factory()->count(10)->create();
|
|
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
User::query()
|
|
->select('name')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
User::query()
|
|
->select('name')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.name')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('author.name', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with nested relationship', function (): void {
|
|
foreach (['Birch', 'Maple', 'Birch', 'Cedar', 'Ash'] as $teamName) {
|
|
Post::factory()->state([
|
|
'author_id' => User::factory()->state([
|
|
'team_id' => Team::factory()->state(['name' => $teamName]),
|
|
]),
|
|
])->create();
|
|
}
|
|
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
Team::query()
|
|
->select('teams.name')
|
|
->whereColumn('teams.id', 'users.team_id')
|
|
->join('users', 'users.team_id', '=', 'teams.id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
Team::query()
|
|
->select('teams.name')
|
|
->whereColumn('teams.id', 'users.team_id')
|
|
->join('users', 'users.team_id', '=', 'teams.id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderByDesc('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.team.name')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('author.team.name', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records by multiple columns in the order they are passed', function (): void {
|
|
$posts = collect([
|
|
['rating' => 1, 'title' => 'Charlie'],
|
|
['rating' => 2, 'title' => 'Alpha'],
|
|
['rating' => 1, 'title' => 'Bravo'],
|
|
['rating' => 2, 'title' => 'Delta'],
|
|
])->map(fn (array $attributes): Post => Post::factory()->create($attributes));
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('rating_and_title')
|
|
->assertCanSeeTableRecords([$posts[2], $posts[0], $posts[1], $posts[3]], inOrder: true)
|
|
->sortTable('rating_and_title', 'desc')
|
|
->assertCanSeeTableRecords([$posts[3], $posts[1], $posts[0], $posts[2]], inOrder: true);
|
|
});
|
|
|
|
it('can sort records with JSON column', function (): void {
|
|
$posts = Post::factory()->count(10)->state(fn (): array => [
|
|
'json' => ['foo' => Str::random()],
|
|
])->create();
|
|
|
|
// Get database-sorted results to match actual query behavior
|
|
$sortedAsc = Post::query()->orderBy('json->foo')->orderBy('id')->get();
|
|
$sortedDesc = Post::query()->orderByDesc('json->foo')->orderBy('id')->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('json.foo')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('json.foo', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with nested JSON column', function (): void {
|
|
$posts = Post::factory()->count(10)->state(fn (): array => [
|
|
'json' => ['bar' => ['baz' => Str::random()]],
|
|
])->create();
|
|
|
|
// Get database-sorted results to match actual query behavior
|
|
$sortedAsc = Post::query()->orderBy('json->bar->baz')->orderBy('id')->get();
|
|
$sortedDesc = Post::query()->orderByDesc('json->bar->baz')->orderBy('id')->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('json.bar.baz')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('json.bar.baz', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with relationship JSON column', function (): void {
|
|
$posts = Post::factory()->count(10)->state(fn (): array => [
|
|
'author_id' => User::factory()->state(['json' => ['foo' => Str::random()]]),
|
|
])->create();
|
|
|
|
// Get database-sorted results using subquery orderBy
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
User::query()
|
|
->select('json->foo')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
User::query()
|
|
->select('json->foo')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.json.foo')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('author.json.foo', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with relationship nested JSON column', function (): void {
|
|
$posts = Post::factory()->count(10)->state(fn (): array => [
|
|
'author_id' => User::factory()->state(['json' => ['bar' => ['baz' => Str::random()]]]),
|
|
])->create();
|
|
|
|
// Get database-sorted results using subquery orderBy
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
User::query()
|
|
->select('json->bar->baz')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
User::query()
|
|
->select('json->bar->baz')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.json.bar.baz')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('author.json.bar.baz', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
});
|
|
|
|
describe('searching', function (): void {
|
|
it('can search records', function (): void {
|
|
$posts = Post::factory()->count(10)->create();
|
|
|
|
$title = $posts->first()->title;
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable($title)
|
|
->assertCanSeeTableRecords($posts->where('title', $title))
|
|
->assertCanNotSeeTableRecords($posts->where('title', '!=', $title));
|
|
});
|
|
|
|
it('treats LIKE wildcard characters as literal text when searching records', function (): void {
|
|
$literalMatch = Post::factory()->create(['title' => 'Before café!_100% After']);
|
|
$wildcardMatch = Post::factory()->create(['title' => 'Before café!X100 anything After']);
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable('café!_100%')
|
|
->assertCanSeeTableRecords([$literalMatch])
|
|
->assertCanNotSeeTableRecords([$wildcardMatch]);
|
|
});
|
|
|
|
it('can search individual column records', function (): void {
|
|
$posts = Post::factory()->count(10)->create();
|
|
|
|
$content = $posts->first()->content;
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTableColumns(['content' => $content])
|
|
->assertCanSeeTableRecords($posts->where('content', $content))
|
|
->assertCanNotSeeTableRecords($posts->where('content', '!=', $content));
|
|
});
|
|
|
|
it('can search posts with relationship', function (): void {
|
|
$posts = Post::factory()->count(10)->create();
|
|
|
|
$author = $posts->first()->author->name;
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable($author)
|
|
->assertCanSeeTableRecords($posts->where('author.name', $author))
|
|
->assertCanNotSeeTableRecords($posts->where('author.name', '!=', $author));
|
|
});
|
|
|
|
it('can search posts with JSON column', function (): void {
|
|
$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 (): void {
|
|
$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 (): void {
|
|
$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 (): void {
|
|
$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 (): void {
|
|
$posts = Post::factory()->count(10)->create();
|
|
|
|
$authorEmail = $posts->first()->author->email;
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTableColumns(['author.email' => $authorEmail])
|
|
->assertCanSeeTableRecords($posts->where('author.email', $authorEmail))
|
|
->assertCanNotSeeTableRecords($posts->where('author.email', '!=', $authorEmail));
|
|
});
|
|
|
|
it('can search multiple individual columns', function (): void {
|
|
$posts = Post::factory()->count(10)->create();
|
|
|
|
$content = $posts->first()->content;
|
|
$authorEmail = $posts->first()->author->email;
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTableColumns([
|
|
'content' => $content,
|
|
'author.email' => $authorEmail,
|
|
])
|
|
->assertCanSeeTableRecords($posts->where('author.email', $authorEmail))
|
|
->assertCanNotSeeTableRecords($posts->where('author.email', '!=', $authorEmail));
|
|
});
|
|
|
|
it('can use `splitIndividualSearchTerms(false)` on a column to disable search term splitting for its individual search', function (): void {
|
|
$exactPhrasePost = Post::factory()->create(['content' => 'ipsum dolor']);
|
|
$separateWordsPost = Post::factory()->create(['content' => 'ipsum sit dolor']);
|
|
|
|
livewire(PostsTableWithColumnIndividualSearchTermSplittingDisabled::class)
|
|
->searchTableColumns(['content' => 'ipsum dolor'])
|
|
->assertCanSeeTableRecords([$exactPhrasePost])
|
|
->assertCanNotSeeTableRecords([$separateWordsPost]);
|
|
});
|
|
|
|
it('splits search terms for an individual column search by default, when the column does not define `splitIndividualSearchTerms()`', function (): void {
|
|
$exactPhrasePost = Post::factory()->create(['title' => 'ipsum dolor']);
|
|
$separateWordsPost = Post::factory()->create(['title' => 'ipsum sit dolor']);
|
|
|
|
livewire(PostsTableWithColumnIndividualSearchTermSplittingDisabled::class)
|
|
->searchTableColumns(['title' => 'ipsum dolor'])
|
|
->assertCanSeeTableRecords([$exactPhrasePost, $separateWordsPost]);
|
|
});
|
|
|
|
it('can use `splitIndividualSearchTerms()` on a column to enable search term splitting for its individual search when the table uses `splitSearchTerms(false)`', function (): void {
|
|
$exactPhrasePost = Post::factory()->create(['content' => 'ipsum dolor']);
|
|
$separateWordsPost = Post::factory()->create(['content' => 'ipsum sit dolor']);
|
|
|
|
livewire(PostsTableWithColumnIndividualSearchTermSplittingEnabled::class)
|
|
->searchTableColumns(['content' => 'ipsum dolor'])
|
|
->assertCanSeeTableRecords([$exactPhrasePost, $separateWordsPost]);
|
|
});
|
|
|
|
it('does not split search terms for an individual column search when the table uses `splitSearchTerms(false)` and the column does not define `splitIndividualSearchTerms()`', function (): void {
|
|
$exactPhrasePost = Post::factory()->create(['title' => 'ipsum dolor']);
|
|
$separateWordsPost = Post::factory()->create(['title' => 'ipsum sit dolor']);
|
|
|
|
livewire(PostsTableWithColumnIndividualSearchTermSplittingEnabled::class)
|
|
->searchTableColumns(['title' => 'ipsum dolor'])
|
|
->assertCanSeeTableRecords([$exactPhrasePost])
|
|
->assertCanNotSeeTableRecords([$separateWordsPost]);
|
|
});
|
|
|
|
it('does not leak the `orWhere` of a non-split individual column search across multiple columns past an active filter', function (): void {
|
|
// The searchable column matches on both `title` and `content`. With `splitSearchTerms(false)`,
|
|
// the constraint emits `where(title LIKE ?)->orWhere(content LIKE ?)`. If that pair is not
|
|
// wrapped in its own `where()` group, the trailing `orWhere` escapes the filter group and
|
|
// re-exposes records the filter excluded.
|
|
$search = Str::random();
|
|
|
|
$publishedMatch = Post::factory()->create([
|
|
'is_published' => true,
|
|
'content' => $search,
|
|
]);
|
|
|
|
$unpublishedMatch = Post::factory()->create([
|
|
'is_published' => false,
|
|
'content' => $search,
|
|
]);
|
|
|
|
livewire(PostsTableWithNonSplitMultiColumnIndividualSearch::class)
|
|
->filterTable('is_published')
|
|
->searchTableColumns(['title' => $search])
|
|
->assertCanSeeTableRecords([$publishedMatch])
|
|
->assertCanNotSeeTableRecords([$unpublishedMatch]);
|
|
});
|
|
|
|
it('seeds individually searchable columns whose names collide with JS array properties on mount so `tableColumnSearches` serializes as a JSON object', function (): void {
|
|
livewire(PostsTableWithReservedJsPropertyColumnSearch::class)
|
|
->assertSet('tableColumnSearches', ['length' => '', 'sort' => '']);
|
|
});
|
|
|
|
it('does not seed individually searchable columns whose names do not collide with JS array properties', function (): void {
|
|
livewire(PostsTableWithReservedJsPropertyColumnSearch::class)
|
|
->assertSet('tableColumnSearches', fn (array $tableColumnSearches): bool => ! array_key_exists('title', $tableColumnSearches));
|
|
});
|
|
|
|
it('does not remove a reserved key from `tableColumnSearches` when clearing an individual column search value', function (): void {
|
|
livewire(PostsTableWithReservedJsPropertyColumnSearch::class)
|
|
->set('tableColumnSearches.length', 'foo')
|
|
->assertSet('tableColumnSearches.length', 'foo')
|
|
->set('tableColumnSearches.length', '')
|
|
->assertSet('tableColumnSearches.length', '');
|
|
});
|
|
|
|
it('removes a non-reserved key from `tableColumnSearches` when clearing an individual column search value', function (): void {
|
|
livewire(PostsTableWithReservedJsPropertyColumnSearch::class)
|
|
->set('tableColumnSearches.title', 'foo')
|
|
->assertSet('tableColumnSearches.title', 'foo')
|
|
->set('tableColumnSearches.title', '')
|
|
->assertSet('tableColumnSearches', fn (array $tableColumnSearches): bool => ! array_key_exists('title', $tableColumnSearches));
|
|
});
|
|
|
|
it('keeps reserved keys present in `tableColumnSearches` after resetting all column searches', function (): void {
|
|
livewire(PostsTableWithReservedJsPropertyColumnSearch::class)
|
|
->set('tableColumnSearches.length', 'foo')
|
|
->set('tableColumnSearches.sort', 'bar')
|
|
->call('resetTableColumnSearches')
|
|
->assertSet('tableColumnSearches', ['length' => '', 'sort' => '']);
|
|
});
|
|
|
|
it('renders, responsively hides, and clears individual column search inputs with associated labels for columns named after JavaScript array properties in the browser', function (): void {
|
|
retry(10, function (): void {
|
|
Artisan::call('filament:assets');
|
|
|
|
$this->actingAs(User::factory()->create());
|
|
|
|
Post::factory()->count(3)->create();
|
|
|
|
visit('/individual-column-search-browser-test')
|
|
->assertValue('.fi-ta-individual-search-cell-length input', '')
|
|
->assertValue('.fi-ta-individual-search-cell-sort input', '')
|
|
->assertValue('.fi-ta-individual-search-cell-title input', '')
|
|
->resize(375, 812)
|
|
->assertScript("(() => { const expectedLabels = { length: 'Length', sort: 'Sort', title: 'Title' }; return Object.entries(expectedLabels).every(([column, label]) => document.querySelector('.fi-ta-individual-search-cell-' + column + ' input').labels[0]?.textContent.trim() === label) })()", true)
|
|
->fill('.fi-ta-individual-search-cell-length input', 'foo')
|
|
->wait(1)
|
|
->assertValue('.fi-ta-individual-search-cell-length input', 'foo')
|
|
->fill('.fi-ta-individual-search-cell-length input', '')
|
|
->wait(1)
|
|
->assertValue('.fi-ta-individual-search-cell-length input', '')
|
|
->resize(700, 812)
|
|
->assertScript("document.querySelector('.fi-ta-individual-search-row').checkVisibility()", false)
|
|
->resize(800, 812)
|
|
->assertScript("document.querySelector('.fi-ta-individual-search-row').checkVisibility()", true)
|
|
->assertNoSmoke()
|
|
->assertNoAccessibilityIssues();
|
|
|
|
visit('/individual-column-search-browser-test')
|
|
->inDarkMode()
|
|
->resize(375, 812)
|
|
->assertNoAccessibilityIssues();
|
|
});
|
|
});
|
|
|
|
it('scopes column manager checkbox ids to each table in the browser', function (): void {
|
|
retry(10, function (): void {
|
|
Artisan::call('filament:assets');
|
|
|
|
$this->actingAs(User::factory()->create());
|
|
|
|
Post::factory()->count(3)->create();
|
|
|
|
visit('/column-manager-browser-test')
|
|
->assertPresent('#first-table .fi-ta-header-cell-title')
|
|
->assertPresent('#second-table .fi-ta-header-cell-title')
|
|
// Open the first table's column manager too, so its checkboxes are also rendered when the second manager's labels are clicked.
|
|
->click('#first-table button[aria-label="Column manager"]')
|
|
->click('#second-table button[aria-label="Column manager"]')
|
|
// Every rendered column manager checkbox id must be unique, otherwise a label's `for` can activate a checkbox in another table's manager.
|
|
->assertScript('new Set(Array.from(document.querySelectorAll(\'.fi-ta-col-manager-label input[type="checkbox"]\')).map((checkbox) => checkbox.id)).size === document.querySelectorAll(\'.fi-ta-col-manager-label input[type="checkbox"]\').length', true)
|
|
// Clicking the `Title` label must toggle the checkbox in this table's column manager, not the checkbox of the other table's manager that shares the column name.
|
|
->click('#second-table .fi-ta-col-manager-label[for$="-title"]')
|
|
->wait(1)
|
|
->assertMissing('#second-table .fi-ta-header-cell-title')
|
|
->assertPresent('#first-table .fi-ta-header-cell-title')
|
|
// Restore the toggled column, since the column manager persists in the session and a retried attempt must start from the default state.
|
|
->click('#second-table .fi-ta-col-manager-label[for$="-title"]')
|
|
->wait(1)
|
|
->assertPresent('#second-table .fi-ta-header-cell-title')
|
|
->assertNoSmoke()
|
|
->assertNoAccessibilityIssues();
|
|
|
|
visit('/column-manager-browser-test')
|
|
->inDarkMode()
|
|
->click('#second-table button[aria-label="Column manager"]')
|
|
->assertNoAccessibilityIssues();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('column properties and assertions', function (): void {
|
|
it('can hide a column', function (): void {
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnVisible('visible')
|
|
->assertTableColumnHidden('hidden');
|
|
});
|
|
|
|
it('can call a column action', function (): void {
|
|
$post = Post::factory()->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->callTableColumnAction('title', $post)
|
|
->assertDispatched('title-action-called');
|
|
});
|
|
|
|
it('can call a column action object', function (): void {
|
|
$post = Post::factory()->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->callTableAction('column-action-object', $post)
|
|
->assertDispatched('column-action-object-called');
|
|
});
|
|
|
|
it('can state whether a column has the correct value with Eloquent records', function (): void {
|
|
$post = Post::factory()->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnStateSet('with_state', 'correct state', $post)
|
|
->assertTableColumnStateNotSet('with_state', 'incorrect state', $post);
|
|
});
|
|
|
|
it('can state whether a column has the correct value with custom data', function (): void {
|
|
livewire(CustomDataTable::class)
|
|
->assertTableColumnStateSet('title', 'Second item', 2)
|
|
->assertTableColumnStateNotSet('title', 'incorrect state', 2);
|
|
});
|
|
|
|
it('can state whether a column with `counts()` has the correct value', function (): void {
|
|
$user = User::factory()
|
|
->has(Post::factory()->count(3))
|
|
->create();
|
|
|
|
livewire(UsersTable::class)
|
|
->assertTableColumnStateSet('posts_count', 3, $user)
|
|
->assertTableColumnStateNotSet('posts_count', 0, $user);
|
|
});
|
|
|
|
it('can state whether a column has the correct formatted value with Eloquent records', function (): void {
|
|
$post = Post::factory()->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnFormattedStateSet('formatted_state', 'formatted state', $post)
|
|
->assertTableColumnFormattedStateNotSet('formatted_state', 'incorrect formatted state', $post);
|
|
});
|
|
|
|
it('can state whether a column has the correct formatted value with custom data', function (): void {
|
|
livewire(CustomDataTable::class)
|
|
->assertTableColumnFormattedStateSet('formatted_state', 'formatted state', 1)
|
|
->assertTableColumnFormattedStateNotSet('formatted_state', 'incorrect formatted state', 1);
|
|
});
|
|
|
|
it('can output JSON values', function (): void {
|
|
$post = Post::factory()->create([
|
|
'json' => ['foo' => 'bar'],
|
|
]);
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnStateSet('json.foo', 'bar', $post);
|
|
});
|
|
|
|
it('can output nested JSON values', function (): void {
|
|
$post = Post::factory()->create([
|
|
'json' => ['bar' => ['baz' => 'qux']],
|
|
]);
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnStateSet('json.bar.baz', 'qux', $post);
|
|
});
|
|
|
|
it('can output relationship JSON values', function (): void {
|
|
$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 (): void {
|
|
$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 (): void {
|
|
$post = Post::factory()->create([
|
|
'json_array_of_objects' => [
|
|
['value' => 'foo'],
|
|
['value' => 'bar'],
|
|
['value' => 'baz'],
|
|
],
|
|
]);
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnStateSet('json_array_of_objects.*.value', ['foo', 'bar', 'baz'], $post);
|
|
});
|
|
|
|
it('can output values in a JSON column with a non-relationship accessor method', function (): void {
|
|
$post = Post::factory()->create([
|
|
'config' => ['setting' => 'foo'],
|
|
]);
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnStateSet('config.setting', 'foo', $post);
|
|
});
|
|
|
|
it('can state whether a column has extra attributes with Eloquent records', function (): void {
|
|
$post = Post::factory()->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnHasExtraAttributes('extra_attributes', ['class' => 'text-danger-500'], $post)
|
|
->assertTableColumnDoesNotHaveExtraAttributes('extra_attributes', ['class' => 'text-primary-500'], $post);
|
|
});
|
|
|
|
it('can state whether a column has extra attributes with custom data', function (): void {
|
|
livewire(CustomDataTable::class)
|
|
->assertTableColumnHasExtraAttributes('extra_attributes', ['class' => 'text-danger-500'], 1)
|
|
->assertTableColumnDoesNotHaveExtraAttributes('extra_attributes', ['class' => 'text-primary-500'], 1);
|
|
});
|
|
|
|
it('can state whether a column has a description with Eloquent records', function (): void {
|
|
$post = Post::factory()->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnHasDescription('with_description', 'description below', $post)
|
|
->assertTableColumnHasDescription('with_description', 'description above', $post, 'above')
|
|
->assertTableColumnDoesNotHaveDescription('with_description', 'incorrect description below', $post)
|
|
->assertTableColumnDoesNotHaveDescription('with_description', 'incorrect description above', $post, 'above');
|
|
});
|
|
|
|
it('can state whether a column has a description with custom data', function (): void {
|
|
livewire(CustomDataTable::class)
|
|
->assertTableColumnHasDescription('with_description', 'description below', 1)
|
|
->assertTableColumnHasDescription('with_description', 'description above', 1, 'above')
|
|
->assertTableColumnDoesNotHaveDescription('with_description', 'incorrect description below', 1)
|
|
->assertTableColumnDoesNotHaveDescription('with_description', 'incorrect description above', 1, 'above');
|
|
});
|
|
|
|
it('can state whether a select column has options with Eloquent records', function (): void {
|
|
$post = Post::factory()->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableSelectColumnHasOptions('with_options', ['red' => 'Red', 'blue' => 'Blue'], $post)
|
|
->assertTableSelectColumnDoesNotHaveOptions('with_options', ['one' => 'One', 'two' => 'Two'], $post);
|
|
});
|
|
|
|
it('can state whether a select column has options with custom data', function (): void {
|
|
livewire(CustomDataTable::class)
|
|
->assertTableSelectColumnHasOptions('with_options', ['red' => 'Red', 'blue' => 'Blue'], 2)
|
|
->assertTableSelectColumnDoesNotHaveOptions('with_options', ['one' => 'One', 'two' => 'Two'], 2);
|
|
});
|
|
|
|
it('can assert that a column exists with the given configuration', function (): void {
|
|
$publishedPost = Post::factory()->create([
|
|
'is_published' => true,
|
|
]);
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnExists('title2', function (TextColumn $column) {
|
|
return $column->isSortable() &&
|
|
$column->isSearchable() &&
|
|
$column->getPrefix() == 'published';
|
|
}, $publishedPost);
|
|
|
|
$unpublishedPost = Post::factory()->create([
|
|
'is_published' => false,
|
|
]);
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnExists('title2', function (TextColumn $column) {
|
|
return $column->getPrefix() == 'unpublished';
|
|
}, $unpublishedPost);
|
|
|
|
$this->expectException('PHPUnit\Framework\ExpectationFailedException');
|
|
$this->expectExceptionMessage('Failed asserting that a column with the name [title] and provided configuration exists on the [' . PostsTable::class . '] component');
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnExists('title', function (TextColumn $column) {
|
|
return $column->isTime();
|
|
}, $publishedPost);
|
|
});
|
|
|
|
it('can automatically detect boolean cast attribute in icon column', function (): void {
|
|
$post = Post::factory()
|
|
->create(['is_published' => false]);
|
|
|
|
livewire(PostsTable::class)
|
|
->assertTableColumnExists('is_published', function (IconColumn $column) {
|
|
return $column->isBoolean();
|
|
}, $post);
|
|
});
|
|
|
|
it('can toggle all table columns', function (): void {
|
|
Post::factory()->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->assertSuccessful()
|
|
->assertCountTableRecords(1)
|
|
->assertDontSeeText('Toggleable column state')
|
|
->toggleAllTableColumns()
|
|
->assertSeeText('Toggleable column state')
|
|
->toggleAllTableColumns(false)
|
|
->assertDontSeeText('Toggleable column state');
|
|
});
|
|
});
|
|
|
|
describe('relationship columns', function (): void {
|
|
it('can output the state of a nested relationship through a `MorphTo` relationship', function (): void {
|
|
$team = Team::factory()->create(['name' => 'Team Alpha']);
|
|
$user = User::factory()->create([
|
|
'name' => 'Alice',
|
|
'team_id' => $team->id,
|
|
]);
|
|
$image = Image::factory()->for($user, 'imageable')->create();
|
|
|
|
livewire(ImagesTable::class)
|
|
->assertTableColumnStateSet('imageable.team.name', 'Team Alpha', $image)
|
|
->assertTableColumnStateNotSet('imageable.team.name', 'Alice', $image)
|
|
->assertTableColumnFormattedStateSet('imageable.team.name', 'Team Alpha', $image)
|
|
->assertTableColumnFormattedStateNotSet('imageable.team.name', 'Alice', $image);
|
|
});
|
|
|
|
it('can output the state of multiple nested relationships through the same `MorphTo` relationship', function (): void {
|
|
$company = Company::factory()->create(['name' => 'Acme Corporation']);
|
|
$team = Team::factory()->create([
|
|
'name' => 'Team Alpha',
|
|
'company_id' => $company->id,
|
|
]);
|
|
$user = User::factory()->create([
|
|
'name' => 'Alice',
|
|
'team_id' => $team->id,
|
|
]);
|
|
$image = Image::factory()->for($user, 'imageable')->create();
|
|
|
|
livewire(ImagesTable::class)
|
|
->assertTableColumnStateSet('imageable.team.name', 'Team Alpha', $image)
|
|
->assertTableColumnStateSet('imageable.company.name', 'Acme Corporation', $image);
|
|
});
|
|
|
|
it('can output the state of nested relationships through a `MorphTo` relationship with mixed related model types', function (): void {
|
|
$userCompany = Company::factory()->create(['name' => 'Acme Corporation']);
|
|
$team = Team::factory()->create(['company_id' => $userCompany->id]);
|
|
$user = User::factory()->create([
|
|
'name' => 'Alice',
|
|
'team_id' => $team->id,
|
|
]);
|
|
$userImage = Image::factory()->for($user, 'imageable')->create();
|
|
|
|
$profileCompany = Company::factory()->create(['name' => 'Globex Corporation']);
|
|
$profile = Profile::factory()->create([
|
|
'user_id' => $user->id,
|
|
'company_id' => $profileCompany->id,
|
|
]);
|
|
$profileImage = Image::factory()->for($profile, 'imageable')->create();
|
|
|
|
livewire(ImagesTable::class)
|
|
->assertTableColumnStateSet('imageable.company.name', 'Acme Corporation', $userImage)
|
|
->assertTableColumnStateSet('imageable.company.name', 'Globex Corporation', $profileImage);
|
|
});
|
|
|
|
it('can search and sort by relationship column when both tables have the same column name', function (): void {
|
|
$teamAlpha = Team::factory()->create(['name' => 'Team Alpha']);
|
|
$teamBeta = Team::factory()->create(['name' => 'Team Beta']);
|
|
|
|
$userAlice = User::factory()->create([
|
|
'name' => 'Alice',
|
|
'team_id' => $teamAlpha->id,
|
|
]);
|
|
|
|
$userBob = User::factory()->create([
|
|
'name' => 'Bob',
|
|
'team_id' => $teamBeta->id,
|
|
]);
|
|
|
|
livewire(UsersWithTeamTable::class)
|
|
->searchTable('Alice')
|
|
->sortTable('team.name')
|
|
->assertCanSeeTableRecords([$userAlice])
|
|
->assertCanNotSeeTableRecords([$userBob]);
|
|
});
|
|
|
|
it('can search and sort by nested relationship column when tables have the same column name, with a qualified column', function (): void {
|
|
// Create teams (teams table has 'name' column)
|
|
$teamAlpha = Team::factory()->create(['name' => 'Team Alpha']);
|
|
$teamBeta = Team::factory()->create(['name' => 'Team Beta']);
|
|
$teamGamma = Team::factory()->create(['name' => 'Team Gamma']);
|
|
|
|
// Create users with names and team assignments (users table has 'name' column)
|
|
$userAlice = User::factory()->create([
|
|
'name' => 'Alice',
|
|
'team_id' => $teamAlpha->id,
|
|
]);
|
|
|
|
$userBob = User::factory()->create([
|
|
'name' => 'Bob',
|
|
'team_id' => $teamBeta->id,
|
|
]);
|
|
|
|
$userCharlie = User::factory()->create([
|
|
'name' => 'Charlie',
|
|
'team_id' => $teamGamma->id,
|
|
]);
|
|
|
|
// Create posts with those authors (posts table has searchable 'title' column)
|
|
$postByAlice = Post::factory()->create([
|
|
'author_id' => $userAlice->id,
|
|
'title' => 'Alice\'s Post',
|
|
]);
|
|
|
|
$postByBob = Post::factory()->create([
|
|
'author_id' => $userBob->id,
|
|
'title' => 'Bob\'s Post',
|
|
]);
|
|
|
|
$postByCharlie = Post::factory()->create([
|
|
'author_id' => $userCharlie->id,
|
|
'title' => 'Charlie\'s Post',
|
|
]);
|
|
|
|
livewire(PostsTableWithQualifiedColumns::class)
|
|
->searchTable('Alice')
|
|
->sortTable('author.team.name')
|
|
->assertCanSeeTableRecords([$postByAlice])
|
|
->assertCanNotSeeTableRecords([$postByBob, $postByCharlie]);
|
|
});
|
|
|
|
it('can sort records with `BelongsTo` -> `HasOne` relationship', function (): void {
|
|
Post::factory()->count(5)->state(fn (): array => [
|
|
'author_id' => User::factory()->has(
|
|
Profile::factory()->state(fn (): array => [
|
|
'bio' => fake()->sentence(),
|
|
]),
|
|
'profile'
|
|
),
|
|
])->create();
|
|
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
Profile::query()
|
|
->select('bio')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->join('users', 'users.id', '=', 'profiles.user_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
Profile::query()
|
|
->select('bio')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->join('users', 'users.id', '=', 'profiles.user_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.profile.bio')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('author.profile.bio', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with `BelongsTo` -> `HasOne` -> `BelongsTo` relationship', function (): void {
|
|
Post::factory()->count(5)->state(fn (): array => [
|
|
'author_id' => User::factory()->has(
|
|
Profile::factory()->state(fn (): array => [
|
|
'company_id' => Company::factory(),
|
|
]),
|
|
'profile'
|
|
),
|
|
])->create();
|
|
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
Company::query()
|
|
->select('companies.name')
|
|
->whereColumn('companies.id', 'profiles.company_id')
|
|
->join('profiles', 'profiles.company_id', '=', 'companies.id')
|
|
->join('users', 'users.id', '=', 'profiles.user_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
Company::query()
|
|
->select('companies.name')
|
|
->whereColumn('companies.id', 'profiles.company_id')
|
|
->join('profiles', 'profiles.company_id', '=', 'companies.id')
|
|
->join('users', 'users.id', '=', 'profiles.user_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.profile.company.name')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('author.profile.company.name', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can search records with `BelongsTo` -> `HasOne` relationship', function (): void {
|
|
$searchBio = 'unique bio for testing';
|
|
|
|
$matchingUser = User::factory()->has(
|
|
Profile::factory()->state([
|
|
'bio' => $searchBio,
|
|
]),
|
|
'profile'
|
|
)->create();
|
|
|
|
$matchingPost = Post::factory()->for($matchingUser, 'author')->create();
|
|
|
|
$nonMatchingPosts = Post::factory()->count(3)->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable($searchBio)
|
|
->assertCanSeeTableRecords([$matchingPost])
|
|
->assertCanNotSeeTableRecords($nonMatchingPosts);
|
|
});
|
|
|
|
it('can search records with `BelongsTo` -> `HasOne` -> `BelongsTo` relationship', function (): void {
|
|
$searchCompany = 'Acme Corporation Testing';
|
|
|
|
$company = Company::factory()->create([
|
|
'name' => $searchCompany,
|
|
]);
|
|
|
|
$matchingUser = User::factory()->has(
|
|
Profile::factory()->for($company, 'company'),
|
|
'profile'
|
|
)->create();
|
|
|
|
$matchingPost = Post::factory()->for($matchingUser, 'author')->create();
|
|
|
|
$nonMatchingPosts = Post::factory()->count(3)->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable($searchCompany)
|
|
->assertCanSeeTableRecords([$matchingPost])
|
|
->assertCanNotSeeTableRecords($nonMatchingPosts);
|
|
});
|
|
|
|
it('can sort records with `HasOne` -> `BelongsTo` relationship', function (): void {
|
|
for ($i = 0; $i < 5; $i++) {
|
|
$company = Company::factory()->create(['name' => 'Company ' . chr(65 + $i)]); // A, B, C, D, E
|
|
User::factory()->has(
|
|
Profile::factory()->for($company, 'company'),
|
|
'profile'
|
|
)->create();
|
|
}
|
|
|
|
$sortedAsc = User::query()
|
|
->orderBy(
|
|
Company::query()
|
|
->select('name')
|
|
->whereColumn('companies.id', 'profiles.company_id')
|
|
->join('profiles', 'profiles.company_id', '=', 'companies.id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
$sortedDesc = User::query()
|
|
->orderByDesc(
|
|
Company::query()
|
|
->select('name')
|
|
->whereColumn('companies.id', 'profiles.company_id')
|
|
->join('profiles', 'profiles.company_id', '=', 'companies.id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
livewire(UsersTable::class)
|
|
->sortTable('profile.company.name')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('profile.company.name', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with `HasOne` -> `HasOne` relationship', function (): void {
|
|
$themes = ['alpha', 'beta', 'gamma', 'delta', 'epsilon'];
|
|
|
|
foreach ($themes as $theme) {
|
|
User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['theme' => $theme]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
}
|
|
|
|
$sortedAsc = User::query()
|
|
->orderBy(
|
|
Setting::query()
|
|
->select('theme')
|
|
->whereColumn('settings.profile_id', 'profiles.id')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
$sortedDesc = User::query()
|
|
->orderByDesc(
|
|
Setting::query()
|
|
->select('theme')
|
|
->whereColumn('settings.profile_id', 'profiles.id')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
livewire(UsersTable::class)
|
|
->sortTable('profile.setting.theme')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('profile.setting.theme', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can search records with `HasOne` -> `BelongsTo` relationship', function (): void {
|
|
$searchCompany = 'TechCorp Testing Inc';
|
|
|
|
$company = Company::factory()->create(['name' => $searchCompany]);
|
|
|
|
$matchingUser = User::factory()->has(
|
|
Profile::factory()->for($company, 'company'),
|
|
'profile'
|
|
)->create();
|
|
|
|
$nonMatchingUsers = User::factory()->count(3)->create();
|
|
|
|
livewire(UsersTable::class)
|
|
->searchTable($searchCompany)
|
|
->assertCanSeeTableRecords([$matchingUser])
|
|
->assertCanNotSeeTableRecords($nonMatchingUsers);
|
|
});
|
|
|
|
it('can search records with `HasOne` -> `HasOne` relationship', function (): void {
|
|
$searchTheme = 'midnight-purple';
|
|
|
|
$matchingUser = User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['theme' => $searchTheme]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
|
|
$nonMatchingUsers = User::factory()->count(3)->create();
|
|
|
|
livewire(UsersTable::class)
|
|
->searchTable($searchTheme)
|
|
->assertCanSeeTableRecords([$matchingUser])
|
|
->assertCanNotSeeTableRecords($nonMatchingUsers);
|
|
});
|
|
|
|
it('can sort records with `MorphOne` relationship', function (): void {
|
|
$urls = ['alpha.jpg', 'beta.jpg', 'gamma.jpg', 'delta.jpg', 'epsilon.jpg'];
|
|
|
|
foreach ($urls as $url) {
|
|
$user = User::factory()->create();
|
|
Image::factory()->create([
|
|
'url' => $url,
|
|
'imageable_type' => User::class,
|
|
'imageable_id' => $user->id,
|
|
]);
|
|
}
|
|
|
|
$sortedAsc = User::query()
|
|
->orderBy(
|
|
Image::query()
|
|
->select('url')
|
|
->whereColumn('images.imageable_id', 'users.id')
|
|
->where('images.imageable_type', User::class)
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
$sortedDesc = User::query()
|
|
->orderByDesc(
|
|
Image::query()
|
|
->select('url')
|
|
->whereColumn('images.imageable_id', 'users.id')
|
|
->where('images.imageable_type', User::class)
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
livewire(UsersTable::class)
|
|
->sortTable('image.url')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('image.url', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with `BelongsTo` -> `MorphOne` relationship', function (): void {
|
|
$urls = ['alpha.jpg', 'beta.jpg', 'gamma.jpg', 'delta.jpg', 'epsilon.jpg'];
|
|
|
|
foreach ($urls as $url) {
|
|
$user = User::factory()->create();
|
|
Image::factory()->create([
|
|
'url' => $url,
|
|
'imageable_type' => User::class,
|
|
'imageable_id' => $user->id,
|
|
]);
|
|
Post::factory()->create(['author_id' => $user->id]);
|
|
}
|
|
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
Image::query()
|
|
->select('url')
|
|
->whereColumn('images.imageable_id', 'users.id')
|
|
->where('images.imageable_type', User::class)
|
|
->join('users', 'users.id', '=', 'images.imageable_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
Image::query()
|
|
->select('url')
|
|
->whereColumn('images.imageable_id', 'users.id')
|
|
->where('images.imageable_type', User::class)
|
|
->join('users', 'users.id', '=', 'images.imageable_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.image.url')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('author.image.url', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with `BelongsTo` -> `HasOne` -> `MorphOne` relationship', function (): void {
|
|
$altTexts = ['alt alpha', 'alt beta', 'alt gamma', 'alt delta', 'alt epsilon'];
|
|
|
|
foreach ($altTexts as $altText) {
|
|
$user = User::factory()->create();
|
|
$profile = Profile::factory()->create(['user_id' => $user->id]);
|
|
Image::factory()->create([
|
|
'alt_text' => $altText,
|
|
'imageable_type' => Profile::class,
|
|
'imageable_id' => $profile->id,
|
|
]);
|
|
Post::factory()->create(['author_id' => $user->id]);
|
|
}
|
|
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
Image::query()
|
|
->select('alt_text')
|
|
->whereColumn('images.imageable_id', 'profiles.id')
|
|
->where('images.imageable_type', Profile::class)
|
|
->join('profiles', 'profiles.id', '=', 'images.imageable_id')
|
|
->join('users', 'users.id', '=', 'profiles.user_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
Image::query()
|
|
->select('alt_text')
|
|
->whereColumn('images.imageable_id', 'profiles.id')
|
|
->where('images.imageable_type', Profile::class)
|
|
->join('profiles', 'profiles.id', '=', 'images.imageable_id')
|
|
->join('users', 'users.id', '=', 'profiles.user_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.profile.image.alt_text')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('author.profile.image.alt_text', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can search records with `MorphOne` relationship', function (): void {
|
|
$searchUrl = 'unique-image-url.jpg';
|
|
|
|
$matchingUser = User::factory()->create();
|
|
Image::factory()->create([
|
|
'url' => $searchUrl,
|
|
'imageable_type' => User::class,
|
|
'imageable_id' => $matchingUser->id,
|
|
]);
|
|
|
|
$nonMatchingUsers = User::factory()->count(3)->create();
|
|
|
|
livewire(UsersTable::class)
|
|
->searchTable($searchUrl)
|
|
->assertCanSeeTableRecords([$matchingUser])
|
|
->assertCanNotSeeTableRecords($nonMatchingUsers);
|
|
});
|
|
|
|
it('can search records with `BelongsTo` -> `MorphOne` relationship', function (): void {
|
|
$searchUrl = 'unique-author-image.jpg';
|
|
|
|
$matchingUser = User::factory()->create();
|
|
Image::factory()->create([
|
|
'url' => $searchUrl,
|
|
'imageable_type' => User::class,
|
|
'imageable_id' => $matchingUser->id,
|
|
]);
|
|
|
|
$matchingPost = Post::factory()->for($matchingUser, 'author')->create();
|
|
$nonMatchingPosts = Post::factory()->count(3)->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable($searchUrl)
|
|
->assertCanSeeTableRecords([$matchingPost])
|
|
->assertCanNotSeeTableRecords($nonMatchingPosts);
|
|
});
|
|
|
|
it('can search records with `BelongsTo` -> `HasOne` -> `MorphOne` relationship', function (): void {
|
|
$searchAltText = 'unique profile image alt text';
|
|
|
|
$matchingUser = User::factory()->create();
|
|
$matchingProfile = Profile::factory()->create(['user_id' => $matchingUser->id]);
|
|
Image::factory()->create([
|
|
'alt_text' => $searchAltText,
|
|
'imageable_type' => Profile::class,
|
|
'imageable_id' => $matchingProfile->id,
|
|
]);
|
|
|
|
$matchingPost = Post::factory()->for($matchingUser, 'author')->create();
|
|
$nonMatchingPosts = Post::factory()->count(3)->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable($searchAltText)
|
|
->assertCanSeeTableRecords([$matchingPost])
|
|
->assertCanNotSeeTableRecords($nonMatchingPosts);
|
|
});
|
|
|
|
it('can sort records with nullable `BelongsTo` relationship', function (): void {
|
|
// Create posts with authors
|
|
$userAlice = User::factory()->create(['name' => 'Alice']);
|
|
$userBob = User::factory()->create(['name' => 'Bob']);
|
|
|
|
$postWithAlice = Post::factory()->create(['author_id' => $userAlice->id]);
|
|
$postWithBob = Post::factory()->create(['author_id' => $userBob->id]);
|
|
|
|
// Create posts without authors (null relationship)
|
|
$postWithoutAuthor1 = Post::factory()->create(['author_id' => null]);
|
|
$postWithoutAuthor2 = Post::factory()->create(['author_id' => null]);
|
|
|
|
$allPosts = collect([$postWithAlice, $postWithBob, $postWithoutAuthor1, $postWithoutAuthor2]);
|
|
|
|
// Just verify sorting doesn't crash with nullable relationships
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.name')
|
|
->assertCanSeeTableRecords($allPosts)
|
|
->sortTable('author.name', 'desc')
|
|
->assertCanSeeTableRecords($allPosts);
|
|
});
|
|
|
|
it('can sort records with nullable `HasOne` relationship', function (): void {
|
|
// Create users with profiles
|
|
$userWithProfile1 = User::factory()->has(
|
|
Profile::factory()->state(['bio' => 'Alpha bio']),
|
|
'profile'
|
|
)->create();
|
|
|
|
$userWithProfile2 = User::factory()->has(
|
|
Profile::factory()->state(['bio' => 'Beta bio']),
|
|
'profile'
|
|
)->create();
|
|
|
|
// Create users without profiles (null relationship)
|
|
$userWithoutProfile1 = User::factory()->create();
|
|
$userWithoutProfile2 = User::factory()->create();
|
|
|
|
$allUsers = collect([$userWithProfile1, $userWithProfile2, $userWithoutProfile1, $userWithoutProfile2]);
|
|
|
|
// Just verify sorting doesn't crash with nullable relationships
|
|
livewire(UsersTable::class)
|
|
->sortTable('profile.bio')
|
|
->assertCanSeeTableRecords($allUsers)
|
|
->sortTable('profile.bio', 'desc')
|
|
->assertCanSeeTableRecords($allUsers);
|
|
});
|
|
|
|
it('can sort records with nullable `MorphOne` relationship', function (): void {
|
|
// Create users with images
|
|
$userWithImage1 = User::factory()->create();
|
|
Image::factory()->create([
|
|
'url' => 'alpha.jpg',
|
|
'imageable_type' => User::class,
|
|
'imageable_id' => $userWithImage1->id,
|
|
]);
|
|
|
|
$userWithImage2 = User::factory()->create();
|
|
Image::factory()->create([
|
|
'url' => 'beta.jpg',
|
|
'imageable_type' => User::class,
|
|
'imageable_id' => $userWithImage2->id,
|
|
]);
|
|
|
|
// Create users without images (null relationship)
|
|
$userWithoutImage1 = User::factory()->create();
|
|
$userWithoutImage2 = User::factory()->create();
|
|
|
|
$allUsers = collect([$userWithImage1, $userWithImage2, $userWithoutImage1, $userWithoutImage2]);
|
|
|
|
// Just verify sorting doesn't crash with nullable relationships
|
|
livewire(UsersTable::class)
|
|
->sortTable('image.url')
|
|
->assertCanSeeTableRecords($allUsers)
|
|
->sortTable('image.url', 'desc')
|
|
->assertCanSeeTableRecords($allUsers);
|
|
});
|
|
|
|
it('can sort records with nullable nested `BelongsTo` -> `HasOne` relationship', function (): void {
|
|
// Post with author that has profile
|
|
$userWithProfile = User::factory()->has(
|
|
Profile::factory()->state(['bio' => 'Alpha bio']),
|
|
'profile'
|
|
)->create();
|
|
$postWithAuthorAndProfile = Post::factory()->create(['author_id' => $userWithProfile->id]);
|
|
|
|
// Post with author but no profile
|
|
$userWithoutProfile = User::factory()->create();
|
|
$postWithAuthorNoProfile = Post::factory()->create(['author_id' => $userWithoutProfile->id]);
|
|
|
|
// Post with no author
|
|
$postWithoutAuthor = Post::factory()->create(['author_id' => null]);
|
|
|
|
$allPosts = collect([$postWithAuthorAndProfile, $postWithAuthorNoProfile, $postWithoutAuthor]);
|
|
|
|
// Just verify sorting doesn't crash with nullable nested relationships
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.profile.bio')
|
|
->assertCanSeeTableRecords($allPosts)
|
|
->sortTable('author.profile.bio', 'desc')
|
|
->assertCanSeeTableRecords($allPosts);
|
|
});
|
|
|
|
it('can sort records with nullable nested `BelongsTo` -> `HasOne` -> `BelongsTo` relationship', function (): void {
|
|
// Post with author->profile->company
|
|
$company = Company::factory()->create(['name' => 'Acme Corp']);
|
|
$userWithProfileAndCompany = User::factory()->has(
|
|
Profile::factory()->for($company, 'company'),
|
|
'profile'
|
|
)->create();
|
|
$postComplete = Post::factory()->create(['author_id' => $userWithProfileAndCompany->id]);
|
|
|
|
// Post with author and profile but no company
|
|
$userWithProfileNoCompany = User::factory()->has(
|
|
Profile::factory()->state(['company_id' => null]),
|
|
'profile'
|
|
)->create();
|
|
$postNoCompany = Post::factory()->create(['author_id' => $userWithProfileNoCompany->id]);
|
|
|
|
// Post with author but no profile
|
|
$userNoProfile = User::factory()->create();
|
|
$postNoProfile = Post::factory()->create(['author_id' => $userNoProfile->id]);
|
|
|
|
// Post with no author
|
|
$postNoAuthor = Post::factory()->create(['author_id' => null]);
|
|
|
|
$allPosts = collect([$postComplete, $postNoCompany, $postNoProfile, $postNoAuthor]);
|
|
|
|
// Just verify sorting doesn't crash with nullable nested relationships
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.profile.company.name')
|
|
->assertCanSeeTableRecords($allPosts)
|
|
->sortTable('author.profile.company.name', 'desc')
|
|
->assertCanSeeTableRecords($allPosts);
|
|
});
|
|
|
|
it('can sort records with nullable nested `BelongsTo` -> `MorphOne` relationship', function (): void {
|
|
// Post with author that has image
|
|
$userWithImage = User::factory()->create();
|
|
Image::factory()->create([
|
|
'url' => 'alpha.jpg',
|
|
'imageable_type' => User::class,
|
|
'imageable_id' => $userWithImage->id,
|
|
]);
|
|
$postWithAuthorAndImage = Post::factory()->create(['author_id' => $userWithImage->id]);
|
|
|
|
// Post with author but no image
|
|
$userWithoutImage = User::factory()->create();
|
|
$postWithAuthorNoImage = Post::factory()->create(['author_id' => $userWithoutImage->id]);
|
|
|
|
// Post with no author
|
|
$postWithoutAuthor = Post::factory()->create(['author_id' => null]);
|
|
|
|
$allPosts = collect([$postWithAuthorAndImage, $postWithAuthorNoImage, $postWithoutAuthor]);
|
|
|
|
// Just verify sorting doesn't crash with nullable nested relationships
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.image.url')
|
|
->assertCanSeeTableRecords($allPosts)
|
|
->sortTable('author.image.url', 'desc')
|
|
->assertCanSeeTableRecords($allPosts);
|
|
});
|
|
|
|
it('can search records with nullable `BelongsTo` relationship', function (): void {
|
|
$userAlice = User::factory()->create(['name' => 'Alice Unique']);
|
|
|
|
$matchingPost = Post::factory()->create(['author_id' => $userAlice->id]);
|
|
$postWithoutAuthor = Post::factory()->create(['author_id' => null]);
|
|
$nonMatchingPost = Post::factory()->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable('Alice Unique')
|
|
->assertCanSeeTableRecords([$matchingPost])
|
|
->assertCanNotSeeTableRecords([$postWithoutAuthor, $nonMatchingPost]);
|
|
});
|
|
|
|
it('can search records with nullable `HasOne` relationship', function (): void {
|
|
$userWithProfile = User::factory()->has(
|
|
Profile::factory()->state(['bio' => 'Unique bio search']),
|
|
'profile'
|
|
)->create();
|
|
|
|
$userWithoutProfile = User::factory()->create();
|
|
$otherUsers = User::factory()->count(2)->create();
|
|
|
|
livewire(UsersTable::class)
|
|
->searchTable('Unique bio search')
|
|
->assertCanSeeTableRecords([$userWithProfile])
|
|
->assertCanNotSeeTableRecords([$userWithoutProfile, ...$otherUsers]);
|
|
});
|
|
|
|
it('can sort records with `BelongsToThrough` relationship', function (): void {
|
|
Post::factory()->count(5)->state(fn (): array => [
|
|
'author_id' => User::factory()->state([
|
|
'team_id' => Team::factory(),
|
|
]),
|
|
])->create();
|
|
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
Team::query()
|
|
->select('teams.name')
|
|
->whereColumn('teams.id', 'users.team_id')
|
|
->join('users', 'users.team_id', '=', 'teams.id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
Team::query()
|
|
->select('teams.name')
|
|
->whereColumn('teams.id', 'users.team_id')
|
|
->join('users', 'users.team_id', '=', 'teams.id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('team.name')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('team.name', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with nullable `BelongsToThrough` relationship', function (): void {
|
|
// Post with author that has team
|
|
$teamAlpha = Team::factory()->create(['name' => 'Alpha Team']);
|
|
$userWithTeam = User::factory()->create(['team_id' => $teamAlpha->id]);
|
|
$postWithTeam = Post::factory()->create(['author_id' => $userWithTeam->id]);
|
|
|
|
// Post with author but no team
|
|
$userWithoutTeam = User::factory()->create(['team_id' => null]);
|
|
$postWithAuthorNoTeam = Post::factory()->create(['author_id' => $userWithoutTeam->id]);
|
|
|
|
// Post with no author
|
|
$postWithoutAuthor = Post::factory()->create(['author_id' => null]);
|
|
|
|
$allPosts = collect([$postWithTeam, $postWithAuthorNoTeam, $postWithoutAuthor]);
|
|
|
|
// Just verify sorting doesn't crash with nullable relationships
|
|
livewire(PostsTable::class)
|
|
->sortTable('team.name')
|
|
->assertCanSeeTableRecords($allPosts)
|
|
->sortTable('team.name', 'desc')
|
|
->assertCanSeeTableRecords($allPosts);
|
|
});
|
|
|
|
it('can search records with `BelongsToThrough` relationship', function (): void {
|
|
$searchTeam = 'Unique Team Name';
|
|
|
|
$team = Team::factory()->create(['name' => $searchTeam]);
|
|
$matchingUser = User::factory()->create(['team_id' => $team->id]);
|
|
$matchingPost = Post::factory()->for($matchingUser, 'author')->create();
|
|
|
|
$nonMatchingPosts = Post::factory()->count(3)->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable($searchTeam)
|
|
->assertCanSeeTableRecords([$matchingPost])
|
|
->assertCanNotSeeTableRecords($nonMatchingPosts);
|
|
});
|
|
|
|
it('can sort records with nested `BelongsToThrough` -> `BelongsTo` relationship', function (): void {
|
|
Post::factory()->count(5)->state(fn (): array => [
|
|
'author_id' => User::factory()->state([
|
|
'team_id' => Team::factory()->state([
|
|
'company_id' => Company::factory(),
|
|
]),
|
|
]),
|
|
])->create();
|
|
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
Company::query()
|
|
->select('companies.name')
|
|
->whereColumn('companies.id', 'teams.company_id')
|
|
->join('teams', 'teams.company_id', '=', 'companies.id')
|
|
->join('users', 'users.team_id', '=', 'teams.id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
Company::query()
|
|
->select('companies.name')
|
|
->whereColumn('companies.id', 'teams.company_id')
|
|
->join('teams', 'teams.company_id', '=', 'companies.id')
|
|
->join('users', 'users.team_id', '=', 'teams.id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('team.company.name')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('team.company.name', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with nullable nested `BelongsToThrough` -> `BelongsTo` relationship', function (): void {
|
|
// Post with team and company
|
|
$company = Company::factory()->create(['name' => 'Acme Corp']);
|
|
$teamWithCompany = Team::factory()->create(['company_id' => $company->id]);
|
|
$userWithTeamAndCompany = User::factory()->create(['team_id' => $teamWithCompany->id]);
|
|
$postComplete = Post::factory()->create(['author_id' => $userWithTeamAndCompany->id]);
|
|
|
|
// Post with team but no company
|
|
$teamWithoutCompany = Team::factory()->create(['company_id' => null]);
|
|
$userWithTeamNoCompany = User::factory()->create(['team_id' => $teamWithoutCompany->id]);
|
|
$postNoCompany = Post::factory()->create(['author_id' => $userWithTeamNoCompany->id]);
|
|
|
|
// Post with author but no team
|
|
$userWithoutTeam = User::factory()->create(['team_id' => null]);
|
|
$postNoTeam = Post::factory()->create(['author_id' => $userWithoutTeam->id]);
|
|
|
|
// Post with no author
|
|
$postNoAuthor = Post::factory()->create(['author_id' => null]);
|
|
|
|
$allPosts = collect([$postComplete, $postNoCompany, $postNoTeam, $postNoAuthor]);
|
|
|
|
// Just verify sorting doesn't crash with nullable nested relationships
|
|
livewire(PostsTable::class)
|
|
->sortTable('team.company.name')
|
|
->assertCanSeeTableRecords($allPosts)
|
|
->sortTable('team.company.name', 'desc')
|
|
->assertCanSeeTableRecords($allPosts);
|
|
});
|
|
|
|
it('can search records with nested `BelongsToThrough` -> `BelongsTo` relationship', function (): void {
|
|
$searchCompany = 'Unique Company Name';
|
|
|
|
$company = Company::factory()->create(['name' => $searchCompany]);
|
|
$team = Team::factory()->create(['company_id' => $company->id]);
|
|
$matchingUser = User::factory()->create(['team_id' => $team->id]);
|
|
$matchingPost = Post::factory()->for($matchingUser, 'author')->create();
|
|
|
|
$nonMatchingPosts = Post::factory()->count(3)->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable($searchCompany)
|
|
->assertCanSeeTableRecords([$matchingPost])
|
|
->assertCanNotSeeTableRecords($nonMatchingPosts);
|
|
});
|
|
|
|
it('can sort records with `BelongsTo` -> `BelongsToThrough` relationship', function (): void {
|
|
Post::factory()->count(5)->state(fn (): array => [
|
|
'author_id' => User::factory()->state([
|
|
'team_id' => Team::factory()->state([
|
|
'company_id' => Company::factory(),
|
|
]),
|
|
]),
|
|
])->create();
|
|
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
Company::query()
|
|
->select('companies.name')
|
|
->whereColumn('companies.id', 'teams.company_id')
|
|
->join('teams', 'teams.company_id', '=', 'companies.id')
|
|
->join('users', 'users.team_id', '=', 'teams.id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
Company::query()
|
|
->select('companies.name')
|
|
->whereColumn('companies.id', 'teams.company_id')
|
|
->join('teams', 'teams.company_id', '=', 'companies.id')
|
|
->join('users', 'users.team_id', '=', 'teams.id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.company.name')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('author.company.name', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with nullable `BelongsTo` -> `BelongsToThrough` relationship', function (): void {
|
|
// Post with author, team, and company
|
|
$company = Company::factory()->create(['name' => 'Acme Corp']);
|
|
$teamWithCompany = Team::factory()->create(['company_id' => $company->id]);
|
|
$userComplete = User::factory()->create(['team_id' => $teamWithCompany->id]);
|
|
$postComplete = Post::factory()->create(['author_id' => $userComplete->id]);
|
|
|
|
// Post with author and team but no company
|
|
$teamWithoutCompany = Team::factory()->create(['company_id' => null]);
|
|
$userNoCompany = User::factory()->create(['team_id' => $teamWithoutCompany->id]);
|
|
$postNoCompany = Post::factory()->create(['author_id' => $userNoCompany->id]);
|
|
|
|
// Post with author but no team
|
|
$userNoTeam = User::factory()->create(['team_id' => null]);
|
|
$postNoTeam = Post::factory()->create(['author_id' => $userNoTeam->id]);
|
|
|
|
// Post with no author
|
|
$postNoAuthor = Post::factory()->create(['author_id' => null]);
|
|
|
|
$allPosts = collect([$postComplete, $postNoCompany, $postNoTeam, $postNoAuthor]);
|
|
|
|
// Just verify sorting doesn't crash with nullable relationships
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.company.name')
|
|
->assertCanSeeTableRecords($allPosts)
|
|
->sortTable('author.company.name', 'desc')
|
|
->assertCanSeeTableRecords($allPosts);
|
|
});
|
|
|
|
it('can search records with `BelongsTo` -> `BelongsToThrough` relationship', function (): void {
|
|
$searchCompany = 'Specific Company Name';
|
|
|
|
$company = Company::factory()->create(['name' => $searchCompany]);
|
|
$team = Team::factory()->create(['company_id' => $company->id]);
|
|
$matchingUser = User::factory()->create(['team_id' => $team->id]);
|
|
$matchingPost = Post::factory()->for($matchingUser, 'author')->create();
|
|
|
|
$nonMatchingPosts = Post::factory()->count(3)->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable($searchCompany)
|
|
->assertCanSeeTableRecords([$matchingPost])
|
|
->assertCanNotSeeTableRecords($nonMatchingPosts);
|
|
});
|
|
|
|
it('can sort records with `HasOneThrough` relationship', function (): void {
|
|
$themes = ['alpha', 'beta', 'gamma', 'delta', 'epsilon'];
|
|
|
|
foreach ($themes as $theme) {
|
|
User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['theme' => $theme]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
}
|
|
|
|
$sortedAsc = User::query()
|
|
->orderBy(
|
|
Setting::query()
|
|
->select('theme')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
$sortedDesc = User::query()
|
|
->orderByDesc(
|
|
Setting::query()
|
|
->select('theme')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
livewire(UsersTable::class)
|
|
->sortTable('setting.theme')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('setting.theme', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with nullable `HasOneThrough` relationship', function (): void {
|
|
// User with profile and setting
|
|
$userWithSetting = User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['theme' => 'dark']),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
|
|
// User with profile but no setting
|
|
$userWithProfileNoSetting = User::factory()->has(
|
|
Profile::factory(),
|
|
'profile'
|
|
)->create();
|
|
|
|
// User without profile (no setting possible)
|
|
$userWithoutProfile = User::factory()->create();
|
|
|
|
$allUsers = collect([$userWithSetting, $userWithProfileNoSetting, $userWithoutProfile]);
|
|
|
|
// Just verify sorting doesn't crash with nullable relationships
|
|
livewire(UsersTable::class)
|
|
->sortTable('setting.theme')
|
|
->assertCanSeeTableRecords($allUsers)
|
|
->sortTable('setting.theme', 'desc')
|
|
->assertCanSeeTableRecords($allUsers);
|
|
});
|
|
|
|
it('can search records with `HasOneThrough` relationship', function (): void {
|
|
$searchTheme = 'unique-theme-for-testing';
|
|
|
|
$matchingUser = User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['theme' => $searchTheme]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
|
|
$nonMatchingUsers = User::factory()->count(3)->create();
|
|
|
|
livewire(UsersTable::class)
|
|
->searchTable($searchTheme)
|
|
->assertCanSeeTableRecords([$matchingUser])
|
|
->assertCanNotSeeTableRecords($nonMatchingUsers);
|
|
});
|
|
|
|
it('can sort records with `HasOneThrough` relationship using different column', function (): void {
|
|
$languages = ['de', 'en', 'es', 'fr', 'it'];
|
|
|
|
foreach ($languages as $language) {
|
|
User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['language' => $language]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
}
|
|
|
|
$sortedAsc = User::query()
|
|
->orderBy(
|
|
Setting::query()
|
|
->select('language')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
$sortedDesc = User::query()
|
|
->orderByDesc(
|
|
Setting::query()
|
|
->select('language')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
livewire(UsersTable::class)
|
|
->sortTable('setting.language')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('setting.language', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can search records with `HasOneThrough` relationship using different column', function (): void {
|
|
$searchLanguage = 'unique-language';
|
|
|
|
$matchingUser = User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['language' => $searchLanguage]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
|
|
$nonMatchingUsers = User::factory()->count(3)->create();
|
|
|
|
livewire(UsersTable::class)
|
|
->searchTable($searchLanguage)
|
|
->assertCanSeeTableRecords([$matchingUser])
|
|
->assertCanNotSeeTableRecords($nonMatchingUsers);
|
|
});
|
|
|
|
it('can sort records with `BelongsTo` -> `HasOneThrough` relationship', function (): void {
|
|
$themes = ['alpha', 'beta', 'gamma', 'delta', 'epsilon'];
|
|
|
|
foreach ($themes as $theme) {
|
|
$user = User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['theme' => $theme]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
|
|
Post::factory()->create(['author_id' => $user->id]);
|
|
}
|
|
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
Setting::query()
|
|
->select('theme')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->join('users', 'users.id', '=', 'profiles.user_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
Setting::query()
|
|
->select('theme')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->join('users', 'users.id', '=', 'profiles.user_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.setting.theme')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('author.setting.theme', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with nullable `BelongsTo` -> `HasOneThrough` relationship', function (): void {
|
|
// Post with author that has setting
|
|
$userWithSetting = User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['theme' => 'dark']),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
$postWithSetting = Post::factory()->create(['author_id' => $userWithSetting->id]);
|
|
|
|
// Post with author with profile but no setting
|
|
$userWithProfileNoSetting = User::factory()->has(
|
|
Profile::factory(),
|
|
'profile'
|
|
)->create();
|
|
$postNoSetting = Post::factory()->create(['author_id' => $userWithProfileNoSetting->id]);
|
|
|
|
// Post with author but no profile
|
|
$userNoProfile = User::factory()->create();
|
|
$postNoProfile = Post::factory()->create(['author_id' => $userNoProfile->id]);
|
|
|
|
// Post with no author
|
|
$postNoAuthor = Post::factory()->create(['author_id' => null]);
|
|
|
|
$allPosts = collect([$postWithSetting, $postNoSetting, $postNoProfile, $postNoAuthor]);
|
|
|
|
// Just verify sorting doesn't crash with nullable nested relationships
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.setting.theme')
|
|
->assertCanSeeTableRecords($allPosts)
|
|
->sortTable('author.setting.theme', 'desc')
|
|
->assertCanSeeTableRecords($allPosts);
|
|
});
|
|
|
|
it('can search records with `BelongsTo` -> `HasOneThrough` relationship', function (): void {
|
|
$searchTheme = 'unique-post-author-theme';
|
|
|
|
$matchingUser = User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['theme' => $searchTheme]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
|
|
$matchingPost = Post::factory()->for($matchingUser, 'author')->create();
|
|
$nonMatchingPosts = Post::factory()->count(3)->create();
|
|
|
|
livewire(PostsTable::class)
|
|
->searchTable($searchTheme)
|
|
->assertCanSeeTableRecords([$matchingPost])
|
|
->assertCanNotSeeTableRecords($nonMatchingPosts);
|
|
});
|
|
|
|
it('can sort records with `HasOneThrough` -> `BelongsTo` relationship', function (): void {
|
|
$languageNames = ['Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon'];
|
|
|
|
foreach ($languageNames as $languageName) {
|
|
$language = Language::factory()->create(['name' => $languageName]);
|
|
User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['language_id' => $language->id]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
}
|
|
|
|
$sortedAsc = User::query()
|
|
->orderBy(
|
|
Language::query()
|
|
->select('languages.name')
|
|
->join('settings', 'settings.language_id', '=', 'languages.id')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
$sortedDesc = User::query()
|
|
->orderByDesc(
|
|
Language::query()
|
|
->select('languages.name')
|
|
->join('settings', 'settings.language_id', '=', 'languages.id')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
livewire(UsersTable::class)
|
|
->sortTable('setting.language.name')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('setting.language.name', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with nullable `HasOneThrough` -> `BelongsTo` relationship', function (): void {
|
|
// User with full chain
|
|
$language = Language::factory()->create(['name' => 'English']);
|
|
$userWithLanguage = User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['language_id' => $language->id]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
|
|
// User with profile and setting but no language
|
|
$userWithSettingNoLanguage = User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['language_id' => null]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
|
|
// User with profile but no setting
|
|
$userWithProfileNoSetting = User::factory()->has(
|
|
Profile::factory(),
|
|
'profile'
|
|
)->create();
|
|
|
|
// User without profile
|
|
$userWithoutProfile = User::factory()->create();
|
|
|
|
$allUsers = collect([$userWithLanguage, $userWithSettingNoLanguage, $userWithProfileNoSetting, $userWithoutProfile]);
|
|
|
|
livewire(UsersTable::class)
|
|
->sortTable('setting.language.name')
|
|
->assertCanSeeTableRecords($allUsers)
|
|
->sortTable('setting.language.name', 'desc')
|
|
->assertCanSeeTableRecords($allUsers);
|
|
});
|
|
|
|
it('can sort records with `BelongsTo` -> `HasOneThrough` -> `BelongsTo` relationship', function (): void {
|
|
$languageNames = ['Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon'];
|
|
|
|
foreach ($languageNames as $languageName) {
|
|
$language = Language::factory()->create(['name' => $languageName]);
|
|
$user = User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['language_id' => $language->id]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
Post::factory()->create(['author_id' => $user->id]);
|
|
}
|
|
|
|
$sortedAsc = Post::query()
|
|
->orderBy(
|
|
Language::query()
|
|
->select('languages.name')
|
|
->join('settings', 'settings.language_id', '=', 'languages.id')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->join('users', 'users.id', '=', 'profiles.user_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
$sortedDesc = Post::query()
|
|
->orderByDesc(
|
|
Language::query()
|
|
->select('languages.name')
|
|
->join('settings', 'settings.language_id', '=', 'languages.id')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->join('users', 'users.id', '=', 'profiles.user_id')
|
|
->whereColumn('users.id', 'posts.author_id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('posts.id')
|
|
->get();
|
|
|
|
livewire(PostsTable::class)
|
|
->sortTable('author.setting.language.name')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('author.setting.language.name', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with `HasOne` relationship that uses a custom `where()` constraint', function (): void {
|
|
// User 1: has both published and unpublished posts; should sort by published
|
|
$user1 = User::factory()->create();
|
|
Post::factory()->create(['author_id' => $user1->id, 'is_published' => false, 'title' => 'Z Unpublished']);
|
|
Post::factory()->create(['author_id' => $user1->id, 'is_published' => true, 'title' => 'A Published']);
|
|
|
|
// User 2: only has unpublished posts; should sort as null
|
|
$user2 = User::factory()->create();
|
|
Post::factory()->create(['author_id' => $user2->id, 'is_published' => false, 'title' => 'B Unpublished']);
|
|
|
|
// User 3: has a published post
|
|
$user3 = User::factory()->create();
|
|
Post::factory()->create(['author_id' => $user3->id, 'is_published' => true, 'title' => 'M Published']);
|
|
|
|
$sortedAsc = User::query()
|
|
->orderBy(
|
|
Post::query()
|
|
->select('posts.title')
|
|
->whereColumn('posts.author_id', 'users.id')
|
|
->where('posts.is_published', true)
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
$sortedDesc = User::query()
|
|
->orderByDesc(
|
|
Post::query()
|
|
->select('posts.title')
|
|
->whereColumn('posts.author_id', 'users.id')
|
|
->where('posts.is_published', true)
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
livewire(UsersTable::class)
|
|
->sortTable('publishedPost.title')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('publishedPost.title', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
|
|
it('can sort records with `HasOneThrough` -> `BelongsTo` relationship that uses `withTrashed()` when the related model is soft-deleted', function (): void {
|
|
$alphaLanguage = Language::factory()->create(['name' => 'Alpha']);
|
|
$betaLanguage = Language::factory()->create(['name' => 'Beta']);
|
|
$gammaLanguage = Language::factory()->create(['name' => 'Gamma']);
|
|
|
|
foreach ([$alphaLanguage, $betaLanguage, $gammaLanguage] as $language) {
|
|
User::factory()->has(
|
|
Profile::factory()->has(
|
|
Setting::factory()->state(['language_id' => $language->id]),
|
|
'setting'
|
|
),
|
|
'profile'
|
|
)->create();
|
|
}
|
|
|
|
// Soft-delete the Gamma language
|
|
$gammaLanguage->delete();
|
|
|
|
$sortedAsc = User::query()
|
|
->orderBy(
|
|
Language::query()
|
|
->withTrashed()
|
|
->select('languages.name')
|
|
->join('settings', 'settings.language_id', '=', 'languages.id')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
$sortedDesc = User::query()
|
|
->orderByDesc(
|
|
Language::query()
|
|
->withTrashed()
|
|
->select('languages.name')
|
|
->join('settings', 'settings.language_id', '=', 'languages.id')
|
|
->join('profiles', 'profiles.id', '=', 'settings.profile_id')
|
|
->whereColumn('profiles.user_id', 'users.id')
|
|
->limit(1)
|
|
)
|
|
->orderBy('users.id')
|
|
->get();
|
|
|
|
livewire(UsersTable::class)
|
|
->sortTable('setting.languageWithTrashed.name')
|
|
->assertCanSeeTableRecords($sortedAsc, inOrder: true)
|
|
->sortTable('setting.languageWithTrashed.name', 'desc')
|
|
->assertCanSeeTableRecords($sortedDesc, inOrder: true);
|
|
});
|
|
});
|
|
|
|
it('can search records using table-level searchable columns', function (): void {
|
|
$posts = Post::factory()->count(10)->create();
|
|
|
|
$title = $posts->first()->title;
|
|
|
|
livewire(PostsTableWithTableSearchableColumns::class)
|
|
->searchTable($title)
|
|
->assertCanSeeTableRecords($posts->where('title', $title))
|
|
->assertCanNotSeeTableRecords($posts->where('title', '!=', $title));
|
|
});
|
|
|
|
it('can search records using table-level searchable columns with relationship', function (): void {
|
|
$posts = Post::factory()->count(10)->create();
|
|
|
|
$author = $posts->first()->author->name;
|
|
|
|
livewire(PostsTableWithTableSearchableColumns::class)
|
|
->searchTable($author)
|
|
->assertCanSeeTableRecords($posts->where('author.name', $author))
|
|
->assertCanNotSeeTableRecords($posts->where('author.name', '!=', $author));
|
|
});
|
|
|
|
it('can search records using table-level searchable columns case insensitively', function (): void {
|
|
$matchingPost = Post::factory()->create([
|
|
'title' => 'Test Post Title',
|
|
]);
|
|
|
|
$nonMatchingPosts = Post::factory()->count(3)->create();
|
|
|
|
livewire(PostsTableWithTableSearchableColumns::class)
|
|
->searchTable('test post title')
|
|
->assertCanSeeTableRecords([$matchingPost])
|
|
->assertCanNotSeeTableRecords($nonMatchingPosts);
|
|
});
|
|
|
|
it('can search records using table-level searchable columns with relationship case insensitively', function (): void {
|
|
$matchingAuthor = User::factory()->create(['name' => 'John Smith']);
|
|
|
|
$matchingPosts = Post::factory()
|
|
->for($matchingAuthor, 'author')
|
|
->count(3)
|
|
->create();
|
|
|
|
$nonMatchingPosts = Post::factory()->count(3)->create();
|
|
|
|
livewire(PostsTableWithTableSearchableColumns::class)
|
|
->searchTable('john smith')
|
|
->assertCanSeeTableRecords($matchingPosts)
|
|
->assertCanNotSeeTableRecords($nonMatchingPosts);
|
|
});
|