Languages

Version

Theme

表单构造器

测试

概述

所有本指南内的示例都使用 Pest 编写。要使用 Pest 的 Livewire 插件进行测试,请参考 Pest 文档中的插件安装说明:Pest 的 Livewire 插件。不过,你也可以将其适配到 PHPUnit。

因为表单构造器是基于 Livewire 组件的,因此你也可以使用 Livewire 测试辅助函数。不过,我们自定义了一些测试辅助函数,让你可以用于表单测试:

填充表单

要填充表单,请将数据传入 fillform()

use function Pest\Livewire\livewire;
 
livewire(CreatePost::class)
->fillForm([
'title' => fake()->sentence(),
// ...
]);

如果一个 Livewire 组件中有多个表单,你可以使用 fillForm([...], 'createPostForm') 指定你想要填充哪个表单。

使用 assertFormSet() 断言表单中有数据:

use Illuminate\Support\Str;
use function Pest\Livewire\livewire;
 
it('can automatically generate a slug from the title', function () {
$title = fake()->sentence();
 
livewire(CreatePost::class)
->fillForm([
'title' => $title,
])
->assertFormSet([
'slug' => Str::slug($title),
]);
});

注意,如果一个 Livewire 组件中有多个表单,你可以使用 assertFormSet([...], 'createPostForm') 指定你想要检测哪个表单。

assertFormSet() 中传入一个函数,将允许你访问表单的 $state 并执行额外的断言:

use Illuminate\Support\Str;
use function Pest\Livewire\livewire;
 
it('can automatically generate a slug from the title without any spaces', function () {
$title = fake()->sentence();
 
livewire(CreatePost::class)
->fillForm([
'title' => $title,
])
->assertFormSet(function (array $state): array {
expect($state['slug'])
->not->toContain(' ');
return [
'slug' => Str::slug($title),
];
});
});

如果你希望 Filament 在函数运行后继续断言表单状态,你可以在该函数中返回数组。

验证

使用 assertHasFormErrors() 断言对表单中的数据进行有效验证:

use function Pest\Livewire\livewire;
 
it('can validate input', function () {
livewire(CreatePost::class)
->fillForm([
'title' => null,
])
->call('create')
->assertHasFormErrors(['title' => 'required']);
});

同时 assertHasNoFormErrors() 断言没有验证错误:

use function Pest\Livewire\livewire;
 
livewire(CreatePost::class)
->fillForm([
'title' => fake()->sentence(),
// ...
])
->call('create')
->assertHasNoFormErrors();

注意,如果一个 Livewire 组件中有多个表单,你可以将指定表单名作为第二个参数传入,像这样 assertHasFormErrors(['title' => 'required'], 'createPostForm')assertHasNoFormErrors([], 'createPostForm')

表单存在

断言 Livewire 组件中有一个表单,请使用 assertFormExists():

use function Pest\Livewire\livewire;
 
it('has a form', function () {
livewire(CreatePost::class)
->assertFormExists();
});

如果一个 Livewire 组件中有多个表单,你可以将指定的表单名传入 assertFormExists('createPostForm')

字段

断言表单中有某个给定字段,请将字段名传入到 assertFormFieldExists()

use function Pest\Livewire\livewire;
 
it('has a title field', function () {
livewire(CreatePost::class)
->assertFormFieldExists('title');
});

你也可以传入函数作为额外的参数,来断言一个字段传入了一个"真值测试"。着对断言一个字段有某个特定配置非常有用:

use function Pest\Livewire\livewire;
 
it('has a title field', function () {
livewire(CreatePost::class)
->assertFormFieldExists('title', function (TextInput $field): bool {
return $field->isDisabled();
});
});

要断言表单中不包含给定字段,请将字段名传给 assertFormFieldDoesNotExist()

use function Pest\Livewire\livewire;
 
it('does not have a conditional field', function () {
livewire(CreatePost::class)
->assertFormFieldDoesNotExist('no-such-field');
});

如果 Livewire 组件有多个表单,你可以指定哪个表单要断言字段是否存在。比如 assertFormFieldExists('title', 'createPostForm')

Hidden 字段

断言表单中有某个字段可见,将字段名传入到 assertFormFieldIsVisible():

use function Pest\Livewire\livewire;
 
test('title is visible', function () {
livewire(CreatePost::class)
->assertFormFieldIsVisible('title');
});

或者断言表单中隐藏某个字段,将字段名传入到 assertFormFieldIsHidden():

use function Pest\Livewire\livewire;
 
test('title is hidden', function () {
livewire(CreatePost::class)
->assertFormFieldIsHidden('title');
});

对于 assertFormFieldIsHidden()assertFormFieldIsVisible(),你可以传递该字段所属表单的名称作为第二个参数,比如 assertFormFieldIsHidden('title', 'createPostForm')

禁用字段

要断言字段可用,请传递字段名到 assertFormFieldIsEnabled()

use function Pest\Livewire\livewire;
 
test('title is enabled', function () {
livewire(CreatePost::class)
->assertFormFieldIsEnabled('title');
});

要断言字段禁用,请传递字段名到 assertFormFieldIsDisabled()

use function Pest\Livewire\livewire;
 
test('title is disabled', function () {
livewire(CreatePost::class)
->assertFormFieldIsDisabled('title');
});

对于 assertFormFieldIsEnabled()assertFormFieldIsDisabled(),你可以传递该字段所属表单的名称作为第二个参数,比如 assertFormFieldIsEnabled('title', 'createPostForm')

布局组件

如果你想确定特定的布局组件(而非字段)是否存在,你可以使用 assertFormComponentExists()。由于布局组件没有名称,该方法使用了由开发者提供的 Key():

use Filament\Forms\Components\Section;
 
Section::make('Comments')
->key('comments-section')
->schema([
//
])
use function Pest\Livewire\livewire;
 
test('comments section exists' function () {
livewire(EditPost::class)
->assertFormComponentExists('comments-section');
});

要断言表单中不包含给定组件,请将组件的 key 传给 assertFormComponentDoesNotExist()

use function Pest\Livewire\livewire;
 
it('does not have a conditional component', function () {
livewire(CreatePost::class)
->assertFormComponentDoesNotExist('no-such-section');
});

要检测组件是否存在以及是否传入给定的真值测试,你可以传递函数给 assertFormComponentExists() 的第二个参数,根据组件是否通过测试来返回 true 或 false:

use Filament\Forms\Components\Component;
 
use function Pest\Livewire\livewire;
 
test('comments section has heading' function () {
livewire(EditPost::class)
->assertFormComponentExists(
'comments-section',
function (Component $component): bool {
return $component->getHeading() === 'Comments';
},
);
});

如果你想要更多信息的测试结果,你可以在真值测试回调中嵌入一个断言:

use Filament\Forms\Components\Component;
use Illuminate\Testing\Assert;
 
use function Pest\Livewire\livewire;
 
test('comments section is enabled' function () {
livewire(EditPost::class)
->assertFormComponentExists(
'comments-section',
function (Component $component): bool {
Assert::assertTrue(
$component->isEnabled(),
'Failed asserting that comments-section is enabled.',
);
return true;
},
);
});

Wizard

要转到向导卡的下一步,请使用 goToNextWizardStep():

use function Pest\Livewire\livewire;
 
it('moves to next wizard step', function () {
livewire(CreatePost::class)
->goToNextWizardStep()
->assertHasFormErrors(['title']);
});

你也可以调用 goToPreviousWizardStep() 转到上一步:

use function Pest\Livewire\livewire;
 
it('moves to next wizard step', function () {
livewire(CreatePost::class)
->goToPreviousWizardStep()
->assertHasFormErrors(['title']);
});

如果要跳转到指定的步骤,请使用 goToWizardStep(),然后是 assertWizardCurrentStep 方法,以确保你在没有上一步验证错误的情况下到达需要的步骤:

use function Pest\Livewire\livewire;
 
it('moves to the wizards second step', function () {
livewire(CreatePost::class)
->goToWizardStep(2)
->assertWizardCurrentStep(2);
});

如果你在单个 Livewire 组件中有多个表单,任何的 wizard 测试 helper 都接受 formName 参数:

use function Pest\Livewire\livewire;
 
it('moves to next wizard step only for fooForm', function () {
livewire(CreatePost::class)
->goToNextWizardStep(formName: 'fooForm')
->assertHasFormErrors(['title'], formName: 'fooForm');
});

Actions

你可以传递 Action 的表单组件名及 Action 名到 callFormComponentAction 中调用 Action:

use function Pest\Livewire\livewire;
 
it('can send invoices', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->callFormComponentAction('customer_id', 'send');
 
expect($invoice->refresh())
->isSent()->toBeTrue();
});

使用 data 参数,传递数据数组给 Action:

use function Pest\Livewire\livewire;
 
it('can send invoices', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->callFormComponentAction('customer_id', 'send', data: [
'email' => $email = fake()->email(),
])
->assertHasNoFormComponentActionErrors();
 
expect($invoice->refresh())
->isSent()->toBeTrue()
->recipient_email->toBe($email);
});

如果只需要设置 Action 的数据而不立即调用它,可以使用 setFormComponentActionData():

use function Pest\Livewire\livewire;
 
it('can send invoices', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->mountFormComponentAction('customer_id', 'send')
->setFormComponentActionData([
'email' => $email = fake()->email(),
])
});

执行

要检测 Action 是否被中止,可以使用 assertFormComponentActionHalted()

use function Pest\Livewire\livewire;
 
it('stops sending if invoice has no email address', function () {
$invoice = Invoice::factory(['email' => null])->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->callFormComponentAction('customer_id', 'send')
->assertFormComponentActionHalted('customer_id', 'send');
});

错误

assertHasNoFormComponentActionErrors() 用于断言提交 Action 表单没有出现验证错误。

使用 assertHasFormComponentActionErrors() 用于使用数据断言验证错误,类似于 Livewire 中的 assertHasErrors():

use function Pest\Livewire\livewire;
 
it('can validate invoice recipient email', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->callFormComponentAction('customer_id', 'send', data: [
'email' => Str::random(),
])
->assertHasFormComponentActionErrors(['email' => ['email']]);
});

要断言 Action 预装填数据,可以使用 assertFormComponentActionDataSet() 方法:

use function Pest\Livewire\livewire;
 
it('can send invoices to the primary contact by default', function () {
$invoice = Invoice::factory()->create();
$recipientEmail = $invoice->company->primaryContact->email;
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->mountFormComponentAction('customer_id', 'send')
->assertFormComponentActionDataSet([
'email' => $recipientEmail,
])
->callMountedFormComponentAction()
->assertHasNoFormComponentActionErrors();
expect($invoice->refresh())
->isSent()->toBeTrue()
->recipient_email->toBe($recipientEmail);
});

Action state

要断言 Action 存在于或者不在表单中,可以使用 assertFormComponentActionExists()assertFormComponentActionDoesNotExist() 方法:

use function Pest\Livewire\livewire;
 
it('can send but not unsend invoices', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->assertFormComponentActionExists('customer_id', 'send')
->assertFormComponentActionDoesNotExist('customer_id', 'unsend');
});

要断言 Action 对用户隐藏或可见,可以使用 assertFormComponentActionHidden()assertFormComponentActionVisible() 方法:

use function Pest\Livewire\livewire;
 
it('can only print customers', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->assertFormComponentActionHidden('customer_id', 'send')
->assertFormComponentActionVisible('customer_id', 'print');
});

要断言 Action 是对用户启用还是禁用,可以使用 assertFormComponentActionEnabled()assertFormComponentActionDisabled() 方法:

use function Pest\Livewire\livewire;
 
it('can only print a customer for a sent invoice', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->assertFormComponentActionDisabled('customer_id', 'send')
->assertFormComponentActionEnabled('customer_id', 'print');
});

要断言 Action 对某个用户隐藏,可以使用 assertFormComponentActionHidden() 方法:

use function Pest\Livewire\livewire;
 
it('can not send invoices', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->assertFormComponentActionHidden('customer_id', 'send');
});

按钮外观

要断言 Action 的标签正确,可以使用 assertFormComponentActionHasLabel()assertFormComponentActionDoesNotHaveLabel()

use function Pest\Livewire\livewire;
 
it('send action has correct label', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->assertFormComponentActionHasLabel('customer_id', 'send', 'Email Invoice')
->assertFormComponentActionDoesNotHaveLabel('customer_id', 'send', 'Send');
});

要断言 Action 按钮的图标正确显示,可以使用 assertFormComponentActionHasIcon()assertFormComponentActionDoesNotHaveIcon()

use function Pest\Livewire\livewire;
 
it('when enabled the send button has correct icon', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->assertFormComponentActionEnabled('customer_id', 'send')
->assertFormComponentActionHasIcon('customer_id', 'send', 'envelope-open')
->assertFormComponentActionDoesNotHaveIcon('customer_id', 'send', 'envelope');
});

要断言 Action 按钮的颜色正确显示,可以使用 assertFormComponentActionHasColor()assertFormComponentActionDoesNotHaveColor()

use function Pest\Livewire\livewire;
 
it('actions display proper colors', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->assertFormComponentActionHasColor('customer_id', 'delete', 'danger')
->assertFormComponentActionDoesNotHaveColor('customer_id', 'print', 'danger');
});

URL

要断言 Action 的 URL 正确,可以使用 assertFormComponentActionHasUrl()assertFormComponentActionDoesNotHaveUrl()assertFormComponentActionShouldOpenUrlInNewTab()assertFormComponentActionShouldNotOpenUrlInNewTab()

use function Pest\Livewire\livewire;
 
it('links to the correct Filament sites', function () {
$invoice = Invoice::factory()->create();
 
livewire(EditInvoice::class, [
'invoice' => $invoice,
])
->assertFormComponentActionHasUrl('customer_id', 'filament', 'https://filamentphp.com/')
->assertFormComponentActionDoesNotHaveUrl('customer_id', 'filament', 'https://github.com/filamentphp/filament')
->assertFormComponentActionShouldOpenUrlInNewTab('customer_id', 'filament')
->assertFormComponentActionShouldNotOpenUrlInNewTab('customer_id', 'github');
});
Edit on GitHub

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