@tapjs/reporter
Version:
Pretty test output reporters for tap
189 lines • 5.31 kB
JavaScript
// json representation of the test run
import EventEmitter from 'events';
import { Minipass } from 'minipass';
import { Parser } from 'tap-parser';
const trimResult = (o) => {
const entries = Object.entries(o)
.map(([k, v]) => [
k,
!!v && typeof v === 'object' && !Array.isArray(v) ?
trimResult(v)
: v,
])
.filter(([k, v]) => k === 'ok' ||
k === 'id' ||
(v !== false &&
v !== null &&
v !== undefined &&
v !== '' &&
!(Array.isArray(v) && !v.length)));
return Object.fromEntries(entries);
};
export class Case {
result;
constructor(result) {
this.result = result;
}
get failures() {
return this.result.ok && !this.result.skip && !this.result.todo ?
0
: 1;
}
get skipped() {
return this.result.skip || this.result.todo ? 1 : 0;
}
get tests() {
return 1;
}
toJSON() {
return trimResult(this.result);
}
}
export class Suite extends EventEmitter {
parser;
suites = [];
cases = [];
name;
results;
summary;
constructor(parser) {
super();
this.parser = parser;
this.name = parser.name;
this.parser.on('complete', res => (this.results = res));
this.parser.on('child', p => {
const s = new Suite(p);
this.suites.push(s);
this.emit('suite', s);
s.on('suite', s => this.emit('suite', s));
s.on('suiteEnd', s => this.emit('suiteEnd', s));
s.on('case', c => this.emit('case', c));
});
this.parser.on('assert', a => this.onAssert(a));
}
onAssert(a) {
// check to see if it's the summary test point for a suite
const s = this.suites[this.suites.length - 1];
if (s && !s.summary && a.name === s.name) {
s.summary = trimResult(a);
this.emit('suiteEnd', s);
}
else {
const c = new Case(a);
this.cases.push(c);
this.emit('case', c);
}
}
toJSON() {
const { tests, failures, assertions, skipped } = this;
return trimResult({
name: this.name,
level: this.parser.level,
...(this.summary || {}),
plan: this.results?.plan,
bailout: this.results?.bailout,
tests,
failures,
assertions,
skipped,
suites: this.suites,
cases: this.cases,
});
}
get tests() {
return [...this.suites, ...this.cases]
.map(s => s.tests)
.reduce((a, b) => a + b, this.suites.length);
}
get failures() {
return [...this.suites, ...this.cases]
.map(s => s.failures)
.reduce((a, b) => a + b, 0);
}
get assertions() {
return this.suites
.map(s => s.assertions)
.reduce((a, b) => a + b, this.cases.length);
}
get skipped() {
return [...this.suites, ...this.cases]
.map(s => s.skipped)
.reduce((a, b) => a + b, (this.results?.plan.skipAll ||
this.summary?.skip ||
this.summary?.todo) ?
1
: 0);
}
}
export class Suites extends Suite {
constructor(p) {
super(p);
this.name ||= 'TAP Tests';
}
}
export class JSONReport extends Minipass {
parser = new Parser();
constructor() {
super({ encoding: 'utf8' });
const suites = new Suites(this.parser);
this.parser.on('complete', () => {
super.write(JSON.stringify(suites, null, 2));
super.end();
});
}
write(chunk, encoding, cb) {
return this.parser.write(chunk, encoding, cb);
}
end(chunk, encoding, cb) {
this.parser.end(chunk, encoding, cb);
return this;
}
}
export class JSONStream extends Minipass {
parser = new Parser();
constructor() {
super({ encoding: 'utf8' });
const suites = new Suites(this.parser);
suites.on('suite', s => this.suiteStart(s));
suites.on('case', c => this.onCase(c));
suites.on('suiteEnd', s => this.suiteEnd(s));
this.suiteStart(suites);
this.parser.on('complete', () => {
this.suiteEnd(suites);
super.end();
});
}
onCase(c) {
super.write(JSON.stringify([
c.result.skip ? 'skip'
: c.result.todo ? 'todo'
: c.result.ok ? 'pass'
: 'fail',
c,
]) + '\n');
}
suiteStart(s) {
super.write(JSON.stringify([
'start',
{ name: s.name, level: s.parser.level },
]) + '\n');
}
suiteEnd(s) {
super.write(JSON.stringify([
'end',
{
...s.toJSON(),
suites: undefined,
cases: undefined,
},
]) + '\n');
}
write(chunk, encoding, cb) {
return this.parser.write(chunk, encoding, cb);
}
end(chunk, encoding, cb) {
this.parser.end(chunk, encoding, cb);
return this;
}
}
//# sourceMappingURL=json.js.map