UNPKG

@japa/runner

Version:

A simple yet powerful testing framework for Node.js

296 lines (290 loc) 7.96 kB
// modules/core/main.ts import { Emitter, Refiner, Test as BaseTest, Suite as BaseSuite, Group as BaseGroup, Runner as BaseRunner, TestContext as BaseTestContext } from "@japa/core"; import { inspect } from "util"; import { AssertionError } from "assert"; // modules/core/reporters/base.ts import ms from "ms"; import { ErrorsPrinter } from "@japa/errors-printer"; // src/helpers.ts import useColors from "@poppinss/colors"; import supportsColor from "supports-color"; var colors = supportsColor.stdout ? useColors.ansi() : useColors.silent(); var icons = process.platform === "win32" && !process.env.WT_SESSION ? { tick: "\u221A", cross: "\xD7", bullet: "*", nodejs: "\u2666", pointer: ">", info: "i", warning: "\u203C", branch: " -", squareSmallFilled: "[\u2588]" } : { tick: "\u2714", cross: "\u2716", bullet: "\u25CF", nodejs: "\u2B22", pointer: "\u276F", info: "\u2139", warning: "\u26A0", branch: "\u2514\u2500\u2500", squareSmallFilled: "\u25FC" }; // modules/core/reporters/base.ts var BaseReporter = class { runner; /** * Path to the file for which the tests are getting executed */ currentFileName; /** * Suite for which the tests are getting executed */ currentSuiteName; /** * Group for which the tests are getting executed */ currentGroupName; options; constructor(options = {}) { this.options = Object.assign({ stackLinesCount: 2 }, options); } /** * Pretty prints the aggregates */ printAggregates(summary) { const tests = []; if (summary.aggregates.passed) { tests.push(colors.green(`${summary.aggregates.passed} passed`)); } if (summary.aggregates.failed) { tests.push(colors.red(`${summary.aggregates.failed} failed`)); } if (summary.aggregates.todo) { tests.push(colors.cyan(`${summary.aggregates.todo} todo`)); } if (summary.aggregates.skipped) { tests.push(colors.yellow(`${summary.aggregates.skipped} skipped`)); } if (summary.aggregates.regression) { tests.push(colors.magenta(`${summary.aggregates.regression} regression`)); } this.runner.summaryBuilder.use(() => { return [ { key: colors.dim("Tests"), value: `${tests.join(", ")} ${colors.dim(`(${summary.aggregates.total})`)}` }, { key: colors.dim("Time"), value: colors.dim(ms(summary.duration)) } ]; }); console.log(this.runner.summaryBuilder.build().join("\n")); } /** * Aggregates errors tree to a flat array */ aggregateErrors(summary) { const errorsList = []; summary.failureTree.forEach((suite) => { suite.errors.forEach((error) => errorsList.push({ title: suite.name, ...error })); suite.children.forEach((testOrGroup) => { if (testOrGroup.type === "test") { testOrGroup.errors.forEach((error) => { errorsList.push({ title: `${suite.name} / ${testOrGroup.title}`, ...error }); }); return; } testOrGroup.errors.forEach((error) => { errorsList.push({ title: testOrGroup.name, ...error }); }); testOrGroup.children.forEach((test) => { test.errors.forEach((error) => { errorsList.push({ title: `${testOrGroup.name} / ${test.title}`, ...error }); }); }); }); }); return errorsList; } /** * Pretty print errors */ async printErrors(summary) { if (!summary.failureTree.length) { return; } const errorPrinter = new ErrorsPrinter({ framesMaxLimit: this.options.framesMaxLimit }); errorPrinter.printSectionHeader("ERRORS"); await errorPrinter.printErrors(this.aggregateErrors(summary)); } /** * Handlers to capture events */ onTestStart(_) { } onTestEnd(_) { } onGroupStart(_) { } onGroupEnd(_) { } onSuiteStart(_) { } onSuiteEnd(_) { } async start(_) { } async end(_) { } /** * Print tests summary */ async printSummary(summary) { await this.printErrors(summary); console.log(""); if (summary.aggregates.total === 0 && !summary.hasError) { console.log(colors.bgYellow().black(" NO TESTS EXECUTED ")); return; } if (summary.hasError) { console.log(colors.bgRed().black(" FAILED ")); } else { console.log(colors.bgGreen().black(" PASSED ")); } console.log(""); this.printAggregates(summary); } /** * Invoked by the tests runner when tests are about to start */ boot(runner, emitter) { this.runner = runner; emitter.on("test:start", (payload) => { this.currentFileName = payload.meta.fileName; this.onTestStart(payload); }); emitter.on("test:end", (payload) => { this.onTestEnd(payload); }); emitter.on("group:start", (payload) => { this.currentGroupName = payload.title; this.currentFileName = payload.meta.fileName; this.onGroupStart(payload); }); emitter.on("group:end", (payload) => { this.currentGroupName = void 0; this.onGroupEnd(payload); }); emitter.on("suite:start", (payload) => { this.currentSuiteName = payload.name; this.onSuiteStart(payload); }); emitter.on("suite:end", (payload) => { this.currentSuiteName = void 0; this.onSuiteEnd(payload); }); emitter.on("runner:start", async (payload) => { await this.start(payload); }); emitter.on("runner:end", async (payload) => { await this.end(payload); }); } }; // modules/core/main.ts var TestContext = class extends BaseTestContext { constructor(test) { super(); this.test = test; this.cleanup = (cleanupCallback) => { test.cleanup(cleanupCallback); }; } }; var Test = class extends BaseTest { /** * @inheritdoc */ static executedCallbacks = []; /** * @inheritdoc */ static executingCallbacks = []; /** * Assert the test throws an exception with a certain error message * and optionally is an instance of a given Error class. */ throws(message, errorConstructor) { const errorInPoint = new AssertionError({}); const existingExecutor = this.options.executor; if (!existingExecutor) { throw new Error('Cannot use "test.throws" method without a test callback'); } this.options.executor = async (...args) => { let raisedException; try { await existingExecutor(...args); } catch (error) { raisedException = error; } if (!raisedException) { errorInPoint.message = "Expected test to throw an exception"; throw errorInPoint; } if (errorConstructor && !(raisedException instanceof errorConstructor)) { errorInPoint.message = `Expected test to throw "${inspect(errorConstructor)}"`; throw errorInPoint; } const exceptionMessage = raisedException.message; if (!exceptionMessage || typeof exceptionMessage !== "string") { errorInPoint.message = "Expected test to throw an exception with message property"; throw errorInPoint; } if (typeof message === "string") { if (exceptionMessage !== message) { errorInPoint.message = `Expected test to throw "${message}". Instead received "${raisedException.message}"`; errorInPoint.actual = raisedException.message; errorInPoint.expected = message; throw errorInPoint; } return; } if (!message.test(exceptionMessage)) { errorInPoint.message = `Expected test error to match "${message}" regular expression`; throw errorInPoint; } }; return this; } }; var Group = class extends BaseGroup { }; var Suite = class extends BaseSuite { }; var Runner = class extends BaseRunner { }; export { colors, icons, BaseReporter, Emitter, Refiner, TestContext, Test, Group, Suite, Runner };