Laravel Duskを WSL上のLaravel + vue.js on LaraDockで動かす

ブラウザテストに興味が出たので導入してみた

# この記事でわかること
- Duskを動かすためのLaradockの設定
    - `WORKSPACE_INSTALL_DUSK_DEPS=true`を設定
- Duskを動かす時のエラー回避法
    - `APP_URL`合わせよう
    - `php artisan serve`実行しよう
    - `no-sandbox`オプションつけよう
- フロントがvue.jsのときでもDuskを動かす方法
    - `waitFor`メソッドを使って対象DOMがロードされるまで待つ

# 筆者環境
OS: Windows10
WSL:Ubuntu22.04
バックエンド：Laravel 8.x on LaraDock
フロントエンド：vue.js

# 前提
すでにvue.jsでログインフォームだけ作成済み(後述)

# Laradock側の修正
duskでのテスト実行では、Chromeブラウザ、ChromeDriverが必要。
最新のLaradockではDockerFileを変更しなくても、下記envだけ変更すれば標準でインストールできる。
``` diff_php:.env.example
- WORKSPACE_INSTALL_DUSK_DEPS=false
+ WORKSPACE_INSTALL_DUSK_DEPS=true
```

Laradockのほうの.envに反映

LaraDockでduskの環境を整えるという発想は、下記のサイトが参考になりました。

https://qiita.com/kurosuke1117/items/a7b85b06c56a51029693


# Laravel側の修正
dusk実行時の下記エラーを防ぐために、envのAPP_URLを変更する
```
1) Tests\Browser\ExampleTest::testBasicExample
Facebook\WebDriver\Exception\UnknownErrorException: unknown error: net::ERR_CONNECTION_REFUSED
  (Session info: headless chrome=106.0.5249.119)
```

```laravel:.env.example
- APP_URL=http://localhost
+ APP_URL=http://localhost:8000
```
変更したらlaravelの.envに反映

こちらが参考になりました。

https://qiita.com/conpass/items/24c68744eb17001b6719

# Laradockの再ビルド
```
docker-compose build --no-cache workspace
docker-compose up -d (ご使用のコンテナ)
```

# Duskのインストール

LaraDockのworkspaceコンテナで下記を実行
```LaraDock:workspaceコンテナ
composer require --dev laravel/dusk
php artisan dusk:install
```
コマンドは公式のドキュメントを参考にしました

https://readouble.com/laravel/8.x/ja/dusk.html

```
vendor/laravel/dusk/bin/chromedriver-linux -v
```

↑公式ドキュメントにある、ChromedrDriverをインストールするコマンドは不要。
`WORKSPACE_INSTALL_DUSK_DEPS=true`
にしたことで、再ビルド時にインストールされている。

# サンプルテストの実施
```LaraDock:workspaceコンテナ
php artisan serve 
// 別ターミナルで
php artisan dusk
```

ここで下記エラーが発生した場合は次の設定をする
```
1) Tests\Browser\ExampleTest::testBasicExample
Facebook\WebDriver\Exception\UnknownErrorException: unknown error: Chrome failed to start: crashed.
  (unknown error: DevToolsActivePort file doesn't exist)
  (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)

/var/www/vendor/php-webdriver/webdriver/lib/Exception/WebDriverException.php:143
/var/www/vendor/php-webdriver/webdriver/lib/Remote/HttpCommandExecutor.php:385
/var/www/vendor/php-webdriver/webdriver/lib/Remote/RemoteWebDriver.php:134
/var/www/tests/DuskTestCase.php:46
/var/www/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:219
/var/www/vendor/laravel/framework/src/Illuminate/Support/helpers.php:234
/var/www/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:220
/var/www/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:98
/var/www/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:66
/var/www/tests/Browser/ExampleTest.php:21
```

```diff_php:laravel-i-kura/tests/DuskTestCase.php
    protected function driver()
    {
        $options = (new ChromeOptions)->addArguments(collect([
            $this->shouldStartMaximized() ? '--start-maximized' : '--window-size=1920,1080',
        ])->unless($this->hasHeadlessDisabled(), function ($items) {
            return $items->merge([
                '--disable-gpu',
                '--headless',
+               '--no-sandbox' // このコードを追加
            ]);
        })->all());

        return RemoteWebDriver::create(
            $_ENV['DUSK_DRIVER_URL'] ?? 'http://localhost:9515',
            DesiredCapabilities::chrome()->setCapability(
                ChromeOptions::CAPABILITY, $options
            )
        );
    }
```

この状態で
```
php artisan dusk
```
を実行すると、下記のようにテストは通らない(Failuer)ものの、エラーは出ないはず
(テストが実行できる状態)

```
F                                                                   1 / 1 (100%)

Time: 00:04.673, Memory: 22.00 MB

There was 1 failure:

1) Tests\Browser\ExampleTest::testBasicExample
Did not see expected text [Laravel] within element [body].
Failed asserting that false is true.

/var/www/vendor/laravel/dusk/src/Concerns/MakesAssertions.php:181
/var/www/vendor/laravel/dusk/src/Concerns/MakesAssertions.php:152
/var/www/tests/Browser/ExampleTest.php:20
/var/www/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:69
/var/www/tests/Browser/ExampleTest.php:21

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

```

# vue.js + laravelのテストを通す方法

ここで動かしたテストを見てみる
```php:tests/Browser/ExampleTest.php
    public function testBasicExample()
    {
        $this->browse(function (Browser $browser) {
            $browser->visit('/')
                    ->assertSee('Laravel');
        });
    }
```

vue.jsでつくられた`/`(トップページ)には、メールアドレスという文字列があるので、

```diff_php:tests/Browser/ExampleTest.php
- ->assertSee('Laravel');
+ ->assertSee('メールアドレス');
```
とすれば、通りそうだが実際には通らない。
解決策はこちら↓

```diff_php:tests/Browser/ExampleTest.php
    public function testBasicExample()
    {
        $this->browse(function (Browser $browser) {
            $browser->visit('/')
+               ->waitFor('.index-component-root')
                ->assertSee('メールアドレス');
        });
    }
```
vue.jsを読み込んでいるので、それが終わってからassertしないと、無いことになってしまう。
なので、
```
->waitFor('.index-component-root')
```
で、文字列を含むDOM要素`.index-component-root`が読み込まれるのを待ってから、
```
->assertSee('メールアドレス');
```
するようにする

この状態で
```
php artisan dusk
```
を実行すると、サンプルテストが通るようになる

```
.                                                                   1 / 1 (100%)

Time: 00:02.373, Memory: 22.00 MB

OK (1 test, 1 assertion)

```

今後これをもとにブラウザテストを実装していくことにする