Update qkf/stepRegistry.js

This commit is contained in:
2026-02-08 21:29:50 -06:00
parent 8cf19b8016
commit 7d3bbbe01c

View File

@@ -1,125 +1,137 @@
@echo off // qkf/stepRegistry.js
setlocal enabledelayedexpansion
REM ============================ function escapeRegex(s) {
REM QKF Full Test Runner return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
REM ============================ }
echo. function normalizeText(t) {
echo ============================ return String(t || "").trim().replace(/\s+/g, " ");
echo QKF Test Execution }
echo ============================
echo.
REM ---------------------------- function literalToRegex(lit) {
REM Arguments // Escape + make spaces flexible
REM ---------------------------- return escapeRegex(normalizeText(lit)).replace(/\s+/g, "\\s+");
set FORCE_INSTALL=false }
if "%1"=="--install" set FORCE_INSTALL=true
REM ---------------------------- /**
REM Config (edit if needed) * Pattern supports "{string}" placeholders.
REM ---------------------------- * Matches:
set QKF_BROWSER=chrome * - "quoted value"
set QKF_HEADLESS=false * - 'quoted value'
set QKF_TEST_TIMEOUT_MS=120000 * - unquoted value (single token or multi-word)
set QKF_BASE_URL=http://gitea.cloud.qacg/user/login *
* Unquoted capture:
* - if there is a next literal => capture lazily until that literal (lookahead)
* - if last placeholder => capture rest of line
*/
function patternToRegex(pattern) {
const parts = String(pattern).split("{string}");
const litParts = parts.map((p) => literalToRegex(p));
REM ---------------------------- // Each placeholder yields 3 capturing groups: dbl, sgl, unquoted
REM Paths const QUOTED_OR_UNQUOTED = (nextLiteralRegex) => {
REM ---------------------------- const dbl = `"([^"]+)"`;
set FEATURES_DIR=features const sgl = `'([^']+)'`;
set GENERATED_DIR=test\generated
set ALLURE_RESULTS=allure-results
set ALLURE_REPORT=allure-report
REM ---------------------------- if (nextLiteralRegex && nextLiteralRegex.length > 0) {
REM Node dependencies // Capture up to the next literal (lazy) without consuming it
REM ---------------------------- // Allow optional whitespace before the next literal
if not exist "node_modules" ( const unq = `(.+?)(?=\\s*${nextLiteralRegex})`;
echo. return `(?:${dbl}|${sgl}|${unq})`;
echo node_modules not found running npm install... }
npm install
if errorlevel 1 (
echo npm install failed
exit /b 1
)
) else if "%FORCE_INSTALL%"=="true" (
echo.
echo --install flag detected running npm install...
npm install
if errorlevel 1 (
echo npm install failed
exit /b 1
)
) else (
echo.
echo node_modules already present skipping npm install
)
REM ---------------------------- // Last placeholder: capture rest of line
REM Clean previous output const unqLast = `(.+)`;
REM ---------------------------- return `(?:${dbl}|${sgl}|${unqLast})`;
if exist "%GENERATED_DIR%" ( };
echo Cleaning generated specs...
rmdir /s /q "%GENERATED_DIR%"
)
if exist "%ALLURE_RESULTS%" ( let re = "^\\s*";
echo Cleaning Allure results...
rmdir /s /q "%ALLURE_RESULTS%"
)
if exist "%ALLURE_REPORT%" ( for (let i = 0; i < litParts.length; i++) {
echo Cleaning Allure report... const lit = litParts[i];
rmdir /s /q "%ALLURE_REPORT%"
)
REM ---------------------------- // Add the literal segment (if any)
REM Generate specs if (lit) re += lit;
REM ----------------------------
echo.
echo [1/4] Generating specs from Gherkin...
node scripts\transform.js --features "%FEATURES_DIR%" --verbose
if errorlevel 1 ( // If a placeholder follows this literal:
echo Spec generation failed if (i < litParts.length - 1) {
exit /b 1 const nextLit = litParts[i + 1];
)
REM ---------------------------- // Allow whitespace between literal and value
REM Run tests re += "\\s+";
REM ---------------------------- re += QUOTED_OR_UNQUOTED(nextLit);
echo. // IMPORTANT: whitespace after value is optional; next literal (if any) will handle it
echo [2/4] Running Mocha + Selenium + Allure... re += "\\s*";
npx mocha ^ }
--timeout %QKF_TEST_TIMEOUT_MS% ^ }
--reporter allure-mocha ^
"%GENERATED_DIR%\*.spec.js"
if errorlevel 1 ( re += "\\s*$";
echo Tests finished with failures (continuing to report generation) return new RegExp(re, "i");
) }
REM ---------------------------- function createStepRegistry(initialCtx = {}) {
REM Generate Allure report const defs = [];
REM ----------------------------
echo.
echo [3/4] Generating Allure report...
allure generate "%ALLURE_RESULTS%" -o "%ALLURE_REPORT%" --clean
if errorlevel 1 ( function register(pattern, fn, meta = {}) {
echo Allure report generation failed defs.push({
exit /b 1 pattern: String(pattern),
) regex: patternToRegex(pattern),
fn,
meta,
});
}
REM ---------------------------- // DSL helpers
REM Open Allure report function WHEN(pattern, fn) { register(pattern, fn, { keyword: "WHEN" }); }
REM ---------------------------- function AND(pattern, fn) { register(pattern, fn, { keyword: "AND" }); }
echo. function THEN(pattern, fn) { register(pattern, fn, { keyword: "THEN" }); }
echo [4/4] Opening Allure report... function GIVEN(pattern, fn){ register(pattern, fn, { keyword: "GIVEN" }); }
allure open "%ALLURE_REPORT%" function BUT(pattern, fn) { register(pattern, fn, { keyword: "BUT" }); }
echo. async function run(stepText, extraCtx = {}) {
echo Done. const text = normalizeText(stepText);
endlocal
for (const d of defs) {
const m = d.regex.exec(text);
if (!m) continue;
// Each {string} adds 3 capture groups: dbl, sgl, unquoted
const captures = [];
for (let i = 1; i < m.length; i += 3) {
const v = m[i] || m[i + 1] || m[i + 2] || "";
captures.push(normalizeText(v));
}
const ctx = { ...initialCtx, ...extraCtx };
// Auto-await QKF calls (if you implemented these hooks)
if (ctx.qkf && typeof ctx.qkf.__beginStep === "function") ctx.qkf.__beginStep();
try {
const res = d.fn.apply(null, [...captures, ctx]);
await res;
if (ctx.qkf && typeof ctx.qkf.__endStep === "function") {
await ctx.qkf.__endStep();
}
return;
} catch (err) {
try {
if (ctx.qkf && typeof ctx.qkf.__endStep === "function") {
await ctx.qkf.__endStep();
}
} catch (_) {}
throw err;
}
}
const available = defs.map((x) => `- ${x.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 };