Laravel Dusk
- Introduction
- Installation
- Getting Started
- Browser Basics
- Interacting With Elements
- Available Assertions
- Pages
- Components
- Continuous Integration
Introduction
Laravel Dusk 提供了富有表現力、易於使用的瀏覽器自動化和測試 API。預設情況下,Dusk 不要求您在本機電腦上安裝 JDK 或 Selenium。相對地,Dusk 使用獨立的 ChromeDriver 安裝。但是,您可以自由使用任何其他您希望的 Selenium 相容驅動程式。
Installation
若要開始使用,您應該安裝 Google Chrome 並將 laravel/dusk
Composer 依賴項新增至您的專案
1composer require laravel/dusk --dev
如果您正在手動註冊 Dusk 的服務提供器,您**絕不**應該在您的生產環境中註冊它,因為這樣做可能會導致任意使用者能夠通過您的應用程式進行身份驗證。
安裝 Dusk 套件後,執行 dusk:install
Artisan 命令。 dusk:install
命令將建立 tests/Browser
目錄、一個 Dusk 範例測試,並為您的作業系統安裝 Chrome Driver 二進位檔
1php artisan dusk:install
接下來,在您應用程式的 .env
檔案中設定 APP_URL
環境變數。此值應與您在瀏覽器中用於存取應用程式的 URL 相符。
如果您使用 Laravel Sail 來管理您的本機開發環境,也請參閱關於 設定和執行 Dusk 測試的 Sail 文件。
Managing ChromeDriver Installations
如果您想安裝與 Laravel Dusk 通過 dusk:install
命令安裝的版本不同的 ChromeDriver 版本,您可以使用 dusk:chrome-driver
命令
1# Install the latest version of ChromeDriver for your OS... 2php artisan dusk:chrome-driver 3 4# Install a given version of ChromeDriver for your OS... 5php artisan dusk:chrome-driver 86 6 7# Install a given version of ChromeDriver for all supported OSs... 8php artisan dusk:chrome-driver --all 9 10# Install the version of ChromeDriver that matches the detected version of Chrome / Chromium for your OS...11php artisan dusk:chrome-driver --detect
Dusk 要求 chromedriver
二進位檔是可執行的。如果您在執行 Dusk 時遇到問題,您應該使用以下命令確保二進位檔是可執行的:chmod -R 0755 vendor/laravel/dusk/bin/
。
Using Other Browsers
預設情況下,Dusk 使用 Google Chrome 和獨立的 ChromeDriver 安裝來執行您的瀏覽器測試。但是,您可以啟動您自己的 Selenium 伺服器,並針對您希望的任何瀏覽器執行測試。
若要開始使用,請開啟您的 tests/DuskTestCase.php
檔案,這是您應用程式的基本 Dusk 測試案例。在此檔案中,您可以移除對 startChromeDriver
方法的呼叫。這將停止 Dusk 自動啟動 ChromeDriver
1/**2 * Prepare for Dusk test execution.3 *4 * @beforeClass5 */6public static function prepare(): void7{8 // static::startChromeDriver();9}
接下來,您可以修改 driver
方法以連接到您選擇的 URL 和埠。此外,您可以修改應傳遞給 WebDriver 的「所需功能」
1use Facebook\WebDriver\Remote\RemoteWebDriver; 2 3/** 4 * Create the RemoteWebDriver instance. 5 */ 6protected function driver(): RemoteWebDriver 7{ 8 return RemoteWebDriver::create( 9 'https://127.0.0.1:4444/wd/hub', DesiredCapabilities::phantomjs()10 );11}
Getting Started
Generating Tests
若要產生 Dusk 測試,請使用 dusk:make
Artisan 命令。產生的測試將放置在 tests/Browser
目錄中
1php artisan dusk:make LoginTest
Resetting the Database After Each Test
您撰寫的大多數測試將與從應用程式資料庫中檢索資料的頁面互動;但是,您的 Dusk 測試絕不應使用 RefreshDatabase
trait。 RefreshDatabase
trait 利用資料庫事務,這將不適用於或跨 HTTP 請求提供。相反,您有兩個選項:DatabaseMigrations
trait 和 DatabaseTruncation
trait。
使用資料庫遷移
DatabaseMigrations
trait 將在每次測試之前執行您的資料庫遷移。但是,為每次測試刪除並重新建立資料庫表通常比截斷表慢
1<?php2 3use Illuminate\Foundation\Testing\DatabaseMigrations;4use Laravel\Dusk\Browser;5 6uses(DatabaseMigrations::class);7 8//
1<?php 2 3namespace Tests\Browser; 4 5use Illuminate\Foundation\Testing\DatabaseMigrations; 6use Laravel\Dusk\Browser; 7use Tests\DuskTestCase; 8 9class ExampleTest extends DuskTestCase10{11 use DatabaseMigrations;12 13 //14}
執行 Dusk 測試時,可能無法使用 SQLite 記憶體資料庫。由於瀏覽器在其自己的進程中執行,因此它將無法存取其他進程的記憶體資料庫。
使用資料庫截斷
DatabaseTruncation
trait 將在第一次測試時遷移您的資料庫,以確保您的資料庫表已正確建立。但是,在後續測試中,資料庫的表將僅被截斷 - 相較於重新執行所有資料庫遷移,這提供了速度提升
1<?php2 3use Illuminate\Foundation\Testing\DatabaseTruncation;4use Laravel\Dusk\Browser;5 6uses(DatabaseTruncation::class);7 8//
1<?php 2 3namespace Tests\Browser; 4 5use App\Models\User; 6use Illuminate\Foundation\Testing\DatabaseTruncation; 7use Laravel\Dusk\Browser; 8use Tests\DuskTestCase; 9 10class ExampleTest extends DuskTestCase11{12 use DatabaseTruncation;13 14 //15}
預設情況下,此 trait 將截斷除 migrations
表之外的所有表。如果您想自訂應截斷的表,您可以在您的測試類別上定義 $tablesToTruncate
屬性
如果您正在使用 Pest,您應該在基本 DuskTestCase
類別或您的測試檔案擴展的任何類別上定義屬性或方法。
1/**2 * Indicates which tables should be truncated.3 *4 * @var array5 */6protected $tablesToTruncate = ['users'];
或者,您可以在您的測試類別上定義 $exceptTables
屬性,以指定應從截斷中排除哪些表
1/**2 * Indicates which tables should be excluded from truncation.3 *4 * @var array5 */6protected $exceptTables = ['users'];
若要指定應截斷其表的資料庫連線,您可以在您的測試類別上定義 $connectionsToTruncate
屬性
1/**2 * Indicates which connections should have their tables truncated.3 *4 * @var array5 */6protected $connectionsToTruncate = ['mysql'];
如果您想在執行資料庫截斷之前或之後執行程式碼,您可以在您的測試類別上定義 beforeTruncatingDatabase
或 afterTruncatingDatabase
方法
1/** 2 * Perform any work that should take place before the database has started truncating. 3 */ 4protected function beforeTruncatingDatabase(): void 5{ 6 // 7} 8 9/**10 * Perform any work that should take place after the database has finished truncating.11 */12protected function afterTruncatingDatabase(): void13{14 //15}
Running Tests
若要執行您的瀏覽器測試,請執行 dusk
Artisan 命令
1php artisan dusk
如果您上次執行 dusk
命令時有測試失敗,您可以通過首先重新執行失敗的測試來節省時間,使用 dusk:fails
命令
1php artisan dusk:fails
dusk
命令接受 Pest / PHPUnit 測試執行器通常接受的任何引數,例如允許您僅針對給定的 群組 執行測試
1php artisan dusk --group=foo
如果您使用 Laravel Sail 來管理您的本機開發環境,也請參閱關於 設定和執行 Dusk 測試的 Sail 文件。
手動啟動 ChromeDriver
預設情況下,Dusk 將自動嘗試啟動 ChromeDriver。如果這不適用於您的特定系統,您可以在執行 dusk
命令之前手動啟動 ChromeDriver。如果您選擇手動啟動 ChromeDriver,您應該註解掉您的 tests/DuskTestCase.php
檔案中的以下程式碼行
1/**2 * Prepare for Dusk test execution.3 *4 * @beforeClass5 */6public static function prepare(): void7{8 // static::startChromeDriver();9}
此外,如果您在 9515 以外的埠上啟動 ChromeDriver,您應該修改同一類別的 driver
方法以反映正確的埠
1use Facebook\WebDriver\Remote\RemoteWebDriver; 2 3/** 4 * Create the RemoteWebDriver instance. 5 */ 6protected function driver(): RemoteWebDriver 7{ 8 return RemoteWebDriver::create( 9 'https://127.0.0.1:9515', DesiredCapabilities::chrome()10 );11}
Environment Handling
若要強制 Dusk 在執行測試時使用其自己的環境檔案,請在您的專案根目錄中建立 .env.dusk.{environment}
檔案。例如,如果您將從您的 local
環境啟動 dusk
命令,您應該建立 .env.dusk.local
檔案。
執行測試時,Dusk 將備份您的 .env
檔案,並將您的 Dusk 環境重新命名為 .env
。測試完成後,您的 .env
檔案將被還原。
Browser Basics
Creating Browsers
若要開始使用,讓我們編寫一個測試,驗證我們可以登入我們的應用程式。產生測試後,我們可以修改它以導航到登入頁面,輸入一些憑證,然後點擊「登入」按鈕。若要建立瀏覽器實例,您可以從您的 Dusk 測試中呼叫 browse
方法
1<?php 2 3use App\Models\User; 4use Illuminate\Foundation\Testing\DatabaseMigrations; 5use Laravel\Dusk\Browser; 6 7uses(DatabaseMigrations::class); 8 9test('basic example', function () {10 $user = User::factory()->create([12 ]);13 14 $this->browse(function (Browser $browser) use ($user) {15 $browser->visit('/login')16 ->type('email', $user->email)17 ->type('password', 'password')18 ->press('Login')19 ->assertPathIs('/home');20 });21});
1<?php 2 3namespace Tests\Browser; 4 5use App\Models\User; 6use Illuminate\Foundation\Testing\DatabaseMigrations; 7use Laravel\Dusk\Browser; 8use Tests\DuskTestCase; 9 10class ExampleTest extends DuskTestCase11{12 use DatabaseMigrations;13 14 /**15 * A basic browser test example.16 */17 public function test_basic_example(): void18 {19 $user = User::factory()->create([21 ]);22 23 $this->browse(function (Browser $browser) use ($user) {24 $browser->visit('/login')25 ->type('email', $user->email)26 ->type('password', 'password')27 ->press('Login')28 ->assertPathIs('/home');29 });30 }31}
如您在上面的範例中所見,browse
方法接受一個閉包。瀏覽器實例將由 Dusk 自動傳遞到此閉包,並且是用於與您的應用程式互動並對其進行斷言的主要物件。
建立多個瀏覽器
有時您可能需要多個瀏覽器才能正確執行測試。例如,可能需要多個瀏覽器來測試與 websockets 互動的聊天畫面。若要建立多個瀏覽器,只需將更多瀏覽器引數新增至給定 browse
方法的閉包簽名中
1$this->browse(function (Browser $first, Browser $second) { 2 $first->loginAs(User::find(1)) 3 ->visit('/home') 4 ->waitForText('Message'); 5 6 $second->loginAs(User::find(2)) 7 ->visit('/home') 8 ->waitForText('Message') 9 ->type('message', 'Hey Taylor')10 ->press('Send');11 12 $first->waitForText('Hey Taylor')13 ->assertSee('Jeffrey Way');14});
Navigation
visit
方法可用於導航到您應用程式中給定的 URI
1$browser->visit('/login');
您可以使用 visitRoute
方法導航到具名路由
1$browser->visitRoute($routeName, $parameters);
您可以使用 back
和 forward
方法來「返回」和「前進」
1$browser->back();2 3$browser->forward();
您可以使用 refresh
方法來重新整理頁面
1$browser->refresh();
Resizing Browser Windows
您可以使用 resize
方法來調整瀏覽器視窗的大小
1$browser->resize(1920, 1080);
maximize
方法可用於最大化瀏覽器視窗
1$browser->maximize();
fitContent
方法將調整瀏覽器視窗的大小以符合其內容的大小
1$browser->fitContent();
當測試失敗時,Dusk 將自動調整瀏覽器大小以適合內容,然後再拍攝螢幕截圖。您可以通過在您的測試中呼叫 disableFitOnFailure
方法來停用此功能
1$browser->disableFitOnFailure();
您可以使用 move
方法將瀏覽器視窗移動到螢幕上的不同位置
1$browser->move($x = 100, $y = 100);
Browser Macros
如果您想定義一個自訂瀏覽器方法,您可以在各種測試中重複使用,您可以使用 Browser
類別上的 macro
方法。通常,您應該從服務提供器的 boot
方法中呼叫此方法
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Support\ServiceProvider; 6use Laravel\Dusk\Browser; 7 8class DuskServiceProvider extends ServiceProvider 9{10 /**11 * Register Dusk's browser macros.12 */13 public function boot(): void14 {15 Browser::macro('scrollToElement', function (string $element = null) {16 $this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");17 18 return $this;19 });20 }21}
macro
函數接受一個名稱作為其第一個引數,一個閉包作為其第二個引數。當在 Browser
實例上將巨集作為方法呼叫時,將會執行巨集的閉包
1$this->browse(function (Browser $browser) use ($user) {2 $browser->visit('/pay')3 ->scrollToElement('#credit-card-details')4 ->assertSee('Enter Credit Card Details');5});
Authentication
通常,您將測試需要身份驗證的頁面。您可以使用 Dusk 的 loginAs
方法,以避免在每次測試期間與應用程式的登入畫面互動。 loginAs
方法接受與您的可驗證模型或可驗證模型實例關聯的主鍵
1use App\Models\User;2use Laravel\Dusk\Browser;3 4$this->browse(function (Browser $browser) {5 $browser->loginAs(User::find(1))6 ->visit('/home');7});
在使用 loginAs
方法後,使用者會話將在檔案中的所有測試中維護。
Cookies
您可以使用 cookie
方法來取得或設定加密 cookie 的值。預設情況下,Laravel 建立的所有 cookie 都是加密的
1$browser->cookie('name');2 3$browser->cookie('name', 'Taylor');
您可以使用 plainCookie
方法來取得或設定未加密 cookie 的值
1$browser->plainCookie('name');2 3$browser->plainCookie('name', 'Taylor');
您可以使用 deleteCookie
方法來刪除給定的 cookie
1$browser->deleteCookie('name');
Executing JavaScript
您可以使用 script
方法在瀏覽器中執行任意 JavaScript 語句
1$browser->script('document.documentElement.scrollTop = 0');2 3$browser->script([4 'document.body.scrollTop = 0',5 'document.documentElement.scrollTop = 0',6]);7 8$output = $browser->script('return window.location.pathname');
Taking a Screenshot
您可以使用 screenshot
方法拍攝螢幕截圖,並使用給定的檔案名稱儲存它。所有螢幕截圖都將儲存在 tests/Browser/screenshots
目錄中
1$browser->screenshot('filename');
responsiveScreenshots
方法可用於在各種斷點拍攝一系列螢幕截圖
1$browser->responsiveScreenshots('filename');
screenshotElement
方法可用於拍攝頁面上特定元素的螢幕截圖
1$browser->screenshotElement('#selector', 'filename');
Storing Console Output to Disk
您可以使用 storeConsoleLog
方法將目前瀏覽器的主控台輸出寫入到磁碟,並使用給定的檔案名稱。主控台輸出將儲存在 tests/Browser/console
目錄中
1$browser->storeConsoleLog('filename');
Storing Page Source to Disk
您可以使用 storeSource
方法將目前頁面的原始碼寫入到磁碟,並使用給定的檔案名稱。頁面原始碼將儲存在 tests/Browser/source
目錄中
1$browser->storeSource('filename');
Interacting With Elements
Dusk Selectors
為與元素互動選擇良好的 CSS 選擇器是編寫 Dusk 測試最困難的部分之一。隨著時間的推移,前端變更可能會導致像以下這樣的 CSS 選擇器中斷您的測試
1// HTML...2 3<button>Login</button>
1// Test...2 3$browser->click('.login-page .container div > button');
Dusk 選擇器允許您專注於編寫有效的測試,而不是記住 CSS 選擇器。若要定義選擇器,請將 dusk
屬性新增至您的 HTML 元素。然後,當與 Dusk 瀏覽器互動時,在選擇器前加上 @
以操作測試中附加的元素
1// HTML...2 3<button dusk="login-button">Login</button>
1// Test...2 3$browser->click('@login-button');
如果需要,您可以通過 selectorHtmlAttribute
方法自訂 Dusk 選擇器使用的 HTML 屬性。通常,此方法應從您應用程式的 AppServiceProvider
的 boot
方法中呼叫
1use Laravel\Dusk\Dusk;2 3Dusk::selectorHtmlAttribute('data-dusk');
Text, Values, and Attributes
檢索和設定值
Dusk 提供了幾種方法,用於與頁面上元素的目前值、顯示文字和屬性互動。例如,若要取得與給定 CSS 或 Dusk 選擇器相符的元素的「值」,請使用 value
方法
1// Retrieve the value...2$value = $browser->value('selector');3 4// Set the value...5$browser->value('selector', 'value');
您可以使用 inputValue
方法來取得具有給定欄位名稱的輸入元素的「值」
1$value = $browser->inputValue('field');
檢索文字
text
方法可用於檢索與給定選擇器相符的元素的顯示文字
1$text = $browser->text('selector');
檢索屬性
最後,attribute
方法可用於檢索與給定選擇器相符的元素的屬性值
1$attribute = $browser->attribute('selector', 'value');
Interacting With Forms
輸入值
Dusk 提供了多種方法,用於與表單和輸入元素互動。首先,讓我們看看將文字輸入到輸入欄位的範例
請注意,雖然該方法在必要時接受一個引數,但我們不需要將 CSS 選擇器傳遞到 type
方法中。如果未提供 CSS 選擇器,Dusk 將搜尋具有給定 name
屬性的 input
或 textarea
欄位。
若要在不清除其內容的情況下將文字附加到欄位,您可以使用 append
方法
1$browser->type('tags', 'foo')2 ->append('tags', ', bar, baz');
您可以使用 clear
方法來清除輸入的值
1$browser->clear('email');
您可以指示 Dusk 使用 typeSlowly
方法緩慢輸入。預設情況下,Dusk 將在按鍵之間暫停 100 毫秒。若要自訂按鍵之間的時間量,您可以將適當的毫秒數作為方法的第三個引數傳遞
1$browser->typeSlowly('mobile', '+1 (202) 555-5555');2 3$browser->typeSlowly('mobile', '+1 (202) 555-5555', 300);
您可以使用 appendSlowly
方法來緩慢附加文字
1$browser->type('tags', 'foo')2 ->appendSlowly('tags', ', bar, baz');
下拉式選單
若要選取 select
元素上可用的值,您可以使用 select
方法。與 type
方法類似,select
方法不需要完整的 CSS 選擇器。當將值傳遞到 select
方法時,您應該傳遞底層選項值,而不是顯示文字
1$browser->select('size', 'Large');
您可以通過省略第二個引數來選取隨機選項
1$browser->select('size');
通過提供一個陣列作為第二個```php
``` 引數到 select
方法,您可以指示該方法選取多個選項
1$browser->select('categories', ['Art', 'Music']);
核取方塊
若要「勾選」核取方塊輸入,您可以使用 check
方法。與許多其他輸入相關方法類似,不需要完整的 CSS 選擇器。如果找不到 CSS 選擇器匹配項,Dusk 將搜尋具有相符 name
屬性的核取方塊
1$browser->check('terms');
uncheck
方法可用於「取消勾選」核取方塊輸入
1$browser->uncheck('terms');
單選按鈕
若要「選取」radio
輸入選項,您可以使用 radio
方法。與許多其他輸入相關方法類似,不需要完整的 CSS 選擇器。如果找不到 CSS 選擇器匹配項,Dusk 將搜尋具有相符 name
和 value
屬性的 radio
輸入
1$browser->radio('size', 'large');
Attaching Files
attach
方法可用於將檔案附加到 file
輸入元素。與許多其他輸入相關方法類似,不需要完整的 CSS 選擇器。如果找不到 CSS 選擇器匹配項,Dusk 將搜尋具有相符 name
屬性的 file
輸入
1$browser->attach('photo', __DIR__.'/photos/mountains.png');
attach 函數要求在您的伺服器上安裝並啟用 Zip
PHP 擴展。
Pressing Buttons
press
方法可用於點擊頁面上的按鈕元素。給定 press
方法的引數可以是按鈕的顯示文字或 CSS / Dusk 選擇器
1$browser->press('Login');
在提交表單時,許多應用程式會在按下表單的提交按鈕後停用它,然後在表單提交的 HTTP 請求完成時重新啟用該按鈕。若要按下按鈕並等待按鈕重新啟用,您可以使用 pressAndWaitFor
方法
1// Press the button and wait a maximum of 5 seconds for it to be enabled...2$browser->pressAndWaitFor('Save');3 4// Press the button and wait a maximum of 1 second for it to be enabled...5$browser->pressAndWaitFor('Save', 1);
Clicking Links
若要點擊連結,您可以使用瀏覽器實例上的 clickLink
方法。 clickLink
方法將點擊具有給定顯示文字的連結
1$browser->clickLink($linkText);
您可以使用 seeLink
方法來判斷具有給定顯示文字的連結是否在頁面上可見
1if ($browser->seeLink($linkText)) {2 // ...3}
這些方法與 jQuery 互動。如果頁面上沒有 jQuery,Dusk 將自動將其注入到頁面中,以便在測試期間可用。
Using the Keyboard
keys
方法允許您為給定元素提供比 type
方法通常允許的更複雜的輸入序列。例如,您可以指示 Dusk 在輸入值時按住修飾鍵。在此範例中,在將 taylor
輸入到與給定選擇器相符的元素時,將按住 shift
鍵。在輸入 taylor
後,將輸入 swift
而不使用任何修飾鍵
1$browser->keys('selector', ['{shift}', 'taylor'], 'swift');
keys
方法的另一個有價值的用例是將「鍵盤快捷鍵」組合傳送到您應用程式的主要 CSS 選擇器
1$browser->keys('.app', ['{command}', 'j']);
所有修飾鍵(例如 {command}
)都包裝在 {}
字元中,並與 Facebook\WebDriver\WebDriverKeys
類別中定義的常數相符,這些常數可以在 GitHub 上找到。
流暢的鍵盤互動
Dusk 還提供了 withKeyboard
方法,允許您通過 Laravel\Dusk\Keyboard
類別流暢地執行複雜的鍵盤互動。 Keyboard
類別提供了 press
、release
、type
和 pause
方法
1use Laravel\Dusk\Keyboard;2 3$browser->withKeyboard(function (Keyboard $keyboard) {4 $keyboard->press('c')5 ->pause(1000)6 ->release('c')7 ->type(['c', 'e', 'o']);8});
鍵盤巨集
如果您想定義自訂鍵盤互動,以便在整個測試套件中輕鬆重複使用,您可以使用 Keyboard
類別提供的 macro
方法。通常,您應該從服務提供器的 boot
方法中呼叫此方法
1<?php 2 3namespace App\Providers; 4 5use Facebook\WebDriver\WebDriverKeys; 6use Illuminate\Support\ServiceProvider; 7use Laravel\Dusk\Keyboard; 8use Laravel\Dusk\OperatingSystem; 9 10class DuskServiceProvider extends ServiceProvider11{12 /**13 * Register Dusk's browser macros.14 */15 public function boot(): void16 {17 Keyboard::macro('copy', function (string $element = null) {18 $this->type([19 OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'c',20 ]);21 22 return $this;23 });24 25 Keyboard::macro('paste', function (string $element = null) {26 $this->type([27 OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'v',28 ]);29 30 return $this;31 });32 }33}
macro
函數接受一個名稱作為其第一個引數,一個閉包作為其第二個引數。當在 Keyboard
實例上將巨集作為方法呼叫時,將會執行巨集的閉包
1$browser->click('@textarea')2 ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy())3 ->click('@another-textarea')4 ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste());
Using the Mouse
點擊元素
click
方法可用於點擊與給定 CSS 或 Dusk 選擇器相符的元素
1$browser->click('.selector');
clickAtXPath
方法可用於點擊與給定 XPath 表達式相符的元素
1$browser->clickAtXPath('//div[@class = "selector"]');
clickAtPoint
方法可用於點擊瀏覽器可視區域的給定座標對處的最頂層元素
1$browser->clickAtPoint($x = 0, $y = 0);
doubleClick
方法可用於模擬滑鼠的雙擊
1$browser->doubleClick();2 3$browser->doubleClick('.selector');
rightClick
方法可用於模擬滑鼠的右鍵點擊
1$browser->rightClick();2 3$browser->rightClick('.selector');
clickAndHold
方法可用於模擬滑鼠按鈕被點擊並按住。後續呼叫 releaseMouse
方法將撤消此行為並釋放滑鼠按鈕
1$browser->clickAndHold('.selector');2 3$browser->clickAndHold()4 ->pause(1000)5 ->releaseMouse();
controlClick
方法可用於模擬瀏覽器中的 ctrl+click
事件
1$browser->controlClick();2 3$browser->controlClick('.selector');
滑鼠懸停
當您需要將滑鼠移動到與給定 CSS 或 Dusk 選擇器相符的元素上方時,可以使用 mouseover
方法
1$browser->mouseover('.selector');
拖放
drag
方法可用於將與給定選擇器相符的元素拖曳到另一個元素
1$browser->drag('.from-selector', '.to-selector');
或者,您可以沿單個方向拖曳元素
1$browser->dragLeft('.selector', $pixels = 10);2$browser->dragRight('.selector', $pixels = 10);3$browser->dragUp('.selector', $pixels = 10);4$browser->dragDown('.selector', $pixels = 10);
最後,您可以通過給定的偏移量拖曳元素
1$browser->dragOffset('.selector', $x = 10, $y = 10);
JavaScript Dialogs
Dusk 提供了各種方法來與 JavaScript 對話方塊互動。例如,您可以使用 waitForDialog
方法等待 JavaScript 對話方塊出現。此方法接受一個可選引數,指示等待對話方塊出現的秒數
1$browser->waitForDialog($seconds = null);
assertDialogOpened
方法可用於斷言已顯示對話方塊並包含給定的訊息
1$browser->assertDialogOpened('Dialog message');
如果 JavaScript 對話方塊包含提示,您可以使用 typeInDialog
方法在提示中輸入值
1$browser->typeInDialog('Hello World');
若要通過點擊「確定」按鈕來關閉開啟的 JavaScript 對話方塊,您可以調用 acceptDialog
方法
1$browser->acceptDialog();
若要通過點擊「取消」按鈕來關閉開啟的 JavaScript 對話方塊,您可以調用 dismissDialog
方法
1$browser->dismissDialog();
Interacting With Inline Frames
如果您需要與 iframe 中的元素互動,您可以使用 withinFrame
方法。在提供給 withinFrame
方法的閉包中發生的所有元素互動都將限定在指定 iframe 的上下文中
1$browser->withinFrame('#credit-card-details', function ($browser) {2 $browser->type('input[name="cardnumber"]', '4242424242424242')3 ->type('input[name="exp-date"]', '1224')4 ->type('input[name="cvc"]', '123')5 ->press('Pay');6});
Scoping Selectors
有時您可能希望在限定給定選擇器的範圍內執行多個操作。例如,您可能希望斷言某些文字僅存在於表格中,然後點擊該表格中的按鈕。您可以使用 with
方法來完成此操作。在提供給 with
方法的閉包中執行的所有操作都將限定在原始選擇器的範圍內
1$browser->with('.table', function (Browser $table) {2 $table->assertSee('Hello World')3 ->clickLink('Delete');4});
您偶爾可能需要在目前範圍之外執行斷言。您可以使用 elsewhere
和 elsewhereWhenAvailable
方法來完成此操作
1$browser->with('.table', function (Browser $table) { 2 // Current scope is `body .table`... 3 4 $browser->elsewhere('.page-title', function (Browser $title) { 5 // Current scope is `body .page-title`... 6 $title->assertSee('Hello World'); 7 }); 8 9 $browser->elsewhereWhenAvailable('.page-title', function (Browser $title) {10 // Current scope is `body .page-title`...11 $title->assertSee('Hello World');12 });13});
Waiting for Elements
在測試廣泛使用 JavaScript 的應用程式時,通常有必要「等待」某些元素或資料可用,然後再繼續進行測試。Dusk 使這變得輕而易舉。使用各種方法,您可以等待元素在頁面上變得可見,甚至等待給定的 JavaScript 表達式評估為 true
。
等待
如果您只需要將測試暫停給定的毫秒數,請使用 pause
方法
1$browser->pause(1000);
如果您只需要在特定條件為 true
時暫停測試,請使用 pauseIf
方法
1$browser->pauseIf(App::environment('production'), 1000);
同樣地,如果您需要在特定條件為 true
以外的情況暫停測試,您可以使用 pauseUnless
方法
1$browser->pauseUnless(App::environment('testing'), 1000);
等待選擇器
可以使用 waitFor
方法來暫停測試的執行,直到頁面上顯示符合指定 CSS 或 Dusk 選擇器的元素為止。預設情況下,這會暫停測試最多五秒鐘,然後拋出例外。如有必要,您可以將自訂逾時閾值作為方法的第二個參數傳遞
1// Wait a maximum of five seconds for the selector...2$browser->waitFor('.selector');3 4// Wait a maximum of one second for the selector...5$browser->waitFor('.selector', 1);
您也可以等待直到符合指定選擇器的元素包含指定的文字
1// Wait a maximum of five seconds for the selector to contain the given text...2$browser->waitForTextIn('.selector', 'Hello World');3 4// Wait a maximum of one second for the selector to contain the given text...5$browser->waitForTextIn('.selector', 'Hello World', 1);
您也可以等待直到符合指定選擇器的元素從頁面中消失
1// Wait a maximum of five seconds until the selector is missing...2$browser->waitUntilMissing('.selector');3 4// Wait a maximum of one second until the selector is missing...5$browser->waitUntilMissing('.selector', 1);
或者,您可以等待直到符合指定選擇器的元素啟用或停用
1// Wait a maximum of five seconds until the selector is enabled... 2$browser->waitUntilEnabled('.selector'); 3 4// Wait a maximum of one second until the selector is enabled... 5$browser->waitUntilEnabled('.selector', 1); 6 7// Wait a maximum of five seconds until the selector is disabled... 8$browser->waitUntilDisabled('.selector'); 9 10// Wait a maximum of one second until the selector is disabled...11$browser->waitUntilDisabled('.selector', 1);
在可用時限定選擇器範圍
有時,您可能希望等待符合指定選擇器的元素出現,然後與該元素互動。例如,您可能希望等待直到模態視窗可用,然後按下模態視窗內的「確定」按鈕。可以使用 whenAvailable
方法來完成此操作。在給定閉包中執行的所有元素操作都將限定於原始選擇器的範圍
1$browser->whenAvailable('.modal', function (Browser $modal) {2 $modal->assertSee('Hello World')3 ->press('OK');4});
等待文字
可以使用 waitForText
方法來等待直到頁面上顯示指定的文字
1// Wait a maximum of five seconds for the text...2$browser->waitForText('Hello World');3 4// Wait a maximum of one second for the text...5$browser->waitForText('Hello World', 1);
您可以使用 waitUntilMissingText
方法來等待直到顯示的文字從頁面中移除
1// Wait a maximum of five seconds for the text to be removed...2$browser->waitUntilMissingText('Hello World');3 4// Wait a maximum of one second for the text to be removed...5$browser->waitUntilMissingText('Hello World', 1);
等待連結
可以使用 waitForLink
方法來等待直到頁面上顯示指定的連結文字
1// Wait a maximum of five seconds for the link...2$browser->waitForLink('Create');3 4// Wait a maximum of one second for the link...5$browser->waitForLink('Create', 1);
等待輸入欄位
可以使用 waitForInput
方法來等待直到頁面上顯示指定的輸入欄位
1// Wait a maximum of five seconds for the input...2$browser->waitForInput($field);3 4// Wait a maximum of one second for the input...5$browser->waitForInput($field, 1);
等待頁面位置
當進行路徑斷言(例如 $browser->assertPathIs('/home')
)時,如果 window.location.pathname
正在非同步更新,則斷言可能會失敗。您可以使用 waitForLocation
方法來等待位置成為給定的值
1$browser->waitForLocation('/secret');
waitForLocation
方法也可以用於等待目前視窗位置成為完全合格的 URL
1$browser->waitForLocation('https://example.com/path');
您也可以等待具名路由的位置
1$browser->waitForRoute($routeName, $parameters);
等待頁面重新載入
如果您需要在執行動作後等待頁面重新載入,請使用 waitForReload
方法
1use Laravel\Dusk\Browser;2 3$browser->waitForReload(function (Browser $browser) {4 $browser->press('Submit');5})6->assertSee('Success!');
由於等待頁面重新載入的需求通常在點擊按鈕後發生,因此您可以為了方便起見使用 clickAndWaitForReload
方法
1$browser->clickAndWaitForReload('.selector')2 ->assertSee('something');
等待 JavaScript 表達式
有時您可能希望暫停測試的執行,直到給定的 JavaScript 表達式評估為 true
。您可以使用 waitUntil
方法輕鬆完成此操作。當將表達式傳遞給此方法時,您不需要包含 return
關鍵字或結尾的分號
1// Wait a maximum of five seconds for the expression to be true...2$browser->waitUntil('App.data.servers.length > 0');3 4// Wait a maximum of one second for the expression to be true...5$browser->waitUntil('App.data.servers.length > 0', 1);
等待 Vue 表達式
waitUntilVue
和 waitUntilVueIsNot
方法可用於等待直到 Vue 元件 屬性具有給定的值
1// Wait until the component attribute contains the given value...2$browser->waitUntilVue('user.name', 'Taylor', '@user');3 4// Wait until the component attribute doesn't contain the given value...5$browser->waitUntilVueIsNot('user.name', null, '@user');
等待 JavaScript 事件
可以使用 waitForEvent
方法來暫停測試的執行,直到 JavaScript 事件發生
1$browser->waitForEvent('load');
事件監聽器附加到目前的範圍,預設情況下是 body
元素。當使用限定範圍的選擇器時,事件監聽器將附加到符合的元素
1$browser->with('iframe', function (Browser $iframe) {2 // Wait for the iframe's load event...3 $iframe->waitForEvent('load');4});
您也可以提供選擇器作為 waitForEvent
方法的第二個參數,將事件監聽器附加到特定元素
1$browser->waitForEvent('load', '.selector');
您也可以等待 document
和 window
物件上的事件
1// Wait until the document is scrolled...2$browser->waitForEvent('scroll', 'document');3 4// Wait a maximum of five seconds until the window is resized...5$browser->waitForEvent('resize', 'window', 5);
使用回呼等待
Dusk 中的許多「等待」方法都依賴於底層的 waitUsing
方法。您可以直接使用此方法來等待給定的閉包返回 true
。waitUsing
方法接受要等待的最大秒數、應評估閉包的間隔、閉包和一個可選的失敗訊息
1$browser->waitUsing(10, 1, function () use ($something) {2 return $something->isReady();3}, "Something wasn't ready in time.");
Scrolling an Element Into View
有時您可能無法點擊元素,因為它位於瀏覽器可視區域之外。scrollIntoView
方法將捲動瀏覽器視窗,直到給定選擇器的元素位於視圖中
1$browser->scrollIntoView('.selector')2 ->click('.selector');
Available Assertions
Dusk 提供了各種斷言,您可以針對您的應用程式進行斷言。所有可用的斷言都記錄在下面的列表中
assertTitle assertTitleContains assertUrlIs assertSchemeIs assertSchemeIsNot assertHostIs assertHostIsNot assertPortIs assertPortIsNot assertPathBeginsWith assertPathEndsWith assertPathContains 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 assertAttributeMissing assertAttributeContains assertAttributeDoesntContain assertAriaAttribute assertDataAttribute assertVisible assertPresent assertNotPresent assertMissing assertInputPresent assertInputMissing assertDialogOpened assertEnabled assertDisabled assertButtonEnabled assertButtonDisabled assertFocused assertNotFocused assertAuthenticated assertGuest assertAuthenticatedAs assertVue assertVueIsNot assertVueContains assertVueDoesntContain
assertTitle
斷言頁面標題與給定的文字相符
1$browser->assertTitle($title);
assertTitleContains
斷言頁面標題包含給定的文字
1$browser->assertTitleContains($title);
assertUrlIs
斷言目前的 URL(不包含查詢字串)與給定的字串相符
1$browser->assertUrlIs($url);
assertSchemeIs
斷言目前的 URL 協定與給定的協定相符
1$browser->assertSchemeIs($scheme);
assertSchemeIsNot
斷言目前的 URL 協定與給定的協定不相符
1$browser->assertSchemeIsNot($scheme);
assertHostIs
斷言目前的 URL 主機與給定的主機相符
1$browser->assertHostIs($host);
assertHostIsNot
斷言目前的 URL 主機與給定的主機不相符
1$browser->assertHostIsNot($host);
assertPortIs
斷言目前的 URL 埠號與給定的埠號相符
1$browser->assertPortIs($port);
assertPortIsNot
斷言目前的 URL 埠號與給定的埠號不相符
1$browser->assertPortIsNot($port);
assertPathBeginsWith
斷言目前的 URL 路徑以給定的路徑開頭
1$browser->assertPathBeginsWith('/home');
assertPathEndsWith
斷言目前的 URL 路徑以給定的路徑結尾
1$browser->assertPathEndsWith('/home');
assertPathContains
斷言目前的 URL 路徑包含給定的路徑
1$browser->assertPathContains('/home');
assertPathIs
斷言目前的路徑與給定的路徑相符
1$browser->assertPathIs('/home');
assertPathIsNot
斷言目前的路徑與給定的路徑不相符
1$browser->assertPathIsNot('/home');
assertRouteIs
斷言目前的 URL 與給定的具名路由 URL 相符
1$browser->assertRouteIs($name, $parameters);
assertQueryStringHas
斷言給定的查詢字串參數存在
1$browser->assertQueryStringHas($name);
斷言給定的查詢字串參數存在且具有給定的值
1$browser->assertQueryStringHas($name, $value);
assertQueryStringMissing
斷言給定的查詢字串參數不存在
1$browser->assertQueryStringMissing($name);
assertFragmentIs
斷言 URL 目前的雜湊片段與給定的片段相符
1$browser->assertFragmentIs('anchor');
assertFragmentBeginsWith
斷言 URL 目前的雜湊片段以給定的片段開頭
1$browser->assertFragmentBeginsWith('anchor');
assertFragmentIsNot
斷言 URL 目前的雜湊片段與給定的片段不相符
1$browser->assertFragmentIsNot('anchor');
assertHasCookie
斷言給定的加密 Cookie 存在
1$browser->assertHasCookie($name);
assertHasPlainCookie
斷言給定的未加密 Cookie 存在
1$browser->assertHasPlainCookie($name);
assertCookieMissing
斷言給定的加密 Cookie 不存在
1$browser->assertCookieMissing($name);
assertPlainCookieMissing
斷言給定的未加密 Cookie 不存在
1$browser->assertPlainCookieMissing($name);
assertCookieValue
斷言加密的 Cookie 具有給定的值
1$browser->assertCookieValue($name, $value);
assertPlainCookieValue
斷言未加密的 Cookie 具有給定的值
1$browser->assertPlainCookieValue($name, $value);
assertSee
斷言給定的文字存在於頁面上
1$browser->assertSee($text);
assertDontSee
斷言給定的文字不存在於頁面上
1$browser->assertDontSee($text);
assertSeeIn
斷言給定的文字存在於選擇器內
1$browser->assertSeeIn($selector, $text);
assertDontSeeIn
斷言給定的文字不存在於選擇器內
1$browser->assertDontSeeIn($selector, $text);
assertSeeAnythingIn
斷言任何文字存在於選擇器內
1$browser->assertSeeAnythingIn($selector);
assertSeeNothingIn
斷言沒有文字存在於選擇器內
1$browser->assertSeeNothingIn($selector);
assertScript
斷言給定的 JavaScript 表達式評估為給定的值
1$browser->assertScript('window.isLoaded')2 ->assertScript('document.readyState', 'complete');
assertSourceHas
斷言給定的原始碼存在於頁面上
1$browser->assertSourceHas($code);
assertSourceMissing
斷言給定的原始碼不存在於頁面上
1$browser->assertSourceMissing($code);
assertSeeLink
斷言給定的連結存在於頁面上
1$browser->assertSeeLink($linkText);
assertDontSeeLink
斷言給定的連結不存在於頁面上
1$browser->assertDontSeeLink($linkText);
assertInputValue
斷言給定的輸入欄位具有給定的值
1$browser->assertInputValue($field, $value);
assertInputValueIsNot
斷言給定的輸入欄位不具有給定的值
1$browser->assertInputValueIsNot($field, $value);
assertChecked
斷言給定的核取方塊已勾選
1$browser->assertChecked($field);
assertNotChecked
斷言給定的核取方塊未勾選
1$browser->assertNotChecked($field);
assertIndeterminate
斷言給定的核取方塊處於不確定狀態
1$browser->assertIndeterminate($field);
assertRadioSelected
斷言給定的單選欄位已選取
1$browser->assertRadioSelected($field, $value);
assertRadioNotSelected
斷言給定的單選欄位未選取
1$browser->assertRadioNotSelected($field, $value);
assertSelected
斷言給定的下拉式選單已選取給定的值
1$browser->assertSelected($field, $value);
assertNotSelected
斷言給定的下拉式選單未選取給定的值
1$browser->assertNotSelected($field, $value);
assertSelectHasOptions
斷言給定的值陣列可用於選取
1$browser->assertSelectHasOptions($field, $values);
assertSelectMissingOptions
斷言給定的值陣列不可用於選取
1$browser->assertSelectMissingOptions($field, $values);
assertSelectHasOption
斷言給定的值在給定的欄位上可用於選取
1$browser->assertSelectHasOption($field, $value);
assertSelectMissingOption
斷言給定的值不可用於選取
1$browser->assertSelectMissingOption($field, $value);
assertValue
斷言符合給定選擇器的元素具有給定的值
1$browser->assertValue($selector, $value);
assertValueIsNot
斷言符合給定選擇器的元素不具有給定的值
1$browser->assertValueIsNot($selector, $value);
assertAttribute
斷言符合給定選擇器的元素在提供的屬性中具有給定的值
1$browser->assertAttribute($selector, $attribute, $value);
assertAttributeMissing
斷言符合給定選擇器的元素缺少提供的屬性
1$browser->assertAttributeMissing($selector, $attribute);
assertAttributeContains
斷言符合給定選擇器的元素在提供的屬性中包含給定的值
1$browser->assertAttributeContains($selector, $attribute, $value);
assertAttributeDoesntContain
斷言符合給定選擇器的元素在提供的屬性中不包含給定的值
1$browser->assertAttributeDoesntContain($selector, $attribute, $value);
assertAriaAttribute
斷言符合給定選擇器的元素在提供的 aria 屬性中具有給定的值
1$browser->assertAriaAttribute($selector, $attribute, $value);
例如,給定標記 <button aria-label="Add"></button>
,您可以像這樣針對 aria-label
屬性進行斷言
1$browser->assertAriaAttribute('button', 'label', 'Add')
assertDataAttribute
斷言符合給定選擇器的元素在提供的 data 屬性中具有給定的值
1$browser->assertDataAttribute($selector, $attribute, $value);
例如,給定標記 <tr id="row-1" data-content="attendees"></tr>
,您可以像這樣針對 data-label
屬性進行斷言
1$browser->assertDataAttribute('#row-1', 'content', 'attendees')
assertVisible
斷言符合給定選擇器的元素是可見的
1$browser->assertVisible($selector);
assertPresent
斷言符合給定選擇器的元素存在於原始碼中
1$browser->assertPresent($selector);
assertNotPresent
斷言符合給定選擇器的元素不存在於原始碼中
1$browser->assertNotPresent($selector);
assertMissing
斷言符合給定選擇器的元素是不可見的
1$browser->assertMissing($selector);
assertInputPresent
斷言具有給定名稱的輸入欄位存在
1$browser->assertInputPresent($name);
assertInputMissing
斷言具有給定名稱的輸入欄位不存在於原始碼中
1$browser->assertInputMissing($name);
assertDialogOpened
斷言已開啟具有給定訊息的 JavaScript 對話方塊
1$browser->assertDialogOpened($message);
assertEnabled
斷言給定的欄位已啟用
1$browser->assertEnabled($field);
assertDisabled
斷言給定的欄位已停用
1$browser->assertDisabled($field);
assertButtonEnabled
斷言給定的按鈕已啟用
1$browser->assertButtonEnabled($button);
assertButtonDisabled
斷言給定的按鈕已停用
1$browser->assertButtonDisabled($button);
assertFocused
斷言給定的欄位已聚焦
1$browser->assertFocused($field);
assertNotFocused
斷言給定的欄位未聚焦
1$browser->assertNotFocused($field);
assertAuthenticated
斷言使用者已通過身份驗證
1$browser->assertAuthenticated();
assertGuest
斷言使用者未通過身份驗證
1$browser->assertGuest();
assertAuthenticatedAs
斷言使用者已通過身份驗證為給定的使用者
1$browser->assertAuthenticatedAs($user);
assertVue
Dusk 甚至允許您對 Vue 元件 資料的狀態進行斷言。例如,假設您的應用程式包含以下 Vue 元件
1// HTML... 2 3<profile dusk="profile-component"></profile> 4 5// Component Definition... 6 7Vue.component('profile', { 8 template: '<div>{{ user.name }}</div>', 9 10 data: function () {11 return {12 user: {13 name: 'Taylor'14 }15 };16 }17});
您可以像這樣對 Vue 元件的狀態進行斷言
1test('vue', function () {2 $this->browse(function (Browser $browser) {3 $browser->visit('/')4 ->assertVue('user.name', 'Taylor', '@profile-component');5 });6});
1/** 2 * A basic Vue test example. 3 */ 4public function test_vue(): void 5{ 6 $this->browse(function (Browser $browser) { 7 $browser->visit('/') 8 ->assertVue('user.name', 'Taylor', '@profile-component'); 9 });10}
assertVueIsNot
斷言給定的 Vue 元件資料屬性與給定的值不相符
1$browser->assertVueIsNot($property, $value, $componentSelector = null);
assertVueContains
斷言給定的 Vue 元件資料屬性是一個陣列,並且包含給定的值
1$browser->assertVueContains($property, $value, $componentSelector = null);
assertVueDoesntContain
斷言給定的 Vue 元件資料屬性是一個陣列,並且不包含給定的值
1$browser->assertVueDoesntContain($property, $value, $componentSelector = null);
Pages
有時,測試需要依序執行幾個複雜的動作。這可能會使您的測試更難以閱讀和理解。Dusk 頁面允許您定義表達性動作,然後可以透過單一方法在給定頁面上執行這些動作。頁面還允許您為應用程式或單一頁面定義常用選擇器的快捷方式。
Generating Pages
若要產生頁面物件,請執行 dusk:page
Artisan 命令。所有頁面物件都將放置在您應用程式的 tests/Browser/Pages
目錄中
1php artisan dusk:page Login
Configuring Pages
預設情況下,頁面具有三種方法:url
、assert
和 elements
。我們現在將討論 url
和 assert
方法。elements
方法將在下文更詳細地討論。
url
方法
url
方法應傳回代表頁面的 URL 路徑。當在瀏覽器中導航到頁面時,Dusk 將使用此 URL
1/**2 * Get the URL for the page.3 */4public function url(): string5{6 return '/login';7}
assert
方法
assert
方法可以進行任何必要的斷言,以驗證瀏覽器實際上位於給定的頁面上。實際上,不一定需要在此方法中放置任何內容;但是,如果您願意,可以自由進行這些斷言。當導航到頁面時,這些斷言將自動執行
1/**2 * Assert that the browser is on the page.3 */4public function assert(Browser $browser): void5{6 $browser->assertPathIs($this->url());7}
Navigating to Pages
定義頁面後,您可以使用 visit
方法導航到該頁面
1use Tests\Browser\Pages\Login;2 3$browser->visit(new Login);
有時您可能已經在給定的頁面上,並且需要將頁面的選擇器和方法「載入」到目前的測試上下文中。當按下按鈕並重新導向到給定的頁面而沒有明確導航到該頁面時,這很常見。在這種情況下,您可以使用 on
方法來載入頁面
1use Tests\Browser\Pages\CreatePlaylist;2 3$browser->visit('/dashboard')4 ->clickLink('Create Playlist')5 ->on(new CreatePlaylist)6 ->assertSee('@create');
Shorthand Selectors
頁面類別中的 elements
方法允許您為頁面上的任何 CSS 選擇器定義快速、易於記憶的快捷方式。例如,讓我們為應用程式登入頁面的「電子郵件」輸入欄位定義一個快捷方式
1/** 2 * Get the element shortcuts for the page. 3 * 4 * @return array<string, string> 5 */ 6public function elements(): array 7{ 8 return [ 9 '@email' => 'input[name=email]',10 ];11}
定義快捷方式後,您可以在通常使用完整 CSS 選擇器的任何地方使用速記選擇器
全域速記選擇器
安裝 Dusk 後,基本 Page
類別將放置在您的 tests/Browser/Pages
目錄中。此類別包含一個 siteElements
方法,可用於定義應在整個應用程式的每個頁面上可用的全域速記選擇器
1/** 2 * Get the global element shortcuts for the site. 3 * 4 * @return array<string, string> 5 */ 6public static function siteElements(): array 7{ 8 return [ 9 '@element' => '#selector',10 ];11}
Page Methods
除了頁面上定義的預設方法之外,您還可以定義可在整個測試中使用的其他方法。例如,假設我們正在建置音樂管理應用程式。應用程式一個頁面的常見動作可能是建立播放清單。您可以直接在頁面類別上定義 createPlaylist
方法,而無需在每個測試中重新編寫建立播放清單的邏輯
1<?php 2 3namespace Tests\Browser\Pages; 4 5use Laravel\Dusk\Browser; 6use Laravel\Dusk\Page; 7 8class Dashboard extends Page 9{10 // Other page methods...11 12 /**13 * Create a new playlist.14 */15 public function createPlaylist(Browser $browser, string $name): void16 {17 $browser->type('name', $name)18 ->check('share')19 ->press('Create Playlist');20 }21}
定義方法後,您可以在任何使用該頁面的測試中使用它。瀏覽器實例將自動作為第一個參數傳遞給自訂頁面方法
1use Tests\Browser\Pages\Dashboard;2 3$browser->visit(new Dashboard)4 ->createPlaylist('My Playlist')5 ->assertSee('My Playlist');
Components
元件與 Dusk 的「頁面物件」類似,但適用於在整個應用程式中重複使用的 UI 和功能片段,例如導航欄或通知視窗。因此,元件不受特定 URL 的約束。
Generating Components
若要產生元件,請執行 dusk:component
Artisan 命令。新元件放置在 tests/Browser/Components
目錄中
1php artisan dusk:component DatePicker
如上所示,「日期選擇器」是在應用程式中的各種頁面上可能存在的元件範例。手動編寫瀏覽器自動化邏輯以在測試套件中的數十個測試中選擇日期可能會變得繁瑣。相反,我們可以定義一個 Dusk 元件來表示日期選擇器,讓我們能夠將該邏輯封裝在元件中
1<?php 2 3namespace Tests\Browser\Components; 4 5use Laravel\Dusk\Browser; 6use Laravel\Dusk\Component as BaseComponent; 7 8class DatePicker extends BaseComponent 9{10 /**11 * Get the root selector for the component.12 */13 public function selector(): string14 {15 return '.date-picker';16 }17 18 /**19 * Assert that the browser page contains the component.20 */21 public function assert(Browser $browser): void22 {23 $browser->assertVisible($this->selector());24 }25 26 /**27 * Get the element shortcuts for the component.28 *29 * @return array<string, string>30 */31 public function elements(): array32 {33 return [34 '@date-field' => 'input.datepicker-input',35 '@year-list' => 'div > div.datepicker-years',36 '@month-list' => 'div > div.datepicker-months',37 '@day-list' => 'div > div.datepicker-days',38 ];39 }40 41 /**42 * Select the given date.43 */44 public function selectDate(Browser $browser, int $year, int $month, int $day): void45 {46 $browser->click('@date-field')47 ->within('@year-list', function (Browser $browser) use ($year) {48 $browser->click($year);49 })50 ->within('@month-list', function (Browser $browser) use ($month) {51 $browser->click($month);52 })53 ->within('@day-list', function (Browser $browser) use ($day) {54 $browser->click($day);55 });56 }57}
Using Components
定義元件後,我們可以從任何測試中輕鬆地在日期選擇器中選擇日期。而且,如果選擇日期所需的邏輯發生變化,我們只需要更新元件
1<?php 2 3use Illuminate\Foundation\Testing\DatabaseMigrations; 4use Laravel\Dusk\Browser; 5use Tests\Browser\Components\DatePicker; 6 7uses(DatabaseMigrations::class); 8 9test('basic example', function () {10 $this->browse(function (Browser $browser) {11 $browser->visit('/')12 ->within(new DatePicker, function (Browser $browser) {13 $browser->selectDate(2019, 1, 30);14 })15 ->assertSee('January');16 });17});
1<?php 2 3namespace Tests\Browser; 4 5use Illuminate\Foundation\Testing\DatabaseMigrations; 6use Laravel\Dusk\Browser; 7use Tests\Browser\Components\DatePicker; 8use Tests\DuskTestCase; 9 10class ExampleTest extends DuskTestCase11{12 /**13 * A basic component test example.14 */15 public function test_basic_example(): void16 {17 $this->browse(function (Browser $browser) {18 $browser->visit('/')19 ->within(new DatePicker, function (Browser $browser) {20 $browser->selectDate(2019, 1, 30);21 })22 ->assertSee('January');23 });24 }25}
Continuous Integration
大多數 Dusk 持續整合配置都期望您的 Laravel 應用程式使用內建的 PHP 開發伺服器在埠號 8000 上提供服務。因此,在繼續之前,您應確保您的持續整合環境具有 APP_URL
環境變數值 http://127.0.0.1:8000
。
Heroku CI
若要在 Heroku CI 上執行 Dusk 測試,請將以下 Google Chrome buildpack 和指令碼新增至您的 Heroku app.json
檔案
1{ 2 "environments": { 3 "test": { 4 "buildpacks": [ 5 { "url": "heroku/php" }, 6 { "url": "https://github.com/heroku/heroku-buildpack-chrome-for-testing" } 7 ], 8 "scripts": { 9 "test-setup": "cp .env.testing .env",10 "test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux --port=9515 > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve --no-reload > /dev/null 2>&1 &' && php artisan dusk"11 }12 }13 }14}
Travis CI
若要在 Travis CI 上執行 Dusk 測試,請使用以下 .travis.yml
配置。由於 Travis CI 不是圖形環境,我們需要採取一些額外步驟才能啟動 Chrome 瀏覽器。此外,我們將使用 php artisan serve
來啟動 PHP 的內建網站伺服器
1language: php 2 3php: 4 - 8.2 5 6addons: 7 chrome: stable 8 9install:10 - cp .env.testing .env11 - travis_retry composer install --no-interaction --prefer-dist12 - php artisan key:generate13 - php artisan dusk:chrome-driver14 15before_script:16 - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 https://127.0.0.1 &17 - php artisan serve --no-reload &18 19script:20 - php artisan dusk
GitHub Actions
如果您使用 GitHub Actions 來執行 Dusk 測試,您可以使用以下配置文件作為起點。與 TravisCI 類似,我們將使用 php artisan serve
命令來啟動 PHP 的內建網站伺服器
1name: CI 2on: [push] 3jobs: 4 5 dusk-php: 6 runs-on: ubuntu-latest 7 env: 8 APP_URL: "http://127.0.0.1:8000" 9 DB_USERNAME: root10 DB_PASSWORD: root11 MAIL_MAILER: log12 steps:13 - uses: actions/checkout@v414 - name: Prepare The Environment15 run: cp .env.example .env16 - name: Create Database17 run: |18 sudo systemctl start mysql19 mysql --user="root" --password="root" -e "CREATE DATABASE \`my-database\` character set UTF8mb4 collate utf8mb4_bin;"20 - name: Install Composer Dependencies21 run: composer install --no-progress --prefer-dist --optimize-autoloader22 - name: Generate Application Key23 run: php artisan key:generate24 - name: Upgrade Chrome Driver25 run: php artisan dusk:chrome-driver --detect26 - name: Start Chrome Driver27 run: ./vendor/laravel/dusk/bin/chromedriver-linux --port=9515 &28 - name: Run Laravel Server29 run: php artisan serve --no-reload &30 - name: Run Dusk Tests31 run: php artisan dusk32 - name: Upload Screenshots33 if: failure()34 uses: actions/upload-artifact@v435 with:36 name: screenshots37 path: tests/Browser/screenshots38 - name: Upload Console Logs39 if: failure()40 uses: actions/upload-artifact@v441 with:42 name: console43 path: tests/Browser/console
Chipper CI
如果您使用 Chipper CI 來執行 Dusk 測試,您可以使用以下配置文件作為起點。我們將使用 PHP 的內建伺服器來執行 Laravel,以便我們可以監聽請求
1# file .chipperci.yml 2version: 1 3 4environment: 5 php: 8.2 6 node: 16 7 8# Include Chrome in the build environment 9services:10 - dusk11 12# Build all commits13on:14 push:15 branches: .*16 17pipeline:18 - name: Setup19 cmd: |20 cp -v .env.example .env21 composer install --no-interaction --prefer-dist --optimize-autoloader22 php artisan key:generate23 24 # Create a dusk env file, ensuring APP_URL uses BUILD_HOST25 cp -v .env .env.dusk.ci26 sed -i "s@APP_URL=.*@APP_URL=http://$BUILD_HOST:8000@g" .env.dusk.ci27 28 - name: Compile Assets29 cmd: |30 npm ci --no-audit31 npm run build32 33 - name: Browser Tests34 cmd: |35 php -S [::0]:8000 -t public 2>server.log &36 sleep 237 php artisan dusk:chrome-driver $CHROME_DRIVER38 php artisan dusk --env=ci
若要深入瞭解如何在 Chipper CI 上執行 Dusk 測試,包括如何使用資料庫,請參閱官方 Chipper CI 文件。