bouzuya/eater-b-reporter をつくった。
bouzuya/eater-b-reporter は yosuke-furukawa/eater の reporter だ。
bouzuya/eater-b-reporter の目的は eater の reporter の弱さを補うものだ。これは 2016-06-01 にも書いた。 bouzuya/rfc6570-expand の test に yosuke-furukawa/eater を使用した際に感じた eater の reporter の問題点を解決する。
ぼくが eater の標準の reporter (Reporter) で気になる点は 3 つある。
- Error の表示が流れてしまう。
- Error の表示順序がばらばらだ。
- 成功・失敗の件数が分からない。
Error が流れてしまうため、赤い文字に気づいたら scroll して確認しないといけない。極めて面倒だ。test の件数が増えると致命的になる。ご丁寧にすべての file 内の test 別に成否を問わずに出力するため、相当量が流れる。
Error の表示順序がばらばらというのは、複数の file の Error が入り乱れる状態だ。file が child process だから file 順にならない……という程度ならまだいい。eater の reporter は複数の file の Error が入り乱れる。failure の行とその次の行の stack trace が同じ file の Error かさえ分からない。ひどい。
成功・失敗の件数が分からない。これは TapReporter なら良いのだけど (こちらはこちらで……) 、標準の Reporter だと表示されない。どれくらい成功しているのかがつかめないので、あと何件直すのか分からない。先の Error が流れる問題もあって、どこまで scroll すればいいのか分からない状態になる。それも実行するたびだ。
その解決策が bouzuya/eater-b-reporter だ。
次の例は成功時の出力だ。
npm test > rfc6570-expand@0.1.0 test /home/ubuntu/rfc6570-expand > eater 19 files ✓ success: .tmp/test/index.js ✓ success: .tmp/test/extended2.js ✓ success: .tmp/test/extended1.js ✓ success: .tmp/test/section3-2-1.js ✓ success: .tmp/test/extended3.js ✓ success: .tmp/test/section3-2-6.js ✓ success: .tmp/test/section3-2-5.js ✓ success: .tmp/test/section3-2-8.js ✓ success: .tmp/test/section3-2-7.js ✓ success: .tmp/test/level2.js ✓ success: .tmp/test/level1.js ✓ success: .tmp/test/extended4.js ✓ success: .tmp/test/negative.js ✓ success: .tmp/test/level3.js ✓ success: .tmp/test/section3-2-9.js ✓ success: .tmp/test/section3-2-3.js ✓ success: .tmp/test/level4.js ✓ success: .tmp/test/section3-2-2.js ✓ success: .tmp/test/section3-2-4.js ✓ 19 files 234 tests completed
copy & paste したものなので分かりづらいが ✓
な行は green で表示されている。最初にすべての file 数を表示する。成功した file は名前のみを表示する。最後に file 数と test 数を表示する。成功した file については test()
単位では表示しないため簡素になる。また最後の行で file 数や test 数が表示されるので、きちんと完了したことが分かる。
次の例は失敗時の出力だ。
19 files ✓ success: .tmp/test/extended3.js ✓ success: .tmp/test/extended1.js ✓ success: .tmp/test/extended2.js ✓ success: .tmp/test/extended4.js ✓ success: .tmp/test/index.js ✓ success: .tmp/test/level2.js ✓ success: .tmp/test/level4.js ✓ success: .tmp/test/negative.js ✓ success: .tmp/test/section3-2-1.js ✓ success: .tmp/test/section3-2-3.js ✓ success: .tmp/test/section3-2-4.js ✓ success: .tmp/test/section3-2-5.js ✓ success: .tmp/test/section3-2-6.js ✓ success: .tmp/test/section3-2-7.js ✓ success: .tmp/test/section3-2-8.js ✓ success: .tmp/test/section3-2-9.js ✗ failure: .tmp/test/level1.js AssertionError: {hello} / Hello%20World%21 / Hello%20World%s21 # .tmp/test/level1.js:12 assert(uris.some(u => u === uri), `${ template } / ${ uris } / ${ uri }`) | | | false ["Hello%20World%21"] at Decorator._callFunc (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:110:20) at Decorator.concreteAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:103:17) at decoratedAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorate.js:49:30) at powerAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/index.js:63:32) at runner_1.test.resolve (.tmp/test/level1.js:12:9) at EventEmitter.nextTest (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:22:26) at emitNone (events.js:86:13) at EventEmitter.emit (events.js:185:7) at checkPromise.then (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:26:18) at process._tickCallback (internal/process/next_tick.js:103:7) ✗ failure: .tmp/test/level3.js AssertionError: {x,hello,y} / 1024,Hello%20World%21,768 / 1024,Hello%20World%s21,768 # .tmp/test/level3.js:12 assert(uris.some(u => u === uri), `${ template } / ${ uris } / ${ uri }`) | | | false ["1024,Hello%20World%21,768"] at Decorator._callFunc (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:110:20) at Decorator.concreteAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:103:17) at decoratedAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorate.js:49:30) at powerAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/index.js:63:32) at runner_1.test.resolve (.tmp/test/level3.js:12:9) at EventEmitter.nextTest (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:22:26) at emitNone (events.js:86:13) at EventEmitter.emit (events.js:185:7) at checkPromise.then (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:26:18) at process._tickCallback (internal/process/next_tick.js:103:7) ✗ failure: .tmp/test/section3-2-2.js AssertionError: {hello} / Hello%20World%21 / Hello%20World%s21 # .tmp/test/section3-2-2.js:14 assert(uris.some(u => u === uri), `${ template } / ${ uris } / ${ uri }`) | | | false ["Hello%20World%21"] at Decorator._callFunc (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:110:20) at Decorator.concreteAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:103:17) at decoratedAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorate.js:49:30) at powerAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/index.js:63:32) at runner_1.test.resolve (.tmp/test/section3-2-2.js:14:9) at EventEmitter.nextTest (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:22:26) at emitNone (events.js:86:13) at EventEmitter.emit (events.js:185:7) at checkPromise.then (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:26:18) at process._tickCallback (internal/process/next_tick.js:103:7) AssertionError: {x,hello,y} / 1024,Hello%20World%21,768 / 1024,Hello%20World%s21,768 # .tmp/test/section3-2-2.js:14 assert(uris.some(u => u === uri), `${ template } / ${ uris } / ${ uri }`) | | | false ["1024,Hello%20World%21,768"] at Decorator._callFunc (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:110:20) at Decorator.concreteAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:103:17) at decoratedAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorate.js:49:30) at powerAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/index.js:63:32) at runner_1.test.resolve (.tmp/test/section3-2-2.js:14:9) at EventEmitter.nextTest (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:22:26) at emitNone (events.js:86:13) at EventEmitter.emit (events.js:185:7) at checkPromise.then (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:26:18) at process._tickCallback (internal/process/next_tick.js:103:7) ✗ 3 of 19 files, 4 of 234 tests failed npm ERR! Test failed. See above for more details.
reporter とは関係ないが Error 表示は power-assert-js/power-assert を使っていることもあって詳細だ。
Error は最後に集中して表示する。流さない。Error の表示順序はファイルごとに並んでいる。最後に全体のうちいくつの file, test が失敗したのかを表示している。安心感がある。
さきに挙げた 3 つの問題を解決していることが分かる。
- Error の表示が流れてしまう。
- Error の表示順序がばらばらだ。
- 成功・失敗の件数が分からない。
bouzuya/eater-b-reporter の実装は例によって TypeScript だ。eater は JavaScript なので、型は自分で用意した。
reporter の API に関しては Qiita に記事を書いたので、そちらを参照すると良い。 link は eater の reporter の API - Qiita だ。
つくる過程で Node.js の bug を踏んだのだけど、これについてはまた日を改める。
bouzuya/eater-b-reporter の実例を見たいなら bouzuya/rfc6570-expand で試してみるといい。
$ git clone https://github.com/bouzuya/rfc6570-expand $ cd rfc6570-expand/ $ npm i $ npm test
適当に test が通らないようにすれば失敗例も確認できる。