Laravel Dusk
簡介
Laravel Dusk 提供了一個表達性強、易於使用的瀏覽器自動化和測試 API。預設情況下,Dusk 不需要您在本機電腦上安裝 JDK 或 Selenium。相反,Dusk 使用獨立的 ChromeDriver 安裝。但是,您可以自由使用任何您希望的其他 Selenium 相容驅動程式。
安裝
要開始使用,您應該安裝 Google Chrome,並將 laravel/dusk
Composer 相依性新增至您的專案
composer require laravel/dusk --dev
如果您正在手動註冊 Dusk 的服務提供者,您應該絕對不要在您的生產環境中註冊它,因為這樣做可能會導致任意使用者能夠使用您的應用程式進行身份驗證。
安裝 Dusk 套件後,執行 dusk:install
Artisan 命令。 dusk:install
命令將建立一個 tests/Browser
目錄、一個 Dusk 測試範例,並為您的作業系統安裝 Chrome Driver 二進位檔
php artisan dusk:install
接下來,在您應用程式的 .env
檔案中設定 APP_URL
環境變數。此值應與您在瀏覽器中用來存取應用程式的 URL 相符。
如果您使用 Laravel Sail 來管理您的本機開發環境,請同時參閱 Sail 文件中關於 設定和執行 Dusk 測試 的內容。
管理 ChromeDriver 安裝
如果您想安裝與 Laravel Dusk 通過 dusk:install
命令安裝的不同版本的 ChromeDriver,您可以使用 dusk:chrome-driver
命令
# Install the latest version of ChromeDriver for your OS...php artisan dusk:chrome-driver # Install a given version of ChromeDriver for your OS...php artisan dusk:chrome-driver 86 # Install a given version of ChromeDriver for all supported OSs...php artisan dusk:chrome-driver --all # Install the version of ChromeDriver that matches the detected version of Chrome / Chromium for your OS...php artisan dusk:chrome-driver --detect
Dusk 要求 chromedriver
二進位檔是可執行的。如果您在執行 Dusk 時遇到問題,您應該使用以下命令確保二進位檔是可執行的:chmod -R 0755 vendor/laravel/dusk/bin/
。
使用其他瀏覽器
預設情況下,Dusk 使用 Google Chrome 和獨立的 ChromeDriver 安裝來執行您的瀏覽器測試。但是,您可以啟動自己的 Selenium 伺服器,並針對您希望的任何瀏覽器執行測試。
要開始使用,請開啟您的 tests/DuskTestCase.php
檔案,這是您應用程式的基本 Dusk 測試案例。在此檔案中,您可以移除對 startChromeDriver
方法的呼叫。這將停止 Dusk 自動啟動 ChromeDriver
/** * Prepare for Dusk test execution. * * @beforeClass */public static function prepare(): void{ // static::startChromeDriver();}
接下來,您可以修改 driver
方法以連線到您選擇的 URL 和連接埠。此外,您可以修改應該傳遞給 WebDriver 的「所需功能」
use Facebook\WebDriver\Remote\RemoteWebDriver; /** * Create the RemoteWebDriver instance. */protected function driver(): RemoteWebDriver{ return RemoteWebDriver::create( 'https://127.0.0.1:4444/wd/hub', DesiredCapabilities::phantomjs() );}
入門
產生測試
要產生 Dusk 測試,請使用 dusk:make
Artisan 命令。產生的測試將放置在 tests/Browser
目錄中
php artisan dusk:make LoginTest
每次測試後重置資料庫
您撰寫的大多數測試都將與從您應用程式的資料庫檢索資料的頁面互動;但是,您的 Dusk 測試絕不應該使用 RefreshDatabase
trait。 RefreshDatabase
trait 利用資料庫交易,這些交易在 HTTP 請求中不可用或無法使用。相反,您有兩個選項: DatabaseMigrations
trait 和 DatabaseTruncation
trait。
使用資料庫遷移
DatabaseMigrations
trait 將在每次測試之前執行您的資料庫遷移。但是,為每次測試刪除和重新建立資料庫表格通常比截斷表格慢
<?php use Illuminate\Foundation\Testing\DatabaseMigrations;use Laravel\Dusk\Browser; uses(DatabaseMigrations::class); //
<?php namespace Tests\Browser; use Illuminate\Foundation\Testing\DatabaseMigrations;use Laravel\Dusk\Browser;use Tests\DuskTestCase; class ExampleTest extends DuskTestCase{ use DatabaseMigrations; //}
執行 Dusk 測試時,不能使用 SQLite 記憶體資料庫。由於瀏覽器在其自己的程序中執行,因此它將無法存取其他程序的記憶體資料庫。
使用資料庫截斷
DatabaseTruncation
trait 將在第一個測試時遷移您的資料庫,以確保已正確建立您的資料庫表格。但是,在後續測試中,資料庫的表格將只是被截斷 - 與重新執行所有資料庫遷移相比,速度更快
<?php use Illuminate\Foundation\Testing\DatabaseTruncation;use Laravel\Dusk\Browser; uses(DatabaseTruncation::class); //
<?php namespace Tests\Browser; use App\Models\User;use Illuminate\Foundation\Testing\DatabaseTruncation;use Laravel\Dusk\Browser;use Tests\DuskTestCase; class ExampleTest extends DuskTestCase{ use DatabaseTruncation; //}
預設情況下,此 trait 將截斷除 migrations
表格之外的所有表格。如果您想自訂應該截斷的表格,您可以在測試類別上定義 $tablesToTruncate
屬性
如果您使用 Pest,您應該在基本 DuskTestCase
類別或您的測試檔案擴展的任何類別上定義屬性或方法。
/** * Indicates which tables should be truncated. * * @var array */protected $tablesToTruncate = ['users'];
或者,您可以在測試類別上定義 $exceptTables
屬性,以指定應從截斷中排除的表格
/** * Indicates which tables should be excluded from truncation. * * @var array */protected $exceptTables = ['users'];
要指定應該截斷其表格的資料庫連線,您可以在測試類別上定義 $connectionsToTruncate
屬性
/** * Indicates which connections should have their tables truncated. * * @var array */protected $connectionsToTruncate = ['mysql'];
如果您想在執行資料庫截斷之前或之後執行程式碼,您可以在測試類別上定義 beforeTruncatingDatabase
或 afterTruncatingDatabase
方法
/** * Perform any work that should take place before the database has started truncating. */protected function beforeTruncatingDatabase(): void{ //} /** * Perform any work that should take place after the database has finished truncating. */protected function afterTruncatingDatabase(): void{ //}
執行測試
要執行您的瀏覽器測試,請執行 dusk
Artisan 命令
php artisan dusk
如果您上次執行 dusk
命令時測試失敗,您可以使用 dusk:fails
命令先重新執行失敗的測試來節省時間
php artisan dusk:fails
dusk
命令接受 Pest / PHPUnit 測試執行器通常接受的任何引數,例如允許您僅針對給定的 群組 執行測試
php artisan dusk --group=foo
如果您使用 Laravel Sail 來管理您的本機開發環境,請參閱 Sail 文件中關於 設定和執行 Dusk 測試 的內容。
手動啟動 ChromeDriver
預設情況下,Dusk 會自動嘗試啟動 ChromeDriver。如果這在您的特定系統上無法運作,您可以在執行 dusk
命令之前手動啟動 ChromeDriver。如果您選擇手動啟動 ChromeDriver,您應該註解掉 tests/DuskTestCase.php
檔案中的以下程式碼行
/** * Prepare for Dusk test execution. * * @beforeClass */public static function prepare(): void{ // static::startChromeDriver();}
此外,如果您的 ChromeDriver 在 9515 以外的埠口啟動,您應該修改同一個類別的 driver
方法,以反映正確的埠口
use Facebook\WebDriver\Remote\RemoteWebDriver; /** * Create the RemoteWebDriver instance. */protected function driver(): RemoteWebDriver{ return RemoteWebDriver::create( 'https://127.0.0.1:9515', DesiredCapabilities::chrome() );}
環境處理
若要強制 Dusk 在執行測試時使用自己的環境檔案,請在您的專案根目錄中建立 .env.dusk.{environment}
檔案。例如,如果您將從 local
環境啟動 dusk
命令,您應該建立 .env.dusk.local
檔案。
在執行測試時,Dusk 會備份您的 .env
檔案,並將您的 Dusk 環境檔案重新命名為 .env
。測試完成後,您的 .env
檔案將會還原。
瀏覽器基礎知識
建立瀏覽器
為了開始,讓我們編寫一個測試來驗證我們是否可以登入應用程式。產生測試後,我們可以修改它以導航至登入頁面、輸入憑證,然後按一下「登入」按鈕。若要建立瀏覽器實例,您可以從 Dusk 測試中呼叫 browse
方法
<?php use App\Models\User;use Illuminate\Foundation\Testing\DatabaseMigrations;use Laravel\Dusk\Browser; uses(DatabaseMigrations::class); test('basic example', function () { $user = User::factory()->create([ ]); $this->browse(function (Browser $browser) use ($user) { $browser->visit('/login') ->type('email', $user->email) ->type('password', 'password') ->press('Login') ->assertPathIs('/home'); });});
<?php namespace Tests\Browser; use App\Models\User;use Illuminate\Foundation\Testing\DatabaseMigrations;use Laravel\Dusk\Browser;use Tests\DuskTestCase; class ExampleTest extends DuskTestCase{ use DatabaseMigrations; /** * A basic browser test example. */ public function test_basic_example(): void { $user = User::factory()->create([ ]); $this->browse(function (Browser $browser) use ($user) { $browser->visit('/login') ->type('email', $user->email) ->type('password', 'password') ->press('Login') ->assertPathIs('/home'); }); }}
如您在上面的範例中所見,browse
方法接受一個閉包。瀏覽器實例將會由 Dusk 自動傳遞給此閉包,並且是主要用於與您的應用程式互動和進行斷言的物件。
建立多個瀏覽器
有時您可能需要多個瀏覽器才能正確執行測試。例如,可能需要多個瀏覽器來測試與 WebSocket 互動的聊天畫面。若要建立多個瀏覽器,只需在傳遞給 browse
方法的閉包簽名中新增更多瀏覽器參數即可
$this->browse(function (Browser $first, Browser $second) { $first->loginAs(User::find(1)) ->visit('/home') ->waitForText('Message'); $second->loginAs(User::find(2)) ->visit('/home') ->waitForText('Message') ->type('message', 'Hey Taylor') ->press('Send'); $first->waitForText('Hey Taylor') ->assertSee('Jeffrey Way');});
導覽
visit
方法可用於導航至應用程式中指定的 URI
$browser->visit('/login');
您可以使用 visitRoute
方法導航至具名路由
$browser->visitRoute($routeName, $parameters);
您可以使用 back
和 forward
方法「返回」和「前進」
$browser->back(); $browser->forward();
您可以使用 refresh
方法重新整理頁面
$browser->refresh();
調整瀏覽器視窗大小
您可以使用 resize
方法調整瀏覽器視窗的大小
$browser->resize(1920, 1080);
maximize
方法可用於最大化瀏覽器視窗
$browser->maximize();
fitContent
方法會調整瀏覽器視窗的大小以符合其內容的大小
$browser->fitContent();
當測試失敗時,Dusk 會在擷取螢幕截圖之前自動調整瀏覽器大小以符合內容。您可以透過在測試中呼叫 disableFitOnFailure
方法來停用此功能
$browser->disableFitOnFailure();
您可以使用 move
方法將瀏覽器視窗移動到螢幕上的不同位置
$browser->move($x = 100, $y = 100);
瀏覽器巨集
如果您想定義一個可以在各種測試中重複使用的自訂瀏覽器方法,您可以使用 Browser
類別的 macro
方法。通常,您應該從服務提供者的 boot
方法中呼叫此方法
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider;use Laravel\Dusk\Browser; class DuskServiceProvider extends ServiceProvider{ /** * Register Dusk's browser macros. */ public function boot(): void { Browser::macro('scrollToElement', function (string $element = null) { $this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);"); return $this; }); }}
macro
函式接受一個名稱作為其第一個引數,一個閉包作為其第二個引數。當在 Browser
實例上作為方法呼叫巨集時,將會執行巨集的閉包
$this->browse(function (Browser $browser) use ($user) { $browser->visit('/pay') ->scrollToElement('#credit-card-details') ->assertSee('Enter Credit Card Details');});
身份驗證
通常,您會測試需要身份驗證的頁面。您可以使用 Dusk 的 loginAs
方法,以避免在每次測試期間與應用程式的登入畫面互動。loginAs
方法接受與您的可驗證模型關聯的主鍵或可驗證模型實例
use App\Models\User;use Laravel\Dusk\Browser; $this->browse(function (Browser $browser) { $browser->loginAs(User::find(1)) ->visit('/home');});
在使用 loginAs
方法後,使用者工作階段將會在檔案中的所有測試中保持。
Cookie
您可以使用 cookie
方法取得或設定加密 Cookie 的值。預設情況下,Laravel 建立的所有 Cookie 都會加密
$browser->cookie('name'); $browser->cookie('name', 'Taylor');
您可以使用 plainCookie
方法取得或設定未加密 Cookie 的值
$browser->plainCookie('name'); $browser->plainCookie('name', 'Taylor');
您可以使用 deleteCookie
方法刪除指定的 Cookie
$browser->deleteCookie('name');
執行 JavaScript
您可以使用 script
方法在瀏覽器中執行任意 JavaScript 陳述式
$browser->script('document.documentElement.scrollTop = 0'); $browser->script([ 'document.body.scrollTop = 0', 'document.documentElement.scrollTop = 0',]); $output = $browser->script('return window.location.pathname');
截取螢幕快照
您可以使用 screenshot
方法擷取螢幕截圖,並以指定的檔名儲存。所有螢幕截圖都會儲存在 tests/Browser/screenshots
目錄中
$browser->screenshot('filename');
responsiveScreenshots
方法可用於在各種斷點擷取一系列螢幕截圖
$browser->responsiveScreenshots('filename');
screenshotElement
方法可用於擷取頁面上特定元素的螢幕截圖
$browser->screenshotElement('#selector', 'filename');
將主控台輸出儲存到磁碟
您可以使用 storeConsoleLog
方法將目前瀏覽器的主控台輸出寫入磁碟,並指定檔名。主控台輸出將會儲存在 tests/Browser/console
目錄中
$browser->storeConsoleLog('filename');
將頁面來源儲存到磁碟
您可以使用 storeSource
方法將目前頁面的原始碼寫入磁碟,並指定檔名。頁面原始碼將會儲存在 tests/Browser/source
目錄中
$browser->storeSource('filename');
與元素互動
Dusk 選擇器
為與元素互動選擇良好的 CSS 選擇器是編寫 Dusk 測試中最困難的部分之一。隨著時間的推移,前端變更可能會導致如下的 CSS 選擇器破壞您的測試
// HTML... <button>Login</button> // Test... $browser->click('.login-page .container div > button');
Dusk 選擇器讓您專注於編寫有效的測試,而不是記住 CSS 選擇器。若要定義選擇器,請將 dusk
屬性新增至您的 HTML 元素。然後,在與 Dusk 瀏覽器互動時,在選擇器前面加上 @
,以在測試中操作附加的元素
// HTML... <button dusk="login-button">Login</button> // Test... $browser->click('@login-button');
如果需要,您可以透過 selectorHtmlAttribute
方法自訂 Dusk 選擇器使用的 HTML 屬性。通常,此方法應從您應用程式的 AppServiceProvider
的 boot
方法中呼叫
use Laravel\Dusk\Dusk; Dusk::selectorHtmlAttribute('data-dusk');
文字、值和屬性
擷取和設定值
Dusk 提供了幾種方法來與頁面上元素的目前值、顯示文字和屬性互動。例如,若要取得符合給定 CSS 或 Dusk 選擇器的元素的「值」,請使用 value
方法
// Retrieve the value...$value = $browser->value('selector'); // Set the value...$browser->value('selector', 'value');
您可以使用 inputValue
方法取得具有指定欄位名稱的輸入元素的「值」
$value = $browser->inputValue('field');
擷取文字
text
方法可用於擷取符合指定選擇器的元素的顯示文字
$text = $browser->text('selector');
擷取屬性
最後,attribute
方法可用於擷取符合指定選擇器的元素的屬性值
$attribute = $browser->attribute('selector', 'value');
與表單互動
輸入值
Dusk 提供了各種方法來與表單和輸入元素互動。首先,讓我們看一下將文字輸入到輸入欄位的範例
請注意,雖然該方法在必要時接受一個,但我們不需要將 CSS 選擇器傳遞到 type
方法中。如果未提供 CSS 選擇器,Dusk 將會搜尋具有指定 name
屬性的 input
或 textarea
欄位。
若要在不清除其內容的情況下將文字附加到欄位,您可以使用 append
方法
$browser->type('tags', 'foo') ->append('tags', ', bar, baz');
您可以使用 clear
方法清除輸入的值
$browser->clear('email');
您可以使用 typeSlowly
方法指示 Dusk 慢慢輸入。預設情況下,Dusk 會在按鍵之間暫停 100 毫秒。若要自訂按鍵之間的時間量,您可以將適當的毫秒數作為方法的第三個引數傳遞
$browser->typeSlowly('mobile', '+1 (202) 555-5555'); $browser->typeSlowly('mobile', '+1 (202) 555-5555', 300);
您可以使用 appendSlowly
方法慢慢附加文字
$browser->type('tags', 'foo') ->appendSlowly('tags', ', bar, baz');
下拉式選單
若要選取 select
元素上可用的值,您可以使用 select
方法。與 type
方法一樣,select
方法不需要完整的 CSS 選擇器。當將值傳遞給 select
方法時,您應該傳遞底層選項值而不是顯示文字
$browser->select('size', 'Large');
您可以透過省略第二個引數來選取隨機選項
$browser->select('size');
透過將陣列作為 select
方法的第二個引數提供,您可以指示該方法選取多個選項
$browser->select('categories', ['Art', 'Music']);
核取方塊
若要「勾選」核取方塊輸入,您可以使用 check
方法。與許多其他輸入相關的方法一樣,不需要完整的 CSS 選擇器。如果找不到 CSS 選擇器相符項,Dusk 將會搜尋具有相符 name
屬性的核取方塊
$browser->check('terms');
uncheck
方法可用於「取消勾選」核取方塊輸入
$browser->uncheck('terms');
單選按鈕
若要「選取」radio
輸入選項,您可以使用 radio
方法。與許多其他輸入相關的方法一樣,不需要完整的 CSS 選擇器。如果找不到 CSS 選擇器相符項,Dusk 將會搜尋具有相符 name
和 value
屬性的 radio
輸入
$browser->radio('size', 'large');
附加檔案
attach
方法可用於將檔案附加到 file
輸入元素。與許多其他輸入相關的方法一樣,不需要完整的 CSS 選擇器。如果找不到 CSS 選擇器相符項,Dusk 將會搜尋具有相符 name
屬性的 file
輸入
$browser->attach('photo', __DIR__.'/photos/mountains.png');
附加函式需要您的伺服器上安裝並啟用 Zip
PHP 擴充功能。
按下按鈕
press
方法可用於按一下頁面上的按鈕元素。傳遞給 press
方法的引數可以是按鈕的顯示文字或 CSS / Dusk 選擇器
$browser->press('Login');
在提交表單時,許多應用程式會在按下表單的提交按鈕後停用該按鈕,然後在表單提交的 HTTP 請求完成時重新啟用該按鈕。若要按下按鈕並等待按鈕重新啟用,您可以使用 pressAndWaitFor
方法
// Press the button and wait a maximum of 5 seconds for it to be enabled...$browser->pressAndWaitFor('Save'); // Press the button and wait a maximum of 1 second for it to be enabled...$browser->pressAndWaitFor('Save', 1);
點擊連結
若要按一下連結,您可以使用瀏覽器實例上的 clickLink
方法。clickLink
方法會按一下具有指定顯示文字的連結
$browser->clickLink($linkText);
您可以使用 seeLink
方法判斷頁面上是否可見具有指定顯示文字的連結
if ($browser->seeLink($linkText)) { // ...}
這些方法會與 jQuery 互動。如果頁面上沒有 jQuery,Dusk 會自動將其注入到頁面中,以便在測試期間使用。
使用鍵盤
與 type
方法通常允許的情況相比,keys
方法可讓您為指定元素提供更複雜的輸入序列。例如,您可以指示 Dusk 在輸入值時按住修飾鍵。在此範例中,當 taylor
輸入到符合指定選擇器的元素時,將會按住 shift
鍵。在輸入 taylor
後,swift
將會在沒有任何修飾鍵的情況下輸入
$browser->keys('selector', ['{shift}', 'taylor'], 'swift');
keys
方法的另一個有價值的用例是將「鍵盤快捷鍵」組合傳送至您應用程式的主要 CSS 選擇器
$browser->keys('.app', ['{command}', 'j']);
所有修飾鍵,例如 {command}
,都包在 {}
字元中,並與 Facebook\WebDriver\WebDriverKeys
類別中定義的常數相符,這些常數可以在 GitHub 上找到。
流暢的鍵盤互動
Dusk 也提供了 withKeyboard
方法,讓您可以使用 Laravel\Dusk\Keyboard
類別流暢地執行複雜的鍵盤互動。Keyboard
類別提供了 press
、release
、type
和 pause
方法。
use Laravel\Dusk\Keyboard; $browser->withKeyboard(function (Keyboard $keyboard) { $keyboard->press('c') ->pause(1000) ->release('c') ->type(['c', 'e', 'o']);});
鍵盤巨集
如果您想要定義可以在整個測試套件中輕鬆重複使用的自訂鍵盤互動,可以使用 Keyboard
類別提供的 macro
方法。通常,您應該從服務提供者的 boot
方法中呼叫此方法。
<?php namespace App\Providers; use Facebook\WebDriver\WebDriverKeys;use Illuminate\Support\ServiceProvider;use Laravel\Dusk\Keyboard;use Laravel\Dusk\OperatingSystem; class DuskServiceProvider extends ServiceProvider{ /** * Register Dusk's browser macros. */ public function boot(): void { Keyboard::macro('copy', function (string $element = null) { $this->type([ OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'c', ]); return $this; }); Keyboard::macro('paste', function (string $element = null) { $this->type([ OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'v', ]); return $this; }); }}
macro
函式接受名稱作為第一個參數,並接受閉包作為第二個參數。當在 Keyboard
實例上以方法形式呼叫巨集時,將會執行巨集的閉包。
$browser->click('@textarea') ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy()) ->click('@another-textarea') ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste());
使用滑鼠
點擊元素
可以使用 click
方法點擊符合給定 CSS 或 Dusk 選擇器的元素。
$browser->click('.selector');
可以使用 clickAtXPath
方法點擊符合給定 XPath 表達式的元素。
$browser->clickAtXPath('//div[@class = "selector"]');
可以使用 clickAtPoint
方法點擊瀏覽器可視區域中給定座標的頂層元素。
$browser->clickAtPoint($x = 0, $y = 0);
可以使用 doubleClick
方法模擬滑鼠的雙擊。
$browser->doubleClick(); $browser->doubleClick('.selector');
可以使用 rightClick
方法模擬滑鼠的右鍵點擊。
$browser->rightClick(); $browser->rightClick('.selector');
可以使用 clickAndHold
方法模擬滑鼠按鈕被點擊並按住不放。後續呼叫 releaseMouse
方法將會取消此行為並釋放滑鼠按鈕。
$browser->clickAndHold('.selector'); $browser->clickAndHold() ->pause(1000) ->releaseMouse();
可以使用 controlClick
方法模擬瀏覽器中的 ctrl+click
事件。
$browser->controlClick(); $browser->controlClick('.selector');
滑鼠懸停
當您需要將滑鼠移動到符合給定 CSS 或 Dusk 選擇器的元素上方時,可以使用 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');
如果 JavaScript 對話方塊包含提示,您可以使用 typeInDialog
方法在提示中輸入值。
$browser->typeInDialog('Hello World');
要透過點擊「確定」按鈕關閉開啟的 JavaScript 對話方塊,您可以呼叫 acceptDialog
方法。
$browser->acceptDialog();
要透過點擊「取消」按鈕關閉開啟的 JavaScript 對話方塊,您可以呼叫 dismissDialog
方法。
$browser->dismissDialog();
與內嵌框架互動
如果您需要與 iframe 中的元素互動,可以使用 withinFrame
方法。在提供給 withinFrame
方法的閉包內發生的所有元素互動,其作用範圍都將限定在指定的 iframe 的上下文中。
$browser->withinFrame('#credit-card-details', function ($browser) { $browser->type('input[name="cardnumber"]', '4242424242424242') ->type('input[name="exp-date"]', '1224') ->type('input[name="cvc"]', '123') ->press('Pay');});
設定選擇器範圍
有時您可能希望在將所有操作範圍限定在給定選擇器內時執行多個操作。例如,您可能希望斷言某些文字僅存在於表格中,然後點擊該表格內的按鈕。您可以使用 with
方法來完成此操作。在提供給 with
方法的閉包內執行的所有操作,其作用範圍都將限定在原始選擇器。
$browser->with('.table', function (Browser $table) { $table->assertSee('Hello World') ->clickLink('Delete');});
您偶爾可能需要在目前範圍之外執行斷言。您可以使用 elsewhere
和 elsewhereWhenAvailable
方法來完成此操作。
$browser->with('.table', function (Browser $table) { // Current scope is `body .table`... $browser->elsewhere('.page-title', function (Browser $title) { // Current scope is `body .page-title`... $title->assertSee('Hello World'); }); $browser->elsewhereWhenAvailable('.page-title', function (Browser $title) { // Current scope is `body .page-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 或 Dusk 選擇器的元素。預設情況下,這將會暫停測試最多五秒鐘,然後才會擲回例外狀況。如有必要,您可以將自訂的逾時閾值作為第二個參數傳遞給此方法。
// Wait a maximum of five seconds for the selector...$browser->waitFor('.selector'); // Wait a maximum of one second for the selector...$browser->waitFor('.selector', 1);
您也可以等待直到符合給定選擇器的元素包含給定的文字。
// Wait a maximum of five seconds for the selector to contain the given text...$browser->waitForTextIn('.selector', 'Hello World'); // Wait a maximum of one second for the selector to contain the given text...$browser->waitForTextIn('.selector', 'Hello World', 1);
您也可以等待直到符合給定選擇器的元素從頁面中消失。
// Wait a maximum of five seconds until the selector is missing...$browser->waitUntilMissing('.selector'); // Wait a maximum of one second until the selector is missing...$browser->waitUntilMissing('.selector', 1);
或者,您可以等待直到符合給定選擇器的元素被啟用或停用。
// Wait a maximum of five seconds until the selector is enabled...$browser->waitUntilEnabled('.selector'); // Wait a maximum of one second until the selector is enabled...$browser->waitUntilEnabled('.selector', 1); // Wait a maximum of five seconds until the selector is disabled...$browser->waitUntilDisabled('.selector'); // Wait a maximum of one second until the selector is disabled...$browser->waitUntilDisabled('.selector', 1);
可用時限定選擇器範圍
有時,您可能希望等待符合給定選擇器的元素出現,然後與該元素互動。例如,您可能希望等待直到模式視窗可用,然後點擊模式視窗內的「確定」按鈕。可以使用 whenAvailable
方法來完成此操作。在給定閉包內執行的所有元素操作,其作用範圍都將限定在原始選擇器。
$browser->whenAvailable('.modal', function (Browser $modal) { $modal->assertSee('Hello World') ->press('OK');});
等待文字
可以使用 waitForText
方法等待直到給定的文字顯示在頁面上。
// Wait a maximum of five seconds for the text...$browser->waitForText('Hello World'); // Wait a maximum of one second for the text...$browser->waitForText('Hello World', 1);
您可以使用 waitUntilMissingText
方法等待直到顯示的文字從頁面中移除。
// Wait a maximum of five seconds for the text to be removed...$browser->waitUntilMissingText('Hello World'); // Wait a maximum of one second for the text to be removed...$browser->waitUntilMissingText('Hello World', 1);
等待連結
可以使用 waitForLink
方法等待直到給定的連結文字顯示在頁面上。
// Wait a maximum of five seconds for the link...$browser->waitForLink('Create'); // Wait a maximum of one second for the link...$browser->waitForLink('Create', 1);
等待輸入
可以使用 waitForInput
方法等待直到給定的輸入欄位在頁面上可見。
// Wait a maximum of five seconds for the input...$browser->waitForInput($field); // Wait a maximum of one second for the input...$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
關鍵字或結尾的分號。
// Wait a maximum of five seconds for the expression to be true...$browser->waitUntil('App.data.servers.length > 0'); // Wait a maximum of one second for the expression to be true...$browser->waitUntil('App.data.servers.length > 0', 1);
等待 Vue 表達式
可以使用 waitUntilVue
和 waitUntilVueIsNot
方法等待直到Vue 元件屬性具有給定的值。
// Wait until the component attribute contains the given value...$browser->waitUntilVue('user.name', 'Taylor', '@user'); // Wait until the component attribute doesn't contain the given value...$browser->waitUntilVueIsNot('user.name', null, '@user');
等待 JavaScript 事件
可以使用 waitForEvent
方法暫停測試的執行,直到發生 JavaScript 事件。
$browser->waitForEvent('load');
事件監聽器會附加到目前範圍,預設情況下是 body
元素。當使用範圍選擇器時,事件監聽器將會附加到符合的元素。
$browser->with('iframe', function (Browser $iframe) { // Wait for the iframe's load event... $iframe->waitForEvent('load');});
您也可以提供選擇器作為 waitForEvent
方法的第二個參數,以將事件監聽器附加到特定元素。
$browser->waitForEvent('load', '.selector');
您也可以等待 document
和 window
物件上的事件。
// Wait until the document is scrolled...$browser->waitForEvent('scroll', 'document'); // Wait a maximum of five seconds until the window is resized...$browser->waitForEvent('resize', 'window', 5);
使用回呼進行等待
Dusk 中的許多「等待」方法都依賴於底層的 waitUsing
方法。您可以直接使用此方法等待給定的閉包傳回 true
。waitUsing
方法接受等待的最大秒數、應該評估閉包的間隔、閉包以及可選的失敗訊息。
$browser->waitUsing(10, 1, function () use ($something) { return $something->isReady();}, "Something wasn't ready in time.");
將元素捲動至可見範圍
有時您可能無法點擊元素,因為它位於瀏覽器可視區域之外。scrollIntoView
方法會捲動瀏覽器視窗,直到給定選擇器的元素位於視圖內。
$browser->scrollIntoView('.selector') ->click('.selector');
可用的斷言
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 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
斷言頁面標題與給定的文字相符。
$browser->assertTitle($title);
assertTitleContains
斷言頁面標題包含給定的文字。
$browser->assertTitleContains($title);
assertUrlIs
斷言目前的 URL (不含查詢字串) 與給定的字串相符。
$browser->assertUrlIs($url);
assertSchemeIs
斷言目前的 URL 協定與給定的協定相符。
$browser->assertSchemeIs($scheme);
assertSchemeIsNot
斷言目前的 URL 協定與給定的協定不相符。
$browser->assertSchemeIsNot($scheme);
assertHostIs
斷言目前的 URL 主機與給定的主機相符。
$browser->assertHostIs($host);
assertHostIsNot
斷言目前的 URL 主機與給定的主機不相符。
$browser->assertHostIsNot($host);
assertPortIs
斷言目前的 URL 連接埠與給定的連接埠相符。
$browser->assertPortIs($port);
assertPortIsNot
斷言目前的 URL 連接埠與給定的連接埠不相符。
$browser->assertPortIsNot($port);
assertPathBeginsWith
斷言目前的 URL 路徑以給定的路徑開頭。
$browser->assertPathBeginsWith('/home');
assertPathEndsWith
斷言目前的 URL 路徑以給定的路徑結尾。
$browser->assertPathEndsWith('/home');
assertPathContains
斷言當前 URL 路徑包含給定的路徑
$browser->assertPathContains('/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);
assertSeeNothingIn
斷言沒有文字出現在選擇器內
$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
斷言給定的輸入欄位具有給定的值
$browser->assertInputValue($field, $value);
assertInputValueIsNot
斷言給定的輸入欄位不具有給定的值
$browser->assertInputValueIsNot($field, $value);
assertChecked
斷言給定的核取方塊已選取
$browser->assertChecked($field);
assertNotChecked
斷言給定的核取方塊未選取
$browser->assertNotChecked($field);
assertIndeterminate
斷言給定的核取方塊處於不確定狀態
$browser->assertIndeterminate($field);
assertRadioSelected
斷言給定的單選欄位已選取
$browser->assertRadioSelected($field, $value);
assertRadioNotSelected
斷言給定的單選欄位未選取
$browser->assertRadioNotSelected($field, $value);
assertSelected
斷言給定的下拉式選單已選取給定的值
$browser->assertSelected($field, $value);
assertNotSelected
斷言給定的下拉式選單未選取給定的值
$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);
assertAttributeDoesntContain
斷言符合給定選擇器的元素在提供的屬性中不包含給定的值
$browser->assertAttributeDoesntContain($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 元件
// HTML... <profile dusk="profile-component"></profile> // Component Definition... Vue.component('profile', { template: '<div>{{ user.name }}</div>', data: function () { return { user: { name: 'Taylor' } }; }});
您可以像這樣對 Vue 元件的狀態進行斷言
test('vue', function () { $this->browse(function (Browser $browser) { $browser->visit('/') ->assertVue('user.name', 'Taylor', '@profile-component'); });});
/** * A basic Vue test example. */public function test_vue(): void{ $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);
assertVueDoesntContain
斷言給定的 Vue 元件資料屬性是一個陣列,且不包含給定的值
$browser->assertVueDoesntContain($property, $value, $componentSelector = null);
頁面
有時,測試需要按順序執行幾個複雜的動作。這會讓您的測試更難閱讀和理解。Dusk 頁面允許您定義富有表達性的動作,然後可以透過單一方法在給定的頁面上執行。頁面也允許您為您的應用程式或單一頁面定義常用選擇器的捷徑。
產生頁面
若要產生頁面物件,請執行 dusk:page
Artisan 命令。所有頁面物件都會放置在您應用程式的 tests/Browser/Pages
目錄中
php artisan dusk:page Login
設定頁面
預設情況下,頁面具有三個方法:url
、assert
和 elements
。我們現在將討論 url
和 assert
方法。elements
方法將在 下面詳細討論。
url
方法
url
方法應傳回代表頁面的 URL 路徑。當在瀏覽器中導覽至頁面時,Dusk 將使用此 URL
/** * Get the URL for the page. */public function url(): string{ return '/login';}
assert
方法
assert
方法可以進行任何必要的斷言,以驗證瀏覽器實際上在給定的頁面上。實際上,沒有必要在此方法中放置任何東西;但是,如果您願意,可以自由進行這些斷言。當導覽至頁面時,這些斷言將自動執行
/** * Assert that the browser is on the page. */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 選擇器定義快速、易於記憶的捷徑。例如,讓我們為應用程式登入頁面的「電子郵件」輸入欄位定義捷徑
/** * Get the element shortcuts for the page. * * @return array<string, string> */public function elements(): array{ return [ '@email' => 'input[name=email]', ];}
一旦定義了捷徑,您可以在通常使用完整 CSS 選擇器的任何地方使用簡寫選擇器
全域簡寫選擇器
安裝 Dusk 後,將在您的 tests/Browser/Pages
目錄中放置一個基本 Page
類別。此類別包含一個 siteElements
方法,可用於定義應該在應用程式中每個頁面上可用的全域簡寫選擇器
/** * Get the global element shortcuts for the site. * * @return array<string, string> */public static function siteElements(): array{ return [ '@element' => '#selector', ];}
頁面方法
除了在頁面上定義的預設方法外,您還可以定義可以在整個測試中使用的其他方法。例如,假設我們正在建立一個音樂管理應用程式。應用程式的一個頁面的常見動作可能是建立播放清單。您可以在頁面類別上定義 createPlaylist
方法,而不是在每個測試中重新撰寫建立播放清單的邏輯
<?php namespace Tests\Browser\Pages; use Laravel\Dusk\Browser;use Laravel\Dusk\Page; class Dashboard extends Page{ // Other page methods... /** * Create a new playlist. */ 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
如上所示,「日期選擇器」是應用程式中可能存在於各種頁面上的一個元件範例。手動撰寫瀏覽器自動化邏輯以在測試套件的數十個測試中選取日期可能會變得繁瑣。相反地,我們可以定義一個 Dusk 元件來代表日期選擇器,讓我們將該邏輯封裝在元件中
<?php namespace Tests\Browser\Components; use Laravel\Dusk\Browser;use Laravel\Dusk\Component as BaseComponent; class DatePicker extends BaseComponent{ /** * Get the root selector for the component. */ public function selector(): string { return '.date-picker'; } /** * Assert that the browser page contains the component. */ public function assert(Browser $browser): void { $browser->assertVisible($this->selector()); } /** * Get the element shortcuts for the component. * * @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', ]; } /** * Select the given date. */ 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 use Illuminate\Foundation\Testing\DatabaseMigrations;use Laravel\Dusk\Browser;use Tests\Browser\Components\DatePicker; uses(DatabaseMigrations::class); test('basic example', function () { $this->browse(function (Browser $browser) { $browser->visit('/') ->within(new DatePicker, function (Browser $browser) { $browser->selectDate(2019, 1, 30); }) ->assertSee('January'); });});
<?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{ /** * A basic component test example. */ 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 應用程式使用內建的 PHP 開發伺服器在 8000 連接埠上提供服務。因此,在繼續之前,您應該確保您的持續整合環境的 APP_URL
環境變數值為 http://127.0.0.1:8000
。
Heroku CI
若要在 Heroku CI 上執行 Dusk 測試,請將以下 Google Chrome 建置套件和指令碼新增至您的 Heroku app.json
檔案
{ "environments": { "test": { "buildpacks": [ { "url": "heroku/php" }, { "url": "https://github.com/heroku/heroku-buildpack-chrome-for-testing" } ], "scripts": { "test-setup": "cp .env.testing .env", "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" } } }}
Travis CI
若要在 Travis CI 上執行 Dusk 測試,請使用以下 .travis.yml
配置。由於 Travis CI 不是圖形環境,我們需要採取一些額外的步驟才能啟動 Chrome 瀏覽器。此外,我們將使用 php artisan serve
啟動 PHP 的內建 Web 伺服器
language: php php: - 8.2 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 https://127.0.0.1 & - php artisan serve --no-reload & script: - php artisan dusk
GitHub Actions
如果您使用 GitHub Actions 執行 Dusk 測試,您可以將以下設定檔作為起點。如同 TravisCI,我們將使用 php artisan serve
命令啟動 PHP 的內建 Web 伺服器
name: CIon: [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@v4 - 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 --port=9515 & - 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@v4 with: name: screenshots path: tests/Browser/screenshots - name: Upload Console Logs if: failure() uses: actions/upload-artifact@v4 with: name: console path: tests/Browser/console
Chipper CI
如果您使用 Chipper CI 執行 Dusk 測試,您可以將以下設定檔作為起點。我們將使用 PHP 的內建伺服器執行 Laravel,以便我們可以監聽請求
# file .chipperci.ymlversion: 1 environment: php: 8.2 node: 16 # Include Chrome in the build environmentservices: - dusk # Build all commitson: push: branches: .* pipeline: - name: Setup cmd: | cp -v .env.example .env composer install --no-interaction --prefer-dist --optimize-autoloader php artisan key:generate # Create a dusk env file, ensuring APP_URL uses BUILD_HOST cp -v .env .env.dusk.ci sed -i "s@APP_URL=.*@APP_URL=http://$BUILD_HOST:8000@g" .env.dusk.ci - name: Compile Assets cmd: | npm ci --no-audit npm run build - name: Browser Tests cmd: | php -S [::0]:8000 -t public 2>server.log & sleep 2 php artisan dusk:chrome-driver $CHROME_DRIVER php artisan dusk --env=ci
若要深入了解如何在 Chipper CI 上執行 Dusk 測試,包括如何使用資料庫,請參閱官方 Chipper CI 文件。