サイトを更新しています。 数日間、レイアウトや翻訳に不具合が出ることがあります。ドキュメントは引き続きご利用いただけます。表示が崩れている場合は、後ほど再読み込みしてください。

ホーム Laravel 10.x Laravel Dusk

Laravel Dusk

10.x 2026年3月7日

#はじめに

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テスト、そしてOSに対応したChrome Driverバイナリを作成・インストールします。

php artisan dusk:install

次に、アプリケーションの .env ファイルで APP_URL 環境変数を設定してください。この値はブラウザでアクセスするURLと一致させる必要があります。

Примечание

ローカル開発環境を Laravel Sail で管理している場合は、Sailのドキュメントにある Duskテストの設定と実行 も参照してください。

#ChromeDriverの管理

dusk:install コマンドでインストールされるChromeDriverとは別のバージョンをインストールしたい場合は、dusk:chrome-driver コマンドを使用できます。

# OSに対応した最新のChromeDriverをインストール...
php artisan dusk:chrome-driver

# OSに対応した指定バージョンのChromeDriverをインストール...
php artisan dusk:chrome-driver 86

# サポートされている全OS向けに指定バージョンのChromeDriverをインストール...
php artisan dusk:chrome-driver --all

# OSのChrome / Chromiumの検出バージョンに合ったChromeDriverをインストール...
php artisan dusk:chrome-driver --detect
Внимание

Duskは chromedriver バイナリが実行可能である必要があります。Duskの実行に問題がある場合は、次のコマンドでバイナリの実行権限を確認してください:chmod -R 0755 vendor/laravel/dusk/bin/

#他のブラウザの使用

デフォルトでは、DuskはGoogle ChromeとスタンドアロンのChromeDriverを使ってブラウザテストを実行します。ただし、自分でSeleniumサーバーを起動し、任意のブラウザでテストを実行することも可能です。

はじめに、アプリケーションのベースDuskテストケースである tests/DuskTestCase.php ファイルを開きます。このファイル内で startChromeDriver メソッドの呼び出しを削除すると、Duskが自動的にChromeDriverを起動しなくなります。

/**
 * Duskテスト実行の準備。
 *
 * @beforeClass
 */
public static function prepare(): void
{
    // static::startChromeDriver();
}

次に、driver メソッドを修正して任意のURLとポートに接続できます。また、WebDriverに渡す「desired capabilities」も変更可能です。

use Facebook\WebDriver\Remote\RemoteWebDriver;

/**
 * RemoteWebDriverインスタンスを作成。
 */
protected function driver(): RemoteWebDriver
{
    return RemoteWebDriver::create(
        'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
    );
}

#はじめに

#テストの生成

Duskテストを生成するには、dusk:make Artisanコマンドを使います。生成されたテストは tests/Browser ディレクトリに配置されます。

php artisan dusk:make LoginTest

#各テスト後のデータベースリセット

ほとんどのテストはアプリケーションのデータベースからデータを取得するページとやり取りしますが、Duskテストでは RefreshDatabase トレイトを使ってはいけません。RefreshDatabase はデータベーストランザクションを利用しますが、HTTPリクエスト間では適用できません。代わりに、DatabaseMigrations トレイトか DatabaseTruncation トレイトのどちらかを使います。

#Database Migrationsの使用

DatabaseMigrations トレイトは各テスト前にデータベースマイグレーションを実行します。ただし、テーブルを毎回ドロップして再作成するため、通常はテーブルをトランケートするより遅くなります。

<?php

namespace Tests\Browser;

use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;

class ExampleTest extends DuskTestCase
{
    use DatabaseMigrations;
}
Внимание

SQLiteのインメモリデータベースはDuskテストで使用できません。ブラウザは独立したプロセスで実行されるため、他のプロセスのインメモリデータベースにアクセスできません。

#Database Truncationの使用

DatabaseTruncation トレイトを使う前に、Composerで doctrine/dbal パッケージをインストールしてください。

composer require --dev doctrine/dbal

DatabaseTruncation トレイトは最初のテストでデータベースマイグレーションを実行し、テーブルが正しく作成されていることを保証します。その後のテストではテーブルをトランケートするだけなので、マイグレーションを再実行するより高速です。

<?php

namespace Tests\Browser;

use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;

class ExampleTest extends DuskTestCase
{
    use DatabaseTruncation;
}

デフォルトでは、このトレイトは migrations テーブルを除くすべてのテーブルをトランケートします。トランケートするテーブルをカスタマイズしたい場合は、テストクラスに $tablesToTruncate プロパティを定義できます。

/**
 * トランケート対象のテーブルを指定。
 *
 * @var array
 */
protected $tablesToTruncate = ['users'];

または、トランケートから除外するテーブルを $exceptTables プロパティで指定できます。

/**
 * トランケート除外のテーブルを指定。
 *
 * @var array
 */
protected $exceptTables = ['users'];

トランケート対象のデータベース接続を指定したい場合は、テストクラスに $connectionsToTruncate プロパティを定義できます。

/**
 * トランケート対象の接続を指定。
 *
 * @var array
 */
protected $connectionsToTruncate = ['mysql'];

トランケートの前後に処理を実行したい場合は、テストクラスに beforeTruncatingDatabase または afterTruncatingDatabase メソッドを定義できます。

/**
 * データベースのトランケート開始前に実行する処理。
 */
protected function beforeTruncatingDatabase(): void
{
    //
}

/**
 * データベースのトランケート完了後に実行する処理。
 */
protected function afterTruncatingDatabase(): void
{
    //
}

#テストの実行

ブラウザテストを実行するには、dusk Artisanコマンドを使います。

php artisan dusk

前回の dusk コマンド実行でテストが失敗していた場合、dusk:fails コマンドで失敗したテストだけを先に再実行して時間を節約できます。

php artisan dusk:fails

dusk コマンドは、PHPUnitのテストランナーが通常受け付ける引数をすべて受け付けます。例えば、特定のgroupのテストだけを実行することも可能です。

php artisan dusk --group=foo
Примечание

ローカル開発環境を Laravel Sail で管理している場合は、Sailのドキュメントにある Duskテストの設定と実行 を参照してください。

#ChromeDriverの手動起動

デフォルトでは、Duskは自動的にChromeDriverを起動します。システムによってはうまく動作しない場合があるため、その場合は dusk コマンド実行前に手動でChromeDriverを起動してください。手動起動する場合は、tests/DuskTestCase.php ファイル内の以下の行をコメントアウトしてください。

/**
 * Duskテスト実行の準備。
 *
 * @beforeClass
 */
public static function prepare(): void
{
    // static::startChromeDriver();
}

また、9515以外のポートでChromeDriverを起動する場合は、同じクラスの driver メソッドを修正して正しいポートを指定してください。

use Facebook\WebDriver\Remote\RemoteWebDriver;

/**
 * RemoteWebDriverインスタンスを作成。
 */
protected function driver(): RemoteWebDriver
{
    return RemoteWebDriver::create(
        'http://localhost:9515', DesiredCapabilities::chrome()
    );
}

#環境設定の管理

テスト実行時にDusk独自の環境ファイルを使いたい場合は、プロジェクトルートに .env.dusk.{environment} ファイルを作成してください。例えば、local 環境で dusk コマンドを実行する場合は .env.dusk.local を作成します。

テスト実行時、Duskは .env ファイルをバックアップし、Dusk環境ファイルを .env にリネームします。テスト終了後に元の .env ファイルが復元されます。

#ブラウザの基本

#ブラウザの作成

はじめに、アプリケーションにログインできるかを確認するテストを書いてみましょう。テストを生成したら、ログインページに移動し、認証情報を入力して「Login」ボタンをクリックするように修正します。ブラウザインスタンスはDuskテスト内で browse メソッドを呼び出して作成します。

<?php

namespace Tests\Browser;

use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;

class ExampleTest extends DuskTestCase
{
    use DatabaseMigrations;

    /**
     * 基本的なブラウザテストの例です。
     */
    public function test_basic_example(): void
    {
        $user = User::factory()->create([
            'email' => '[email protected]',
        ]);

        $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('login');

backforward メソッドを使って「戻る」や「進む」操作ができます:

$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
{
    /**
     * Dusk のブラウザマクロを登録します。
     */
    public function boot(): void
    {
        Browser::macro('scrollToElement', function (string $element = null) {
            $this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");

            return $this;
        });
    }
}

macro 関数は最初の引数に名前を、2番目にクロージャを受け取ります。マクロのクロージャは 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 メソッドを使うと暗号化されたクッキーの値を取得または設定できます。Laravel が作成するクッキーはデフォルトで暗号化されています:

$browser->cookie('name');

$browser->cookie('name', 'Taylor');

plainCookie メソッドを使うと暗号化されていないクッキーの値を取得または設定できます:

$browser->plainCookie('name');

$browser->plainCookie('name', 'Taylor');

deleteCookie メソッドを使うと指定したクッキーを削除できます:

$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');

#コンソール出力の保存

storeConsoleLog メソッドを使うと現在のブラウザのコンソール出力を指定したファイル名で保存できます。コンソール出力は tests/Browser/console ディレクトリに保存されます:

$browser->storeConsoleLog('filename');

#ページソースの保存

storeSource メソッドを使うと現在のページのソースを指定したファイル名で保存できます。ページソースは tests/Browser/source ディレクトリに保存されます:

$browser->storeSource('filename');

#要素との対話

#Dusk セレクター

要素と対話するための適切な CSS セレクターを選ぶのは Dusk テストを書く上で最も難しい部分の一つです。フロントエンドの変更により、以下のような CSS セレクターがテストを壊すことがあります:

// HTML...

<button>Login</button>

// テスト...

$browser->click('.login-page .container div > button');

Dusk セレクターを使うと、CSS セレクターを覚えるよりも効果的なテストを書くことに集中できます。セレクターを定義するには、HTML 要素に dusk 属性を追加します。Dusk ブラウザで操作するときは、セレクターの前に @ を付けてテスト内でその要素を操作します:

// HTML...

<button dusk="login-button">Login</button>

// テスト...

$browser->click('@login-button');

必要に応じて、Dusk セレクターが利用する HTML 属性を selectorHtmlAttribute メソッドでカスタマイズできます。通常はアプリケーションの AppServiceProviderboot メソッド内で呼び出します:

use Laravel\Dusk\Dusk;

Dusk::selectorHtmlAttribute('data-dusk');

#テキスト、値、属性

#値の取得と設定

Dusk はページ上の要素の現在の値、表示テキスト、属性と対話するためのいくつかのメソッドを提供します。例えば、指定した CSS または Dusk セレクターにマッチする要素の「value」を取得するには value メソッドを使います:

// 値を取得...
$value = $browser->value('selector');

// 値を設定...
$browser->value('selector', 'value');

inputValue メソッドを使うと、指定したフィールド名を持つ入力要素の「value」を取得できます:

$value = $browser->inputValue('field');

#テキストの取得

text メソッドを使うと、指定したセレクターにマッチする要素の表示テキストを取得できます:

$text = $browser->text('selector');

#属性の取得

最後に、attribute メソッドを使うと、指定したセレクターにマッチする要素の属性値を取得できます:

$attribute = $browser->attribute('selector', 'value');

#フォームとの対話

#値の入力

Dusk はフォームや入力要素と対話するための様々なメソッドを提供します。まずは入力フィールドにテキストを入力する例を見てみましょう:

$browser->type('email', '[email protected]');

type メソッドは必要に応じて CSS セレクターを受け取りますが、必須ではありません。CSS セレクターが指定されない場合、Dusk は指定した name 属性を持つ input または textarea フィールドを探します。

フィールドの内容をクリアせずにテキストを追加したい場合は、append メソッドを使えます:

$browser->type('tags', 'foo')
        ->append('tags', ', bar, baz');

clear メソッドを使うと入力値をクリアできます:

$browser->clear('email');

typeSlowly メソッドを使うとゆっくりタイプするよう指示できます。デフォルトではキー入力間に100ミリ秒の間隔があります。間隔を変更したい場合は、3番目の引数にミリ秒数を渡します:

$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メソッドに値を渡す際は、表示テキストではなく、対応する optionvalue を渡してください:

$browser->select('size', 'Large');

第2引数を省略するとランダムなオプションを選択します:

$browser->select('size');

selectメソッドの第2引数に配列を渡すと、複数のオプションを選択するようメソッドに指示できます:

$browser->select('categories', ['Art', 'Music']);

#チェックボックス

チェックボックスを「チェック」するには check メソッドを使います。他の入力関連メソッドと同様に完全な CSS セレクターは不要です。マッチする CSS セレクターが見つからない場合は、name 属性が一致するチェックボックスを探します:

$browser->check('terms');

uncheck メソッドを使うとチェックボックスのチェックを外せます:

$browser->uncheck('terms');

#ラジオボタン

radio メソッドを使うと radio 入力のオプションを選択できます。他の入力関連メソッドと同様に完全な CSS セレクターは不要です。マッチする CSS セレクターが見つからない場合は、namevalue 属性が一致する radio 入力を探します:

$browser->radio('size', 'large');

#ファイルの添付

attach メソッドを使うと file 入力要素にファイルを添付できます。他の入力関連メソッドと同様に完全な CSS セレクターは不要です。マッチする CSS セレクターが見つからない場合は、name 属性が一致する file 入力を探します:

$browser->attach('photo', __DIR__.'/photos/mountains.png');
Внимание

attach 関数を使うにはサーバーに Zip PHP 拡張がインストールされ、有効になっている必要があります。

#ボタンのクリック

press メソッドはページ上のボタン要素をクリックするために使用できます。press メソッドに渡す引数は、ボタンの表示テキスト、または CSS / Dusk セレクタのいずれかです:

$browser->press('Login');

フォームを送信する際、多くのアプリケーションは送信ボタンを押した後に無効化し、フォーム送信のHTTPリクエストが完了するとボタンを再度有効化します。ボタンを押してから再び有効になるのを待つには、pressAndWaitFor メソッドを使えます。

// ボタンを押して、最大5秒間ボタンが有効になるのを待つ...
$browser->pressAndWaitFor('Save');

// ボタンを押して、最大1秒間ボタンが有効になるのを待つ...
$browser->pressAndWaitFor('Save', 1);

リンクをクリックするには、ブラウザインスタンスの clickLink メソッドを使います。clickLink は指定した表示テキストを持つリンクをクリックします。

$browser->clickLink($linkText);

ページ上に指定した表示テキストのリンクが見えるかどうかを確認するには、seeLink メソッドを使えます。

if ($browser->seeLink($linkText)) {
    // ...
}
Внимание

これらのメソッドはjQueryと連携します。ページにjQueryがない場合、Duskはテストの実行中に自動的にjQueryをページに注入します。

#キーボードの使用

keys メソッドは、type メソッドで通常可能なものよりも複雑な入力シーケンスを指定した要素に与えることができます。たとえば、値を入力する際に修飾キーを押し続けるよう Dusk に指示できます。この例では、指定したセレクタに一致する要素に taylor を入力している間、shift キーが押し続けられます。taylor の入力が終わった後、swift は修飾キーなしで入力されます:

$browser->keys('selector', ['{shift}', 'taylor'], 'swift');

keys メソッドのもう一つの有用な使い方は、アプリケーションの主要なCSSセレクターに対して「キーボードショートカット」の組み合わせを送ることです。

$browser->keys('.app', ['{command}', 'j']);
Примечание

{command} のような修飾キーはすべて {} で囲まれており、Facebook\WebDriver\WebDriverKeys クラスで定義された定数に対応しています。詳細はGitHubで確認できます。

#Fluentなキーボード操作

Duskは withKeyboard メソッドも提供しており、Laravel\Dusk\Keyboard クラスを使って複雑なキーボード操作を流暢に実行できます。Keyboard クラスは pressreleasetypepause メソッドを提供します。

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
{
    /**
     * Duskのブラウザマクロを登録します。
     */
    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 関数は最初の引数に名前を、2番目にクロージャを受け取ります。マクロのクロージャは、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');

#マウスオーバー

mouseover メソッドは、指定したCSSまたはDuskセレクターにマッチする要素にマウスを移動させるときに使います。

$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ダイアログを「OK」ボタンで閉じるには、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"]', '12/24')
        ->type('input[name="cvc"]', '123');
    })->press('Pay');
});

#セレクターのスコープ指定

場合によっては、特定のセレクタ内に操作を限定しつつ、複数の操作を行いたいことがあります。例えば、あるテキストがテーブルの内部にのみ存在することをアサートし、その同じテーブル内のボタンをクリックしたい場合です。これを実現するには with メソッドを使用できます。with メソッドに渡したクロージャ内で行われるすべての操作は、元のセレクタにスコープされます:

$browser->with('.table', function (Browser $table) {
    $table->assertSee('Hello World')
          ->clickLink('Delete');
});

時には現在のスコープ外でアサーションを実行したい場合があります。その場合は elsewhereelsewhereWhenAvailable メソッドを使えます。

 $browser->with('.table', function (Browser $table) {
    // 現在のスコープは `body .table`...

    $browser->elsewhere('.page-title', function (Browser $title) {
        // 現在のスコープは `body .page-title`...
        $title->assertSee('Hello World');
    });

    $browser->elsewhereWhenAvailable('.page-title', function (Browser $title) {
        // 現在のスコープは `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セレクターにマッチする要素がページに表示されるまでテストの実行を一時停止します。デフォルトでは最大5秒間待機し、それを超えると例外を投げます。必要に応じて第2引数でカスタムのタイムアウトを指定できます。

// セレクターが表示されるまで最大5秒待つ...
$browser->waitFor('.selector');

// セレクターが表示されるまで最大1秒待つ...
$browser->waitFor('.selector', 1);

指定したセレクターにマッチする要素が指定したテキストを含むまで待つこともできます。

// セレクターが指定したテキストを含むまで最大5秒待つ...
$browser->waitForTextIn('.selector', 'Hello World');

// セレクターが指定したテキストを含むまで最大1秒待つ...
$browser->waitForTextIn('.selector', 'Hello World', 1);

指定したセレクターにマッチする要素がページから消えるまで待つこともできます。

// セレクターが消えるまで最大5秒待つ...
$browser->waitUntilMissing('.selector');

// セレクターが消えるまで最大1秒待つ...
$browser->waitUntilMissing('.selector', 1);

または、指定したセレクターが有効または無効になるまで待つこともできます。

// セレクターが有効になるまで最大5秒待つ...
$browser->waitUntilEnabled('.selector');

// セレクターが有効になるまで最大1秒待つ...
$browser->waitUntilEnabled('.selector', 1);

// セレクターが無効になるまで最大5秒待つ...
$browser->waitUntilDisabled('.selector');

// セレクターが無効になるまで最大1秒待つ...
$browser->waitUntilDisabled('.selector', 1);

#利用可能になったセレクターのスコープ指定

指定したセレクターにマッチする要素が現れるのを待ち、その要素とやり取りしたい場合があります。例えば、モーダルウィンドウが表示されるのを待ってから、その中の「OK」ボタンを押す場合です。whenAvailable メソッドを使うとこれができます。クロージャ内のすべての要素操作は元のセレクターにスコープされます。

$browser->whenAvailable('.modal', function (Browser $modal) {
    $modal->assertSee('Hello World')
          ->press('OK');
});

#テキストの待機

waitForText メソッドは、指定したテキストがページに表示されるまで待機するために使えます:

// テキストが表示されるまで最大5秒待機...
$browser->waitForText('Hello World');

// テキストが表示されるまで最大1秒待機...
$browser->waitForText('Hello World', 1);

waitUntilMissingText メソッドは、指定したテキストがページから消えるまで待機するために使えます:

// テキストが消えるまで最大5秒待機...
$browser->waitUntilMissingText('Hello World');

// テキストが消えるまで最大1秒待機...
$browser->waitUntilMissingText('Hello World', 1);

waitForLink メソッドは、指定したリンクテキストがページに表示されるまで待機するために使えます:

// リンクが表示されるまで最大5秒待機...
$browser->waitForLink('Create');

// リンクが表示されるまで最大1秒待機...
$browser->waitForLink('Create', 1);

#入力フィールドの待機

waitForInput メソッドは、指定した入力フィールドがページに表示されるまで待機するために使えます:

// 入力フィールドが表示されるまで最大5秒待機...
$browser->waitForInput($field);

// 入力フィールドが表示されるまで最大1秒待機...
$browser->waitForInput($field, 1);

#ページの場所の待機

$browser->assertPathIs('/home') のようなパスのアサーションは、window.location.pathname が非同期で更新されている場合に失敗することがあります。waitForLocation メソッドを使うと、指定した場所になるまで待機できます:

$browser->waitForLocation('/secret');

waitForLocation メソッドは、現在のウィンドウの場所が完全なURLになるまで待機することもできます:

$browser->waitForLocation('https://example.com/path');

named route の場所を待機することもできます:

$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 キーワードや末尾のセミコロンは不要です:

// 式が true になるまで最大5秒待機...
$browser->waitUntil('App.data.servers.length > 0');

// 式が true になるまで最大1秒待機...
$browser->waitUntil('App.data.servers.length > 0', 1);

#Vue 式の待機

waitUntilVuewaitUntilVueIsNot メソッドは、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 の load イベントを待機...
    $iframe->waitForEvent('load');
});

waitForEvent メソッドの第2引数にセレクターを渡すと、特定の要素にイベントリスナーをアタッチできます:

$browser->waitForEvent('load', '.selector');

documentwindow オブジェクトのイベントを待つこともできます:

// ドキュメントがスクロールされるまで待機...
$browser->waitForEvent('scroll', 'document');

// ウィンドウのリサイズを最大5秒待機...
$browser->waitForEvent('resize', 'window', 5);

#コールバックを使った待機

Dusk の多くの「待機」メソッドは内部で waitUsing メソッドを使っています。waitUsing を直接使うと、指定したクロージャが true を返すまで待機できます。引数は、最大待機秒数、クロージャの評価間隔、クロージャ、オプションの失敗メッセージです:

$browser->waitUsing(10, 1, function () use ($something) {
    return $something->isReady();
}, "Something wasn't ready in time.");

#要素をビュー内にスクロール

要素がブラウザの表示範囲外にあるためクリックできない場合があります。scrollIntoView メソッドは、指定したセレクターの要素がビュー内に入るまでスクロールします:

$browser->scrollIntoView('.selector')
        ->click('.selector');

#利用可能なアサーション

Dusk はアプリケーションに対して様々なアサーションを提供します。利用可能なアサーションは以下のリストにまとめられています:

<style> .collection-method-list > p { columns: 10.8em 3; -moz-columns: 10.8em 3; -webkit-columns: 10.8em 3; } .collection-method-list a { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } </style>

#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');

#assertPathIs

現在のパスが指定したパスと一致することをアサートします:

$browser->assertPathIs('/home');

#assertPathIsNot

現在のパスが指定したパスと一致しないことをアサートします:

$browser->assertPathIsNot('/home');

#assertRouteIs

現在のURLが指定したnamed routeの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

指定した暗号化されたクッキーが存在することをアサートします:

$browser->assertHasCookie($name);

#assertHasPlainCookie

指定した暗号化されていないクッキーが存在することをアサートします:

$browser->assertHasPlainCookie($name);

#assertCookieMissing

指定した暗号化されたクッキーが存在しないことをアサートします:

$browser->assertCookieMissing($name);

#assertPlainCookieMissing

指定した暗号化されていないクッキーが存在しないことをアサートします:

$browser->assertPlainCookieMissing($name);

#assertCookieValue

暗号化されたクッキーが指定した値を持つことをアサートします:

$browser->assertCookieValue($name, $value);

#assertPlainCookieValue

暗号化されていないクッキーが指定した値を持っていることを検証します:

$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);

ページに指定したリンクテキストが存在することを検証します:

$browser->assertSeeLink($linkText);

ページに指定したリンクテキストが存在しないことを検証します:

$browser->assertDontSeeLink($linkText);

#assertInputValue

指定した入力フィールドが指定した値を持っていることを検証します:

$browser->assertInputValue($field, $value);

#assertInputValueIsNot

指定した入力フィールドが指定した値を持っていないことを検証します:

$browser->assertInputValueIsNot($field, $value);

#assertChecked

指定したチェックボックスがチェックされていることを検証します:

$browser->assertChecked($field);

#assertNotChecked

指定したチェックボックスがチェックされていないことを検証します:

$browser->assertNotChecked($field);

#assertIndeterminate

指定したチェックボックスが不確定状態(indeterminate)であることを検証します:

$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

指定したフィールドが有効(enabled)であることを検証します:

$browser->assertEnabled($field);

#assertDisabled

指定したフィールドが無効(disabled)であることを検証します:

$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>

// コンポーネント定義...

Vue.component('profile', {
    template: '<div>{{ user.name }}</div>',

    data: function () {
        return {
            user: {
                name: 'Taylor'
            }
        };
    }
});

Vueコンポーネントの状態に対して次のように検証できます:

/**
 * 基本的なVueテストの例です。
 */
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);

#Pages

テストでは複雑な操作を連続して行う必要がある場合があります。これによりテストが読みづらく理解しにくくなることがあります。Dusk Pages を使うと、ページ上で実行する表現力豊かな操作を単一のメソッドで定義できます。また、Pages はアプリケーション全体や単一ページの共通セレクターのショートカットも定義できます。

#ページの生成

ページオブジェクトを生成するには、dusk:page Artisanコマンドを実行します。生成されたページオブジェクトはすべてアプリケーションの tests/Browser/Pages ディレクトリに配置されます:

php artisan dusk:page Login

#ページの設定

デフォルトで、ページには urlassertelements の3つのメソッドがあります。ここでは urlassert メソッドについて説明します。elements メソッドは後述のショートハンドセレクターで詳しく説明します。

#url メソッド

url メソッドはページを表すURLのパスを返す必要があります。Duskはブラウザでページに移動する際にこのURLを使用します:

/**
 * ページのURLを取得します。
 */
public function url(): string
{
    return '/login';
}

#assert メソッド

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をインストールすると、tests/Browser/Pages ディレクトリにベースの Page クラスが配置されます。このクラスには 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
{
    // Other page methods...

    /**
     * 新しいプレイリストを作成します。
     */
    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
{
    /**
     * コンポーネントのルートセレクターを取得します。
     */
    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開発サーバーで提供することを想定しています。したがって、続行する前に、継続的インテグレーション環境の 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-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の組み込みウェブサーバーを起動するために php artisan serve を使用します。

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テストを実行する場合、以下の設定ファイルを出発点として使えます。Travis CIと同様に、PHPの組み込みウェブサーバーを起動するために php artisan serve コマンドを使用します。

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@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 &
      - 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

#Chipper CI

Chipper CIでDuskテストを実行する場合、以下の設定ファイルを出発点として使えます。Laravelを実行するためにPHPの組み込みサーバーを使い、リクエストを待ち受けます。

# file .chipperci.yml
version: 1

environment:
  php: 8.2
  node: 16

# ビルド環境にChromeを含める
services:
  - dusk

# すべてのコミットをビルド
on:
   push:
      branches: .*

pipeline:
  - name: Setup
    cmd: |
      cp -v .env.example .env
      composer install --no-interaction --prefer-dist --optimize-autoloader
      php artisan key:generate
      
      # dusk用の環境ファイルを作成し、APP_URLが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ドキュメントを参照してください。