@plugjs/expect5
Version:
Unit Testing for the PlugJS Build System ========================================
273 lines (271 loc) • 10 kB
JavaScript
"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 = /* @__PURE__ */ Symbol.for("plugjs:expect5:singleton:suiteStorage");
var skipKey = /* @__PURE__ */ 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 = /* @__PURE__ */ 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 = /* @__PURE__ */ 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 = /* @__PURE__ */ 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