UNPKG

@japa/runner

Version:

A simple yet powerful testing framework for Node.js

259 lines (258 loc) 8.47 kB
import { fileURLToPath } from "node:url"; import { ErrorsPrinter } from "@japa/errors-printer"; import { inspect } from "node:util"; import string from "@poppinss/string"; import timekeeper from "timekeeper"; import useColors from "@poppinss/colors"; import supportsColor from "supports-color"; import { parse } from "error-stack-parser-es"; import { Emitter, Group, Refiner, Runner, Suite, Test, TestContext } from "@japa/core"; import { AssertionError } from "node:assert"; var BaseReporter = class { runner; currentFileName; currentSuiteName; currentGroupName; options; constructor(options = {}) { this.options = Object.assign({ stackLinesCount: 2 }, options); } 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(string.milliseconds.format(summary.duration)) }]; }); console.log(this.runner.summaryBuilder.build().join("\n")); } 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; } 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)); } onTestStart(_) {} onTestEnd(_) {} onGroupStart(_) {} onGroupEnd(_) {} onSuiteStart(_) {} onSuiteEnd(_) {} async start(_) {} async end(_) {} 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); } 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); }); } }; var TestContext$1 = class extends TestContext { constructor(test) { super(); this.test = test; this.cleanup = (cleanupCallback) => { test.cleanup(cleanupCallback); }; } }; var Test$1 = class extends Test { static executedCallbacks = []; static executingCallbacks = []; 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$1 = class extends Group {}; var Suite$1 = class extends Suite {}; var Runner$1 = class extends Runner {}; const colors = supportsColor.stdout ? useColors.ansi() : useColors.silent(); const icons = process.platform === "win32" && !process.env.WT_SESSION ? { tick: "√", cross: "×", bullet: "*", nodejs: "♦", pointer: ">", info: "i", warning: "‼", branch: " -", squareSmallFilled: "[█]" } : { tick: "✔", cross: "✖", bullet: "●", nodejs: "⬢", pointer: "❯", info: "ℹ", warning: "⚠", branch: "└──", squareSmallFilled: "◼" }; function formatPinnedTest(test) { let fileName = ""; let line = 0; let column = 0; try { test.options.meta.abort("Finding pinned test location"); } catch (error) { const frame = parse(error).find((f) => f.fileName && f.lineNumber !== void 0 && f.columnNumber !== void 0 && !f.fileName.includes("node:") && !f.fileName.includes("ext:") && !f.fileName.includes("node_modules/")); if (frame) { fileName = frame.fileName.startsWith("file:") ? string.toUnixSlash(fileURLToPath(frame.fileName)) : string.toUnixSlash(frame.fileName); line = frame.lineNumber; column = frame.columnNumber; } } return `${colors.yellow(` ⁃ ${test.title}`)}\n${colors.dim(` ${fileName}:${line}:${column}`)}`; } function printPinnedTests(runner) { let pinnedTests = []; runner.suites.forEach((suite) => { suite.stack.forEach((testOrGroup) => { if (testOrGroup instanceof Group$1) testOrGroup.tests.forEach(($test) => { if ($test.isPinned) pinnedTests.push(formatPinnedTest($test)); }); else if (testOrGroup.isPinned) pinnedTests.push(formatPinnedTest(testOrGroup)); }); }); if (pinnedTests.length) { console.log(colors.bgYellow().black(` ${pinnedTests.length} pinned test(s) found `)); pinnedTests.forEach((row) => console.log(row)); } else console.log(colors.bgYellow().black(` No pinned tests found `)); } const dateTimeDoubles = { reset() { timekeeper.reset(); }, travelTo(durationOrDate) { if (durationOrDate instanceof Date) timekeeper.travel(durationOrDate); else { const travelToDate = /* @__PURE__ */ new Date(); travelToDate.setMilliseconds(travelToDate.getMilliseconds() + string.milliseconds.parse(durationOrDate)); timekeeper.travel(travelToDate); } }, freeze(date) { timekeeper.freeze(date); } }; export { Emitter as a, Runner$1 as c, TestContext$1 as d, BaseReporter as f, printPinnedTests as i, Suite$1 as l, dateTimeDoubles as n, Group$1 as o, icons as r, Refiner as s, colors as t, Test$1 as u };