@testduet/given-when-then
Version:
Write behavior-driven development (BDD) tests using "given-when-then" pattern and execute them in any traditional "describe-before-it-after" test framework.
212 lines (208 loc) • 6.88 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
mergeInto: () => mergeInto,
scenario: () => scenario
});
module.exports = __toCommonJS(src_exports);
// src/private/isPromiseLike.ts
function isPromiseLike(promise) {
return !!promise && typeof promise === "object" && "then" in promise && typeof promise.then === "function";
}
// src/givenWhenThen.ts
function createChain(mutableStacks, stack) {
const given = (isConjunction) => {
const given2 = (message, setup, teardown) => {
const nextChain = createChain(
mutableStacks,
Object.freeze([
...stack,
{
isConjunction,
operation: "given",
permutations: [[message, setup, teardown]]
}
])
);
return {
and: nextChain.given(true),
when: nextChain.when()
};
};
given2.oneOf = (permutations) => {
const nextChain = createChain(
mutableStacks,
Object.freeze([
...stack,
{
isConjunction,
operation: "given",
permutations
}
])
);
return {
and: nextChain.given(true),
when: nextChain.when()
};
};
return given2;
};
const when = () => {
const when2 = (message, setup, teardown) => {
const nextChain = createChain(
mutableStacks,
Object.freeze([...stack, { operation: "when", permutations: [[message, setup, teardown]] }])
);
return {
then: nextChain.then(false)
};
};
when2.oneOf = (permutations) => {
const nextChain = createChain(
mutableStacks,
Object.freeze([
...stack,
{
operation: "when",
permutations
}
])
);
return {
then: nextChain.then(false)
};
};
return when2;
};
const then = (isConjunction) => {
return (message, fn) => {
mutableStacks.push(
Object.freeze([
...stack,
{
isConjunction,
fn,
message,
operation: "then"
}
])
);
const currentChain = createChain(mutableStacks, stack);
return {
and: currentChain.then(true),
when: currentChain.when()
};
};
};
return { given, then, when };
}
function scenario(message, fn, options) {
const stacks = [];
const fnResult = fn({
given: createChain(stacks, Object.freeze([])).given(false)
});
if (isPromiseLike(fnResult)) {
throw new Error("The function passed to scenario() cannot asynchronous");
} else if (!stacks.some((frames) => frames.some((frame) => frame.operation === "then"))) {
throw new Error("Scenario should contains at least one then clause");
}
const facility = {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
afterEach: (options == null ? void 0 : options.afterEach) || globalThis.afterEach,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
beforeEach: (options == null ? void 0 : options.beforeEach) || globalThis.beforeEach,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
describe: (options == null ? void 0 : options.describe) || globalThis.describe,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
it: (options == null ? void 0 : options.it) || globalThis.it
};
facility.describe(message, () => {
for (const stack of stacks) {
runStack(stack, { value: void 0 }, { value: void 0 }, facility);
}
});
}
function runStack(stack, preconditionRef, outcomeRef, facility) {
const frame = stack.at(0);
if (!frame) {
return;
}
if (frame.operation === "given") {
for (const [message, setup, teardown] of frame.permutations) {
facility.describe(`${frame.isConjunction ? "and" : "given"} ${message}`, () => {
let currentPreconditionRef;
facility.beforeEach(() => {
const save = (value2) => {
currentPreconditionRef = { value: value2 };
preconditionRef.value = value2;
};
const value = setup(preconditionRef.value);
return isPromiseLike(value) ? Promise.resolve(value.then(save)) : save(value);
});
facility.afterEach(() => {
const value = teardown == null ? void 0 : teardown(currentPreconditionRef.value);
return isPromiseLike(value) ? Promise.resolve(value) : value;
});
return runStack(stack.slice(1), preconditionRef, outcomeRef, facility);
});
}
} else if (frame.operation === "when") {
for (const [message, setup, teardown] of frame.permutations) {
facility.describe(`when ${message}`, () => {
let currentOutcomeRef;
facility.beforeEach(() => {
const save = (value2) => {
currentOutcomeRef = { value: value2 };
outcomeRef.value = value2;
};
const value = setup(preconditionRef.value, outcomeRef.value);
return isPromiseLike(value) ? Promise.resolve(value.then(save)) : save(value);
});
facility.afterEach(() => {
const value = teardown == null ? void 0 : teardown(preconditionRef.value, currentOutcomeRef.value);
return isPromiseLike(value) ? Promise.resolve(value) : value;
});
return runStack(stack.slice(1), preconditionRef, outcomeRef, facility);
});
}
} else if (frame.operation === "then") {
facility.it(
`${frame.isConjunction ? "and" : "then"} ${frame.message}`,
() => frame.fn(preconditionRef.value, outcomeRef.value)
);
return runStack(stack.slice(1), preconditionRef, outcomeRef, facility);
}
}
// src/mergeInto.ts
function mergeInto(objectToMerge) {
return (scope) => ({ ...scope, ...objectToMerge });
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
mergeInto,
scenario
});
//# sourceMappingURL=given-when-then.js.map