Languages

Version

Theme

表格构造器

测试

概述

所有本指南内的示例都使用 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);
});

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);
});

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')
->assertTableActionDoesNotHaveColor('delete', 'danger');
->assertTableBulkActionHasColor('delete', 'warning')
->assertTableBulkActionDoesNotHaveColor('delete', 'danger');
});

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 GitHub

Still need help? Join our Discord community or open a GitHub discussion