diff --git a/qkf/stepRegistry.js b/qkf/stepRegistry.js index fec0504..447dc5e 100644 --- a/qkf/stepRegistry.js +++ b/qkf/stepRegistry.js @@ -1,111 +1,125 @@ -// qkf/stepRegistry.js +@echo off +setlocal enabledelayedexpansion -function escapeRegex(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -} +REM ============================ +REM QKF – Full Test Runner +REM ============================ -function normalizeText(t) { - return String(t || "").trim().replace(/\s+/g, " "); -} +echo. +echo ============================ +echo QKF Test Execution +echo ============================ +echo. -function patternToRegex(pattern) { - // Supports: - // - {string} -> "value" OR 'value' OR bareWord(s) - // Also normalizes whitespace - const norm = normalizeText(pattern); +REM ---------------------------- +REM Arguments +REM ---------------------------- +set FORCE_INSTALL=false +if "%1"=="--install" set FORCE_INSTALL=true - // Split around {string} - const parts = norm.split("{string}").map((p) => escapeRegex(p)); +REM ---------------------------- +REM Config (edit if needed) +REM ---------------------------- +set QKF_BROWSER=chrome +set QKF_HEADLESS=false +set QKF_TEST_TIMEOUT_MS=120000 +set QKF_BASE_URL=http://gitea.cloud.qacg/user/login - // Capture: - // - "..." or '...' or unquoted token(s) (until end) - // We make it non-greedy and allow extra spaces. - const CAPTURE = `(?:"([^"]+)"|'([^']+)'|([^]+?))`; +REM ---------------------------- +REM Paths +REM ---------------------------- +set FEATURES_DIR=features +set GENERATED_DIR=test\generated +set ALLURE_RESULTS=allure-results +set ALLURE_REPORT=allure-report - let regexStr = "^"; - for (let i = 0; i < parts.length; i++) { - regexStr += parts[i]; - if (i < parts.length - 1) { - // allow flexible whitespace around the capture - regexStr += "\\s+" + CAPTURE + "\\s+"; - } - } - regexStr += "$"; +REM ---------------------------- +REM Node dependencies +REM ---------------------------- +if not exist "node_modules" ( + echo. + 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 +) - return new RegExp(regexStr.replace(/\s+/g, "\\s+"), "i"); -} +REM ---------------------------- +REM Clean previous output +REM ---------------------------- +if exist "%GENERATED_DIR%" ( + echo Cleaning generated specs... + rmdir /s /q "%GENERATED_DIR%" +) +if exist "%ALLURE_RESULTS%" ( + echo Cleaning Allure results... + rmdir /s /q "%ALLURE_RESULTS%" +) -function createStepRegistry(initialCtx = {}) { - const defs = []; +if exist "%ALLURE_REPORT%" ( + echo Cleaning Allure report... + rmdir /s /q "%ALLURE_REPORT%" +) - function register(pattern, fn, meta = {}) { - defs.push({ - pattern: String(pattern), - regex: patternToRegex(pattern), - fn, - meta, - }); - } +REM ---------------------------- +REM Generate specs +REM ---------------------------- +echo. +echo [1/4] Generating specs from Gherkin... +node scripts\transform.js --features "%FEATURES_DIR%" --verbose - 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" }); } +if errorlevel 1 ( + echo ❌ Spec generation failed + exit /b 1 +) - async function run(stepText, extraCtx = {}) { - const text = normalizeText(stepText); +REM ---------------------------- +REM Run tests +REM ---------------------------- +echo. +echo [2/4] Running Mocha + Selenium + Allure... +npx mocha ^ + --timeout %QKF_TEST_TIMEOUT_MS% ^ + --reporter allure-mocha ^ + "%GENERATED_DIR%\*.spec.js" - for (const d of defs) { - const m = d.regex.exec(text); - if (!m) continue; +if errorlevel 1 ( + echo ⚠️ Tests finished with failures (continuing to report generation) +) - 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 }; +REM ---------------------------- +REM Generate Allure report +REM ---------------------------- +echo. +echo [3/4] Generating Allure report... +allure generate "%ALLURE_RESULTS%" -o "%ALLURE_REPORT%" --clean - // ✅ 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(); +if errorlevel 1 ( + echo ❌ Allure report generation failed + exit /b 1 +) - 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; - } - } +REM ---------------------------- +REM Open Allure report +REM ---------------------------- +echo. +echo [4/4] Opening Allure report... +allure open "%ALLURE_REPORT%" - 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 }; +echo. +echo ✅ Done. +endlocal