Upload files to "qkf"
This commit is contained in:
87
qkf/stepRegistry.js
Normal file
87
qkf/stepRegistry.js
Normal file
@@ -0,0 +1,87 @@
|
||||
// qkf/stepRegistry.js
|
||||
|
||||
function escapeRegex(s) {
|
||||
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
function normalizeText(t) {
|
||||
return String(t || "").trim().replace(/\s+/g, " ");
|
||||
}
|
||||
|
||||
function patternToRegex(pattern) {
|
||||
const parts = String(pattern).split("{string}");
|
||||
const escaped = parts.map((p) => escapeRegex(normalizeText(p)));
|
||||
const joined = escaped.join("\\s+(.+?)\\s+");
|
||||
const final = "^" + joined.replace(/\s+/g, "\\s+") + "$";
|
||||
return new RegExp(final, "i");
|
||||
}
|
||||
|
||||
function createStepRegistry(initialCtx = {}) {
|
||||
const defs = [];
|
||||
|
||||
function register(pattern, fn, meta = {}) {
|
||||
defs.push({
|
||||
pattern: String(pattern),
|
||||
regex: patternToRegex(pattern),
|
||||
fn,
|
||||
meta,
|
||||
});
|
||||
}
|
||||
|
||||
function WHEN(pattern, fn) { register(pattern, fn, { keyword: "WHEN" }); }
|
||||
function AND(pattern, fn) { register(pattern, fn, { keyword: "AND" }); }
|
||||
function THEN(pattern, fn) { register(pattern, fn, { keyword: "THEN" }); }
|
||||
function GIVEN(pattern, fn){ register(pattern, fn, { keyword: "GIVEN" }); }
|
||||
function BUT(pattern, fn) { register(pattern, fn, { keyword: "BUT" }); }
|
||||
|
||||
async function run(stepText, extraCtx = {}) {
|
||||
const text = normalizeText(stepText);
|
||||
|
||||
for (const d of defs) {
|
||||
const m = d.regex.exec(text);
|
||||
if (!m) continue;
|
||||
|
||||
const captures = m.slice(1).map((x) => normalizeText(x));
|
||||
const ctx = { ...initialCtx, ...extraCtx };
|
||||
|
||||
// ✅ Begin step tracking so QKF calls are awaited even if step defs don't `await`
|
||||
if (ctx.qkf && typeof ctx.qkf.__beginStep === "function") ctx.qkf.__beginStep();
|
||||
|
||||
try {
|
||||
// Step defs get (...captures, ctx)
|
||||
const res = d.fn.apply(null, [...captures, ctx]);
|
||||
// Await the step function itself (if it's async)
|
||||
await res;
|
||||
// ✅ Then await all QKF actions triggered during the step
|
||||
if (ctx.qkf && typeof ctx.qkf.__endStep === "function") await ctx.qkf.__endStep();
|
||||
return;
|
||||
} catch (err) {
|
||||
// try to flush pending actions so failures surface cleanly
|
||||
try {
|
||||
if (ctx.qkf && typeof ctx.qkf.__endStep === "function") await ctx.qkf.__endStep();
|
||||
} catch (_) {
|
||||
// ignore secondary errors
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const available = defs.map((d) => `- ${d.pattern}`).join("\n");
|
||||
throw new Error(
|
||||
`No step definition matched:\n "${text}"\n\nAvailable definitions:\n${available}`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
defs,
|
||||
register,
|
||||
run,
|
||||
WHEN,
|
||||
AND,
|
||||
THEN,
|
||||
GIVEN,
|
||||
BUT,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { createStepRegistry };
|
||||
Reference in New Issue
Block a user