UNPKG

@plugjs/expect5

Version:

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

273 lines (271 loc) 9.94 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); // execution/executable.ts var executable_exports = {}; __export(executable_exports, { Hook: () => Hook, Spec: () => Spec, Suite: () => Suite, getCurrentSuite: () => getCurrentSuite, skip: () => skip }); module.exports = __toCommonJS(executable_exports); var import_node_assert = __toESM(require("node:assert"), 1); var import_node_async_hooks = require("node:async_hooks"); var import_utils = require("@plugjs/plug/utils"); function execute(call, timeout, notify) { return new Promise((resolve) => { let resolved = false; const abort = new AbortController(); const handle = setTimeout(() => { if (resolved) return; const error = new Error(`Timeout of ${timeout} ms reached`); resolve(error); notify?.(error); resolved = true; }, timeout).unref(); void Promise.resolve().then(async () => { try { await call.call(void 0, abort.signal); resolve(void 0); resolved = true; } catch (cause) { const error = cause instanceof Error ? cause : new Error(String(cause)); notify?.(error); resolve(error); resolved = true; } finally { abort.abort("Spec finished"); clearTimeout(handle); } }); }); } var suiteKey = Symbol.for("plugjs:expect5:singleton:suiteStorage"); var skipKey = Symbol.for("plugjs:expect5:singleton:skipStorage"); var suiteStorage = (0, import_utils.getSingleton)(suiteKey, () => new import_node_async_hooks.AsyncLocalStorage()); var skipStorage = (0, import_utils.getSingleton)(skipKey, () => new import_node_async_hooks.AsyncLocalStorage()); function getCurrentSuite() { const suite = suiteStorage.getStore(); (0, import_node_assert.default)(suite, "No suite found"); return suite; } function skip() { const skipState = skipStorage.getStore(); (0, import_node_assert.default)(skipState, 'The "skip" function can only be used in specs or hooks'); skipState.skipped = true; } var suiteMarker = Symbol.for("plugjs:expect5:types:Suite"); var Suite = class { constructor(parent, name, call, timeout = 5e3, flag = void 0) { this.parent = parent; this.name = name; this.call = call; this.timeout = timeout; this.flag = flag; } _beforeAll = []; _beforeEach = []; _afterAll = []; _afterEach = []; _suites = []; _specs = []; _children = []; _setup = false; static { this.prototype[suiteMarker] = suiteMarker; } static [Symbol.hasInstance](instance) { return instance && instance[suiteMarker] === suiteMarker; } get specs() { return this._suites.reduce((n, s) => n + s.specs, 0) + this._specs.length; } /** Add a child {@link Suite} to this */ addSuite(suite) { import_node_assert.default.strictEqual(suite.parent, this, "Suite is not a child of this"); this._children.push(suite); this._suites.push(suite); } /** Add a {@link Spec} to this */ addSpec(spec) { import_node_assert.default.strictEqual(spec.parent, this, "Spec is not a child of this"); this._children.push(spec); this._specs.push(spec); } /** Add a _before all_ {@link Hook} to this */ addBeforeAllHook(hook) { import_node_assert.default.strictEqual(hook.parent, this, "Hook is not a child of this"); import_node_assert.default.strictEqual(hook.name, "beforeAll", `Invalid before all hook name "${hook.name}"`); this._beforeAll.push(hook); } /** Add a _before each_ {@link Hook} to this */ addBeforeEachHook(hook) { import_node_assert.default.strictEqual(hook.parent, this, "Hook is not a child of this"); import_node_assert.default.strictEqual(hook.name, "beforeEach", `Invalid before each hook name "${hook.name}"`); this._beforeEach.push(hook); } /** Add a _after all_ {@link Hook} to this */ addAfterAllHook(hook) { import_node_assert.default.strictEqual(hook.parent, this, "Hook is not a child of this"); import_node_assert.default.strictEqual(hook.name, "afterAll", `Invalid after all hook name "${hook.name}"`); this._afterAll.push(hook); } /** Add a _after each_ {@link Hook} to this */ addAfterEachHook(hook) { import_node_assert.default.strictEqual(hook.parent, this, "Hook is not a child of this"); import_node_assert.default.strictEqual(hook.name, "afterEach", `Invalid after each hook name "${hook.name}"`); this._afterEach.push(hook); } /** * Setup this {@link Suite} invoking its main function, then initializing all * children {@link Suite Suites}, and finally normalizing execution flags. */ async setup() { if (this._setup) return; this._setup = true; await suiteStorage.run(this, async () => { const error = await execute(this.call, this.timeout); if (error) throw error; }); if (this.parent) { this._beforeEach.unshift(...this.parent._beforeEach.map((h) => h.clone(this))); this._afterEach.push(...this.parent._afterEach.map((h) => h.clone(this))); } for (const suite of this._suites) { await suite.setup(); } for (const spec of this._specs) { spec.before.push(...this._beforeEach.map((h) => h.clone(spec))); spec.after.push(...this._afterEach.map((h) => h.clone(spec))); } const only = this._children.reduce((o, c) => o || c.flag === "only", false); if (only) { this._children.forEach((c) => c.flag !== "only" && (c.flag = "skip")); this.flag = "only"; } if (this.flag === "only") { this._children.forEach((c) => c.flag !== "skip" && (c.flag = "only")); } for (const child of this._children) { if (child.flag !== "skip") return; } this.flag = "skip"; } /** * Execute this suite, executing all {@link Hook hooks} and children * {@link Spec specs} and {@link Suite suites} */ async execute(executor, skip2 = false) { const { done } = executor.start(this); if (skip2 || this.flag === "skip") { for (const child of this._children) await child.execute(executor, true); return done(); } for (const hook of this._beforeAll) { const failed = await hook.execute(executor); if (failed) { for (const child of this._children) await child.execute(executor, true); return done(); } } for (const child of this._children) await child.execute(executor); for (const hook of this._afterAll) await hook.execute(executor); done(); } }; var specMarker = Symbol.for("plugjs:expect5:types:Spec"); var Spec = class { constructor(parent, name, call, timeout = 5e3, flag = void 0) { this.parent = parent; this.name = name; this.call = call; this.timeout = timeout; this.flag = flag; } before = []; after = []; static { this.prototype[specMarker] = specMarker; } static [Symbol.hasInstance](instance) { return instance && instance[specMarker] === specMarker; } /** Execute this spec */ async execute(executor, skip2 = false) { const { done, notify } = executor.start(this); if (skip2 || this.flag == "skip") return done(true); for (const hook of this.before) { const failed = await hook.execute(executor); if (failed) return done(true); } const skipState = { skipped: false }; await skipStorage.run(skipState, () => execute(this.call, this.timeout, notify)); for (const hook of this.after) await hook.execute(executor); return done(skipState.skipped); } }; var hookMarker = Symbol.for("plugjs:expect5:types:Hook"); var Hook = class _Hook { constructor(parent, name, call, timeout = 5e3, flag = void 0) { this.parent = parent; this.name = name; this.call = call; this.timeout = timeout; this.flag = flag; } static { this.prototype[hookMarker] = hookMarker; } static [Symbol.hasInstance](instance) { return instance && instance[hookMarker] === hookMarker; } /** Execute this hook */ async execute(executor) { if (this.flag === "skip") return false; const { done, notify } = executor.start(this); const skipState = { skipped: false }; const error = await skipStorage.run(skipState, () => execute(this.call, this.timeout, notify)); done(skipState.skipped); return !!error; } /** Clone this associating it with a new {@link Suite} or {@link Spec} */ clone(parent) { return new _Hook(parent, this.name, this.call, this.timeout, this.flag); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Hook, Spec, Suite, getCurrentSuite, skip }); //# sourceMappingURL=executable.cjs.map