|
| 1 | +const EventEmitter = require('events'); |
| 2 | + |
| 3 | +module.exports = class TapeAdapter extends EventEmitter { |
| 4 | + /** |
| 5 | + * Unlike other adapters, this adapter ends up disabling the default |
| 6 | + * reporter of its framework (in this case, Tape's TAP reporter to |
| 7 | + * console.log). This is the default Tape behaviour, and we choose not to |
| 8 | + * change that. If you wish to enable Tape's default reporter also, and |
| 9 | + * e.g. use CRI only for additional reports to network or artefact files, |
| 10 | + * then run `tape.createStream().pipe(process.stdout);` to have both. |
| 11 | + * |
| 12 | + * @param {Tape} tape |
| 13 | + */ |
| 14 | + constructor (tape) { |
| 15 | + super(); |
| 16 | + |
| 17 | + // TODO: Unable to observe 'prerun' event for some reason. |
| 18 | + let started = false; |
| 19 | + let runStartTime; |
| 20 | + let runStatus = 'passed'; |
| 21 | + const runCounts = { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 }; |
| 22 | + const startOnce = () => { |
| 23 | + if (!started) { |
| 24 | + started = true; |
| 25 | + this.emit('runStart', { name: null, counts: { total: null } }); |
| 26 | + runStartTime = new Date().getTime(); |
| 27 | + } |
| 28 | + }; |
| 29 | + |
| 30 | + let fakeTestId = -1; |
| 31 | + const activeTests = {}; |
| 32 | + const handleRow = (row) => { |
| 33 | + let test; |
| 34 | + switch (row.type) { |
| 35 | + // {type: test, name: '…', id: 0, skip: false, todo: false} |
| 36 | + // {type: test, name: '…', id: 1, skip: false, todo: false, parent: 0} |
| 37 | + case 'test': |
| 38 | + test = activeTests[row.id] = { |
| 39 | + startTime: new Date().getTime(), |
| 40 | + name: row.name, |
| 41 | + parentName: row.parent ? activeTests[row.parent].name : null, |
| 42 | + fullName: row.parent ? [...activeTests[row.parent].fullName, row.name] : [row.name], |
| 43 | + status: row.skip ? 'skipped' : (row.todo ? 'todo' : 'passed'), |
| 44 | + assertions: [], |
| 45 | + errors: [], |
| 46 | + parent: row.parent ? activeTests[row.parent] : null |
| 47 | + }; |
| 48 | + this.emit('testStart', { |
| 49 | + name: test.name, |
| 50 | + parentName: test.parentName, |
| 51 | + fullName: test.fullName |
| 52 | + }); |
| 53 | + break; |
| 54 | + |
| 55 | + // {type: 'end', test: 0} |
| 56 | + case 'end': |
| 57 | + test = activeTests[row.test]; |
| 58 | + delete activeTests[row.test]; |
| 59 | + this.emit('testEnd', { |
| 60 | + name: test.name, |
| 61 | + parentName: test.parentName, |
| 62 | + fullName: test.fullName, |
| 63 | + status: test.status, |
| 64 | + assertions: test.assertions, |
| 65 | + errors: test.errors, |
| 66 | + runtime: test.status === 'skipped' ? null : new Date().getTime() - test.startTime |
| 67 | + }); |
| 68 | + runCounts.total++; |
| 69 | + runCounts[test.status]++; |
| 70 | + if (test.status === 'failed') { |
| 71 | + if (test.parent) { |
| 72 | + test.parent.status = test.status; |
| 73 | + } |
| 74 | + runStatus = 'failed'; |
| 75 | + } |
| 76 | + break; |
| 77 | + |
| 78 | + // {type: 'assert', test: 0, ok: true, skip: false, name: '…', actual: '…', expected: '…', error: Error} |
| 79 | + case 'assert': |
| 80 | + if (row.skip) { |
| 81 | + // Replay as skipped test, ref https://github.com/substack/tape/issues/545 |
| 82 | + const testId = fakeTestId--; |
| 83 | + handleRow({ |
| 84 | + type: 'test', |
| 85 | + name: row.name, |
| 86 | + id: testId, |
| 87 | + skip: row.skip, |
| 88 | + todo: row.todo, |
| 89 | + parent: row.test |
| 90 | + }); |
| 91 | + handleRow({ type: 'end', test: testId }); |
| 92 | + return; |
| 93 | + } |
| 94 | + |
| 95 | + activeTests[row.test].assertions.push({ |
| 96 | + passed: row.ok, |
| 97 | + message: row.name |
| 98 | + }); |
| 99 | + if (!row.ok) { |
| 100 | + if (!row.todo) { |
| 101 | + activeTests[row.test].status = 'failed'; |
| 102 | + } |
| 103 | + activeTests[row.test].errors.push({ |
| 104 | + passed: row.ok, |
| 105 | + message: row.name, |
| 106 | + // actual and expected are optional, e.g. not set for t.fail() |
| 107 | + actual: row.actual, |
| 108 | + expected: row.expected, |
| 109 | + stack: (row.error && row.error.stack) || null |
| 110 | + }); |
| 111 | + } |
| 112 | + break; |
| 113 | + } |
| 114 | + }; |
| 115 | + |
| 116 | + const stream = tape.createStream({ objectMode: true }); |
| 117 | + stream.on('data', (row) => { |
| 118 | + startOnce(); |
| 119 | + handleRow(row); |
| 120 | + }); |
| 121 | + stream.on('end', (row) => { |
| 122 | + startOnce(); |
| 123 | + if (row) { |
| 124 | + handleRow(row); |
| 125 | + } |
| 126 | + this.emit('runEnd', { |
| 127 | + name: null, |
| 128 | + status: runStatus, |
| 129 | + counts: runCounts, |
| 130 | + runtime: new Date().getTime() - runStartTime |
| 131 | + }); |
| 132 | + }); |
| 133 | + } |
| 134 | +}; |
0 commit comments