表格构造器
测试
概述
所有本指南内的示例都使用 Pest 编写。要使用 Pest 的 Livewire 插件进行测试,请参考 Pest 文档中的插件安装说明:Pest 的 Livewire 插件。不过,你也可以将其适配到 PHPUnit。
因为表格构造器是基于 Livewire 组件的,因此你也可以使用 Livewire 测试辅助函数。不过,我们自定义了一些测试辅助函数,让你可以用于表格测试:
渲染
要确保表格组件渲染,请使用 assertSuccessful() Livewire 辅助函数:
use function Pest\Livewire\livewire; it('can render page', function () { livewire(ListPosts::class)->assertSuccessful();});
使用 assertCanSeeTableRecords()、assertCanNotSeeTableRecords() 和 assertCountTableRecords(),可以测试显示了哪条记录:
use function Pest\Livewire\livewire; it('cannot display trashed posts by default', function () { $posts = Post::factory()->count(4)->create(); $trashedPosts = Post::factory()->trashed()->count(6)->create(); livewire(PostResource\Pages\ListPosts::class) ->assertCanSeeTableRecords($posts) ->assertCanNotSeeTableRecords($trashedPosts) ->assertCountTableRecords(4);});
如果表格使用了分页,
assertCanSeeTableRecords()只会检查第一页中的记录。要切换页面,请调用set('page', 2)。
如果表格使用了延迟加载
deferLoading(),你应该在assertCanSeeTableRecords()之前调用loadTable()。
列
要断言渲染了特定的列,请传递列名到 assertCanRenderTableColumn():
use function Pest\Livewire\livewire; it('can render post titles', function () { Post::factory()->count(10)->create(); livewire(PostResource\Pages\ListPosts::class) ->assertCanRenderTableColumn('title');});
这个辅助函数会获取该列的 HTML,并检查其是否在表格中出现。
要测试没有渲染的列,可以使用 assertCanNotRenderTableColumn():
use function Pest\Livewire\livewire; it('can not render post comments', function () { Post::factory()->count(10)->create() livewire(PostResource\Pages\ListPosts::class) ->assertCanNotRenderTableColumn('comments');});
该辅助函数会断言该列的 HTML 默认不展示在当前表格。
排序
想要对表格进行排序,你可以调用 sortTable(),传入要排序的列名。你可以在 sortTable() 的第二个参数中使用 'desc',来反转排序方向。
当表格排序完,你可以使用 assertCanSeeTableRecords() 并带上 inOrder 参数来断言表格记录按顺序渲染:
use function Pest\Livewire\livewire; it('can sort posts by title', function () { $posts = Post::factory()->count(10)->create(); livewire(PostResource\Pages\ListPosts::class) ->sortTable('title') ->assertCanSeeTableRecords($posts->sortBy('title'), inOrder: true) ->sortTable('title', 'desc') ->assertCanSeeTableRecords($posts->sortByDesc('title'), inOrder: true);});
搜索
要搜索表格,请在搜索查询里调用 searchTable() 方法:
你可以使用 assertCanSeeTableRecords() 检查过滤的表格记录,并使用 assertCanNotSeeTableRecords() 断言某些记录不再在表格中:
use function Pest\Livewire\livewire; it('can search posts by title', function () { $posts = Post::factory()->count(10)->create(); $title = $posts->first()->title; livewire(PostResource\Pages\ListPosts::class) ->searchTable($title) ->assertCanSeeTableRecords($posts->where('title', $title)) ->assertCanNotSeeTableRecords($posts->where('title', '!=', $title));});
要搜索单独列,你可以传入要搜索的数组到 searchTableColumns():
use function Pest\Livewire\livewire; it('can search posts by title column', function () { $posts = Post::factory()->count(10)->create(); $title = $posts->first()->title; livewire(PostResource\Pages\ListPosts::class) ->searchTableColumns(['title' => $title]) ->assertCanSeeTableRecords($posts->where('title', $title)) ->assertCanNotSeeTableRecords($posts->where('title', '!=', $title));});
状态
要断言记录中特定的列有或者没有状态,你可以使用 assertTableColumnStateSet() 和 assertTableColumnStateNotSet():
use function Pest\Livewire\livewire; it('can get post author names', function () { $posts = Post::factory()->count(10)->create(); $post = $posts->first(); livewire(PostResource\Pages\ListPosts::class) ->assertTableColumnStateSet('author.name', $post->author->name, record: $post) ->assertTableColumnStateNotSet('author.name', 'Anonymous', record: $post);});
要断言记录中的某个特定列具有或者没有格式化状态,你可以使用 assertTableColumnFormattedStateSet() 和 assertTableColumnFormattedStateNotSet():
use function Pest\Livewire\livewire; it('can get post author names', function () { $post = Post::factory(['name' => 'John Smith'])->create(); livewire(PostResource\Pages\ListPosts::class) ->assertTableColumnFormattedStateSet('author.name', 'Smith, John', record: $post) ->assertTableColumnFormattedStateNotSet('author.name', $post->author->name, record: $post);});
存在
要断言某个列存在,你可以使用 assertTableColumnExists() 方法:
use function Pest\Livewire\livewire; it('has an author column', function () { livewire(PostResource\Pages\ListPosts::class) ->assertTableColumnExists(`author`);});
你可以传递函数作为额外的参数,用以断言列通过给定的"真值测试"。这对于有特定配置的列的断言有用。你也可以传入记录作为第三个参数,如果你的检测是基于哪一个表格行被渲染,这很有用:
use function Pest\Livewire\livewire;use Filament\Tables\Columns\TextColumn; it('has an author column', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->assertTableColumnExists('author', function (TextColumn $column): bool { return $column->getDescriptionBelow() === $post->subtitle; }, $post);});
授权
要断言特定用户不能看见某个列,你可以使用 assertTableColumnVisible() 和 assertTableColumnHidden() 方法:
use function Pest\Livewire\livewire; it('shows the correct columns', function () { livewire(PostResource\Pages\ListPosts::class) ->assertTableColumnVisible(`created_at`) ->assertTableColumnHidden(`author`);});
描述
要断言一个列上面或下面有正确的描述,你可以使用 assertTableColumnHasDescription() 和 assertTableColumnDoesNotHaveDescription() 方法:
use function Pest\Livewire\livewire; it('has the correct descriptions above and below author', function () { $post = Post::factory()->create(); livewire(PostsTable::class) ->assertTableColumnHasDescription('author', 'Author! ↓↓↓', $post, 'above') ->assertTableColumnHasDescription('author', 'Author! ↑↑↑', $post) ->assertTableColumnDoesNotHaveDescription('author', 'Author! ↑↑↑', $post, 'above') ->assertTableColumnDoesNotHaveDescription('author', 'Author! ↓↓↓', $post);});
额外属性
要断言一个列中具有正确的额外属性,你可以使用 assertTableColumnHasExtraAttributes() 和 assertTableColumnDoesNotHaveExtraAttributes():
use function Pest\Livewire\livewire; it('displays author in red', function () { $post = Post::factory()->create(); livewire(PostsTable::class) ->assertTableColumnHasExtraAttributes('author', ['class' => 'text-danger-500'], $post) ->assertTableColumnDoesNotHaveExtraAttributes('author', ['class' => 'text-primary-500'], $post);});
Select Column
使用 assertSelectColumnHasOptions() 和 assertSelectColumnDoesNotHaveOptions() 可以断言 SelectColomn 中的选项是否正确:
use function Pest\Livewire\livewire; it('has the correct statuses', function () { $post = Post::factory()->create(); livewire(PostsTable::class) ->assertSelectColumnHasOptions('status', ['unpublished' => 'Unpublished', 'published' => 'Published'], $post) ->assertSelectColumnDoesNotHaveOptions('status', ['archived' => 'Archived'], $post);});
过滤器
要过滤表格数据,你可以使用 filterTable() 方法,以及 assertCanSeeTableRecords() 和 assertCanNotSeeTableRecords():
use function Pest\Livewire\livewire; it('can filter posts by `is_published`', function () { $posts = Post::factory()->count(10)->create(); livewire(PostResource\Pages\ListPosts::class) ->assertCanSeeTableRecords($posts) ->filterTable('is_published') ->assertCanSeeTableRecords($posts->where('is_published', true)) ->assertCanNotSeeTableRecords($posts->where('is_published', false));});
对于简单过滤器,这只是启用过滤器。
如果你想为 SelectFilter 或者 TernaryFilter 设置值,请传入其值作为第二个参数:
use function Pest\Livewire\livewire; it('can filter posts by `author_id`', function () { $posts = Post::factory()->count(10)->create(); $authorId = $posts->first()->author_id; livewire(PostResource\Pages\ListPosts::class) ->assertCanSeeTableRecords($posts) ->filterTable('author_id', $authorId) ->assertCanSeeTableRecords($posts->where('author_id', $authorId)) ->assertCanNotSeeTableRecords($posts->where('author_id', '!=', $authorId));});
重置过滤器
要将所有过滤器重置到它们初始的状态,请调用 resetTableFilters():
use function Pest\Livewire\livewire; it('can reset table filters`', function () { $posts = Post::factory()->count(10)->create(); livewire(PostResource\Pages\ListPosts::class) ->resetTableFilters();});
移除过滤器
要删除单个过滤器,你可以使用 removeTableFilter():
use function Pest\Livewire\livewire; it('filters list by published', function () { $posts = Post::factory()->count(10)->create(); $unpublishedPosts = $posts->where('is_published', false)->get(); livewire(PostsTable::class) ->filterTable('is_published') ->assertCanNotSeeTableRecords($unpublishedPosts) ->removeTableFilter('is_published') ->assertCanSeeTableRecords($posts);});
要移除所有过滤器,你可以使用 removeTableFilters():
use function Pest\Livewire\livewire; it('can remove all table filters', function () { $posts = Post::factory()->count(10)->forAuthor()->create(); $unpublishedPosts = $posts ->where('is_published', false) ->where('author_id', $posts->first()->author->getKey()); livewire(PostsTable::class) ->filterTable('is_published') ->filterTable('author', $author) ->assertCanNotSeeTableRecords($unpublishedPosts) ->removeTableFilters() ->assertCanSeeTableRecords($posts);});
隐藏过滤器
要确保某个特定用户看不到过滤器,你可以使用 assertTableFilterVisible() 和 assertTableFilterHidden() 方法:
use function Pest\Livewire\livewire; it('shows the correct filters', function () { livewire(PostsTable::class) ->assertTableFilterVisible('created_at') ->assertTableFilterHidden('author');
存在过滤器
要断言某个过滤器存在,你可以使用 assertTableFilterExists() 方法:
use function Pest\Livewire\livewire; it('has an author filter', function () { livewire(PostResource\Pages\ListPosts::class) ->assertTableFilterExists('author');});
你可以传入函数作为额外的参数,来断言某个过滤器通过一个给定的"真值测试"。这对于断言有特定配置的过滤器尤其有用:
use function Pest\Livewire\livewire;use Filament\Tables\Filters\SelectFilter; it('has an author filter', function () { livewire(PostResource\Pages\ListPosts::class) ->assertTableFilterExists('author', function (SelectFilter $column): bool { return $column->getLabel() === 'Select author'; });});
Action
调用 Action
将 Action 名或类传入到 callTableAction() 中,你可以调用该 Action:
use function Pest\Livewire\livewire; it('can delete posts', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->callTableAction(DeleteAction::class, $post); $this->assertModelMissing($post);});
本例假定你表格中有 DeleteAction。如果你有一个自定义的 Action::make('reorder'),你也可以使用 callTableAction('reorder')。
对于列中的 Action,你可以使用 callTableColumnAction():
use function Pest\Livewire\livewire; it('can copy posts', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->callTableColumnAction('copy', $post); $this->assertDatabaseCount((new Post)->getTable(), 2);});
对于批量 Action,传入多条记录到 callTableBulkAction() 中,去执行批量操作:
use function Pest\Livewire\livewire; it('can bulk delete posts', function () { $posts = Post::factory()->count(10)->create(); livewire(PostResource\Pages\ListPosts::class) ->callTableBulkAction(DeleteBulkAction::class, $posts); foreach ($posts as $post) { $this->assertModelMissing($post); }});
使用 data 参数,传入数据数组到 Action 中:
use function Pest\Livewire\livewire; it('can edit posts', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->callTableAction(EditAction::class, $post, data: [ 'title' => $title = fake()->words(asText: true), ]) ->assertHasNoTableActionErrors(); expect($post->refresh()) ->title->toBe($title);});
执行
要断言 Action 或者批量 Action 被中止,你可以使用 assertTableActionHalted() / assertTableBulkActionHalted():
use function Pest\Livewire\livewire; it('will halt delete if post is flagged', function () { $posts= Post::factory()->count(2)->flagged()->create(); livewire(PostResource\Pages\ListPosts::class) ->callTableAction('delete', $posts->first()) ->callTableBulkAction('delete', $posts) ->assertTableActionHalted('delete') ->assertTableBulkActionHalted('delete'); $this->assertModelExists($post);});
错误
assertHasNoTableActionErrors() 用于断言提交 Action 表单时,没有出现验证错误。
要断言数据发生验证错误,请使用 assertHasTableActionErrors(),类似于 Livewire 中的 assertHasErrors():
use function Pest\Livewire\livewire; it('can validate edited post data', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->callTableAction(EditAction::class, $post, data: [ 'title' => null, ]) ->assertHasTableActionErrors(['title' => ['required']]);});
对于批量操作来说,这些方法叫做 assertHasTableBulkActionErrors() 和 assertHasNoTableBulkActionErrors()。
预填充数据
要断言 Action 或批量操作 Action 中是否预填充了数据,你可以使用 assertTableActionDataSet() 或者 assertTableBulkActionDataSet() 方法:
use function Pest\Livewire\livewire; it('can load existing post data for editing', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->mountTableAction(EditAction::class, $post) ->assertTableActionDataSet([ 'title' => $post->title, ]) ->setTableActionData([ 'title' => $title = fake()->words(asText: true), ]) ->callMountedTableAction() ->assertHasNoTableActionErrors(); expect($post->refresh()) ->title->toBe($title);});
You may also find it useful to pass a function to the assertTableActionDataSet() and assertTableBulkActionDataSet() methods, which allow you to access the form $state and perform additional assertions:
use Illuminate\Support\Str;use function Pest\Livewire\livewire; it('can automatically generate a slug from the title without any spaces', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->mountTableAction(EditAction::class, $post) ->assertTableActionDataSet(function (array $state) use ($post): array { expect($state['slug']) ->not->toContain(' '); return [ 'slug' => Str::slug($post->title), ]; });});
Action 状态
要断言 Action 或者批量 Action 存在或者不在表格中,请使用 assertTableActionExists() / assertTableActionDoesNotExist() 或 assertTableBulkActionExists() / assertTableBulkActionDoesNotExist() 方法中:
use function Pest\Livewire\livewire; it('can publish but not unpublish posts', function () { livewire(PostResource\Pages\ListPosts::class) ->assertTableActionExists('publish') ->assertTableActionDoesNotExist('unpublish') ->assertTableBulkActionExists('publish') ->assertTableBulkActionDoesNotExist('unpublish');});
要断言保不同的 Action 集以正确的顺序存在,可以使用各种 "InOrder" 断言:
use function Pest\Livewire\livewire; it('has all actions in expected order', function () { livewire(PostResource\Pages\ListPosts::class) ->assertTableActionsExistInOrder(['edit', 'delete']) ->assertTableBulkActionsExistInOrder(['restore', 'forceDelete']) ->assertTableHeaderActionsExistInOrder(['create', 'attach']) ->assertTableEmptyStateActionsExistInOrder(['create', 'toggle-trashed-filter'])});
要断言 Action 或批量 Action 对用户启用或禁用,你可以使用 assertTableActionEnabled() / assertTableActionDisabled() 或 assertTableBulkActionEnabled() / assertTableBulkActionDisabled():
use function Pest\Livewire\livewire; it('can not publish, but can delete posts', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->assertTableActionDisabled('publish', $post) ->assertTableActionEnabled('delete', $post) ->assertTableBulkActionDisabled('publish') ->assertTableBulkActionEnabled('delete');});
要断言 Action 或者批量 Actio 对用户可见或隐藏,你可以使用 assertTableActionVisible() / assertTableActionHidden() 或者 assertTableBulkActionVisible() / assertTableBulkActionHidden() 方法:
use function Pest\Livewire\livewire; it('can not publish, but can delete posts', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->assertTableActionHidden('publish', $post) ->assertTableActionVisible('delete', $post) ->assertTableBulkActionHidden('publish') ->assertTableBulkActionVisible('delete');});
按钮样式
要断言 Action 或批量操作 Action 拥有正确的标签,你可以使用 assertTableActionHasLabel() / assertTableBulkActionHasLabel() 和 assertTableActionDoesNotHaveLabel() / assertTableBulkActionDoesNotHaveLabel():
use function Pest\Livewire\livewire; it('delete actions have correct labels', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->assertTableActionHasLabel('delete', 'Archive Post') ->assertTableActionDoesNotHaveLabel('delete', 'Delete'); ->assertTableBulkActionHasLabel('delete', 'Archive Post') ->assertTableBulkActionDoesNotHaveLabel('delete', 'Delete');});
要断言 Action 或者批量 Action 的按钮显示正确的图标,你可以使用 assertTableActionHasIcon() / assertTableBulkActionHasIcon() 或者 assertTableActionDoesNotHaveIcon() / assertTableBulkActionDoesNotHaveIcon():
use function Pest\Livewire\livewire; it('delete actions have correct icons', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->assertTableActionHasIcon('delete', 'heroicon-m-archive-box') ->assertTableActionDoesNotHaveIcon('delete', 'heroicon-m-trash'); ->assertTableBulkActionHasIcon('delete', 'heroicon-m-archive-box') ->assertTableBulkActionDoesNotHaveIcon('delete', 'heroicon-m-trash');});
要断言 Action 或者批量 Action 的按钮显示正确的颜色,你可以使用 assertTableActionHasColor() / assertTableBulkActionHasColor() 或 assertTableActionDoesNotHaveColor() / assertTableBulkActionDoesNotHaveColor():
use function Pest\Livewire\livewire; it('delete actions have correct colors', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->assertTableActionHasColor('delete', 'warning', $post) ->assertTableActionDoesNotHaveColor('delete', 'danger', $post); ->assertTableBulkActionHasColor('delete', 'warning', $post) ->assertTableBulkActionDoesNotHaveColor('delete', 'danger', $post);});
URL
要断言 Action 或者批量 Action 的按钮使用正确的 URL trait,你可以使用 assertTableActionHasUrl()、assertTableActionDoesNotHaveUrl()、assertTableActionShouldOpenUrlInNewTab() 和 assertTableActionShouldNotOpenUrlInNewTab():
use function Pest\Livewire\livewire; it('links to the correct Filament sites', function () { $post = Post::factory()->create(); livewire(PostResource\Pages\ListPosts::class) ->assertTableActionHasUrl('filament', 'https://filamentphp.com/') ->assertTableActionDoesNotHaveUrl('filament', 'https://github.com/filamentphp/filament') ->assertTableActionShouldOpenUrlInNewTab('filament') ->assertTableActionShouldNotOpenUrlInNewTab('github');});
汇总
要测试汇总计算是否生效,你可以使用 assertTableColumnSummarySet() 方法:
use function Pest\Livewire\livewire; it('can average values in a column', function () { $posts = Post::factory()->count(10)->create(); livewire(PostResource\Pages\ListPosts::class) ->assertCanSeeTableRecords($posts) ->assertTableColumnSummarySet('rating', 'average', $posts->avg('rating'));});
第一个参数为列名,第二个是汇总 ID,第三个是期望值。
注意,期望值和实际值被归一化,因此,123.12 被视为与 "123.12" 相同,并且 ['Fred', 'Jim'] 与 ['Jim', 'Fred'] 相同。
汇总 ID 可以通过将其传入 make() 方法设置:
use Filament\Tables\Columns\Summarizers\Average;use Filament\Tables\Columns\TextColumn; TextColumn::make('rating') ->summarize(Average::make('average'))
ID 在该列的汇总之间应该是唯一的。
只汇总一个分页页面
只计算一个分页页面的平均值,请使用 isCurrentPaginationPageOnly 参数:
use function Pest\Livewire\livewire; it('can average values in a column', function () { $posts = Post::factory()->count(20)->create(); livewire(PostResource\Pages\ListPosts::class) ->assertCanSeeTableRecords($posts->take(10)) ->assertTableColumnSummarySet('rating', 'average', $posts->take(10)->avg('rating'), isCurrentPaginationPageOnly: true);});
测试范围汇总
要测试一个范围,请将最小值和最大值传入到一个元组风格的 [$minimum, $maximum] 数组中:
use function Pest\Livewire\livewire; it('can average values in a column', function () { $posts = Post::factory()->count(10)->create(); livewire(PostResource\Pages\ListPosts::class) ->assertCanSeeTableRecords($posts) ->assertTableColumnSummarySet('rating', 'range', [$posts->min('rating'), $posts->max('rating')]);});Edit on GitHubStill need help? Join our Discord community or open a GitHub discussion