Laravel Duskをコマンドで使用する方法を試した

## 試したこと
登録したコマンドよりDusk(dusk)を使用できるようにする場合を試しました。

## 作業背景
下記で、duskでブラウザでのテストを試しました。その時、コマンドで実行したい場合で、コマンドファイル内にduskを記載することができず、handleメソッドにArtisan::call('dusk');を記載して、対応しました。
今回は、直接コマンドファイル内に記載する場合どうするのかと思ったのがきっかけです。

https://qiita.com/kenji123/items/33219801e81d06b31656


## コマンド
1.　新規コマンドを作成します。
```
php artisan make:command RunDuskFromCommand
```
app/CommandsにRunDuskFromCommand.phpが作成されます。

２.　コマンドの編集
アプリケーションの他の部分から app(Browser::class) という記述でブラウザインスタンスを取得できるようにしました。

以下のように編集します。
app/Commands/RunDuskFromCommand
```php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Laravel\Dusk\Browser;
use App\Models\User;

class RunDuskFromCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:run-rusk-from-command';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'コマンドよりDuskを実行する';

    /**
     * Execute the console command.
     */
    public function handle()
    {

        $email = 'testuser@example.com';
        $password = 'password';

        $browser = app(Browser::class);
        // Laravel Duskの機能を利用
        $browser->visit('http://localhost:8000/login')
            ->screenshot(storage_path('screenshots/login'))
            ->waitFor('button[type="submit"]', 5)
            ->type('email', $email)
            ->type('password', $password)
            ->click('button[type="submit"]')
            ->assertPathIs('/dashboard');
        // 最後にブラウザを閉じる
        $browser->quit();
    }
}
```

3.　コマンド確認
php artisan listで以下コマンドを確認できます。
```
 app
  app:run-rusk-from-command  コマンドよりDuskを実行する
```

4. その他
スクリーンショットをとる場合、スクリーンショットフォルダを作成


## ブラウザインスタンス取得準備
直接コマンドにduskの機能を記載するためには、Laravel\Dusk\Browser インスタンスをDIコンテナに登録し、そのインスタンスをコマンド内で呼び出す必要がありました。以下に呼び出す手順を記載します。

１.新規サービスプロバイダを作成します。
```
php artisan make:provider BrowserServiceProvider
```
app\Providersに、BrowserServiceProvider.phpが作成されます。

2.1 ブラウザインスタンス作成
次に、作成したサービスプロバイダの registerメソッド内でブラウザインスタンスを登録します。このとき、「Laravel\Dusk\Browser クラスのインスタンスを作成するためには、WebDriverサーバ（ここではChromeDriver）への接続を管理する Facebook\WebDriver\Remote\RemoteWebDriverクラスのインスタンスが必要となります。

```php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Browser;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;

class BrowserServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     */
    public function register(): void
    {
        $this->app->singleton(Browser::class, function ($app) {
            $capabilities = DesiredCapabilities::chrome();
            $driver = RemoteWebDriver::create('http://localhost:9515/', $capabilities);
            return new Browser($driver);
        });
    }

    /**
     * Bootstrap services.
     */
    public function boot(): void
    {
        //
    }
}

```

ChromeDriverがデフォルトのポート9515で実行されている場合、そのURLは http://localhost:9515 になります。
RemoteWebDriver::create のURLはWebDriverサーバー（ChromeDriverなど）のURLとなります。


3.config/app.phpにプロパイダーを追加します。
config/app.php ファイルの providers 配列に作成したサービスプロバイダを追加します。

```
'providers' => [
    // ...
    App\Providers\BrowserServiceProvider::class,
    // ...
],
```

(4). ChromeDriver対応
ChromeDriverドライバーのインストール
ChromeDriverとChromeのバージョンは互いに一致させること

※<b>なぜ4が必要か</b>
Laravel Duskは以下の操作を自動的に行います：

対応するバージョンのChromeDriverをダウンロード（php artisan dusk:chrome-driverを事前に実行している場合）
ChromeDriverサーバーを起動
テストスクリプトの実行
テスト終了後にChromeDriverサーバーを終了

このプロセスにより、Duskを使用したtests/Browserに記載するテストコードはChromeDriverの存在やその操作について意識することなく、ブラウザテストを行うことができます。

ただし、今回のように自分でコマンドを作成してDuskの機能を直接使用する場合、この自動化されたプロセスが提供されず、自身でChromeDriverのセットアップや管理を行う必要がありました。


### ChromeDriverのインストール
ChromeDriverは、Chromeを自動操作するためのツールです。以下にChromeDriverのインストール手順を記載します。

1.　使用しているChromeのバージョンを確認します。(Chromeを開き、右上の三点リーダーメニューから「ヘルプ」->「Chromeについて」を選択することで確認できます。)

2.　確認したバージョンに対応するChromeDriverをChromeDriverの公式ダウンロードページからダウンロードします。(ダウンロードしたファイルを解凍するとchromedriverという実行ファイルが得られます。)
macの場合は以下コマンドでインストールできます。
```
brew install --cask chromedriver
```

3.　chromedriver実行ファイルを、システムのPATHの通ったディレクトリに移動します。一般的には/usr/local/binなどが利用されます。以下に具体的なコマンドを示します（この操作は管理者権限が必要な場合があります）。
```
mv chromedriver /usr/local/bin
```
macでは自動で/usr/local/binにインストールされました。

4.　ChromeDriverとChromeのバージョン一致
WebDriverのプロトコル（ChromeDriverを含む）は、それぞれのブラウザのバージョンに関連しており、Chromeのバージョンがアップデートされるたびに、対応するChromeDriverもアップデートする必要があります。

## 課題
・ChromeDriverとChromeのバージョンは互いに一致させるのに色々手間取った。Laravel Duskのdusk:chrome-driverコマンドを参考にするか、あるいはWebDriverバージョン管理ツールなどを検討する
・LaravelのDatabaseMigrationsトレイトを使用して、テストデータの準備と後処理を自動化すること