TypeScript
React
Cypress

CypressをTypeScript × Reactで動かす

最近、フロントエンド界隈で人気上昇中のE2EテストフレームワークのCypressをTypeScript × React環境に導入したので、備忘録も兼ねて、TypeScript × ReactへのCypress導入方法をシェアしたいと思います。

Cypressについて

「Cypressって一体何?」という方は、公式サイトを含めた、下記の記事などをご参照ください。

本記事では、Cypress自体についてや、他のE2EテストフレームワークのTestCafeと比較した点などについてはあまり言及しません。

が、一応、トレンドに関しては、npm trendsで調べてみましたので、現時点(2018年1月26日)での結果をおまけ的に付けておきます。

スクリーンショット 2018-01-26 22.00.21.png

実行環境

  • Mac OS Sierra: v10.12.6
  • node: v8.2.0
  • npm: v5.3.0

TypeScript × React環境を作成

node.jsがインストールされていることが前提です。
create-react-appを用いて、Reactアプリを構築します。

$ cd ~/
# TypeScriptによるReactアプリをmy-appディレクトリに作成
$ create-react-app my-app --scripts-version=react-scripts-ts
# アプリの起動を確認
$ cd my-app
$ npm start

アプリの起動まで確認できたら、Ctrl + Cでアプリを一旦、終了してください。

Cypressをインストール

公式ドキュメントに則り、上記で作成したプロジェクトにCypressをインストールします。

$ npm install cypress --save-dev
# Cypressの起動を確認
$ npx cypress open

Cypressの画面が立ち上がりますので、テストの対象としてexample_spec.jsをクリックして、テストを開始します。

テスト終了後、Ctrl + Cで、Cypressを一旦、終了します。

アプリの起動を再確認

続いて、npm startでアプリを再起動しようとすると、次のようなコンパイルエラーが発生するかと思います。

Failed to compile.

/node_modules/@types/jest/index.d.ts
(18,13): Duplicate identifier 'beforeEach'.

これはインストールした型定義ファイルのところで問題が発生しているので、型定義ファイルのチェックを無視する設定を行います。

(もしくは、次のようなエラーが発生した場合には、npm installで、外部パッケージをインストールし直してからnpm startを実行してください)

sh: react-scripts-ts: command not found
npm ERR! file sh
npm ERR! code ELIFECYCLE
npm ERR! errno ENOENT
npm ERR! syscall spawn
npm ERR! my-app@0.1.0 start: `react-scripts-ts start`
npm ERR! spawn ENOENT
npm ERR! 
npm ERR! Failed at the my-app@0.1.0 start script.

型定義ファイルのチェックを無視する設定に変更

コンパイルエラーを確認できたら、tsconfig.jsonをエディタで開き、"skipLibCheck": true,compilerOptionsに追加して保存します。

tsconfig.json
  "compilerOptions": {
    ...
    "skipLibCheck": true,
    ...

"skipLibCheck": trueの設定は、全ての型定義ファイル(.d.ts)の整合性チェックを行わないようにするオプションなので、次のリンク先で、Cypressの中の人がコメントされている通り、「理想的ではない」とされています。が、「結局のところオススメの解決策だ。」みたいに言われています。

Confirmed, it was skipLibCheck I added to get around Jest "Set" problem. Ok, so the recommended solution is ultimately
{
 "compilerOptions": {
   "skipLibCheck": true
 }
}
in the root tsconfig.json file. Not ideal, but it is hard or impossible to make globals work without clashing.

参考:Possible type definition conflict on @types/chai

取り敢えず、これでもう一度、npm startでアプリを起動し、コンパイルエラーが発生しないことを確認します。

コンパイルエラーが発生しないことを確認できたら、Ctrl + Cでアプリを終了してください。

テストコードをJSからTSに変更

ここまでで、/cypress/integration以下に存在するテストコードが、
JSファイルのままで良ければこれで終わりですが、TypeScript環境なので、やはりTSファイルとしてテストコードを書いていきたいところです。

試しに、サンプルのテストファイル(/cypress/integration/example_spec.js)の拡張子を.jsから.tsに変更し、ついでに次の内容でサンプルのテストコードを上書きし、簡略化して保存します。

/cypress/integration/example_spec.ts
describe('Kitchen Sink', function () {
  it('.should() - assert that <title> is correct', function () {
    cy.visit('https://example.cypress.io');
    cy.title().should('include', 'Kitchen Sink');
  });
});

しかし、この状態でアプリを再起動しようとすると、次のようなエラーが発生するかと思います。

Failed to compile.

/cypress/integration/example_spec.ts
(17,5): Cannot find name 'cy'.

これはTypeScriptのコンパイル時において、example_spec.tsに書かれているテストコードで、インストールされた型定義ファイル(/node_modules/cypress/types/index.d.ts)から、CypressのAPIにアクセスするための定数cyを読み込んで使おうとしますが、型定義ファイルが参照できず、cyが見つからないことでエラーになってしまっています。

こちらのエラーを回避するためには、型定義ファイルもコンパイルの対象に含めるようにします。

TypeScriptのコンパイル設定を変更

プロジェクトルートに存在するtsconfig.jsonをエディタで開き、compilerOptionsに並ぶ形で、include(コンパイルに含めるもの)とexclude(コンパイルに含めないもの)の設定を追加変更します。

create-react-appでアプリを作成した場合は、デフォルトでexcludenode_modulesが指定されてしまっているので、コメントアウトまたは削除で、指定から外しておきます。

tsconfig.json
{
  "compilerOptions": {
    ...
  },
  "include": [
    "node_modules/cypress/types/*.ts",
    "cypress/*/*.ts"
  ],
  "exclude": [ 
    // "node_modules",
    ...
  ]
}

参考:Typescript with Webpack

設定を保存し、アプリを再起動します。

アプリの再起動が確認できたら、アプリを終了し、npx cypress openで、Cypressの起動〜テスト実行も確認します。

これで無事に、TypeScriptで書いたテストコードでCypressが動くようになるはず、です。

ここまでのコードは、次の場所に置いてあります。

https://github.com/galoi/react-typescript-cypress

git clone後、プロジェクトルートに移動してnpm installでパッケージをインストールすると、アプリの起動〜Cypressのテストが実行可能です。

今後は、このような面倒くさい設定をしなくてもTypeScriptでテストが書けるように、TypeScriptのサポートを厚くしてくれることをCypressに期待しています。