CypressをWSL2上で動かすために必要な作業
Cypressはフロントエンドのend-to-endテストフレームワークの代表格です。ローカル開発環境で npx cypress open
コマンドを実行すれば、Cypressはブラウザを自動で立ち上げて、高速で次々にテストを完了していきます。その軽快な動作は非常に心地よく、フロントエンドのテスト駆動開発を推進するのにピッタリなツールです。CIサーバー上ではブラウザを立ち上げることなくheadlessモードで動作します。
そんなCypressですが、困ったことにWindows Subsystem for Linux2(以下WSL2)上では、単純にインストール後 npx cypress open
コマンドを打ち込むだけでは動いてくれません。ターミナルは何もせず待ち続けるだけで、テストを実行してくれないのです。
(この後何も起こりません、ブラウザが自動で立ち上がってほしいのですが)
この記事では、WSL2上でCypressを動かす方法を、その技術的背景とともにお伝えします。注意点として、ここで紹介する解決法はWSL2向けであって、WSL1ではまた別の問題が発生するようです。
WSL2上でCypressを動かせなくて苦労した人は私以外にもいるようで、Twitter検索でも何人か見かけました。WSL2で快適にフロントエンドのテスト駆動開発をしたい人に向けて、この記事が助けになれば幸いです。
良いニュース - いずれこの記事の手法は必要なくなる
手順を説明する前に、良いニュースを紹介します。将来はこの記事で紹介する面倒な手順は必要なくなります。こちらのMicrosoft公式ブログにあるように、Lniux GUIアプリケーションがネイティブにWSL上でサポートされる予定だからです。
それまでは、CypressをWSL2上で動したいときに、本記事の手法を試してください。
セットアップ手順
まずは忙しい人のために手順だけを列挙します。
- Ubuntu/Debian上でCypressに必要な依存パッケージを
apt-get insall
- Cypressを
npm install
- SourceForgeからVcXsrvをインストール
- xLaunchアプリケーションを立ち上げる
- Disable Access controlをチェックすることに注意
- Windows Defender ファイアウォールの警告でプライベートアクセスのみを許可
- Windows Defender ファイアウォールで「受信の規則」のうち以下のルールを変更
- プロファイル: パブリック
- プロトコル: TCP
- スコープ: プライベートIPアドレスのレンジ(例: 172.16.0.0/12)からのリモートアクセスを許可
- WSL2側でDISPLAY環境変数を設定
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0
- xeyesコマンドでWSL2側からWindows側へのVcXsrvを経由した疎通確認
- DBUSサーバーの立ち上げ
npx cypress open
コマンドでブラウザを自動で立ち上げてテスト開始
セットアップ手順の詳細と背景
それではここから、上記で列挙した手順の詳細について説明します。
Cypressのinstallとverify
セットアップの最初の手順として、Cypress公式の手順に従ってインストールを終える必要があります。下記の公式インストール手順の、Ubuntu/Debianの部分に書いてあるaptコマンドを実行の後、npm install cypress --save-dev
してください。
https://docs.cypress.io/guides/getting-started/installing-cypress
それが終わったら、npx cypress verify
を行ってください。以下のように表示されたら成功です。
npx cypress verify
✔ Verified Cypress! /home/richardimaoka/.cache/Cypress/6.8.0/Cypress
一部のWSL2環境ではこの時点でcypress verifyがうまくいかないことがあるようです。以下のコマンドを実行して原因を探ってみてください。
DEBUG=cypress:* npx cypress verify
DEBUG=cypress:*
の部分が何をしているのかというと、DEBUGレベルのログを出力する設定で、その説明はCypress公式ドキュメント - Print DEBUG logsにあります。
もしDEBUGログから原因がわかって npx cypress verify
が成功すればよいのですが、結局原因がわからず npx cypress verify
が失敗し続ける場合、残念ながらこの先の手順には進めません。
すでに多くの人が「cypress verifyが止まったまま何もしない」という報告を以下のGitHubイシューに寄せていて、原因がわからないケースがたくさんあります。
DEBUGログのメッセージやご自身の環境の詳細を、イシューのコメントとして残すとよいかもしれませんが、現状は決定的な解決策はないようです。困ったことにこのイシュー、タイトルがWSL2なのにWSL1の問題を報告する人や、CI環境であるJenkinsでの問題を報告する人もいて、なかなか混乱の様相を呈しています。
npx cypress verify
が終わった後の手順は、下記の記事の手順がよくまとまっています。それでも、一部必要な手順が書かれていないのと、「なぜその手順が必要なのか?」という背景の説明が不足しているので、本記事ではそれらを補足します。
- Nicky blogs - Using Graphical User Interfaces like Cypress' in WSL2
VcXsrvのインストール
WSL2から直接WindowsのWebブラウザを立ち上げることはできません。そこで必要になるのがXサーバーです。Unix/Linux系のOSではGUI環境を実現するために広く使われるXサーバーですが、WSL環境ではWindows側でXサーバーを起動します。
Windowsで利用できるXサーバーにはVcXsrv, Cygwin/X, Xmingといくつか種類がありますが、その中で最近よく使われるのはVcXsrvなようです。
先ほど「手順がよくまとまっている」と紹介した記事「Nicky blogs - Using Graphical User Interfaces like Cypress' in WSL2」にもVcXsrvを使った例が紹介されていたので、本記事でもVcXsrvを利用します。
VcXsrvのインストール元は安全なのか?
ちなみに、SourceForgeがVcXsrvのダウンロード元と聞いて、次のニュースを覚えている人は不安を覚えるかもしれませんが、少なくとも当時の状況からは大きく改善されたようです。
こちらのニュースにSourceForgeの内部体制の改善が紹介されています。
Microsoft公式ブログからもSourceForgeからのVcXsrvのインストール手順が載っているので、少なくともMicrosoftがすすめるXサーバーの1つとは言えそうです。
VcXsrvの立ち上げにxLaunchアプリケーションを使う
VcXsrvがインストール出来たら、WindowsスタートメニューからXLaunchを立ち上げます。アプリケーション名がVcXsrvではなくXLaunchなことに注意してください。
立ち上げたら次のような画面が続くので、デフォルトの選択肢のまま次へ進み、
こちらの画面もデフォルトの選択肢のまま次へ進みましょう
この画面が表示されたら、デフォルトでチェックが外れているDisable access controlのチェックをつけてください。
Disable access controlのチェックが外れたままだと、後のステップで行うWSL2とVcXsrvの疎通確認でxeyesコマンドを走らせたときに以下のようなエラーが表示されます。かならずDisable access controlをチェックしてください。
> xeyes
Authorization required, but no authorization protocol specified
Error: Can't open display: 172.29.128.1:0.0
xLaunch立ち上げ手順最後の画面はこちらです。そのまま完了を押しましょう。
xLaunchを立ち上げたらすぐに、以下のようなWindows Defenderファイアウォールの警告が表れるので、「プライベート ネットワーク (ホームネットワークや社内ネットワークなど)」のみを許可してください。
このままではCypressに必要なWSL側との通信が出来なくなってしまうのですが、この後「Windows Defender FireWallの設定」というステップでより細かなWindows Defender ファイアウォールの制御を行います。
Ctrl+Shift+EscキーでWindowsタスクマネージャを立ち上げ、VcXsrvのプロセスを確認出来たらこのステップは完了です。
xeyesによるVcXsrv疎通確認「..この時点ではまだ繋がりません
このステップでは、VcXsrvとWSL2側での通信がうまくいっているか疎通確認を行いましょう。
疎通確認はxeyesコマンドを使います。以下のように非常に単純な目玉のGUIアプリケーションを立ち上げるコマンドで、Xサーバーの動作確認として利用できます。… が、この時点ではまだxeyesは立ち上がらないはずです。
xeyesで疎通確認するためには、次に紹介するDISPLAY環境変数の設定と、Windows Defenderファイアウォールの設定が必要になります。
DISPLAY環境変数の設定
VcXsrvとWSL2で通信を行うために、DISPLAY環境変数を変える必要があります。DISPLAY環境変数によってLinuxはどのXサーバーを利用するか指定するからです。
以下のコマンドでDISPLAY環境変数を変えましょう。
# set DISPLAY variable to the IP automatically assigned to WSL2
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0
echo $DISPLAY
172.17.224.1:0.0
ここで使った/etc/resolv.confはWSLが自動で更新するファイルで、自身のプライベートIPアドレスが保存されています。
> cat /etc/resolv.conf
# This file was automatically generated by WSL.
# To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 172.17.224.1
Windows Defender FireWallの設定
ここまでWindows側とWSL2側での疎通のために、VcXsrvの立ち上げとそのアクセスコントロール、そしてWSL2側でのDISPLAY環境変数の設定を行いました。 それらに加えてWindows Defender ファアウォールの設定を行うことで、ようやく疎通確認ができます。
先ほど「VcXsrvの立ち上げにxLaunchアプリケーションを使う」の最後の方で、Windows Defenderファイアウォールのパブリック・ネットワークからのアクセスを全て禁止したので、これから先の設定でその一部を許可します。
Microsoftの公式ドキュメント、Windows Defender ファイアウォールを介してアプリを許可する際のリスクにあるように、Windows Defender ファイアウォールはリスクが伴うので、正しい設定によって最小限のリスクにとどめましょう。
Windows Defender ファイアウォールを介してアプリを許可するには、2 つの方法があります。 どちらも危険です。
- 許可されているアプリの一覧にアプリを追加する (リスクは低くなります)。
- ポートを開く (リスクは高くなります)。
本記事では上記2択のうち、よりリスクの少ない「許可されているアプリの一覧にアプリを追加する」に従います。それではWindowsのスタートメニューからWindows Defender ファイアウォールを立ち上げます。
下記の画面が現れるので詳細設定をクリックし、
Vc-Xsrv windows serverと表示されているルールをみつけてください。4つのルールがあるはずです。
名前 | プロファイル | プロトコル | 備考 |
---|---|---|---|
VcXsrv windows xservr | パブリック | TCP | ここを変更する |
VcXsrv windows xservr | パブリック | UDP | |
VcXsrv windows xservr | プライベート | TCP | |
VcXsrv windows xservr | プライベート | UDP |
プロファイル = パブリック、プロトコル = TCPとなっているVcXsrvのルールを開いたら、「スコープ」タブから プライベートIPアドレスのレンジ(例: 172.16.0.0/12)からのリモートアクセスを許可してください
この設定はこちらのGitHubイシュー上のコメントにならいました。 これにより、プライベート・IPレンジからのアクセスのみVcXsrvへのアクセスが許可されました。ご自身のネットワークに設定を調べ、より狭い範囲のプライベートIPアドレスのレンジを設定すれば、さらに安全になるでしょう。
xeyesによるVcXsrv疎通確認ふたたび
これでようやくxeyesによる疎通確認ができるはずです!
Cypressの立ち上げ
xeyesでの疎通確認が成功していれば、 npx cypress open
コマンドでブラウザが立ち上がって表示されるはずです。下記の公式ドキュメントにそって簡単なテストを書いてみましょう。
DBUSの立ち上げ
先の手順で npx cypress open
を実行した時、以下のようなエラーが出るかもしれません。
[9462:0411/161540.623026:ERROR:bus.cc(393)]
Failed to connect to the bus: Failed to connect to
socket /var/run/dbus/system_bus_socket: No such file or directory
その場合、dbusというプロセスが立ち上がっていないことが原因です。以下のコマンドでまずはdbusをインストールしましょう。
sudo apt-get install dbus
次に次のファイルが存在するかどうか調べてください。ちなみに/etc/init.dを使っているのは、WSL2はデフォルトではsystemdが使えないからです。
ls -la /etc/init.d/dbus
存在しなかったら、下記を展開して /etc/init.d/dbus に手動でペーストしてください。
/etc/init.d.dbusの中身を展開する
#!/bin/sh
### BEGIN INIT INFO
# Provides: dbus
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: D-Bus systemwide message bus
# Description: D-Bus is a simple interprocess messaging system, used
# for sending messages between applications.
### END INIT INFO
# -*- coding: utf-8 -*-
# Debian init.d script for D-BUS
# Copyright © 2003 Colin Walters <walters@debian.org>
# Copyright © 2005 Sjoerd Simons <sjoerd@debian.org>
set -e
DAEMON=/usr/bin/dbus-daemon
UUIDGEN=/usr/bin/dbus-uuidgen
UUIDGEN_OPTS=--ensure
NAME=dbus
DAEMONUSER=messagebus
PIDDIR=/var/run/dbus
PIDFILE=$PIDDIR/pid
DESC="system message bus"
test -x $DAEMON || exit 0
. /lib/lsb/init-functions
# Source defaults file; edit that file to configure this script.
PARAMS=""
if [ -e /etc/default/dbus ]; then
. /etc/default/dbus
fi
create_machineid() {
# Create machine-id file
if [ -x $UUIDGEN ]; then
$UUIDGEN $UUIDGEN_OPTS
fi
}
start_it_up()
{
if [ ! -d $PIDDIR ]; then
mkdir -p $PIDDIR
chown $DAEMONUSER $PIDDIR
chgrp $DAEMONUSER $PIDDIR
fi
if ! mountpoint -q /proc/ ; then
log_failure_msg "Can't start $DESC - /proc is not mounted"
return
fi
if [ -e $PIDFILE ]; then
if $0 status > /dev/null ; then
log_success_msg "$DESC already started; not starting."
return
else
log_success_msg "Removing stale PID file $PIDFILE."
rm -f $PIDFILE
fi
fi
create_machineid
# Force libnss-systemd to avoid trying to communicate via D-Bus, which
# is never going to work well from within dbus-daemon. systemd
# special-cases this internally, but we might need to do the same when
# booting with sysvinit if libnss-systemd is still installed.
# (Workaround for #940971)
export SYSTEMD_NSS_BYPASS_BUS=1
log_daemon_msg "Starting $DESC" "$NAME"
start-stop-daemon --start --quiet --pidfile $PIDFILE \
--exec $DAEMON -- --system $PARAMS
log_end_msg $?
}
shut_it_down()
{
log_daemon_msg "Stopping $DESC" "$NAME"
start-stop-daemon --stop --retry 5 --quiet --oknodo --pidfile $PIDFILE \
--user $DAEMONUSER
# We no longer include these arguments so that start-stop-daemon
# can do its job even given that we may have been upgraded.
# We rely on the pidfile being sanely managed
# --exec $DAEMON -- --system $PARAMS
log_end_msg $?
rm -f $PIDFILE
}
reload_it()
{
create_machineid
log_action_begin_msg "Reloading $DESC config"
dbus-send --print-reply --system --type=method_call \
--dest=org.freedesktop.DBus \
/ org.freedesktop.DBus.ReloadConfig > /dev/null
# hopefully this is enough time for dbus to reload it's config file.
log_action_end_msg $?
}
case "$1" in
start)
start_it_up
;;
stop)
shut_it_down
;;
reload|force-reload)
reload_it
;;
restart)
shut_it_down
start_it_up
;;
status)
status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $?
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|reload|restart|force-reload|status}" >&2
exit 2
;;
esac
以下のコマンドでdbusを立ち上げれば、先ほどのエラーは消えてなくなるはずです。
sudo /etc/init.d/dbus start
それでも残るエラー?
私の環境ではここまでの手順でcypressは動くようになったのですが、どうしても以下のエラーは消すことができませんでした。エラーが残るのは気持ち悪いのですが、動作はしているので今は仕方ないものとして無視しています。
[11210:0411/163510.366298:ERROR:bus.cc(393)]
Failed to connect to the bus: Could not parse server address:
Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
[11210:0411/163510.366996:ERROR:bus.cc(393)]
Failed to connect to the bus: Could not parse server address:
Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
こちらのエラーが気になる、もしくは別のエラーが出ていてどうしても解決したい、という人はDEBUGログを表示して調査してみるとわかるかもしれません。
DEBUG=cypress:* npx cypress open
まとめ
かなり手順の説明が長くなってしまった上、最後に一つエラーメッセージを解決できなかったのは残念ですが、最初は動かすことが出来なかったWSL2上でのCypressを動作させることはできました。 この記事がWSL2上での快適なCypressライフ、フロントエンドテスト駆動開発の助けになれば幸いです。