TypeScript + Create React App + Playwrightでカバレッジレポートを出してみた。
サンプルアプリケーション
https://github.com/takaneko/fata-cra-playwright-coverage
テストを実行すると、jestで--collect-coverage
オプションを付けた時のようにサマリーが出力される。
$ npm run test:e2e:coverage
Running 2 tests using 1 worker
✓ App.test.ts:39:1 › Show initial page. (3s)
✓ App.test.ts:51:1 › Input message and enter, push messages to table. (770ms)
2 passed (8s)
---------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
---------------------|---------|----------|---------|---------|-------------------
All files | 86.48 | 81.81 | 87.5 | 86.48 |
src | 72.22 | 60 | 75 | 72.22 |
App.tsx | 71.05 | 100 | 66.66 | 71.05 | 28-38
index.tsx | 100 | 100 | 100 | 100 |
reportWebVitals.ts | 40 | 33.33 | 100 | 40 | 4-12
src/components | 100 | 100 | 100 | 100 |
MessageForm.tsx | 100 | 100 | 100 | 100 |
Messages.tsx | 100 | 100 | 100 | 100 |
---------------------|---------|----------|---------|---------|-------------------
HTMLレポートも一緒に出力している。
Playwright
E2Eテストフレームワークで、複数ブラウザでテスト出来る。
PWDEBUG=true
を指定した時のデバッグモードがめちゃくちゃわかりやすい。
Playwright Coverage
今はChromiumターゲット時のみカバレッジが取得できる。 ドキュメントの実装例をもとにサンプルアプリケーションでは、
globalSetup
でカバレッジディレクトリを掃除するtest.beforeEach
でpage.coverage.startJSCoverage
を実行する(headlessブラウザ上でカバレッジ計測する)test.afterEach
でpage.coverage.stopJSCoverage
を実行してカバレッジJSONを書き出すglobalTeardown
でテストケースごとのカバレッジJSONを.nyc_output/out.json
にマージする- npm script(
test:e2e:coverage
)でE2Eテスト後にレポート出力するコマンドを実行する
という流れを実装した。(割と自前で実装しないといけないことが多い)
つまづいた点
v8ToIstanbulでv8のカバレッジをIstanbul形式に変換するとき、SourceMapの解決に失敗した。
発生したエラー
1) App.test.ts:43:1 › Show initial page. =========================================================
Error: An error occurred while trying to read the map file at /Users/takaneko/Practices/cra-playwright-coverage/bundle.js.map
Error: ENOENT: no such file or directory, open '/Users/takaneko/Practices/cra-playwright-coverage/bundle.js.map'
28 | // sourceMap: { sourcemap: sourcemapConverter.sourcemap },
29 | });
> 30 | await converter.load();
| ^
31 | converter.applyCoverage(entry.functions);
32 |
33 | // Save coverage JSON for each test.
at readFromFileMap (/Users/takaneko/Practices/cra-playwright-coverage/node_modules/convert-source-map/index.js:40:11)
at new Converter (/Users/takaneko/Practices/cra-playwright-coverage/node_modules/convert-source-map/index.js:47:32)
at Object.exports.fromMapFileComment (/Users/takaneko/Practices/cra-playwright-coverage/node_modules/convert-source-map/index.js:110:10)
at Object.exports.fromMapFileSource (/Users/takaneko/Practices/cra-playwright-coverage/node_modules/convert-source-map/index.js:122:22)
at V8ToIstanbul.load (/Users/takaneko/Practices/cra-playwright-coverage/node_modules/v8-to-istanbul/lib/v8-to-istanbul.js:52:66)
at /Users/takaneko/Practices/cra-playwright-coverage/tests/App.test.ts:30:23
v8-to-istanbulが利用しているconvert-source-mapでは、webpack-dev-serverが生成するbundle.jsのsourceMappingURL
を解決できないのでエラーになっている。
bundle.jsのsourceMappingURL
//# sourceMappingURL=bundle.js.map
このSourceMapはwebpack-dev-serverのメモリ上にしかないので、GETリクエストで取得する必要がある。 v8-to-istanbulには SourceMapを指定するオプションがあるので、webpack-dev-serverから取得したSourceMapを指定することで解決した。
test.afterEachの実装
// Get source map file from webpack-dev-server
const sourceMapResponse = await request.get(`${entry.url}.map`);
const sourcemapConverter = fromJSON(await sourceMapResponse.text());
// Convert from V8 coverage to istanbul format
const converter = v8ToIstanbul("", 0, {
source: entry.source,
originalSource: "",
// set sourceMap
sourceMap: { sourcemap: sourcemapConverter.sourcemap },
});
感想
フロントアプリのE2EテストはRailsや素のjestよりカバレッジ取得が面倒臭い。 Istanbul(JSのカバレッジツール)について少し詳しくなった。
次回
Playwrightでheadlessブラウザ上のIndexedDBを扱うヘルパーを作りたい。