UNPKG

@plugjs/expect5

Version:

Unit Testing for the PlugJS Build System ========================================

288 lines (286 loc) 13.6 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // test.ts var test_exports = {}; __export(test_exports, { Test: () => Test }); module.exports = __toCommonJS(test_exports); var import_node_assert = require("node:assert"); var import_plug = require("@plugjs/plug"); var import_asserts = require("@plugjs/plug/asserts"); var import_logging = require("@plugjs/plug/logging"); var import_paths = require("@plugjs/plug/paths"); var import_executable = require("./execution/executable.cjs"); var import_executor = require("./execution/executor.cjs"); var setup = __toESM(require("./execution/setup.cjs"), 1); var import_diff = require("./expectation/diff.cjs"); var import_expect = require("./expectation/expect.cjs"); var import_print = require("./expectation/print.cjs"); var import_types = require("./expectation/types.cjs"); var _pending = "\u22EF"; var _success = "\u2714"; var _failure = "\u2718"; var _details = "\u2192"; var Test = class { constructor(_options = {}) { this._options = _options; } async pipe(files, context) { (0, import_asserts.assert)(files.length, "No files available for running tests"); const { globals = true, genericErrorDiffs = true, maxFailures = Number.POSITIVE_INFINITY, summary = false } = this._options; if (globals) { const anyGlobal = globalThis; anyGlobal["describe"] = setup.describe; anyGlobal["fdescribe"] = setup.fdescribe; anyGlobal["xdescribe"] = setup.xdescribe; anyGlobal["it"] = setup.it; anyGlobal["fit"] = setup.fit; anyGlobal["xit"] = setup.xit; anyGlobal["afterAll"] = setup.afterAll; anyGlobal["afterEach"] = setup.afterEach; anyGlobal["beforeAll"] = setup.beforeAll; anyGlobal["beforeEach"] = setup.beforeEach; anyGlobal["xafterAll"] = setup.xafterAll; anyGlobal["xafterEach"] = setup.xafterEach; anyGlobal["xbeforeAll"] = setup.xbeforeAll; anyGlobal["xbeforeEach"] = setup.xbeforeEach; anyGlobal["skip"] = import_executable.skip; anyGlobal["expect"] = import_expect.expect; anyGlobal["log"] = import_logging.log; anyGlobal["dirnameFromUrl"] = import_paths.dirnameFromUrl; anyGlobal["filenameFromUrl"] = import_paths.filenameFromUrl; } const suite = new import_executable.Suite(void 0, "", async () => { let count = 0; for (const file of files.absolutePaths()) { import_logging.log.debug("Importing", (0, import_logging.$p)(file), "in suite", (0, import_logging.$gry)(`(${++count}/${files.length})`)); await import(file); } }); await suite.setup(); const snum = suite.specs; const fnum = files.length; const smsg = (0, import_logging.$plur)(snum, "spec", "specs"); const fmsg = (0, import_logging.$plur)(fnum, "file", "files"); (0, import_asserts.assert)(snum, "No specs configured by test files"); const execution = (0, import_executor.runSuite)(suite); execution.on("suite:start", (current) => { if (current.parent === suite) { if (suite.flag !== "only") context.log.notice(""); context.log.enter(import_logging.NOTICE, `${(0, import_logging.$wht)(current.name)}`); context.log.notice(""); } else if (current.parent) { context.log.enter(import_logging.NOTICE, `${(0, import_logging.$blu)(_details)} ${(0, import_logging.$wht)(current.name)}`); } else { context.log.notice(`Running ${smsg} from ${fmsg}`); if (suite.flag === "only") context.log.notice(""); } }); execution.on("suite:done", (current) => { if (current.parent) context.log.leave(); }); execution.on("spec:start", (spec) => { context.log.enter(import_logging.NOTICE, `${(0, import_logging.$blu)(_pending)} ${spec.name}`); }); execution.on("spec:skip", (spec, ms) => { if (suite.flag === "only") return context.log.leave(); context.log.leave(import_logging.WARN, `${(0, import_logging.$ylw)(_pending)} ${spec.name} ${(0, import_logging.$ms)(ms, (0, import_logging.$ylw)("skipped"))}`); }); execution.on("spec:pass", (spec, ms, slow) => { if (slow) { context.log.leave(import_logging.WARN, `${(0, import_logging.$ylw)(_success)} ${spec.name} ${(0, import_logging.$ms)(ms, (0, import_logging.$ylw)("slow"))}`); } else { context.log.leave(import_logging.NOTICE, `${(0, import_logging.$grn)(_success)} ${spec.name} ${(0, import_logging.$ms)(ms)}`); } }); execution.on("spec:fail", (spec, ms, { number }) => { context.log.leave( import_logging.ERROR, `${(0, import_logging.$red)(_failure)} ${spec.name} ${(0, import_logging.$ms)(ms)} ${(0, import_logging.$gry)("[")}${(0, import_logging.$red)("failed")}${(0, import_logging.$gry)("|")}${(0, import_logging.$red)(`${number}`)}${(0, import_logging.$gry)("]")}` ); }); execution.on("hook:fail", (hook, ms, { number }) => { context.log.error(`${(0, import_logging.$red)(_failure)} Hook "${hook.name}" ${(0, import_logging.$ms)(ms)} ${(0, import_logging.$gry)("[")}${(0, import_logging.$red)("failed")}${(0, import_logging.$gry)("|")}${(0, import_logging.$red)(`${number}`)}${(0, import_logging.$gry)("]")}`); }); const { failed, passed, skipped, failures, time, records } = await execution.result; const limit = Math.min(failures.length, maxFailures); for (let i = 0; i < limit; i++) { if (i === 0) context.log.error(""); const { source, error, number } = failures[i]; const names = [""]; for (let p = source.parent; p?.parent; p = p.parent) { if (p) names.unshift(p.name); } const details = names.join(` ${(0, import_logging.$gry)(_details)} `) + (0, import_logging.$wht)(source.name); context.log.enter(import_logging.ERROR, `${(0, import_logging.$gry)("[")}${(0, import_logging.$red)(number)}${(0, import_logging.$gry)("]:")} ${details}`); dumpError(context.log, error, genericErrorDiffs); context.log.leave(); } if (summary) { context.log.notice(""); context.log.notice((0, import_logging.$wht)("Test execution summary:")); records.forEach((record) => dumpRecords(context.log, record)); } const totals = [`${passed} ${(0, import_logging.$gry)("passed")}`]; if (skipped) totals.push(`${skipped} ${(0, import_logging.$gry)("skipped")}`); if (failed) totals.push(`${failed} ${(0, import_logging.$gry)("failed")}`); if (failures.length) totals.push(`${failures.length} ${(0, import_logging.$gry)("total failures")}`); const epilogue = `${(0, import_logging.$gry)("(")}${totals.join((0, import_logging.$gry)(", "))}${(0, import_logging.$gry)(")")}`; const message = `Ran ${smsg} from ${fmsg} ${epilogue} ${(0, import_logging.$ms)(time)}`; if (failures.length) { context.log.error(message); throw new import_plug.BuildFailure(); } else if (suite.flag === "only") { context.log.error(""); context.log.error(message); throw new import_plug.BuildFailure('Suite running in focus ("only") mode'); } else if (skipped) { context.log.warn(""); context.log.warn(message); } else { context.log.notice(""); context.log.notice(message); } } }; function dumpRecords(log2, record) { if (record.type === "suite") { log2.enter(import_logging.NOTICE, `${(0, import_logging.$wht)(record.name)}`); for (const r of record.records) dumpRecords(log2, r); log2.leave(); } else if (record.type === "spec") { switch (record.result) { case "passed": if (record.slow) { log2.notice(`${(0, import_logging.$ylw)(_success)} ${record.name} ${(0, import_logging.$ms)(record.ms, (0, import_logging.$ylw)("slow"))}`); } else { log2.notice(`${(0, import_logging.$grn)(_success)} ${record.name} ${(0, import_logging.$ms)(record.ms)}`); } break; case "skipped": log2.notice(`${(0, import_logging.$ylw)(_pending)} ${record.name} ${(0, import_logging.$ms)(record.ms, (0, import_logging.$ylw)("skipped"))}`); break; case "failed": log2.notice( `${(0, import_logging.$red)(_failure)} ${record.name} ${(0, import_logging.$ms)(record.ms)} ${(0, import_logging.$gry)("[")}${(0, import_logging.$red)("failed")}${(0, import_logging.$gry)("|")}${(0, import_logging.$red)(`${record.failure}`)}${(0, import_logging.$gry)("]")}` ); break; } } else if (record.type === "hook") { log2.error(`${(0, import_logging.$red)(_failure)} Hook "${record.name}" ${(0, import_logging.$gry)("[")}${(0, import_logging.$red)("failed")}${(0, import_logging.$gry)("|")}${(0, import_logging.$red)(`${record.failure}`)}${(0, import_logging.$gry)("]")}`); } } function dumpError(log2, error, genericErrorDiffs) { if (error instanceof import_types.ExpectationError) { log2.enter(import_logging.ERROR, `${(0, import_logging.$gry)("Expectation Error:")} ${(0, import_logging.$red)(error.message)}`); (0, import_logging.githubAnnotation)({ type: "error", title: "Expectation Error" }, error.message); try { dumpProps(log2, 17, error); dumpStack(log2, error); if (error.diff) (0, import_print.printDiff)(log2, error.diff); } finally { log2.error(""); log2.leave(); } } else if (error instanceof import_node_assert.AssertionError) { const [message = "Unknown Error", ...lines] = error.message.split("\n"); log2.enter(import_logging.ERROR, `${(0, import_logging.$gry)("Assertion Error:")} ${(0, import_logging.$red)(message)}`); (0, import_logging.githubAnnotation)({ type: "error", title: "Assertion Error" }, message); try { dumpProps(log2, 15, error); dumpStack(log2, error); if (genericErrorDiffs) { if (!error.generatedMessage) for (const line of lines) log2.error(" ", line); (0, import_print.printDiff)(log2, (0, import_diff.diff)(error.actual, error.expected)); } else { while (lines.length && !lines[0]) lines.shift(); for (const line of lines) log2.error(" ", line); } } finally { log2.error(""); log2.leave(); } } else if (error instanceof Error) { const message = error.message || (error instanceof import_plug.BuildFailure ? "Build Failure" : "Unknown Error"); const string = Object.getPrototypeOf(error)?.constructor?.name || "Error"; const type = string === "AssertionError" ? `${(0, import_logging.$gry)("Assertion Error")}: ` : string === "Error" ? "" : `${(0, import_logging.$gry)(string)}: `; log2.enter(import_logging.ERROR, `${type}${(0, import_logging.$red)(message)}`); (0, import_logging.githubAnnotation)({ type: "error", title: string }, message); try { dumpProps(log2, type.length, error); dumpStack(log2, error); if (genericErrorDiffs && ("actual" in error || "expected" in error)) { (0, import_print.printDiff)(log2, (0, import_diff.diff)(error.actual, error.expected)); } } finally { log2.error(""); log2.leave(); } } else { log2.error((0, import_logging.$gry)("Uknown error:"), error); } } function dumpProps(log2, pad, error) { Object.keys(error).filter((k) => ![ "diff", // expectations error, "actual", // assertion error, chai "expected", // assertion error, chai, "generatedMessage", // assertion error, "message", // error "showDiff", // chai "stack" // error ].includes(k)).filter((k) => !(error[k] === null)).filter((k) => !(error[k] === void 0)).forEach((k) => { const value = error[k]; if (k === "code" && value === "ERR_ASSERTION") return; const details = typeof value === "string" ? value : (0, import_types.stringifyValue)(value); log2.error((0, import_logging.$gry)(`${k}:`.padStart(pad - 1)), (0, import_logging.$ylw)(details)); }); } function dumpStack(log2, error) { if (!error.stack) return log2.error("<no stack trace>"); error.stack.split("\n").filter((line) => line.match(/^\s+at\s+/)).map((line) => line.trim()).forEach((line) => log2.error(line)); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Test }); //# sourceMappingURL=test.cjs.map