表单构造器
测试
概述
所有本指南内的示例都使用 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');});Still need help? Join our Discord community or open a GitHub discussion