結論
以下のようなプロパティを package.json の scripts に書けば良いです。
(ここまで省略) "scripts": { "cypress:exec": "kill $(lsof -i :3100 | grep node | awk '{print $2}') & next build && next start --port 3100 & wait-on -t 30000 http-get://localhost:3100 && cypress run --browser chrome && kill $(lsof -i :3100 | grep node | awk '{print $2}')", (以下略)
具体的には
具体的には以下のコマンドが必要です。
- サーバを起動するコマンド
- サーバを終了させる(プロセスを kill する)コマンド
- サーバの起動を待つコマンド
- Cypress を実行するコマンド
それぞれのコマンドの詳細を見ます。
1. サーバを起動するコマンド
たとえば以下のようなコマンドになります。
$ next build && next start --port 3100
ポート番号は開発用番号とは異なる方が良いでしょう。開発しながらテストを回すことができるためです。
開発環境でのサーバを起動するのか(上記でいえば $ next dev にするのか)どうかというのは個々のケース次第だと思います。prod用の環境変数を使いたい場合やそうでない場合、またバックエンドとの関係など、いろいろなケースがあると思いますので。
2. サーバを終了させる(プロセスを kill する)コマンド
プロセスを一意に選択できて kill できるならばどのようなコマンドでも良いです。たとえば以下のようになります。
$ kill $(lsof -i :3100 | grep node | awk '{print $2}')
3. サーバの起動を待つコマンド
サーバの起動コマンドを実行してからサーバが起動するまでの間に Cypress が走ってしまうと Not found になってしまいます。そのため、サーバの起動を待つコマンドを間に挟みます。
Cypress の公式では、wait-on を使う方法や start-server-and-test を使う方法が紹介されています。自分は最終コミット日時等もふまえて wait-on を使うことにしました*1。
$ wait-on -t 30000 http-get://localhost:3100
上記の -t
オプションの数字の単位はミリ秒なので注意が必要です。
4. Cypress を実行するコマンド
Cypress を実行するコマンドが必要になります。
$ cypress run --browser chrome
コマンド連結実行時の注意
コマンドを連結して実行する際、以下のコマンドは正常終了を待たずに次のコマンドを実行する必要があります。つまり &
でコマンドをつなげます。
- 最初に実行させる「サーバを終了させるコマンド」
- 「サーバを起動するコマンド」
それぞれについて見ます。
最初に実行させる「サーバを終了させるコマンド」
最初に実行させる「サーバを終了させるコマンド」は、サーバが起動していないときにはエラーになってしまうため、正常終了を待ってしまうと次のコマンドが実行されないことがあります*2。
最初にこのコマンドを実行する理由は「以前のテストでサーバプロセスが残っていた場合にそれを冪等に終わらせるため」です。サーバプロセス残存の有無によってコマンドを使い分けたりするぐらいなら、エラー表示が出たとしても、毎回同じコマンドを叩きたいからです。
丁寧に書けば、サーバの起動有無で分岐もできるかと思います*3。
「サーバを起動するコマンド」
「サーバを起動するコマンド」を &&
でつないでしまうと、サーバを正常終了しないと次のステップに進まないので、&
でコマンドをつなげます*4。
改めて結論
上記を踏まえると、冒頭のような結論になります。
一度正常に動作することが確認できたならば、個々のコマンドを細かく分割して run-s
や run-p
を使うのも良いと思います。
また、バックエンドを別に起動する場合などの応用パターンも、本ケースをベースにすれば難しくないと思います*5。
補足
CI で走らせる場合は微妙に異なってきます。たとえば GitHub Actions ですと、package.json のコマンド内で設定した内容を二重に設定することになる場合もあります*6。
あと、この内容だと --spec path/to/foobar.spec.js
のようなオプションで特定の Spec を実行することもできません。一工夫が必要になりますが、本内容をベースにすればそこまで面倒ではないはずです。
感想
- ここまで書いておいてなんですが、シェルスクリプトで書いたほうが良さそうに思います
- さらになんですが、Cypress は原則 GUI で操作するのがいいと思います
*1:GitHub Actions https://github.com/marketplace/actions/cypress-io でも用いられているようです
*2:ほとんどの場合そうなると思います
*3:シェルスクリプトを書くのがいいかも
*4:crtl + c でサーバを終了させると exit 1 なので正常終了はしないのですが、まあそこはおいといて
*5:煩雑にはなる
*6:ここらへんは作り方次第で、custom-test-command で上書きするなどすれば使い回せたりする https://github.com/marketplace/actions/cypress-io#custom-test-command