if ($browser->seeLink($linkText)) {
注意
这些方法与 jQuery 交互。 如果页面上没有 jQuery,Dusk 会自动将其注入到页面中,以便在测试期间可用。
该keys
方法让你可以再指定元素中输入比type
方法更加复杂的输入序列。例如,你可以在输入值的同时按下按键。在这个例子中,输入taylor
时,shift
键也同时被按下。当taylor
输入完之后, 将会输入swift
而不会按下任何按键:
$browser->keys('selector', ['{shift}', 'taylor'], 'swift');
keys
方法的另一个有价值的用例是向你的应用程序的主要 CSS 选择器发送「键盘快捷键」组合:
$browser->keys('.app', ['{command}', 'j']);
技巧
所有修饰符键如{command}
都包裹在{}
字符中,并且与在 Facebook\WebDriver\WebDriverKeys
类中定义的常量匹配,该类可以在 GitHub 上找到.
该click
方法可用于「点击」与给定选择器匹配的元素:
$browser->click('.selector');
该clickAtXPath
方法可用于「单击」与给定 XPath 表达式匹配的元素:
$browser->clickAtXPath('//div[@class = "selector"]');
该clickAtPoint
方法可用于「点击」相对于浏览器可视区域的给定坐标对上的最高元素:
$browser->clickAtPoint($x = 0, $y = 0);
该doubleClick
方法可用于模拟鼠标的双击:
$browser->doubleClick();
该rightClick
方法可用于模拟鼠标的右击:
$browser->rightClick();
$browser->rightClick('.selector');
该clickAndHold
方法可用于模拟被单击并按住的鼠标按钮。 随后调用 releaseMouse
方法将撤消此行为并释放鼠标按钮:
$browser->clickAndHold()
->pause(1000)
->releaseMouse();
该mouseover
方法可用于与给定选择器匹配的元素的鼠标悬停动作:
$browser->mouseover('.selector');
该drag
方法用于将与指定选择器匹配的元素拖到其它元素:
$browser->drag('.from-selector', '.to-selector');
或者,可以在单一方向上拖动元素:
$browser->dragLeft('.selector', $pixels = 10);
$browser->dragRight('.selector', $pixels = 10);
$browser->dragUp('.selector', $pixels = 10);
$browser->dragDown('.selector', $pixels = 10);
最后,你可以将元素拖动给定的偏移量:
$browser->dragOffset('.selector', $x = 10, $y = 10);
JavaScript 对话框
Dusk 提供了各种与 JavaScript 对话框进行交互的方法。例如,你可以使用waitForDialog
方法来等待 JavaScript 对话框的出现。此方法接受一个可选参数,该参数指示等待对话框出现多少秒:
$browser->waitForDialog($seconds = null);
该assertDialogOpened
方法,断言对话框已经显示,并且其消息与给定值匹配:
$browser->assertDialogOpened('Dialog message');
typeInDialog
方法,在打开的 JavaScript 提示对话框中输入给定值:
$browser->typeInDialog('Hello World');
acceptDialog
方法,通过点击确定按钮关闭打开的 JavaScript 对话框:
$browser->acceptDialog();
dismissDialog
方法,通过点击取消按钮关闭打开的 JavaScript 对话框(仅对确认对话框有效):
$browser->dismissDialog();
选择器作用范围
有时可能希望在给定的选择器范围内执行多个操作。比如,可能想要断言表格中存在某些文本,然后点击表格中的一个按钮。那么你可以使用with
方法实现此需求。在传递给with
方法的闭包内执行的所有操作都将限于原始选择器:
$browser->with('.table', function (Browser $table) {
$table->assertSee('Hello World')
->clickLink('Delete');
});
你可能偶尔需要在当前范围之外执行断言。 你可以使用elsewhere
和elsewhereWhenAvailable
方法来完成此操作:
$browser->with('.table', function ($table) {
$browser->elsewhere('.page-title', function ($title) {
$title->assertSee('Hello World');
});
$browser->elsewhereWhenAvailable('.page-title', function ($title) {
$title->assertSee('Hello World');
});
});
在测试大面积使用 JavaScript 的应用时,在进行测试之前,通常有必要 「等待」 某些元素或数据可用。Dusk 可轻松实现。使用一系列方法,可以等到页面元素可用,甚至给定的 JavaScript 表达式执行结果为true
。
如果需要测试暂停指定的毫秒数, 使用pause
方法:
$browser->pause(1000);
如果你只需要在给定条件为true
时暂停测试,请使用pauseIf
方法:
$browser->pauseIf(App::environment('production'), 1000);
同样地,如果你需要暂停测试,除非给定的条件是true
,你可以使用pauseUnless
方法:
$browser->pauseUnless(App::environment('testing'), 1000);
等待选择器
该waitFor
方法可以用于暂停执行测试,直到页面上与给定 CSS 选择器匹配的元素被显示。默认情况下,将在暂停超过 5 秒后抛出异常。如有必要,可以传递自定义超时时长作为其第二个参数:
$browser->waitFor('.selector');
$browser->waitFor('.selector', 1);
你也可以等待选择器显示给定文字:
$browser->waitForTextIn('.selector', 'Hello World');
$browser->waitForTextIn('.selector', 'Hello World', 1);
你也可以等待指定选择器从页面消失:
$browser->waitUntilMissing('.selector');
$browser->waitUntilMissing('.selector', 1);
或者,你可以等待与给定选择器匹配的元素被启用或禁用:
$browser->waitUntilEnabled('.selector');
$browser->waitUntilEnabled('.selector', 1);
$browser->waitUntilDisabled('.selector');
$browser->waitUntilDisabled('.selector', 1);
限定作用域范围(可用时)
有时,你或许希望等待给定选择器出现,然后与匹配选择器的元素进行交互。例如,你可能希望等到模态窗口可用,然后在模态窗口中点击「确定」按钮。在这种情况下,可以使用whenAvailable
方法。给定回调内的所有要执行的元素操作都将被限定在起始选择器上:
$browser->whenAvailable('.modal', function (Browser $modal) {
$modal->assertSee('Hello World')
->press('OK');
});
waitForText
方法可以用于等待页面上给定文字被显示:
$browser->waitForText('Hello World');
$browser->waitForText('Hello World', 1);
你可以使用waitUntilMissingText
方法来等待,直到显示的文本已从页面中删除为止:
$browser->waitUntilMissingText('Hello World');
$browser->waitUntilMissingText('Hello World', 1);
waitForLink
方法用于等待给定链接文字在页面上显示:
$browser->waitForLink('Create');
$browser->waitForLink('Create', 1);
waitForInput
方法可用于等待,直到给定的输入字段在页面上可见:
$browser->waitForInput($field);
$browser->waitForInput($field, 1);
等待页面跳转
当给出类似$browser->assertPathIs('/home')
的路径断言时,如果window.location.pathname
被异步更新,断言就会失败。可以使用waitForLocation
方法等待页面跳转到给定路径:
$browser->waitForLocation('/secret');
waitForLocation
方法还可用于等待当前窗口位置成为完全限定的 URL:
$browser->waitForLocation('https://example.com/path');
还可以使用被命名的路由等待跳转:
$browser->waitForRoute($routeName, $parameters);
等待页面重新加载
如果要在页面重新加载后断言,可以使用waitForReload
方法:
use Laravel\Dusk\Browser;
$browser->waitForReload(function (Browser $browser) {
$browser->press('Submit');
->assertSee('Success!');
由于需要等待页面重新加载通常发生在单击按钮之后,为了方便起见,你可以使用clickAndWaitForReload
方法:
$browser->clickAndWaitForReload('.selector')
->assertSee('something');
等待 JavaScript 表达式
有时候会希望暂停测试的执行,直到给定的 JavaScript 表达式执行结果为true
。可以使用waitUntil
方法轻松地达成此目的。 通过这个方法执行表达式,不需要包含return
关键字或者结束分号:
$browser->waitUntil('App.data.servers.length > 0');
$browser->waitUntil('App.data.servers.length > 0', 1);
等待 Vue 表达式
waitUntilVue
和waitUntilVueIsNot
方法可以一直等待,直到 Vue 组件 的属性包含给定的值:
$browser->waitUntilVue('user.name', 'Taylor', '@user');
$browser->waitUntilVueIsNot('user.name', null, '@user');
等待 JavaScript 事件
waitForEvent
方法可用于暂停测试的执行,直到 JavaScript 事件发生:
$browser->waitForEvent('load');
事件监听器附加到当前作用域,默认情况下是body
元素。当使用范围选择器时,事件监听器将被附加到匹配的元素上:
$browser->with('iframe', function (Browser $iframe) {
$iframe->waitForEvent('load');
});
你也可以提供一个选择器作为waitForEvent
方法的第二个参数,将事件监听器附加到特定的元素上:
$browser->waitForEvent('load', '.selector');
你也可以等待document
和window
对象上的事件:
$browser->waitForEvent('scroll', 'document');
$browser->waitForEvent('resize', 'window', 5);
Dusk 中的许多 「wait」 方法都依赖于底层方法 waitUsing。你可以直接用这个方法去等待一个回调函数返回waitUsing
。你可以直接用这个方法去等待一个回调函数返回true
。该waitUsing
方法接收一个最大的等待秒数,闭包执行间隔时间,闭包,以及一个可选的失败信息:
$browser->waitUsing(10, 1, function () use ($something) {
return $something->isReady();
}, "有些东西没有及时准备好。");
滚动元素到视图中
有时你可能无法单击某个元素,因为该元素在浏览器的可见区域之外。该scrollIntoView
方法可以将元素滚动到浏览器可视窗口内:
$browser->scrollIntoView('.selector')
->click('.selector');
可用的断言
Dusk 提供了各种你可以对应用使用的断言。所有可用的断言罗列如下:
assertTitle
assertTitleContains
assertUrlIs
assertSchemeIs
assertSchemeIsNot
assertHostIs
assertHostIsNot
assertPortIs
assertPortIsNot
assertPathBeginsWith
assertPathIs
assertPathIsNot
assertRouteIs
assertQueryStringHas
assertQueryStringMissing
assertFragmentIs
assertFragmentBeginsWith
assertFragmentIsNot
assertHasCookie
assertHasPlainCookie
assertCookieMissing
assertPlainCookieMissing
assertCookieValue
assertPlainCookieValue
assertSee
assertDontSee
assertSeeIn
assertDontSeeIn
assertSeeAnythingIn
assertSeeNothingIn
assertScript
assertSourceHas
assertSourceMissing
assertSeeLink
assertDontSeeLink
assertInputValue
assertInputValueIsNot
assertChecked
assertNotChecked
assertIndeterminate
assertRadioSelected
assertRadioNotSelected
assertSelected
assertNotSelected
assertSelectHasOptions
assertSelectMissingOptions
assertSelectHasOption
assertSelectMissingOption
assertValue
assertValueIsNot
assertAttribute
assertAttributeContains
assertAriaAttribute
assertDataAttribute
assertVisible
assertPresent
assertNotPresent
assertMissing
assertInputPresent
assertInputMissing
assertDialogOpened
assertEnabled
assertDisabled
assertButtonEnabled
assertButtonDisabled
assertFocused
assertNotFocused
assertAuthenticated
assertGuest
assertAuthenticatedAs
assertVue
assertVueIsNot
assertVueContains
assertVueDoesNotContain
assertTitle
断言页面标题为给定文本:
$browser->assertTitle($title);
assertTitleContains
断言页面标题包含给定文本:
$browser->assertTitleContains($title);
assertUrlIs
断言当前的 URL(不包含 query string)是给定的字符串:
$browser->assertUrlIs($url);
assertSchemeIs
断言当前的 URL scheme 是给定的 scheme:
$browser->assertSchemeIs($scheme);
assertSchemeIsNot
断言当前的 URL scheme 不是给定的 scheme:
$browser->assertSchemeIsNot($scheme);
assertHostIs
断言当前的 URL host 是给定的 host:
$browser->assertHostIs($host);
assertHostIsNot
断言当前的 URL host 不是给定的 host:
$browser->assertHostIsNot($host);
assertPortIs
断言当前的 URL 端口是给定的端口:
$browser->assertPortIs($port);
assertPortIsNot
断言当前的 URL 端口不是给定的端口:
$browser->assertPortIsNot($port);
assertPathBeginsWith
断言当前的 URL 路径以给定的路径开始:
$browser->assertPathBeginsWith('/home');
assertPathIs
断言当前的路径是给定的路径:
$browser->assertPathIs('/home');
assertPathIsNot
断言当前的路径不是给定的路径:
$browser->assertPathIsNot('/home');
assertRouteIs
断言给定的 URL 是给定的命名路由的 URL:
$browser->assertRouteIs($name, $parameters);
assertQueryStringHas
断言给定的查询字符串参数存在:
$browser->assertQueryStringHas($name);
断言给定的查询字符串参数存在并且具有给定的值:
$browser->assertQueryStringHas($name, $value);
assertQueryStringMissing
断言缺少给定的查询字符串参数:
$browser->assertQueryStringMissing($name);
assertFragmentIs
断言 URL 的当前哈希片段与给定的片段匹配:
$browser->assertFragmentIs('anchor');
assertFragmentBeginsWith
断言 URL 的当前哈希片段以给定片段开头:
$browser->assertFragmentBeginsWith('anchor');
assertFragmentIsNot
断言 URL 的当前哈希片段与给定的片段不匹配:
$browser->assertFragmentIsNot('anchor');
assertHasCookie
断言给定的加密 cookie 存在:
$browser->assertHasCookie($name);
assertHasPlainCookie
断言给定的未加密 cookie 存在:
$browser->assertHasPlainCookie($name);
assertCookieMissing
断言给定的加密 cookie 不存在:
$browser->assertCookieMissing($name);
assertPlainCookieMissing
断言给定的未加密 cookie 不存在:
$browser->assertPlainCookieMissing($name);
assertCookieValue
断言加密的 cookie 具有给定值:
$browser->assertCookieValue($name, $value);
assertPlainCookieValue
断言未加密的 cookie 具有给定值:
$browser->assertPlainCookieValue($name, $value);
assertSee
断言在页面中有给定的文本:
$browser->assertSee($text);
assertDontSee
断言在页面中没有给定的文本:
$browser->assertDontSee($text);
assertSeeIn
断言在选择器中有给定的文本:
$browser->assertSeeIn($selector, $text);
assertDontSeeIn
断言在选择器中不存在给定的文本:
$browser->assertDontSeeIn($selector, $text);
assertSeeAnythingIn
断言在选择器中存在任意的文本:
$browser->assertSeeAnythingIn($selector);
断言在选择器中不存在文本:
$browser->assertSeeNothingIn($selector);
assertScript
断言给定的 JavaScript 表达式结果为给定的值:
$browser->assertScript('window.isLoaded')
->assertScript('document.readyState', 'complete');
assertSourceHas
断言在页面中存在给定的源码:
$browser->assertSourceHas($code);
assertSourceMissing
断言页面中没有给定的源码:
$browser->assertSourceMissing($code);
assertSeeLink
断言在页面中存在指定的链接:
$browser->assertSeeLink($linkText);
assertDontSeeLink
断言页面中没有指定的链接:
$browser->assertDontSeeLink($linkText);
assertInputValue
断言输入框(input)有给定的值:
$browser->assertInputValue($field, $value);
assertInputValueIsNot
断言输入框没有给定的值:
$browser->assertInputValueIsNot($field, $value);
assertChecked
断言复选框(checkbox)被选中:
$browser->assertChecked($field);
assertNotChecked
断言复选框没有被选中:
$browser->assertNotChecked($field);
assertRadioSelected
断言单选框(radio)被选中:
$browser->assertRadioSelected($field, $value);
assertRadioNotSelected
断言单选框(radio)没有被选中:
$browser->assertRadioNotSelected($field, $value);
assertSelected
断言下拉框有给定的值:
$browser->assertSelected($field, $value);
断言下拉框没有给定的值:
$browser->assertNotSelected($field, $value);
assertSelectHasOptions
断言给定的数组值是可选的:
$browser->assertSelectHasOptions($field, $values);
assertSelectMissingOptions
断言给定的数组值是不可选的:
$browser->assertSelectMissingOptions($field, $values);
assertSelectHasOption
断言给定的值在给定的地方是可供选择的:
$browser->assertSelectHasOption($field, $value);
assertSelectMissingOption
断言给定的值不可选:
$browser->assertSelectMissingOption($field, $value);
assertValue
断言选择器范围内的元素存在指定的值:
$browser->assertValue($selector, $value);
assertValueIsNot
断言选择器范围内的元素不存在指定的值:
$browser->assertValueIsNot($selector, $value);
assertAttribute
断言与给定选择器匹配的元素在提供的属性中具有给定的值:
$browser->assertAttribute($selector, $attribute, $value);
assertAttributeContains
断言匹配给定选择器的元素在提供的属性中包含给定值:
$browser->assertAttributeContains($selector, $attribute, $value);
assertAriaAttribute
断言与给定选择器匹配的元素在给定的 aria 属性中具有给定的值:
$browser->assertAriaAttribute($selector, $attribute, $value);
例如,给定标记<button aria-label="Add"></button>
,你可以像这样声明aria-label
属性:
$browser->assertAriaAttribute('button', 'label', 'Add')
assertDataAttribute
断言与给定选择器匹配的元素在提供的 data 属性中具有给定的值:
$browser->assertDataAttribute($selector, $attribute, $value);
例如,给定标记<tr id="row-1" data-content="attendees"></tr>
,你可以像这样断言data-label
属性:
$browser->assertDataAttribute('#row-1', 'content', 'attendees')
assertVisible
断言匹配给定选择器的元素可见:
$browser->assertVisible($selector);
assertPresent
断言匹配给定选择器的元素存在:
$browser->assertPresent($selector);
assertNotPresent
断言源中不存在与给定选择器匹配的元素:
$browser->assertNotPresent($selector);
assertMissing
断言匹配给定选择器的元素不可见:
$browser->assertMissing($selector);
assertInputPresent
断言具有给定名称的输入存在:
$browser->assertInputPresent($name);
assertInputMissing
断言源中不存在具有给定名称的输入:
$browser->assertInputMissing($name);
assertDialogOpened
断言已打开带有给定消息的 JavaScript 对话框:
$browser->assertDialogOpened($message);
assertEnabled
断言给定的字段已启用:
$browser->assertEnabled($field);
assertDisabled
断言给定的字段被禁用:
$browser->assertDisabled($field);
assertButtonEnabled
断言给定的按钮已启用:
$browser->assertButtonEnabled($button);
assertButtonDisabled
断言给定的按钮被禁用:
$browser->assertButtonDisabled($button);
assertFocused
断言给定的字段是焦点:
$browser->assertFocused($field);
assertNotFocused
断言给定字段未聚焦:
$browser->assertNotFocused($field);
assertAuthenticated
断言用户已通过身份验证:
$browser->assertAuthenticated();
assertGuest
断言用户未通过身份验证:
$browser->assertGuest();
assertAuthenticatedAs
断言用户已作为给定用户进行身份验证:
$browser->assertAuthenticatedAs($user);
assertVue
Dusk 甚至允许你对 Vue 组件数据的状态进行断言。例如,假设你的应用程序包含以下 Vue 组件:
<profile dusk="profile-component"></profile>
Vue.component('profile', {
template: '<div>{{ user.name }}</div>',
data: function () {
return {
user: {
name: 'Taylor'
});
你可以像这样断言 Vue 组件的状态:
* 一个基本的 Vue 测试示例
* @return void
public function testVue()
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertVue('user.name', 'Taylor', '@profile-component');
});
assertVueIsNot
断言 Vue 组件数据的属性不匹配给定的值:
$browser->assertVueIsNot($property, $value, $componentSelector = null);
assertVueContains
断言 Vue 组件数据的属性是一个数组,并包含给定的值:
$browser->assertVueContains($property, $value, $componentSelector = null);
assertVueDoesNotContain
断言 Vue 组件数据的属性是一个数组,且不包含给定的值:
$browser->assertVueDoesNotContain($property, $value, $componentSelector = null);
Pages
有时,测试需要按顺序执行几个复杂的操作。这会使测试代码更难阅读和理解。 Dusk Pages 允许你定义语义化的操作,然后可以通过单一方法在给定页面上执行这些操作。Pages 还可以为应用或单个页面定义通用选择器的快捷方式。
生成 Pages
dusk:page
Artisan 命令可以生成页面对象。所有的页面对象都位于tests/Browser/Pages
目录:
php artisan dusk:page Login
配置 Pages
默认情况下,页面具有三种方法:url
、assert
和elements
。我们现在将讨论 url
和assert
方法。elements
方法将在下面更详细地讨论。
<code>url</code> 方法
url
方法应该返回表示页面 URL 的路径。 Dusk 将会在浏览器中使用这个 URL 来导航到具体页面:
* 获取页面的 URL。
* @return string
public function url()
return '/login';
<code>assert</code> 方法
assert
方法可以作出任何断言来验证浏览器是否在指定页面上。实际上没有必要在这个方法中放置任何东西;但是,你可以按自己的需求来做出这些断言。导航到页面时,这些断言将自动运行:
* 断言浏览器当前处于指定页面。
public function assert(Browser $browser): void
$browser->assertPathIs($this->url());
导航至页面
一旦页面定义好之后,你可以使用visit
方法导航至页面:
use Tests\Browser\Pages\Login;
$browser->visit(new Login);
有时你可能已经在给定的页面上,需要将页面的选择器和方法「加载」到当前的测试上下文中。 这在通过按钮重定向到指定页面而没有明确导航到该页面时很常见。 在这种情况下,你可以使用on
方法加载页面:
use Tests\Browser\Pages\CreatePlaylist;
$browser->visit('/dashboard')
->clickLink('Create Playlist')
->on(new CreatePlaylist)
->assertSee('@create');
选择器简写
该elements
方法允许你为页面中的任何 CSS 选择器定义简单易记的简写。例如,让我们为应用登录页中的 email 输入框定义一个简写:
* 获取页面元素的简写。
* @return array<string, string>
public function elements(): array
return [
'@email' => 'input[name=email]',
一旦定义了简写,你就可以用这个简写来代替之前在页面中使用的完整 CSS 选择器:
$browser->type('@email', '[email protected]');
全局的选择器简写
安装 Dusk 之后,Page
基类存放在你的tests/Browser/Pages
目录。该类中包含一个siteElements
方法,这个方法可以用来定义全局的选择器简写,这样在你应用中每个页面都可以使用这些全局选择器简写了:
* 获取站点全局的选择器简写。
* @return array<string, string>
public static function siteElements(): array
return [
'@element' => '#selector',
除了页面中已经定义的默认方法之外,你还可以定义在整个测试过程中会使用到的其他方法。例如,假设我们正在开发一个音乐管理应用,在应用中每个页面都可能需要一个公共的方法来创建播放列表,而不是在每一个测试类中都重写一遍创建播放列表的逻辑,这时候你可以在你的页面类中定义一个createPlaylist
方法:
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
class Dashboard extends Page
* 创建一个新的播放列表。
public function createPlaylist(Browser $browser, string $name): void
$browser->type('name', $name)
->check('share')
->press('Create Playlist');
方法被定义之后,你可以在任何使用到该页的测试中使用它了。浏览器实例会自动作为第一个参数传递给自定义页面方法:
use Tests\Browser\Pages\Dashboard;
$browser->visit(new Dashboard)
->createPlaylist('My Playlist')
->assertSee('My Playlist');
组件类似于 Dusk 的 「页面对象」,不过它更多的是贯穿整个应用程序中频繁重用的 UI 和功能片断,比如说导航条或信息通知弹窗。因此,组件并不会绑定于某个明确的 URL。
使用dusk:component
Artisan 命令即可生成组件。新生成的组件位于tests/Browser/Components
目录下:
php artisan dusk:component DatePicker
如上所示,这是生成一个「日期选择器」(date picker)组件的示例,这个组件可能会贯穿使用在你应用程序的许多页面中。在整个测试套件的大量测试页面中,手动编写日期选择的浏览器自动化逻辑会非常麻烦。 更方便的替代办法是,定义一个表示日期选择器的 Dusk 组件,然后把自动化逻辑封装在该组件内:
<?php
namespace Tests\Browser\Components;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Component as BaseComponent;
class DatePicker extends BaseComponent
* 获取组件的 root selector。
public function selector(): string
return '.date-picker';
* 断言浏览器包含组件。
public function assert(Browser $browser): void
$browser->assertVisible($this->selector());
* 读取组件的元素简写。
* @return array<string, string>
public function elements(): array
return [
'@date-field' => 'input.datepicker-input',
'@year-list' => 'div > div.datepicker-years',
'@month-list' => 'div > div.datepicker-months',
'@day-list' => 'div > div.datepicker-days',
* 选择给定日期。
public function selectDate(Browser $browser, int $year, int $month, int $day): void
$browser->click('@date-field')
->within('@year-list', function (Browser $browser) use ($year) {
$browser->click($year);
->within('@month-list', function (Browser $browser) use ($month) {
$browser->click($month);
->within('@day-list', function (Browser $browser) use ($day) {
$browser->click($day);
});
当组件被定义了之后,我们就可以轻松的在任意测试页面通过日期选择器选择一个日期。并且,如果选择日期的逻辑发生了变化,我们只需要更新组件即可:
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\Browser\Components\DatePicker;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
* 一个基础的组件测试用例.
public function test_basic_example(): void
$this->browse(function (Browser $browser) {
$browser->visit('/')
->within(new DatePicker, function (Browser $browser) {
$browser->selectDate(2019, 1, 30);
->assertSee('January');
});
注意
大多数 Dusk 持续集成配置都希望你的 Laravel 应用程序使用端口 8000 上的内置 PHP 开发服务器提供服务。因此,你应该确保持续集成环境有一个值为 http://127.0.0.1:8000
的 APP_URL
环境变量。
Heroku CI
要在 Heroku CI 中运行 Dusk 测试,请将以下 Google Chrome buildpack 和 脚本添加到 Heroku 的 app.json
文件中:
"environments": {
"test": {
"buildpacks": [
{ "url": "heroku/php" },
{ "url": "https://github.com/heroku/heroku-buildpack-google-chrome" }
"scripts": {
"test-setup": "cp .env.testing .env",
"test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve --no-reload > /dev/null 2>&1 &' && php artisan dusk"
Travis CI
要在 Travis CI 运行 Dusk 测试,可以使用下面这个 .travis.yml
配置。由于 Travis CI 不是一个图形化的环境,我们还需要一些额外的步骤以便启动 Chrome 浏览器。此外,我们将会使用 php artisan serve
来启动 PHP 自带的 Web 服务器:
language: php
php:
- 7.3
addons:
chrome: stable
install:
- cp .env.testing .env
- travis_retry composer install --no-interaction --prefer-dist
- php artisan key:generate
- php artisan dusk:chrome-driver
before_script:
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
- php artisan serve --no-reload &
script:
- php artisan dusk
GitHub Actions
如果你正在使用 Github Actions 来运行你的 Dusk 测试,你应该使用以下这份配置文件为模版。像 TravisCI 一样,我们使用 php artisan serve
命令来启动 PHP 的内置 Web 服务:
name: CI
on: [push]
jobs:
dusk-php:
runs-on: ubuntu-latest
env:
APP_URL: "http://127.0.0.1:8000"
DB_USERNAME: root
DB_PASSWORD: root
MAIL_MAILER: log
steps:
- uses: actions/checkout@v3
- name: Prepare The Environment
run: cp .env.example .env
- name: Create Database
run: |
sudo systemctl start mysql
mysql --user="root" --password="root" -e "CREATE DATABASE \`my-database\` character set UTF8mb4 collate utf8mb4_bin;"
- name: Install Composer Dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Generate Application Key
run: php artisan key:generate
- name: Upgrade Chrome Driver
run: php artisan dusk:chrome-driver --detect
- name: Start Chrome Driver
run: ./vendor/laravel/dusk/bin/chromedriver-linux &
- name: Run Laravel Server
run: php artisan serve --no-reload &
- name: Run Dusk Tests
run: php artisan dusk
- name: Upload Screenshots
if: failure()
uses: actions/upload-artifact@v2
with:
name: screenshots
path: tests/Browser/screenshots
- name: Upload Console Logs
if: failure()
uses: actions/upload-artifact@v2
with:
name: console
path: tests/Browser/console
Laravel是一个具有表达力,优雅语法的Web应用程序框架。我们认为,发展必须是一种令人愉悦的创造力,才能真正实现。Laravel试图通过减轻大多数Web项目中使用的常见任务来减轻开发的痛苦.