section-tests
Version:
337 lines (210 loc) • 9.54 kB
JavaScript
import chalk from './lib/chalk.js';
const colorMap = new Map();
colorMap.set('error', 'red');
colorMap.set('warn', 'yellow');
colorMap.set('success', 'green');
colorMap.set('info', 'white');
colorMap.set('notice', 'dim');
export default class SpecReporter {
constructor() {
this.failedStacks = [];
this.currentSectionTree = [];
this.cachedMessages = [];
}
send(message) {
message.padAmount = 4 * message.depth - 2;
this.processMessage(message);
}
getCurrentSectionTree() {
let i = this.currentSectionTree.length -2;
const startSection = this.currentSectionTree[this.currentSectionTree.length - 1];
let lastDepth = startSection.depth;
const sectionTree = [startSection];
while(i >= 0) {
const currentSection = this.currentSectionTree[i];
const currentDepth = currentSection.depth;
if (currentDepth < lastDepth) {
sectionTree.push(currentSection);
lastDepth = currentDepth;
}
i--;
if (currentDepth === 0) break;
}
return sectionTree.reverse();
}
processMessage(message) {
switch (message.type) {
case 'sectionMessage':
this.currentSectionTree.push(message);
return this.displayMessage(message);
case 'testErrorMessage':
case 'setupErrorMessage':
case 'destroyerErrorMessage':
const currentDisplaStack = this.cachedMessages;
currentDisplaStack.push(message);
this.displayMessages(currentDisplaStack);
const messages = this.getCurrentSectionTree().concat(currentDisplaStack);
this.cachedMessages = [];
this.failedStacks.push(messages);
break;
case 'testSuccessMessage': return this.displayMessage(message);
case 'setupSuccessMessage': return this.displayMessage(message);
case 'destroyerSuccessMessage': return this.displayMessage(message);
case 'testSuiteEndMessage': return this.displayMessage(message);
case 'logMessage':
if (this.lastStartMessage) {
this.cachedMessages.push(this.lastStartMessage);
this.lastStartMessage = null;
}
this.cachedMessages.push(message);
return;
case 'destroyerStartMessage':
case 'setupStartMessage':
case 'testStartMessage':
this.lastStartMessage = message;
return;
}
}
displayMessages(messages) {
for (const message of messages) {
this.displayMessage(message);
}
}
displayCachedMessages() {
for (const message of this.cachedMessages) {
this.displayMessage(message);
}
this.cachedMessages = [];
}
displayMessage(message) {
this.padAmount = message.padAmount;
switch (message.type) {
case 'destroyerErrorMessage':
this.displayDestroyerErrorMessage(message);
break;
case 'destroyerStartMessage':
this.displayDestroyerStartMessage(message);
break;
case 'destroyerSuccessMessage':
this.displayDestroyerSuccessMessage(message);
break;
case 'logMessage':
this.displayLogMessage(message);
break;
case 'sectionMessage':
this.displaySectionMessage(message);
break;
case 'setupErrorMessage':
this.displaySetupErrorMessage(message);
break;
case 'setupStartMessage':
this.displaySetupStartMessage(message);
break;
case 'setupSuccessMessage':
this.displaySetupSuccessMessage(message);
break;
case 'testErrorMessage':
this.displayTestErrorMessage(message);
break;
case 'testStartMessage':
this.displayTestStartMessage(message);
break;
case 'testSuccessMessage':
this.displayTestSuccessMessage(message);
break;
case 'testSuiteEndMessage':
this.displayTestSuiteEndMessage(message);
break;
}
this.lastType = message.type;
}
displayTestSuiteEndMessage(message) {
this.padAmount = 0;
// display all fails
if (this.failedStacks.length) {
console.log(`\n\n${this.pad(2)}${chalk.yellow('======================== Failed Tests ======================')}`);
for (const stack of this.failedStacks) {
this.displayMessages(stack);
}
this.padAmount = 0;
console.log(`\n\n${this.pad(2)}${chalk.yellow(`${message.failed} / ${message.ok + message.failed} tests failed!`)}\n\n`);
} else {
console.log(`\n\n${this.pad(2)}${chalk.green.bold(`${message.ok + message.failed} tests executed sucessfully 😊`)}\n\n`);
}
}
displayLogMessage(message) {
const prefix = chalk[colorMap.get(message.level)](`➟ ${message.level}:`);
console.log(`${this.pad(8)}${prefix} ${chalk.white(message.message)}`);
}
displaySetupStartMessage(message) {
console.log(`${this.pad(4)}${chalk.dim('⬇ ')}${chalk.grey(message.name)}`);
}
displaySetupErrorMessage(message) {
console.log(`${this.pad(4)}${chalk.red('✖ ')}${chalk.yellow(`${message.name}:`)} ${chalk.white(message.err.message)}\n`);
message.err.stack.forEach((frame) => {
console.log(`${this.pad(8)}${chalk.dim(`at ${frame.name} (${frame.source}:${frame.line})`)}`);
});
}
displaySetupSuccessMessage(message) {
this.displayCachedMessages();
console.log(`${this.pad(4)}${chalk.dim.green('✔ ')}${chalk.grey(message.name)}${this.getDurationMark(message)}`);
}
displayTestStartMessage(message) {
console.log(`${this.pad(4)}${chalk.dim('⬇ ')}${chalk.white(message.test.name)}`);
}
displayTestSuccessMessage(message) {
this.displayCachedMessages();
console.log(`${this.pad(4)}${chalk.green('✔ ')}${chalk.white(message.test.name)}${this.getDurationMark(message)}`);
}
displayTestErrorMessage(message) {
console.log(`${this.pad(4)}${chalk.red('✖ ')}${chalk.yellow(message.test.name+':')} ${chalk.white(message.err.message)}\n`);
if (typeof message.err.stack === 'string') {
const data = /at (?<functionName>.*) \(file:\/\/(?<fileName>.*.js):(?<lineNumber>\d+)/i.exec(message.err.stack);
console.log(`${this.pad(8)}${chalk.dim(`at ${data.groups.functionName} (${data.groups.fileName}:${data.groups.lineNumber})`)}`);
} else {
console.log(`${this.pad(8)}${chalk.dim(`at ${message.err.stack[0].name || '<unknown>'} (${message.err.stack[0].source}:${message.err.stack[0].line})`)}`);
}
if (message.err.type === 'AssertionError' && message.err.actual !== undefined && message.err.expected !== undefined) {
console.log(`\n${this.pad(8)}${chalk.red('actual: ')} ${chalk.white(message.err.actual)}`);
console.log(`${this.pad(8)}${chalk.dim('operator: ')} ${chalk.dim(message.err.operator)}`);
console.log(`${this.pad(8)}${chalk.green('expected: ')} ${chalk.white(message.err.expected)}\n`);
} else {
// display the friggin stack
if (typeof message.err.stack === 'string') log(message.err.stack);
else {
message.err.stack.slice(1).forEach((frame) => {
console.log(`${this.pad(8)}${chalk.dim(`at ${frame.name} (${frame.source}:${frame.line})`)}`);
});
}
}
}
getDurationMark(message) {
if (message.duration && Number.isInteger(message.duration)) {
if (message.duration > 500) return chalk.dim(` (${chalk.yellow.bold(message.duration)} msec)`);
}
return '';
}
displayDestroyerStartMessage(message) {
console.log(`${this.pad(4)}${chalk.dim('⬇ ')}${chalk.grey(message.name)}`);
}
displayDestroyerErrorMessage(message) {
console.log(`${this.pad(4)}${chalk.red('✖ ')}${chalk.yellow(`${message.name}:`)} ${chalk.white(message.err.message)}\n`);
message.err.stack.forEach((frame) => {
console.log(`${this.pad(8)}${chalk.dim(`at ${frame.name} (${frame.source}:${frame.line})`)}`);
});
}
displayDestroyerSuccessMessage(message) {
this.displayCachedMessages();
console.log(`${this.pad(4)}${chalk.dim.green('✔ ')}${chalk.grey(message.name)}${this.getDurationMark(message)}`);
}
displaySectionMessage(message) {
if (message.sectionName !== 'root' || message.depth !== 0) {
console.log(`${this.lastType === 'sectionMessage' ? '' : '\n'}${this.pad()}${chalk.blue.bold(message.sectionName)}`);
} else {
//console.log(` ${chalk.blue.bold('Executing Tests')}`);
}
}
pad(add = 0) {
return ' '.repeat(this.padAmount+add);
}
}