assuremind 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -12
- package/dist/cli/index.js +197 -197
- package/dist/index.d.mts +13 -6
- package/dist/index.d.ts +13 -6
- package/dist/index.js +16 -16
- package/dist/index.mjs +17 -17
- package/docs/GETTING-STARTED.md +13 -2
- package/package.json +1 -1
- package/templates/docs/GETTING-STARTED.md +1 -1
- package/ui/dist/assets/index-BsxznSas.css +1 -0
- package/ui/dist/assets/index-DdWg6Y6w.js +1032 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-6jqOR-Hh.js +0 -1029
- package/ui/dist/assets/index-BGDCUDyt.css +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
3
|
-
`)}function
|
|
4
|
-
`)}function
|
|
5
|
-
`)}function
|
|
6
|
-
`)}function
|
|
7
|
-
`)}function
|
|
2
|
+
"use strict";var Xf=Object.create;var _n=Object.defineProperty;var Jf=Object.getOwnPropertyDescriptor;var Yf=Object.getOwnPropertyNames;var Qf=Object.getPrototypeOf,Zf=Object.prototype.hasOwnProperty;var b=(r,e,t)=>()=>{if(t)throw t[0];try{return r&&(e=r(r=0)),e}catch(s){throw t=[s],s}};var Mt=(r,e)=>()=>{try{return e||r((e={exports:{}}).exports,e),e.exports}catch(t){throw e=0,t}},ie=(r,e)=>{for(var t in e)_n(r,t,{get:e[t],enumerable:!0})},Nc=(r,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Yf(e))!Zf.call(r,n)&&n!==t&&_n(r,n,{get:()=>e[n],enumerable:!(s=Jf(e,n))||s.enumerable});return r};var x=(r,e,t)=>(t=r!=null?Xf(Qf(r)):{},Nc(e||!r||!r.__esModule?_n(t,"default",{value:r,enumerable:!0}):t,r)),mt=r=>Nc(_n({},"__esModule",{value:!0}),r);var eh,y,g=b(()=>{"use strict";eh=()=>typeof document>"u"?new URL(`file:${__filename}`).href:document.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?document.currentScript.src:new URL("main.js",document.baseURI).href,y=eh()});function Pe(r){process.stdout.write(`${L.success("\u2713")} ${r}
|
|
3
|
+
`)}function se(r){process.stderr.write(`${L.error("\u2717")} ${r}
|
|
4
|
+
`)}function at(r){process.stdout.write(`${L.warn("\u26A0")} ${r}
|
|
5
|
+
`)}function X(r){process.stdout.write(`${L.info("\u2139")} ${r}
|
|
6
|
+
`)}function On(r){process.stdout.write(`${L.dim("\u2013")} ${L.dim(r)}
|
|
7
|
+
`)}function nr(r){let e=Math.max(...r.map(s=>s.length))+4,t=L.accent("\u2500".repeat(e));process.stdout.write(`
|
|
8
8
|
${t}
|
|
9
9
|
`);for(let s of r)process.stdout.write(` ${s}
|
|
10
10
|
`);process.stdout.write(`${t}
|
|
11
11
|
|
|
12
|
-
`)}function
|
|
13
|
-
How to fix: Run "npm init -y" first, then retry "npx assuremind init".`),
|
|
14
|
-
`),
|
|
15
|
-
`),
|
|
16
|
-
`);let t=
|
|
12
|
+
`)}function jt(r){return(0,Fc.default)({text:r,color:"cyan"})}function Bt(r){let e=r instanceof Error?r.message:String(r);se(e),process.exit(1)}var Er,Fc,L,ir=b(()=>{"use strict";g();Er=x(require("chalk")),Fc=x(require("ora")),L={success:Er.default.green,error:Er.default.red,warn:Er.default.yellow,info:Er.default.cyan,dim:Er.default.dim,bold:Er.default.bold,accent:Er.default.hex("#00adf7")}});var Kc={};ie(Kc,{AutotestConfigSchema:()=>zr,BrowserNameSchema:()=>ba,DEFAULT_CONFIG:()=>Sa,DEFAULT_ENVIRONMENTS:()=>xa,EnvironmentProfileSchema:()=>qc,EnvironmentSchema:()=>va,EnvironmentUrlsSchema:()=>Hc,HealingConfigSchema:()=>Vc,PageLoadStrategySchema:()=>Uc,ReportingConfigSchema:()=>Gc,ScreenshotModeSchema:()=>Mc,TraceModeSchema:()=>Bc,VideoModeSchema:()=>jc,ViewportSchema:()=>zc});var A,Mc,jc,Bc,ba,Uc,va,xa,Hc,Vc,Gc,zc,qc,zr,Sa,Ln=b(()=>{"use strict";g();A=require("zod"),Mc=A.z.enum(["off","on","only-on-failure"]),jc=A.z.enum(["off","on","on-first-retry","retain-on-failure"]),Bc=A.z.enum(["off","on","on-first-retry","retain-on-failure"]),ba=A.z.enum(["chromium","firefox","webkit"]),Uc=A.z.enum(["commit","domcontentloaded","load","networkidle"]),va=A.z.string().min(1),xa=["DEV","TEST","STAGE","INT","PROD"],Hc=A.z.record(A.z.string(),A.z.string()).default({}),Vc=A.z.object({enabled:A.z.boolean(),maxLevel:A.z.number().int().min(1).max(5),dailyBudget:A.z.number().positive(),autoPR:A.z.boolean(),healAssertionLocators:A.z.boolean().default(!1)}),Gc=A.z.object({allure:A.z.boolean(),html:A.z.boolean(),json:A.z.boolean()}),zc=A.z.object({width:A.z.number().int().positive(),height:A.z.number().int().positive()}),qc=A.z.object({name:A.z.string().min(1),environment:va,baseUrl:A.z.string().url(),browsers:A.z.array(ba).min(1),headless:A.z.boolean().optional()}),zr=A.z.object({baseUrl:A.z.string().url(),environment:va.default("DEV"),environments:A.z.array(A.z.string().min(1)).default([...xa]),environmentUrls:Hc.default({DEV:"",TEST:"",STAGE:"",INT:"",PROD:""}),browsers:A.z.array(ba).min(1),headless:A.z.boolean(),viewport:zc.default({width:1280,height:720}),timeout:A.z.number().int().positive(),retries:A.z.number().int().min(0),parallel:A.z.number().int().positive(),pageLoad:Uc.default("domcontentloaded"),screenshot:Mc,video:jc,trace:Bc,healing:Vc,reporting:Gc,studioPort:A.z.number().int().min(1024).max(65535),profiles:A.z.array(qc).default([]),activeProfile:A.z.string().optional(),device:A.z.string().optional(),rag:A.z.object({enabled:A.z.boolean(),codeCorpus:A.z.object({enabled:A.z.boolean(),maxEntries:A.z.number().int().positive(),similarityThreshold:A.z.number().min(0).max(1),directUseThreshold:A.z.number().min(0).max(1)}),healingCorpus:A.z.object({enabled:A.z.boolean(),maxEntries:A.z.number().int().positive(),similarityThreshold:A.z.number().min(0).max(1)}),errorCatalog:A.z.object({enabled:A.z.boolean(),maxEntries:A.z.number().int().positive()}),embedder:A.z.literal("tfidf")}).default({enabled:!0,codeCorpus:{enabled:!0,maxEntries:500,similarityThreshold:.65,directUseThreshold:.9},healingCorpus:{enabled:!0,maxEntries:300,similarityThreshold:.6},errorCatalog:{enabled:!0,maxEntries:200},embedder:"tfidf"}),mcp:A.z.object({enabled:A.z.boolean(),headless:A.z.boolean(),actThenScript:A.z.boolean(),proactiveHealing:A.z.boolean(),actionTimeout:A.z.number().int().positive(),idleTimeout:A.z.number().int().positive()}).default({enabled:!0,headless:!0,actThenScript:!1,proactiveHealing:!1,actionTimeout:15e3,idleTimeout:3e4}),ci:A.z.object({writeToRepo:A.z.boolean()}).default({writeToRepo:!1})}),Sa={baseUrl:"http://localhost:3000",environment:"DEV",environments:[...xa],environmentUrls:{DEV:"http://localhost:3000",TEST:"",STAGE:"",INT:"",PROD:""},browsers:["chromium"],headless:!0,viewport:{width:1280,height:720},timeout:3e4,retries:1,parallel:1,pageLoad:"domcontentloaded",screenshot:"only-on-failure",video:"off",trace:"on-first-retry",healing:{enabled:!0,maxLevel:5,dailyBudget:5,autoPR:!1,healAssertionLocators:!1},reporting:{allure:!0,html:!0,json:!0},studioPort:4400,profiles:[],rag:{enabled:!0,codeCorpus:{enabled:!0,maxEntries:500,similarityThreshold:.65,directUseThreshold:.9},healingCorpus:{enabled:!0,maxEntries:300,similarityThreshold:.6},errorCatalog:{enabled:!0,maxEntries:200},embedder:"tfidf"},mcp:{enabled:!0,headless:!0,actThenScript:!1,proactiveHealing:!1,actionTimeout:15e3,idleTimeout:3e4},ci:{writeToRepo:!1}}});var Xc={};ie(Xc,{runInit:()=>ih});function nh(){return Dn.default.resolve(__dirname,"../../templates")}async function ih(r={}){await Ct.default.pathExists("package.json")||Bt(`No package.json found in the current directory.
|
|
13
|
+
How to fix: Run "npm init -y" first, then retry "npx assuremind init".`),X(`Initialising Assuremind in the current directory\u2026
|
|
14
|
+
`),X("Creating directories\u2026");for(let t of th)await Ct.default.ensureDir(t),(await Ct.default.readdir(t)).length===0&&await Ct.default.writeFile(Dn.default.join(t,".gitkeep"),""),Pe(` ${t}/`);process.stdout.write(`
|
|
15
|
+
`),X("Creating config files\u2026");let e=nh();for(let{dest:t,template:s}of rh){if(await Ct.default.pathExists(t)){On(` ${t} (already exists)`);continue}let n=Dn.default.join(e,s);await Ct.default.copy(n,t),Pe(` ${t}`)}for(let t of sh){if(await Ct.default.pathExists(t)){On(` ${t} (already exists)`);continue}await Ct.default.writeJson(t,{},{spaces:2}),Pe(` ${t}`)}if(await Ct.default.pathExists("autotest.config.json"))On(" autotest.config.json (already exists)");else{let{DEFAULT_CONFIG:t}=await Promise.resolve().then(()=>(Ln(),Kc));await Ct.default.writeJson("autotest.config.json",t,{spaces:2}),Pe(" autotest.config.json")}if(!r.skipPlaywright){process.stdout.write(`
|
|
16
|
+
`);let t=jt("Installing Playwright browsers (Chromium, Firefox, WebKit)\u2026");t.start();try{(0,Wc.execSync)("npx playwright install --with-deps",{stdio:"pipe"}),t.succeed("Playwright browsers installed")}catch(s){t.fail("Failed to install Playwright browsers");let n=s instanceof Error?s.message:String(s);process.stderr.write(` Run manually: npx playwright install --with-deps
|
|
17
17
|
Error: ${n}
|
|
18
|
-
`)}}
|
|
19
|
-
`);throw new
|
|
18
|
+
`)}}nr(["\u2705 AssureMind initialised!","","Defaults to local Ollama (free). To use it:"," \u2022 Install Ollama \u2192 https://ollama.com"," \u2022 ollama pull qwen2.5-coder:14b","","Or switch provider in .env, or via Studio \u2192 Settings \u2192 Environment.","","Next steps:"," 1. Run: npx assuremind studio"," 2. Start creating tests in the browser!","","Docs: https://assuremind.dev/docs"])}var Dn,Ct,Wc,th,rh,sh,Jc=b(()=>{"use strict";g();Dn=x(require("path")),Ct=x(require("fs-extra")),Wc=require("child_process");ir();th=["tests","variables","results","results/screenshots","results/videos","results/traces","results/reports","results/healing","fixtures","fixtures/auth","fixtures/data","docs"],rh=[{dest:".env.example",template:"env.example"},{dest:".env",template:"env.minimal"},{dest:".gitignore",template:"gitignore"},{dest:"autotest.config.ts",template:"autotest.config.ts"},{dest:"variables/global.json",template:"global-variables.json"},{dest:"ASSUREMIND.md",template:"ASSUREMIND.md"},{dest:"docs/GETTING-STARTED.md",template:"docs/GETTING-STARTED.md"},{dest:"docs/STUDIO.md",template:"docs/STUDIO.md"},{dest:"docs/CLI-REFERENCE.md",template:"docs/CLI-REFERENCE.md"}],sh=["variables/dev.env.json","variables/staging.env.json","variables/prod.env.json"]});var Ir,V,Re,te,ye,ge=b(()=>{"use strict";g();Ir=class extends Error{code;constructor(e,t){super(e),this.name="AssuremindError",this.code=t,Object.setPrototypeOf(this,new.target.prototype)}},V=class extends Ir{provider;constructor(e,t,s="PROVIDER_ERROR"){super(e,s),this.name="ProviderError",this.provider=t}},Re=class extends Ir{constructor(e,t="CONFIG_ERROR"){super(e,t),this.name="ConfigError"}},te=class extends Ir{field;constructor(e,t,s="VALIDATION_ERROR"){super(e,s),this.name="ValidationError",this.field=t}},ye=class extends Ir{path;constructor(e,t,s="STORAGE_ERROR"){super(e,s),this.name="StorageError",this.path=t}}});function Cr(){if(Nn!==null)return Nn;let r=ah.safeParse(process.env);if(!r.success){let i=r.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
|
|
19
|
+
`);throw new Re(`Missing or invalid environment variables:
|
|
20
20
|
${i}
|
|
21
21
|
|
|
22
|
-
How to fix: Copy .env.example to .env and fill in your AI provider credentials.`,"ENV_VALIDATION_FAILED")}let e=r.data.AI_PROVIDER,s=
|
|
23
|
-
`);throw new
|
|
22
|
+
How to fix: Copy .env.example to .env and fill in your AI provider credentials.`,"ENV_VALIDATION_FAILED")}let e=r.data.AI_PROVIDER,s=bh[e].safeParse(process.env);if(!s.success){let i=s.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
|
|
23
|
+
`);throw new Re(`Provider "${e}" is missing required environment variables:
|
|
24
24
|
${i}
|
|
25
25
|
|
|
26
|
-
How to fix: Check .env.example for the ${e} section and add the required keys to .env.`,"PROVIDER_ENV_MISSING")}return
|
|
26
|
+
How to fix: Check .env.example for the ${e} section and add the required keys to .env.`,"PROVIDER_ENV_MISSING")}return Nn={...r.data,...s.data},Nn}function ar(){try{return Cr()}catch{return null}}var Qc,N,Yc,ah,oh,ch,lh,uh,dh,ph,mh,gh,fh,hh,yh,wh,bh,Nn,Pr=b(()=>{"use strict";g();Qc=require("dotenv"),N=require("zod");ge();(0,Qc.config)();Yc=["anthropic","openai","google","azure-openai","bedrock","deepseek","groq","together","qwen","perplexity","ollama","custom"],ah=N.z.object({NODE_ENV:N.z.enum(["development","production","test"]).default("development"),LOG_LEVEL:N.z.enum(["trace","debug","info","warn","error","fatal"]).default("info"),AI_PROVIDER:N.z.enum(Yc),AI_TIERED_ENABLED:N.z.string().transform(r=>r==="true").default("false"),AI_TIERED_FAST_PROVIDER:N.z.enum(Yc).optional(),AI_TIERED_FAST_MODEL:N.z.string().optional(),AI_TIMEOUT:N.z.coerce.number().int().positive().default(30),AI_MAX_RETRIES:N.z.coerce.number().int().min(0).max(10).default(2)}),oh=N.z.object({ANTHROPIC_API_KEY:N.z.string().min(1),ANTHROPIC_MODEL:N.z.string().default("claude-sonnet-4-20250514")}),ch=N.z.object({OPENAI_API_KEY:N.z.string().min(1),OPENAI_MODEL:N.z.string().default("gpt-4o")}),lh=N.z.object({GOOGLE_API_KEY:N.z.string().min(1),GOOGLE_MODEL:N.z.string().default("gemini-2.5-pro")}),uh=N.z.object({AZURE_OPENAI_API_KEY:N.z.string().min(1),AZURE_OPENAI_ENDPOINT:N.z.string().url(),AZURE_OPENAI_DEPLOYMENT:N.z.string().min(1),AZURE_OPENAI_API_VERSION:N.z.string().default("2024-10-21")}),dh=N.z.object({AWS_ACCESS_KEY_ID:N.z.string().min(1),AWS_SECRET_ACCESS_KEY:N.z.string().min(1),AWS_SESSION_TOKEN:N.z.string().optional(),AWS_REGION:N.z.string().default("us-east-1"),BEDROCK_MODEL:N.z.string().default("anthropic.claude-sonnet-4-20250514-v1:0")}),ph=N.z.object({DEEPSEEK_API_KEY:N.z.string().min(1),DEEPSEEK_MODEL:N.z.string().default("deepseek-chat")}),mh=N.z.object({GROQ_API_KEY:N.z.string().min(1),GROQ_MODEL:N.z.string().default("llama-3.3-70b-versatile")}),gh=N.z.object({TOGETHER_API_KEY:N.z.string().min(1),TOGETHER_MODEL:N.z.string().default("meta-llama/Llama-3.3-70B-Instruct-Turbo")}),fh=N.z.object({QWEN_API_KEY:N.z.string().min(1),QWEN_BASE_URL:N.z.string().url().default("https://dashscope-intl.aliyuncs.com/compatible-mode/v1"),QWEN_MODEL:N.z.string().default("qwen-max")}),hh=N.z.object({PERPLEXITY_API_KEY:N.z.string().min(1),PERPLEXITY_MODEL:N.z.string().default("sonar-pro")}),yh=N.z.object({OLLAMA_BASE_URL:N.z.string().url().default("http://localhost:11434"),OLLAMA_MODEL:N.z.string().default("llama3.3")}),wh=N.z.object({CUSTOM_API_KEY:N.z.string().min(1),CUSTOM_BASE_URL:N.z.string().url(),CUSTOM_MODEL:N.z.string().min(1)}),bh={anthropic:oh,openai:ch,google:lh,"azure-openai":uh,bedrock:dh,deepseek:ph,groq:mh,together:gh,qwen:fh,perplexity:hh,ollama:yh,custom:wh},Nn=null});function I(r){return xh.child({component:r})}async function el(r,e){let t=Ca.default.join(r,"results","logs");await Ia.default.ensureDir(t);let s=Ca.default.join(t,`run-${e}.log`),n=Ia.default.createWriteStream(s,{flags:"a",encoding:"utf-8"});return n.write(`=== Assuremind Test Run: ${e} ===
|
|
27
27
|
`),n.write(`Started: ${new Date().toISOString()}
|
|
28
28
|
`),n.write("=".repeat(60)+`
|
|
29
29
|
|
|
@@ -31,13 +31,13 @@ How to fix: Check .env.example for the ${e} section and add the required keys to
|
|
|
31
31
|
`)},close:()=>{n.write(`
|
|
32
32
|
`+"=".repeat(60)+`
|
|
33
33
|
`),n.write(`Completed: ${new Date().toISOString()}
|
|
34
|
-
`),n.end()}}}var
|
|
35
|
-
`);throw new
|
|
34
|
+
`),n.end()}}}var Ea,Ia,Ca,vh,Zc,xh,j=b(()=>{"use strict";g();Ea=x(require("pino")),Ia=x(require("fs-extra")),Ca=x(require("path")),vh=process.env.NODE_ENV!=="production",Zc=vh?{target:"pino-pretty",options:{colorize:!0,translateTime:"HH:MM:ss",ignore:"pid,hostname",messageFormat:"[assuremind] {msg}"}}:void 0,xh=(0,Ea.default)({level:process.env.LOG_LEVEL??"info",base:{name:"assuremind"}},Zc?Ea.default.transport(Zc):void 0)});async function re(r,e){let t=Pa.default.dirname(r);await Ut.default.ensureDir(t);let s=`${r}.${process.pid}.tmp`;try{await Ut.default.writeJson(s,e,{spaces:2}),await Ut.default.rename(s,r)}catch(n){throw await Ut.default.remove(s).catch(()=>{}),new ye(`Failed to write file "${r}": ${n instanceof Error?n.message:String(n)}`,r,"WRITE_FAILED")}}async function ve(r){try{return await Ut.default.readJson(r)}catch(e){throw new ye(`Failed to read JSON file "${r}": ${e instanceof Error?e.message:String(e)}`,r,"READ_FAILED")}}async function tl(r,e){let t=Pa.default.dirname(r);await Ut.default.ensureDir(t);let s=`${r}.${process.pid}.tmp`;try{await Ut.default.writeFile(s,e,"utf8"),await Ut.default.rename(s,r)}catch(n){throw await Ut.default.remove(s).catch(()=>{}),new ye(`Failed to write file "${r}": ${n instanceof Error?n.message:String(n)}`,r,"WRITE_FAILED")}}var Pa,Ut,ot=b(()=>{"use strict";g();Pa=x(require("path")),Ut=x(require("fs-extra"));ge()});var Ta={};ie(Ta,{configExists:()=>Eh,readConfig:()=>J,updateConfig:()=>Sh,validateConfig:()=>Ih,writeConfig:()=>Mn});async function J(r){let e=us.default.join(r,ka);if(!await Fn.default.pathExists(e))return Ra.debug({rootDir:r},"No autotest.config.json found \u2014 using defaults"),Sa;let t=await ve(e),s=zr.safeParse(t);if(!s.success){let i=s.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
|
|
35
|
+
`);throw new te(`Invalid autotest.config.json at "${e}":
|
|
36
36
|
${i}
|
|
37
37
|
|
|
38
|
-
How to fix: Run "npx assuremind init" to reset to defaults, or manually correct the file using autotest.config.ts as reference.`,"config","INVALID_CONFIG")}let n=s.data;if(t&&typeof t=="object"&&"environments"in t)n.environment&&!n.environments.includes(n.environment)&&(n.environments=[...n.environments,n.environment]);else{let i=new Set;for(let a of Object.keys(n.environmentUrls??{}))i.add(a);n.environment&&i.add(n.environment),i.size>0&&(n.environments=[...i])}return n}async function
|
|
39
|
-
`);throw new
|
|
40
|
-
${i}`,"config","INVALID_CONFIG")}let s=
|
|
38
|
+
How to fix: Run "npx assuremind init" to reset to defaults, or manually correct the file using autotest.config.ts as reference.`,"config","INVALID_CONFIG")}let n=s.data;if(t&&typeof t=="object"&&"environments"in t)n.environment&&!n.environments.includes(n.environment)&&(n.environments=[...n.environments,n.environment]);else{let i=new Set;for(let a of Object.keys(n.environmentUrls??{}))i.add(a);n.environment&&i.add(n.environment),i.size>0&&(n.environments=[...i])}return n}async function Mn(r,e){let t=zr.safeParse(e);if(!t.success){let i=t.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
|
|
39
|
+
`);throw new te(`Cannot write invalid config:
|
|
40
|
+
${i}`,"config","INVALID_CONFIG")}let s=us.default.join(r,ka);await re(s,t.data);let n=us.default.join(r,rl);await tl(n,Ch(t.data)),Ra.info({rootDir:r},"Config saved")}async function Sh(r,e){let t=await J(r),s={...t,...e,healing:{...t.healing,...e.healing??{}},reporting:{...t.reporting,...e.reporting??{}},environmentUrls:{...t.environmentUrls,...e.environmentUrls??{}}};return await Mn(r,s),s}async function Eh(r){let e=us.default.join(r,ka),t=us.default.join(r,rl);return await Fn.default.pathExists(e)||await Fn.default.pathExists(t)}async function Ih(r){let e=await J(r);if(!e.baseUrl)throw new Re('baseUrl is required in autotest.config.ts. Set it to your application URL, e.g. "http://localhost:3000".',"CONFIG_BASE_URL_MISSING");Ra.debug({config:e},"Config validated successfully")}function Ch(r){return`import { defineConfig } from 'assuremind';
|
|
41
41
|
|
|
42
42
|
export default defineConfig({
|
|
43
43
|
baseUrl: '${r.baseUrl}',
|
|
@@ -62,7 +62,7 @@ export default defineConfig({
|
|
|
62
62
|
},
|
|
63
63
|
studioPort: ${r.studioPort},
|
|
64
64
|
});
|
|
65
|
-
`}var
|
|
65
|
+
`}var us,Fn,Ra,ka,rl,Me=b(()=>{"use strict";g();us=x(require("path")),Fn=x(require("fs-extra"));Ln();ge();j();ot();Ra=I("config-store"),ka="autotest.config.json",rl="autotest.config.ts"});var sl=b(()=>{"use strict";g()});var Aa,jn,nl=b(()=>{"use strict";g();j();Aa=I("websocket"),jn=class{sockets=new Set;add(e){this.sockets.add(e),Aa.debug({total:this.sockets.size},"WS client connected"),e.on("close",()=>{this.sockets.delete(e),Aa.debug({total:this.sockets.size},"WS client disconnected")}),e.on("error",t=>{Aa.warn({err:t},"WS socket error"),this.sockets.delete(e)})}broadcast(e,t){if(this.sockets.size===0)return;let s=JSON.stringify({event:e,data:t,timestamp:Date.now()});for(let n of this.sockets)n.readyState===1&&n.send(s)}get clientCount(){return this.sockets.size}}});async function il(r,e){let{default:t}=await import("@fastify/static"),s=en.default.resolve(__dirname,"../../node_modules/playwright-core/lib/vite/traceViewer");await ds.default.pathExists(s)?(await r.register(t,{root:s,prefix:"/trace/",decorateReply:!1}),Zs.info({traceViewerDir:s},"Serving Playwright Trace Viewer at /trace/")):Zs.warn({traceViewerDir:s},"Playwright Trace Viewer not found \u2014 skipping");let n=en.default.join(e,"results","allure-report");await ds.default.ensureDir(n),Zs.info({allureReportDir:n},"Per-run Allure reports will be served at /allure/<runId>");let i=en.default.resolve(__dirname,"../../ui/dist");await ds.default.pathExists(i)?(await r.register(t,{root:i,prefix:"/",decorateReply:!0,index:"index.html"}),Zs.info({uiDist:i},"Serving React UI"),r.setNotFoundHandler(async(o,c)=>{let{url:l,method:u}=o;if(u==="GET"&&l.startsWith("/allure/")){let m=l.slice(8).replace(/\/$/,"").split("/")[0];if(m){let p=en.default.join(e,"results","allure-report",m,"index.html");if(await ds.default.pathExists(p)){let f=await ds.default.readFile(p,"utf-8");return c.code(200).type("text/html").send(f)}}return c.code(200).type("text/html").send(`<!DOCTYPE html>
|
|
66
66
|
<html lang="en">
|
|
67
67
|
<head><meta charset="UTF-8"><title>Allure Report \u2014 Not Generated</title>
|
|
68
68
|
<style>
|
|
@@ -88,31 +88,31 @@ export default defineConfig({
|
|
|
88
88
|
<p style="margin-top:1.2rem;font-size:.8rem;color:#94a3b8">Report directory: <code>results/allure-report/<runId>/</code></p>
|
|
89
89
|
</div>
|
|
90
90
|
</body>
|
|
91
|
-
</html>`)}return u==="GET"&&!l.startsWith("/api/")&&!l.startsWith("/trace/")&&l!=="/ws"?c.sendFile("index.html"):c.code(404).send({message:`Route ${u}:${l} not found`,error:"Not Found",statusCode:404})})):(
|
|
92
|
-
`)}async function tl(r){let e=r.rootDir,t=Qc.default.join(e,".env");r.get("/env",async(s,n)=>{try{let i=await Zr.default.pathExists(t)?await Zr.default.readFile(t,"utf-8"):"",a=Eh(i),o={},c={};for(let l of ya){let u=a[l.key]??"";l.type==="secret"?(o[l.key]="",c[l.key]=u.length>0):o[l.key]=u}return n.send({groups:Zc,values:o,secretSet:c,providerLabels:bh})}catch(i){return w(n,500,"Failed to read .env",i)}}),r.put("/env",async(s,n)=>{let i=Ih.safeParse(s.body);if(!i.success)return n.status(400).send({error:"Invalid env payload",details:i.error.issues});try{let a={};for(let[l,u]of Object.entries(i.data.values))vh.get(l)&&(xh.has(l)&&u===""||(a[l]=u));if(Object.keys(a).length===0)return n.send({ok:!0,updated:0});let o=await Zr.default.pathExists(t)?await Zr.default.readFile(t,"utf-8"):"",c=Ch(o,a);await Zr.default.writeFile(t,c,"utf-8");for(let[l,u]of Object.entries(a))process.env[l]=u;return yh.info({keys:Object.keys(a),path:t},".env updated from Studio"),n.send({ok:!0,updated:Object.keys(a).length,path:t})}catch(a){return w(n,500,"Failed to update .env",a)}})}var Qc,Zr,zs,yh,wh,bh,Zc,ya,vh,xh,el,Ih,rl=b(()=>{"use strict";m();Qc=x(require("path")),Zr=x(require("fs-extra")),zs=require("zod");te();F();yh=C("routes/env-config"),wh=["anthropic","openai","google","azure-openai","bedrock","deepseek","groq","together","qwen","perplexity","ollama","custom"],bh={anthropic:"Anthropic (Claude)",openai:"OpenAI (GPT)",google:"Google (Gemini)","azure-openai":"Azure OpenAI",bedrock:"AWS Bedrock",deepseek:"DeepSeek",groq:"Groq",together:"Together AI",qwen:"Qwen (Alibaba)",perplexity:"Perplexity",ollama:"Ollama (local)",custom:"Custom (OpenAI-compatible)"},Zc=[{id:"ai-core",title:"AI Provider",description:"Choose the AI provider used to generate test code.",fields:[{key:"AI_PROVIDER",label:"Provider",type:"select",options:["ollama","perplexity",...wh.filter(r=>r!=="ollama"&&r!=="perplexity")],default:"ollama",hint:"Which AI provider AssureMind uses."},{key:"AI_TIMEOUT",label:"Request timeout (s)",type:"number",placeholder:"e.g. 30 (use 120+ for Ollama)",default:"30",hint:"Use 120+ for local models (Ollama)."},{key:"AI_MAX_RETRIES",label:"Max retries",type:"number",placeholder:"e.g. 2 (0\u201310)",default:"2",hint:"Retries on a failed AI call (0\u201310)."}]},{id:"p-anthropic",title:"Anthropic",provider:"anthropic",fields:[{key:"ANTHROPIC_API_KEY",label:"API Key",type:"secret",provider:"anthropic",placeholder:"sk-ant-...",hint:"console.anthropic.com"},{key:"ANTHROPIC_MODEL",label:"Model",type:"text",provider:"anthropic",placeholder:"e.g. claude-sonnet-4-20250514",recommended:["claude-sonnet-4-20250514","claude-3-5-sonnet-20241022","claude-3-5-haiku-20241022"]}]},{id:"p-openai",title:"OpenAI",provider:"openai",fields:[{key:"OPENAI_API_KEY",label:"API Key",type:"secret",provider:"openai",placeholder:"sk-...",hint:"platform.openai.com"},{key:"OPENAI_MODEL",label:"Model",type:"text",provider:"openai",placeholder:"e.g. gpt-4o",recommended:["gpt-4o","gpt-4o-mini","gpt-4-turbo","o1-mini"]}]},{id:"p-google",title:"Google Gemini",provider:"google",fields:[{key:"GOOGLE_API_KEY",label:"API Key",type:"secret",provider:"google",placeholder:"AIza...",hint:"aistudio.google.com"},{key:"GOOGLE_MODEL",label:"Model",type:"text",provider:"google",placeholder:"e.g. gemini-2.5-pro",recommended:["gemini-2.5-pro","gemini-2.5-flash","gemini-1.5-pro"]}]},{id:"p-azure",title:"Azure OpenAI",provider:"azure-openai",fields:[{key:"AZURE_OPENAI_API_KEY",label:"API Key",type:"secret",provider:"azure-openai",placeholder:"your Azure resource key"},{key:"AZURE_OPENAI_ENDPOINT",label:"Endpoint",type:"text",provider:"azure-openai",placeholder:"https://<resource>.openai.azure.com"},{key:"AZURE_OPENAI_DEPLOYMENT",label:"Deployment",type:"text",provider:"azure-openai",placeholder:"e.g. gpt-4o"},{key:"AZURE_OPENAI_API_VERSION",label:"API version",type:"text",provider:"azure-openai",placeholder:"e.g. 2024-10-21",recommended:["2024-10-21","2024-08-01-preview","2024-06-01"]}]},{id:"p-bedrock",title:"AWS Bedrock",provider:"bedrock",fields:[{key:"AWS_ACCESS_KEY_ID",label:"Access Key ID",type:"secret",provider:"bedrock",placeholder:"AKIA..."},{key:"AWS_SECRET_ACCESS_KEY",label:"Secret Access Key",type:"secret",provider:"bedrock",placeholder:"your AWS secret access key"},{key:"AWS_SESSION_TOKEN",label:"Session Token (optional)",type:"secret",provider:"bedrock",placeholder:"only for temporary STS credentials"},{key:"AWS_REGION",label:"Region",type:"text",provider:"bedrock",placeholder:"e.g. us-east-1",recommended:["us-east-1","us-west-2","eu-west-1","ap-southeast-1"]},{key:"BEDROCK_MODEL",label:"Model",type:"text",provider:"bedrock",placeholder:"e.g. anthropic.claude-sonnet-4-20250514-v1:0",recommended:["anthropic.claude-sonnet-4-20250514-v1:0","anthropic.claude-3-5-sonnet-20241022-v2:0","anthropic.claude-3-5-haiku-20241022-v1:0"]}]},{id:"p-deepseek",title:"DeepSeek",provider:"deepseek",fields:[{key:"DEEPSEEK_API_KEY",label:"API Key",type:"secret",provider:"deepseek",placeholder:"sk-... (platform.deepseek.com)"},{key:"DEEPSEEK_MODEL",label:"Model",type:"text",provider:"deepseek",placeholder:"e.g. deepseek-chat",recommended:["deepseek-chat","deepseek-reasoner"]}]},{id:"p-groq",title:"Groq",provider:"groq",fields:[{key:"GROQ_API_KEY",label:"API Key",type:"secret",provider:"groq",placeholder:"gsk_... (console.groq.com)"},{key:"GROQ_MODEL",label:"Model",type:"text",provider:"groq",placeholder:"e.g. llama-3.3-70b-versatile",recommended:["llama-3.3-70b-versatile","llama-3.1-8b-instant","mixtral-8x7b-32768"]}]},{id:"p-together",title:"Together AI",provider:"together",fields:[{key:"TOGETHER_API_KEY",label:"API Key",type:"secret",provider:"together",placeholder:"your Together API key"},{key:"TOGETHER_MODEL",label:"Model",type:"text",provider:"together",placeholder:"e.g. meta-llama/Llama-3.3-70B-Instruct-Turbo",recommended:["meta-llama/Llama-3.3-70B-Instruct-Turbo","meta-llama/Llama-3.1-8B-Instruct-Turbo","Qwen/Qwen2.5-Coder-32B-Instruct"]}]},{id:"p-qwen",title:"Qwen",provider:"qwen",fields:[{key:"QWEN_API_KEY",label:"API Key",type:"secret",provider:"qwen",placeholder:"sk-... (dashscope.aliyun.com)"},{key:"QWEN_BASE_URL",label:"Base URL",type:"text",provider:"qwen",placeholder:"https://dashscope-intl.aliyuncs.com/compatible-mode/v1"},{key:"QWEN_MODEL",label:"Model",type:"text",provider:"qwen",placeholder:"e.g. qwen-max",recommended:["qwen-max","qwen-plus","qwen-turbo","qwen2.5-coder-32b-instruct"]}]},{id:"p-perplexity",title:"Perplexity",provider:"perplexity",fields:[{key:"PERPLEXITY_API_KEY",label:"API Key",type:"secret",provider:"perplexity",placeholder:"pplx-..."},{key:"PERPLEXITY_MODEL",label:"Model",type:"text",provider:"perplexity",placeholder:"e.g. sonar-pro",recommended:["sonar-pro","sonar","sonar-reasoning"]}]},{id:"p-ollama",title:"Ollama (local)",provider:"ollama",fields:[{key:"OLLAMA_BASE_URL",label:"Base URL",type:"text",provider:"ollama",placeholder:"http://localhost:11434",default:"http://localhost:11434"},{key:"OLLAMA_MODEL",label:"Model",type:"text",provider:"ollama",placeholder:"e.g. qwen2.5-coder:14b",default:"qwen2.5-coder:14b",recommended:["qwen2.5-coder:14b","qwen2.5-coder:7b","qwen2.5-coder:3b","llama3.3","llama3.2","llama3.2:1b","codellama","mistral"]}]},{id:"p-custom",title:"Custom (OpenAI-compatible)",provider:"custom",fields:[{key:"CUSTOM_API_KEY",label:"API Key",type:"secret",provider:"custom",placeholder:"your API key"},{key:"CUSTOM_BASE_URL",label:"Base URL",type:"text",provider:"custom",placeholder:"https://your-endpoint/v1"},{key:"CUSTOM_MODEL",label:"Model",type:"text",provider:"custom",placeholder:"e.g. my-model"}]},{id:"jira",title:"Jira",description:"Generate tests from Jira issues.",fields:[{key:"JIRA_BASE_URL",label:"Base URL",type:"text",placeholder:"https://your-domain.atlassian.net"},{key:"JIRA_EMAIL",label:"Email",type:"text",placeholder:"you@company.com"},{key:"JIRA_API_TOKEN",label:"API Token",type:"secret",placeholder:"Atlassian API token",hint:"id.atlassian.com \u2192 API tokens"}]},{id:"xray",title:"Xray (Jira)",description:"Sync tests to Jira Xray. Requires Jira above.",fields:[{key:"XRAY_PROJECT_KEY",label:"Project Key",type:"text",placeholder:"e.g. PROJ"},{key:"XRAY_CLIENT_ID",label:"Client ID (Cloud)",type:"text",placeholder:"Xray Cloud client ID (blank for Server/DC)"},{key:"XRAY_CLIENT_SECRET",label:"Client Secret (Cloud)",type:"secret",placeholder:"Xray Cloud client secret"},{key:"XRAY_TEST_ISSUE_TYPE",label:"Test issue type",type:"text",placeholder:"e.g. Test"},{key:"XRAY_TEST_SET_ISSUE_TYPE",label:"Test Set issue type",type:"text",placeholder:"e.g. Test Set"}]},{id:"github",title:"GitHub",description:"Auto-PR on self-healing & PR comments.",fields:[{key:"GITHUB_TOKEN",label:"Personal Access Token",type:"secret",placeholder:"ghp_... (repo scope)",hint:"repo scope"},{key:"GITHUB_REPO_OWNER",label:"Repo Owner",type:"text",placeholder:"e.g. my-org"},{key:"GITHUB_REPO_NAME",label:"Repo Name",type:"text",placeholder:"e.g. my-repo"},{key:"GITHUB_BASE_BRANCH",label:"Base Branch",type:"text",placeholder:"e.g. main",recommended:["main","master","develop"]}]},{id:"advanced",title:"Advanced",description:"Framework runtime settings.",fields:[{key:"NODE_ENV",label:"Node env",type:"select",options:["development","production","test"]},{key:"LOG_LEVEL",label:"Log level",type:"select",options:["trace","debug","info","warn","error","fatal"]}]}],ya=Zc.flatMap(r=>r.fields),vh=new Map(ya.map(r=>[r.key,r])),xh=new Set(ya.filter(r=>r.type==="secret").map(r=>r.key)),el=/^(\s*)([A-Za-z_][A-Za-z0-9_]*)\s*=(.*)$/;Ih=zs.z.object({values:zs.z.record(zs.z.string(),zs.z.string())})});var $,qs,Ph,sl,Rh,wa,ba,nl,va,Ks=b(()=>{"use strict";m();$=require("zod"),qs=$.z.object({id:$.z.string().min(1),order:$.z.number().int().positive(),instruction:$.z.string().min(1),generatedCode:$.z.string(),strategy:$.z.enum(["template","cache","rag","batch","fast","primary","recorder"]),stepType:$.z.enum(["ui","api","mock"]).default("ui"),lastHealed:$.z.string().nullable(),timeout:$.z.number().int().positive().optional(),retries:$.z.number().int().min(0).optional(),mockUrl:$.z.string().optional(),mockResponse:$.z.string().optional(),mockStatus:$.z.number().int().optional(),runAudit:$.z.boolean().optional(),soft:$.z.boolean().optional(),visualCheck:$.z.boolean().optional()}),Ph=$.z.object({type:$.z.enum(["inline","json-file","csv-file"]),path:$.z.string().optional(),data:$.z.array($.z.record($.z.string())).optional()}).optional(),sl=$.z.object({id:$.z.string(),instruction:$.z.string(),generatedCode:$.z.string().default(""),order:$.z.number().int().default(0)}),Rh=$.z.object({before:$.z.array(sl).default([]),after:$.z.array(sl).default([])}).default({before:[],after:[]}),wa=$.z.object({id:$.z.string().min(1),name:$.z.string().min(1),description:$.z.string(),tags:$.z.array($.z.string()),priority:$.z.enum(["critical","high","medium","low"]),timeout:$.z.number().int().positive().optional(),dataSource:Ph,steps:$.z.array(qs),caseHooks:Rh,lighthouseCategories:$.z.array($.z.enum(["performance","accessibility","seo"])).default(["performance","accessibility","seo"]),createdAt:$.z.string().datetime(),updatedAt:$.z.string().datetime()}),ba=$.z.object({id:$.z.string().min(1),name:$.z.string().min(1),description:$.z.string(),tags:$.z.array($.z.string()),type:$.z.enum(["ui","api","audit","performance"]).default("ui"),timeout:$.z.number().int().positive().optional(),createdAt:$.z.string().datetime(),updatedAt:$.z.string().datetime()}),nl=$.z.enum(["before_all","before_each","after_each","after_all"]),va=$.z.object({before_all:$.z.array(qs).default([]),before_each:$.z.array(qs).default([]),after_each:$.z.array(qs).default([]),after_all:$.z.array(qs).default([])})});function es(r){let e=/^```(?:typescript|javascript|ts|js)?\n?([\s\S]*?)```\s*$/m,t=r.match(e);return t?.[1]!==void 0?t[1].trim():r.trim()}function Rn(r){let e=es(r);try{return JSON.parse(e),e}catch{}let t=e.indexOf("{"),s=e.indexOf("["),n=t===-1?s:s===-1?t:Math.min(t,s);if(n===-1)return e;let i=e[n],a=i==="{"?"}":"]",o=0,c=!1,l=!1;for(let u=n;u<e.length;u++){let d=e[u];if(l){l=!1;continue}if(d==="\\"&&c){l=!0;continue}if(d==='"'){c=!c;continue}if(!c){if(d===i)o++;else if(d===a&&(o--,o===0))return e.slice(n,u+1)}}return e.slice(n)}function kh(r){for(let e of Th)if(e.test(r))throw new K(`Generated code contains forbidden pattern: ${e.source}. This may be a prompt injection attempt. Please regenerate the step.`,"generatedCode","UNSAFE_CODE")}function ts(r,e){let t=/\bbutton\b/i.test(r)?"button":/\blink\b/i.test(r)?"link":null;return t===null?e:e.replace(/page\.getByText\((['"])(.*?)\1\)\.click\(\)/g,(s,n,i)=>`page.getByRole('${t}', { name: '${i}' }).click()`)}function il(r){let e=r.replace(Ah,"").trim();return e.length>0?e:r}function $h(r){let e=r;return e=e.replace(/(getBy(?:Label|Placeholder)\(\s*)(['"`])([\s\S]*?)\2/g,(t,s,n,i)=>`${s}${n}${il(i)}${n}`),e=e.replace(/(name:\s*)(['"`])([\s\S]*?)\2/g,(t,s,n,i)=>`${s}${n}${il(i)}${n}`),e}function _h(r){return r.replace(/\blocator\((['"`])([\s\S]*?)\1\)/g,(e,t,s)=>{if(!s.includes(" >> "))return e;let n=s.split(" >> ").map(i=>i.trim()).filter(Boolean);return n.length<2?e:n.map(i=>`locator(${t}${i}${t})`).join(".")})}function rs(r){let e=r;return e=_h(e),e=e.replace(/\.or\((?:[^()]*|\([^()]*\))*\)(?:\.(?:first|last)\(\))?/g,""),e=e.replace(/\.(?:first|last)\(\)(?=\.\w)/g,""),e=$h(e),e}function De(r){let e=es(r);if(!e)throw new K("AI returned an empty code response. Please try regenerating.","generatedCode","EMPTY_CODE");let t=rs(e);return kh(t),t}function kt(r){return r.toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}var Th,Ah,At=b(()=>{"use strict";m();re();Th=[/\brequire\s*\(/,/\bimport\s*\(/,/\bprocess\b/,/\bchild_process\b/,/\bexec\s*\(/,/\bspawn\s*\(/,/\beval\s*\(/,/\bFunction\s*\(/,/\b__dirname\b/,/\b__filename\b/,/\bglobal\b/,/\bwindow\.location\.href\s*=/,/\bdocument\.cookie\b/,/\blocalStorage\b/,/\bsessionStorage\b/,/\bIndexedDB\b/,/\bXMLHttpRequest\b/,/\bfetch\s*\(/,/\bWebSocket\s*\(/];Ah=/\s+(?:field|input|box|textbox|text\s?box|drop-?\s?down|dropdown|combo\s?box|combobox|menu|selector|element|checkbox|radio\s?button|radio)$/i});var Ia={};Q(Ia,{createSuite:()=>Ws,deleteSuite:()=>Sa,findSuiteDirById:()=>de,listSuiteDirs:()=>$t,listSuites:()=>Ar,listSuitesWithCounts:()=>Ca,moveSuiteType:()=>Ea,readSuite:()=>ve,updateSuite:()=>kn,writeSuite:()=>xa});async function ve(r){let e=Re.default.join(r,Tn);if(!await qe.default.pathExists(e))throw new oe(`Suite file not found at "${e}". Ensure the suite directory exists and contains a suite.json file.`,e,"SUITE_NOT_FOUND");let t=await ue(e),s=ba.safeParse(t);if(!s.success){let i=s.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
|
|
93
|
-
`);throw new
|
|
94
|
-
${i}`,"suite","INVALID_SUITE")}if(!t.type){let i=
|
|
95
|
-
`);throw new
|
|
96
|
-
${n}`,"suite","INVALID_SUITE")}await
|
|
97
|
-
`);throw new
|
|
98
|
-
${s}`,"case","INVALID_CASE")}return t.data}async function
|
|
99
|
-
`);throw new
|
|
100
|
-
${s}`,"case","INVALID_CASE")}await
|
|
101
|
-
`);throw new
|
|
102
|
-
${s}`,"variables","INVALID_VARIABLES")}return t.data}async function
|
|
103
|
-
`);throw new
|
|
104
|
-
${s}`,"variables","INVALID_VARIABLES")}await
|
|
105
|
-
`)}function
|
|
106
|
-
`)}var
|
|
107
|
-
await page.keyboard.press('Enter');`},{regex:/^select\s+["'](.+?)["']\s+from\s+(?:the\s+)?(.+)$/i,gen:r=>`await page.getByRole('combobox', { name: '${
|
|
108
|
-
`)){if(!t.trim())continue;let s=t.match(/frame="([^"]*)"/),n=s?s[1]:null,i=[],a=t.match(/^[\w-]+\s+"([^"]*)"/);a&&i.push(a[1]);for(let o of t.matchAll(/(?:text|label|aria-label|placeholder)="([^"]*)"/g))i.push(o[1]);for(let o of i){let c=o.trim().toLowerCase();if(!c)continue;let l=e.get(c);l?l.frame!==n&&(l.ambiguous=!0):e.set(c,{frame:n,ambiguous:!1})}}return e}function
|
|
109
|
-
`),t="",s=[],n=[];for(let l of e){let u=l.trim();if(!u||u==="-")continue;let d=
|
|
110
|
-
`),accessibilityTree:r.slice(0,8e3)}}var
|
|
91
|
+
</html>`)}return u==="GET"&&!l.startsWith("/api/")&&!l.startsWith("/trace/")&&l!=="/ws"?c.sendFile("index.html"):c.code(404).send({message:`Route ${u}:${l} not found`,error:"Not Found",statusCode:404})})):(Zs.warn({uiDist:i},'UI not built \u2014 serving placeholder. Run "npm run build:ui" first.'),r.get("/",(o,c)=>{c.type("application/json").send({message:"Assuremind Studio",status:"ui-not-built",note:'Run "npm run build:ui" to build the React UI, then restart the server.'})}))}var en,ds,Zs,al=b(()=>{"use strict";g();en=x(require("path")),ds=x(require("fs-extra"));j();Zs=I("static")});function w(r,e,t,s){let n=s instanceof Ir?s.code:"INTERNAL_ERROR",i=s instanceof Error?s.message:s!==void 0?String(s):void 0,a=s instanceof Re||Ph.has(n),o=a?422:e,c=i?.includes("JIRA_NOT_CONFIGURED")||i?.includes("Jira not configured"),l=i?.includes("XRAY_NOT_CONFIGURED")||i?.includes("Xray not configured"),u=c?"JIRA_NOT_CONFIGURED":l?"XRAY_NOT_CONFIGURED":a?"AI_NOT_CONFIGURED":void 0;return o>=500?ol.error({err:s},t):ol.debug({err:s},t),r.status(o).send({error:t,...i&&i!==t&&{detail:i},code:n,...u&&{featureHint:u}})}var ol,Ph,de=b(()=>{"use strict";g();ge();j();ol=I("server"),Ph=new Set(["ENV_VALIDATION_FAILED","PROVIDER_ENV_MISSING","UNKNOWN_PROVIDER","CONFIG_ERROR"])});async function cl(r){let e=r.rootDir;r.get("/config",async(t,s)=>{try{let n=await J(e);return s.send(n)}catch(n){return w(s,500,"Failed to read config",n)}}),r.put("/config",async(t,s)=>{let n=Rh.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid config",details:n.error.issues});try{let i=await J(e),a=zr.parse({...i,...n.data});return await Mn(e,a),s.send(a)}catch(i){return w(s,500,"Failed to update config",i)}}),r.get("/ai-provider",async(t,s)=>{let n=(process.env.AI_PROVIDER??"").trim()||"unknown",a={anthropic:{key:"ANTHROPIC_MODEL",fallback:"claude-sonnet-4-20250514"},openai:{key:"OPENAI_MODEL",fallback:"gpt-4o"},google:{key:"GOOGLE_MODEL",fallback:"gemini-2.5-pro"},"azure-openai":{key:"AZURE_OPENAI_DEPLOYMENT",fallback:"gpt-4o"},bedrock:{key:"BEDROCK_MODEL",fallback:"anthropic.claude-sonnet-4-20250514-v1:0"},deepseek:{key:"DEEPSEEK_MODEL",fallback:"deepseek-chat"},groq:{key:"GROQ_MODEL",fallback:"llama-3.3-70b-versatile"},together:{key:"TOGETHER_MODEL",fallback:"meta-llama/Llama-3.3-70B-Instruct-Turbo"},qwen:{key:"QWEN_MODEL",fallback:"qwen-max"},perplexity:{key:"PERPLEXITY_MODEL",fallback:"sonar-pro"},ollama:{key:"OLLAMA_MODEL",fallback:"qwen2.5-coder:14b"},custom:{key:"CUSTOM_MODEL",fallback:"custom"}}[n],o=a?(process.env[a.key]??"").trim()||a.fallback:"unknown";return s.send({provider:n,model:o})}),r.get("/config/status",async(t,s)=>{let n=ar(),i=process.env.JIRA_BASE_URL,a=process.env.JIRA_EMAIL,o=process.env.JIRA_API_TOKEN,c=!!(i&&a&&o),l=process.env.XRAY_PROJECT_KEY,u=c&&!!l;return s.send({ai:{configured:!!n,provider:n?String(n.AI_PROVIDER):null},jira:{configured:c},xray:{configured:u}})}),r.get("/mcp/status",async(t,s)=>{try{let n=await J(e),i=n.mcp?.enabled??!1,a=!1;try{require.resolve("@playwright/mcp"),a=!0}catch{}return s.send({enabled:i,packageAvailable:a,headless:n.mcp?.headless??!0,actThenScript:n.mcp?.actThenScript??!1,proactiveHealing:n.mcp?.proactiveHealing??!1})}catch(n){return w(s,500,"Failed to check MCP status",n)}})}var Rh,ll=b(()=>{"use strict";g();Me();Ln();Pr();de();Rh=zr.partial()});function Oh(r){let e=r.trim();return e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function Lh(r){let e={};for(let t of r.split(/\r?\n/)){if(!t||t.trimStart().startsWith("#"))continue;let s=t.match(ml);s&&(e[s[2]]=Oh(s[3]??""))}return e}function ul(r){return r===""?"":/[\s#"'=]/.test(r)?`"${r.replace(/"/g,'\\"')}"`:r}function Dh(r,e){let t=r.split(/\r?\n/),s=new Set,n=t.map(a=>{let o=a.match(ml);if(o&&Object.prototype.hasOwnProperty.call(e,o[2])){let c=o[2];return s.add(c),`${o[1]??""}${c}=${ul(e[c])}`}return a}),i=Object.keys(e).filter(a=>!s.has(a)&&e[a]!=="");if(i.length>0){n.length>0&&n[n.length-1]!==""&&n.push(""),n.push("# \u2500\u2500 Added via AssureMind Studio \u2500\u2500");for(let a of i)n.push(`${a}=${ul(e[a])}`)}return n.join(`
|
|
92
|
+
`)}async function gl(r){let e=r.rootDir,t=dl.default.join(e,".env");r.get("/env",async(s,n)=>{try{let i=await ps.default.pathExists(t)?await ps.default.readFile(t,"utf-8"):"",a=Lh(i),o={},c={};for(let l of $a){let u=a[l.key]??"";l.type==="secret"?(o[l.key]="",c[l.key]=u.length>0):o[l.key]=u}return n.send({groups:pl,values:o,secretSet:c,providerLabels:Ah})}catch(i){return w(n,500,"Failed to read .env",i)}}),r.put("/env",async(s,n)=>{let i=Nh.safeParse(s.body);if(!i.success)return n.status(400).send({error:"Invalid env payload",details:i.error.issues});try{let a={};for(let[l,u]of Object.entries(i.data.values))$h.get(l)&&(_h.has(l)&&u===""||(a[l]=u));if(Object.keys(a).length===0)return n.send({ok:!0,updated:0});let o=await ps.default.pathExists(t)?await ps.default.readFile(t,"utf-8"):"",c=Dh(o,a);await ps.default.writeFile(t,c,"utf-8");for(let[l,u]of Object.entries(a))process.env[l]=u;return kh.info({keys:Object.keys(a),path:t},".env updated from Studio"),n.send({ok:!0,updated:Object.keys(a).length,path:t})}catch(a){return w(n,500,"Failed to update .env",a)}})}var dl,ps,tn,kh,Th,Ah,pl,$a,$h,_h,ml,Nh,fl=b(()=>{"use strict";g();dl=x(require("path")),ps=x(require("fs-extra")),tn=require("zod");de();j();kh=I("routes/env-config"),Th=["anthropic","openai","google","azure-openai","bedrock","deepseek","groq","together","qwen","perplexity","ollama","custom"],Ah={anthropic:"Anthropic (Claude)",openai:"OpenAI (GPT)",google:"Google (Gemini)","azure-openai":"Azure OpenAI",bedrock:"AWS Bedrock",deepseek:"DeepSeek",groq:"Groq",together:"Together AI",qwen:"Qwen (Alibaba)",perplexity:"Perplexity",ollama:"Ollama (local)",custom:"Custom (OpenAI-compatible)"},pl=[{id:"ai-core",title:"AI Provider",description:"Choose the AI provider used to generate test code.",fields:[{key:"AI_PROVIDER",label:"Provider",type:"select",options:["ollama","perplexity",...Th.filter(r=>r!=="ollama"&&r!=="perplexity")],default:"ollama",hint:"Which AI provider AssureMind uses."},{key:"AI_TIMEOUT",label:"Request timeout (s)",type:"number",placeholder:"e.g. 30 (use 120+ for Ollama)",default:"30",hint:"Use 120+ for local models (Ollama)."},{key:"AI_MAX_RETRIES",label:"Max retries",type:"number",placeholder:"e.g. 2 (0\u201310)",default:"2",hint:"Retries on a failed AI call (0\u201310)."}]},{id:"p-anthropic",title:"Anthropic",provider:"anthropic",fields:[{key:"ANTHROPIC_API_KEY",label:"API Key",type:"secret",provider:"anthropic",placeholder:"sk-ant-...",hint:"console.anthropic.com"},{key:"ANTHROPIC_MODEL",label:"Model",type:"text",provider:"anthropic",placeholder:"e.g. claude-sonnet-4-20250514",recommended:["claude-sonnet-4-20250514","claude-3-5-sonnet-20241022","claude-3-5-haiku-20241022"]}]},{id:"p-openai",title:"OpenAI",provider:"openai",fields:[{key:"OPENAI_API_KEY",label:"API Key",type:"secret",provider:"openai",placeholder:"sk-...",hint:"platform.openai.com"},{key:"OPENAI_MODEL",label:"Model",type:"text",provider:"openai",placeholder:"e.g. gpt-4o",recommended:["gpt-4o","gpt-4o-mini","gpt-4-turbo","o1-mini"]}]},{id:"p-google",title:"Google Gemini",provider:"google",fields:[{key:"GOOGLE_API_KEY",label:"API Key",type:"secret",provider:"google",placeholder:"AIza...",hint:"aistudio.google.com"},{key:"GOOGLE_MODEL",label:"Model",type:"text",provider:"google",placeholder:"e.g. gemini-2.5-pro",recommended:["gemini-2.5-pro","gemini-2.5-flash","gemini-1.5-pro"]}]},{id:"p-azure",title:"Azure OpenAI",provider:"azure-openai",fields:[{key:"AZURE_OPENAI_API_KEY",label:"API Key",type:"secret",provider:"azure-openai",placeholder:"your Azure resource key"},{key:"AZURE_OPENAI_ENDPOINT",label:"Endpoint",type:"text",provider:"azure-openai",placeholder:"https://<resource>.openai.azure.com"},{key:"AZURE_OPENAI_DEPLOYMENT",label:"Deployment",type:"text",provider:"azure-openai",placeholder:"e.g. gpt-4o"},{key:"AZURE_OPENAI_API_VERSION",label:"API version",type:"text",provider:"azure-openai",placeholder:"e.g. 2024-10-21",recommended:["2024-10-21","2024-08-01-preview","2024-06-01"]}]},{id:"p-bedrock",title:"AWS Bedrock",provider:"bedrock",fields:[{key:"AWS_ACCESS_KEY_ID",label:"Access Key ID",type:"secret",provider:"bedrock",placeholder:"AKIA..."},{key:"AWS_SECRET_ACCESS_KEY",label:"Secret Access Key",type:"secret",provider:"bedrock",placeholder:"your AWS secret access key"},{key:"AWS_SESSION_TOKEN",label:"Session Token (optional)",type:"secret",provider:"bedrock",placeholder:"only for temporary STS credentials"},{key:"AWS_REGION",label:"Region",type:"text",provider:"bedrock",placeholder:"e.g. us-east-1",recommended:["us-east-1","us-west-2","eu-west-1","ap-southeast-1"]},{key:"BEDROCK_MODEL",label:"Model",type:"text",provider:"bedrock",placeholder:"e.g. anthropic.claude-sonnet-4-20250514-v1:0",recommended:["anthropic.claude-sonnet-4-20250514-v1:0","anthropic.claude-3-5-sonnet-20241022-v2:0","anthropic.claude-3-5-haiku-20241022-v1:0"]}]},{id:"p-deepseek",title:"DeepSeek",provider:"deepseek",fields:[{key:"DEEPSEEK_API_KEY",label:"API Key",type:"secret",provider:"deepseek",placeholder:"sk-... (platform.deepseek.com)"},{key:"DEEPSEEK_MODEL",label:"Model",type:"text",provider:"deepseek",placeholder:"e.g. deepseek-chat",recommended:["deepseek-chat","deepseek-reasoner"]}]},{id:"p-groq",title:"Groq",provider:"groq",fields:[{key:"GROQ_API_KEY",label:"API Key",type:"secret",provider:"groq",placeholder:"gsk_... (console.groq.com)"},{key:"GROQ_MODEL",label:"Model",type:"text",provider:"groq",placeholder:"e.g. llama-3.3-70b-versatile",recommended:["llama-3.3-70b-versatile","llama-3.1-8b-instant","mixtral-8x7b-32768"]}]},{id:"p-together",title:"Together AI",provider:"together",fields:[{key:"TOGETHER_API_KEY",label:"API Key",type:"secret",provider:"together",placeholder:"your Together API key"},{key:"TOGETHER_MODEL",label:"Model",type:"text",provider:"together",placeholder:"e.g. meta-llama/Llama-3.3-70B-Instruct-Turbo",recommended:["meta-llama/Llama-3.3-70B-Instruct-Turbo","meta-llama/Llama-3.1-8B-Instruct-Turbo","Qwen/Qwen2.5-Coder-32B-Instruct"]}]},{id:"p-qwen",title:"Qwen",provider:"qwen",fields:[{key:"QWEN_API_KEY",label:"API Key",type:"secret",provider:"qwen",placeholder:"sk-... (dashscope.aliyun.com)"},{key:"QWEN_BASE_URL",label:"Base URL",type:"text",provider:"qwen",placeholder:"https://dashscope-intl.aliyuncs.com/compatible-mode/v1"},{key:"QWEN_MODEL",label:"Model",type:"text",provider:"qwen",placeholder:"e.g. qwen-max",recommended:["qwen-max","qwen-plus","qwen-turbo","qwen2.5-coder-32b-instruct"]}]},{id:"p-perplexity",title:"Perplexity",provider:"perplexity",fields:[{key:"PERPLEXITY_API_KEY",label:"API Key",type:"secret",provider:"perplexity",placeholder:"pplx-..."},{key:"PERPLEXITY_MODEL",label:"Model",type:"text",provider:"perplexity",placeholder:"e.g. sonar-pro",recommended:["sonar-pro","sonar","sonar-reasoning"]}]},{id:"p-ollama",title:"Ollama (local)",provider:"ollama",fields:[{key:"OLLAMA_BASE_URL",label:"Base URL",type:"text",provider:"ollama",placeholder:"http://localhost:11434",default:"http://localhost:11434"},{key:"OLLAMA_MODEL",label:"Model",type:"text",provider:"ollama",placeholder:"e.g. qwen2.5-coder:14b",default:"qwen2.5-coder:14b",recommended:["qwen2.5-coder:14b","qwen2.5-coder:7b","qwen2.5-coder:3b","llama3.3","llama3.2","llama3.2:1b","codellama","mistral"]}]},{id:"p-custom",title:"Custom (OpenAI-compatible)",provider:"custom",fields:[{key:"CUSTOM_API_KEY",label:"API Key",type:"secret",provider:"custom",placeholder:"your API key"},{key:"CUSTOM_BASE_URL",label:"Base URL",type:"text",provider:"custom",placeholder:"https://your-endpoint/v1"},{key:"CUSTOM_MODEL",label:"Model",type:"text",provider:"custom",placeholder:"e.g. my-model"}]},{id:"jira",title:"Jira",description:"Generate tests from Jira issues.",fields:[{key:"JIRA_BASE_URL",label:"Base URL",type:"text",placeholder:"https://your-domain.atlassian.net"},{key:"JIRA_EMAIL",label:"Email",type:"text",placeholder:"you@company.com"},{key:"JIRA_API_TOKEN",label:"API Token",type:"secret",placeholder:"Atlassian API token",hint:"id.atlassian.com \u2192 API tokens"}]},{id:"xray",title:"Xray (Jira)",description:"Sync tests to Jira Xray. Requires Jira above.",fields:[{key:"XRAY_PROJECT_KEY",label:"Project Key",type:"text",placeholder:"e.g. PROJ"},{key:"XRAY_CLIENT_ID",label:"Client ID (Cloud)",type:"text",placeholder:"Xray Cloud client ID (blank for Server/DC)"},{key:"XRAY_CLIENT_SECRET",label:"Client Secret (Cloud)",type:"secret",placeholder:"Xray Cloud client secret"},{key:"XRAY_TEST_ISSUE_TYPE",label:"Test issue type",type:"text",placeholder:"e.g. Test"},{key:"XRAY_TEST_SET_ISSUE_TYPE",label:"Test Set issue type",type:"text",placeholder:"e.g. Test Set"}]},{id:"github",title:"GitHub",description:"Auto-PR on self-healing & PR comments.",fields:[{key:"GITHUB_TOKEN",label:"Personal Access Token",type:"secret",placeholder:"ghp_... (repo scope)",hint:"repo scope"},{key:"GITHUB_REPO_OWNER",label:"Repo Owner",type:"text",placeholder:"e.g. my-org"},{key:"GITHUB_REPO_NAME",label:"Repo Name",type:"text",placeholder:"e.g. my-repo"},{key:"GITHUB_BASE_BRANCH",label:"Base Branch",type:"text",placeholder:"e.g. main",recommended:["main","master","develop"]}]},{id:"advanced",title:"Advanced",description:"Framework runtime settings.",fields:[{key:"NODE_ENV",label:"Node env",type:"select",options:["development","production","test"]},{key:"LOG_LEVEL",label:"Log level",type:"select",options:["trace","debug","info","warn","error","fatal"]}]}],$a=pl.flatMap(r=>r.fields),$h=new Map($a.map(r=>[r.key,r])),_h=new Set($a.filter(r=>r.type==="secret").map(r=>r.key)),ml=/^(\s*)([A-Za-z_][A-Za-z0-9_]*)\s*=(.*)$/;Nh=tn.z.object({values:tn.z.record(tn.z.string(),tn.z.string())})});var O,rn,Fh,hl,Mh,_a,Oa,yl,La,sn=b(()=>{"use strict";g();O=require("zod"),rn=O.z.object({id:O.z.string().min(1),order:O.z.number().int().positive(),instruction:O.z.string().min(1),generatedCode:O.z.string(),strategy:O.z.enum(["template","cache","rag","batch","fast","primary","recorder"]),stepType:O.z.enum(["ui","api","mock"]).default("ui"),lastHealed:O.z.string().nullable(),timeout:O.z.number().int().positive().optional(),retries:O.z.number().int().min(0).optional(),mockUrl:O.z.string().optional(),mockResponse:O.z.string().optional(),mockStatus:O.z.number().int().optional(),runAudit:O.z.boolean().optional(),soft:O.z.boolean().optional(),visualCheck:O.z.boolean().optional()}),Fh=O.z.object({type:O.z.enum(["inline","json-file","csv-file"]),path:O.z.string().optional(),data:O.z.array(O.z.record(O.z.string())).optional()}).optional(),hl=O.z.object({id:O.z.string(),instruction:O.z.string(),generatedCode:O.z.string().default(""),order:O.z.number().int().default(0)}),Mh=O.z.object({before:O.z.array(hl).default([]),after:O.z.array(hl).default([])}).default({before:[],after:[]}),_a=O.z.object({id:O.z.string().min(1),name:O.z.string().min(1),description:O.z.string(),tags:O.z.array(O.z.string()),priority:O.z.enum(["critical","high","medium","low"]),timeout:O.z.number().int().positive().optional(),dataSource:Fh,steps:O.z.array(rn),caseHooks:Mh,lighthouseCategories:O.z.array(O.z.enum(["performance","accessibility","seo"])).default(["performance","accessibility","seo"]),createdAt:O.z.string().datetime(),updatedAt:O.z.string().datetime()}),Oa=O.z.object({id:O.z.string().min(1),name:O.z.string().min(1),description:O.z.string(),tags:O.z.array(O.z.string()),type:O.z.enum(["ui","api","audit","performance"]).default("ui"),timeout:O.z.number().int().positive().optional(),createdAt:O.z.string().datetime(),updatedAt:O.z.string().datetime()}),yl=O.z.enum(["before_all","before_each","after_each","after_all"]),La=O.z.object({before_all:O.z.array(rn).default([]),before_each:O.z.array(rn).default([]),after_each:O.z.array(rn).default([]),after_all:O.z.array(rn).default([])})});function ms(r){let e=/^```(?:typescript|javascript|ts|js)?\n?([\s\S]*?)```\s*$/m,t=r.match(e);return t?.[1]!==void 0?t[1].trim():r.trim()}function Bn(r){let e=ms(r);try{return JSON.parse(e),e}catch{}let t=e.indexOf("{"),s=e.indexOf("["),n=t===-1?s:s===-1?t:Math.min(t,s);if(n===-1)return e;let i=e[n],a=i==="{"?"}":"]",o=0,c=!1,l=!1;for(let u=n;u<e.length;u++){let d=e[u];if(l){l=!1;continue}if(d==="\\"&&c){l=!0;continue}if(d==='"'){c=!c;continue}if(!c){if(d===i)o++;else if(d===a&&(o--,o===0))return e.slice(n,u+1)}}return e.slice(n)}function Bh(r){for(let e of jh)if(e.test(r))throw new te(`Generated code contains forbidden pattern: ${e.source}. This may be a prompt injection attempt. Please regenerate the step.`,"generatedCode","UNSAFE_CODE")}function gs(r,e){let t=/\bbutton\b/i.test(r)?"button":/\blink\b/i.test(r)?"link":null;return t===null?e:e.replace(/page\.getByText\((['"])(.*?)\1\)\.click\(\)/g,(s,n,i)=>`page.getByRole('${t}', { name: '${i}' }).click()`)}function wl(r){let e=r.replace(Uh,"").trim();return e.length>0?e:r}function Hh(r){let e=r;return e=e.replace(/(getBy(?:Label|Placeholder)\(\s*)(['"`])([\s\S]*?)\2/g,(t,s,n,i)=>`${s}${n}${wl(i)}${n}`),e=e.replace(/(name:\s*)(['"`])([\s\S]*?)\2/g,(t,s,n,i)=>`${s}${n}${wl(i)}${n}`),e}function Vh(r){return r.replace(/\blocator\((['"`])([\s\S]*?)\1\)/g,(e,t,s)=>{if(!s.includes(" >> "))return e;let n=s.split(" >> ").map(i=>i.trim()).filter(Boolean);return n.length<2?e:n.map(i=>`locator(${t}${i}${t})`).join(".")})}function fs(r){let e=r;return e=Vh(e),e=e.replace(/\.or\((?:[^()]*|\([^()]*\))*\)(?:\.(?:first|last)\(\))?/g,""),e=e.replace(/\.(?:first|last)\(\)(?=\.\w)/g,""),e=Hh(e),e}function Ke(r){let e=ms(r);if(!e)throw new te("AI returned an empty code response. Please try regenerating.","generatedCode","EMPTY_CODE");let t=fs(e);return Bh(t),t}function Ht(r){return r.toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}var jh,Uh,Vt=b(()=>{"use strict";g();ge();jh=[/\brequire\s*\(/,/\bimport\s*\(/,/\bprocess\b/,/\bchild_process\b/,/\bexec\s*\(/,/\bspawn\s*\(/,/\beval\s*\(/,/\bFunction\s*\(/,/\b__dirname\b/,/\b__filename\b/,/\bglobal\b/,/\bwindow\.location\.href\s*=/,/\bdocument\.cookie\b/,/\blocalStorage\b/,/\bsessionStorage\b/,/\bIndexedDB\b/,/\bXMLHttpRequest\b/,/\bfetch\s*\(/,/\bWebSocket\s*\(/];Uh=/\s+(?:field|input|box|textbox|text\s?box|drop-?\s?down|dropdown|combo\s?box|combobox|menu|selector|element|checkbox|radio\s?button|radio)$/i});var Vn={};ie(Vn,{createSuite:()=>nn,deleteSuite:()=>Na,findSuiteDirById:()=>we,listSuiteDirs:()=>Gt,listSuites:()=>qr,listSuitesWithCounts:()=>Ma,moveSuiteType:()=>Fa,readSuite:()=>Ae,updateSuite:()=>Hn,writeSuite:()=>Da});async function Ae(r){let e=Ne.default.join(r,Un);if(!await ct.default.pathExists(e))throw new ye(`Suite file not found at "${e}". Ensure the suite directory exists and contains a suite.json file.`,e,"SUITE_NOT_FOUND");let t=await ve(e),s=Oa.safeParse(t);if(!s.success){let i=s.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
|
|
93
|
+
`);throw new te(`Invalid suite.json at "${e}":
|
|
94
|
+
${i}`,"suite","INVALID_SUITE")}if(!t.type){let i=Ne.default.basename(Ne.default.dirname(r));i==="api"?s.data.type="api":i==="audit"||i==="performance"?s.data.type="audit":s.data.type="ui"}return s.data}async function Da(r,e){await ct.default.ensureDir(r);let t=Ne.default.join(r,Un),s=Oa.safeParse(e);if(!s.success){let n=s.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
|
|
95
|
+
`);throw new te(`Cannot write invalid suite to "${t}":
|
|
96
|
+
${n}`,"suite","INVALID_SUITE")}await re(t,s.data),hs.debug({suiteId:e.id,path:t},"Suite written")}async function nn(r,e){let t=new Date().toISOString(),s=e.type??"ui",n={...e,type:s,id:(0,bl.v4)(),createdAt:t,updatedAt:t},i=Ne.default.join(r,s),a=Ne.default.join(i,Ht(n.name));if(await ct.default.pathExists(Ne.default.join(a,Un)))throw new ye(`Suite directory already exists at "${a}". Choose a different name or delete the existing suite first.`,a,"SUITE_ALREADY_EXISTS");return await Da(a,n),hs.info({suiteId:n.id,path:a},"Suite created"),{suiteDir:a,suiteId:n.id}}async function Hn(r,e){let t=await Ae(r),s={...t,...e,id:t.id,createdAt:t.createdAt,updatedAt:new Date().toISOString()};return await Da(r,s),s}async function Na(r){if(!await ct.default.pathExists(r))throw new ye(`Suite directory not found at "${r}".`,r,"SUITE_NOT_FOUND");await ct.default.remove(r),hs.info({path:r},"Suite deleted")}async function Gt(r){let e=[],t=[Ne.default.join(r,"ui"),Ne.default.join(r,"api"),Ne.default.join(r,"audit"),Ne.default.join(r,"performance"),r];for(let s of t){if(!await ct.default.pathExists(s))continue;let n=await ct.default.readdir(s,{withFileTypes:!0});for(let i of n){if(!i.isDirectory()||s===r&&(i.name==="ui"||i.name==="api"||i.name==="audit"||i.name==="performance"))continue;let a=Ne.default.join(s,i.name,Un);await ct.default.pathExists(a)&&e.push(Ne.default.join(s,i.name))}}return e}async function Fa(r,e,t){let s=await Ae(e),n=Ne.default.basename(e),i=Ne.default.join(r,t),a=Ne.default.join(i,n);if(e===a)return e;if(await ct.default.ensureDir(i),await ct.default.pathExists(a))throw new ye(`Cannot move suite \u2014 "${a}" already exists.`,a,"SUITE_ALREADY_EXISTS");return await ct.default.move(e,a),await Hn(a,{type:t}),hs.info({suiteId:s.id,from:e,to:a},"Suite moved"),a}async function we(r,e){let t=await Gt(r);for(let s of t)try{if((await Ae(s)).id===e)return s}catch{}return null}async function qr(r){let e=await Gt(r),t=[];for(let s of e)try{t.push(await Ae(s))}catch(n){hs.warn({path:s,err:n},"Failed to read suite \u2014 skipping")}return t}async function Ma(r){let e=await Gt(r),t=[];for(let s of e)try{let n=await Ae(s),a=(await ct.default.readdir(s,{withFileTypes:!0})).filter(o=>o.isFile()&&o.name.endsWith(".test.json")).length;t.push({...n,caseCount:a})}catch(n){hs.warn({path:s,err:n},"Failed to read suite \u2014 skipping")}return t}var Ne,ct,bl,hs,Un,et=b(()=>{"use strict";g();Ne=x(require("path")),ct=x(require("fs-extra")),bl=require("uuid");sn();ge();j();Vt();ot();hs=I("suite-store"),Un="suite.json"});async function xl(r){let e=vl.default.join(r.rootDir,"tests");r.get("/suites",async(t,s)=>{try{let n=await Ma(e),i=t.query.type;return(i==="ui"||i==="api"||i==="performance")&&(n=n.filter(a=>a.type===i)),s.send(n)}catch(n){return w(s,500,"Failed to list suites",n)}}),r.post("/suites",async(t,s)=>{let n=Gh.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid suite data",details:n.error.issues});try{let{suiteDir:i}=await nn(e,n.data),a=await Ae(i);return s.status(201).send(a)}catch(i){return w(s,500,"Failed to create suite",i)}}),r.get("/suites/:id",async(t,s)=>{try{let n=await we(e,t.params.id);if(!n)return s.status(404).send({error:`Suite "${t.params.id}" not found`});let i=await Ae(n);return s.send(i)}catch(n){return w(s,500,"Failed to get suite",n)}}),r.patch("/suites/:id",async(t,s)=>{let n=zh.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid suite data",details:n.error.issues});try{let i=await we(e,t.params.id);if(!i)return s.status(404).send({error:`Suite "${t.params.id}" not found`});n.data.type&&(await Ae(i)).type!==n.data.type&&(i=await Fa(e,i,n.data.type));let a=await Hn(i,n.data);return s.send(a)}catch(i){return w(s,500,"Failed to update suite",i)}}),r.delete("/suites/:id",async(t,s)=>{try{let n=await we(e,t.params.id);return n?(await Na(n),s.status(204).send()):s.status(404).send({error:`Suite "${t.params.id}" not found`})}catch(n){return w(s,500,"Failed to delete suite",n)}})}var vl,tt,Gh,zh,Sl=b(()=>{"use strict";g();vl=x(require("path")),tt=require("zod");et();de();Gh=tt.z.object({name:tt.z.string().min(1),description:tt.z.string().default(""),tags:tt.z.array(tt.z.string()).default([]),type:tt.z.enum(["ui","api","audit","performance"]).default("ui")}),zh=tt.z.object({name:tt.z.string().min(1).optional(),description:tt.z.string().optional(),tags:tt.z.array(tt.z.string()).optional(),timeout:tt.z.number().int().positive().optional(),type:tt.z.enum(["ui","api","audit","performance"]).optional()})});function gt(r){return qh.some(e=>e.test(r))?"api":"ui"}var qh,Kr=b(()=>{"use strict";g();qh=[/\b(GET|POST|PUT|DELETE|PATCH)\s+\//i,/\bsend\s+(a\s+)?(GET|POST|PUT|DELETE|PATCH)\b/i,/\b(api|rest|http|graphql)\s+(call|request|endpoint)/i,/\bresponse\s+(status|body|code|header|time|structure|bodies|fields)/i,/\bstatus\s+code/i,/\brequest\s+(to|with|body|header)/i,/\bextract\s+.*\bfrom\s+response\b/i,/\bauthenticate\s+via\s+(api|token)\b/i,/\bfetch\s+.*\bfrom\s+api\b/i,/\bverify\s+(the\s+)?(response|api|endpoint|status)/i,/\b\/api\//i,/\brequest\s+body\b/i,/\b(response|request)\s+(body|code|header)/i,/\b(store|save|extract)\s+(the\s+)?(full\s+)?(response|request|body|token|header)/i,/\berror\s+(fields?|messages?)\s+.*\bresponse/i,/\bresponse\s+.*\b(error|fields?|messages?|bodies|keys|data\s+types?)/i,/\b(three|two|all|multiple)\s+(response|request)/i,/\bscoped.?token\b/i,/\boauth\b/i,/\baccess.?token\b/i,/\b(bearer|authorization)\s+token\b/i,/\bjson\s+(response|body|payload)\b/i,/\bhttp\s+\d{3}\b/i,/\bpayload\b/i]});var Il={};ie(Il,{createCase:()=>bs,deleteCase:()=>an,findCasePathById:()=>Pt,getCasePath:()=>Kh,listCasePaths:()=>or,listCases:()=>lt,readCase:()=>fe,updateCase:()=>We,writeCase:()=>ws});async function fe(r){if(!await ys.default.pathExists(r))throw new ye(`Test case file not found at "${r}".`,r,"CASE_NOT_FOUND");let e=await ve(r),t=_a.safeParse(e);if(!t.success){let s=t.error.issues.map(n=>` \u2022 ${n.path.join(".")}: ${n.message}`).join(`
|
|
97
|
+
`);throw new te(`Invalid test case file at "${r}":
|
|
98
|
+
${s}`,"case","INVALID_CASE")}return t.data}async function ws(r,e){let t=_a.safeParse(e);if(!t.success){let s=t.error.issues.map(n=>` \u2022 ${n.path.join(".")}: ${n.message}`).join(`
|
|
99
|
+
`);throw new te(`Cannot write invalid test case to "${r}":
|
|
100
|
+
${s}`,"case","INVALID_CASE")}await re(r,t.data),zn.debug({caseId:e.id,path:r},"Test case written")}async function bs(r,e){let t=new Date().toISOString(),s={...e,id:(0,El.v4)(),createdAt:t,updatedAt:t},n=Gn.default.join(r,`${Ht(s.name)}${ja}`);return await ws(n,s),zn.info({caseId:s.id,path:n},"Test case created"),n}async function We(r,e){let t=await fe(r),s={...t,...e,id:t.id,createdAt:t.createdAt,updatedAt:new Date().toISOString()};return await ws(r,s),s}async function an(r){if(!await ys.default.pathExists(r))throw new ye(`Test case file not found at "${r}".`,r,"CASE_NOT_FOUND");await ys.default.remove(r),zn.info({path:r},"Test case deleted")}async function or(r){return await ys.default.pathExists(r)?(await ys.default.readdir(r,{withFileTypes:!0})).filter(t=>t.isFile()&&t.name.endsWith(ja)).map(t=>Gn.default.join(r,t.name)):[]}async function lt(r){let e=await or(r),t=[];for(let s of e)try{t.push(await fe(s))}catch(n){zn.warn({path:s,err:n},"Failed to read test case \u2014 skipping")}return t.sort((s,n)=>s.name.localeCompare(n.name))}function Kh(r,e){return Gn.default.join(r,`${Ht(e)}${ja}`)}async function Pt(r,e){let t=await or(r);for(let s of t)try{if((await fe(s)).id===e)return s}catch{}return null}var Gn,ys,El,zn,ja,Rt=b(()=>{"use strict";g();Gn=x(require("path")),ys=x(require("fs-extra")),El=require("uuid");sn();ge();j();Vt();ot();zn=I("case-store"),ja=".test.json"});async function Pl(r){let e=Cl.default.join(r.rootDir,"tests");async function t(s){return we(e,s)}r.post("/suites/:suiteId/cases/bulk-delete",async(s,n)=>{let i=Yh.safeParse(s.body);if(!i.success)return n.status(400).send({error:"Invalid request"});try{let a=await t(s.params.suiteId);if(!a)return n.status(404).send({error:`Suite "${s.params.suiteId}" not found`});let o=0;for(let c of i.data.ids){let l=await Pt(a,c);l&&(await an(l),o++)}return n.send({deleted:o})}catch(a){return w(n,500,"Failed to bulk delete cases",a)}}),r.post("/suites/:suiteId/cases/bulk-move",async(s,n)=>{let i=Qh.safeParse(s.body);if(!i.success)return n.status(400).send({error:"Invalid request"});try{let a=await t(s.params.suiteId);if(!a)return n.status(404).send({error:`Suite "${s.params.suiteId}" not found`});let o=await we(e,i.data.targetSuiteId);if(!o)return n.status(404).send({error:`Target suite "${i.data.targetSuiteId}" not found`});let c=0;for(let l of i.data.ids){let u=await Pt(a,l);if(u){let d=await fe(u);await bs(o,d),await an(u),c++}}return n.send({moved:c})}catch(a){return w(n,500,"Failed to bulk move cases",a)}}),r.get("/suites/:suiteId/cases",async(s,n)=>{try{let i=await t(s.params.suiteId);if(!i)return n.status(404).send({error:`Suite "${s.params.suiteId}" not found`});let a=await lt(i);return n.send(a)}catch(i){return w(n,500,"Failed to list cases",i)}}),r.post("/suites/:suiteId/cases",async(s,n)=>{let i=Wh.safeParse(s.body);if(!i.success)return n.status(400).send({error:"Invalid case data",details:i.error.issues});try{let a=await t(s.params.suiteId);if(!a)return n.status(404).send({error:`Suite "${s.params.suiteId}" not found`});let{name:o,description:c,tags:l,priority:u,steps:d}=i.data,m=await bs(a,{name:o,description:c,tags:l,priority:u,steps:d.map((f,h)=>({id:`step-${h+1}`,order:h+1,instruction:f.instruction,generatedCode:"",strategy:"primary",stepType:gt(f.instruction),lastHealed:null}))}),p=await fe(m);return n.status(201).send(p)}catch(a){return w(n,500,"Failed to create case",a)}}),r.get("/suites/:suiteId/cases/:caseId",async(s,n)=>{try{let i=await t(s.params.suiteId);if(!i)return n.status(404).send({error:`Suite "${s.params.suiteId}" not found`});let a=await Pt(i,s.params.caseId);if(!a)return n.status(404).send({error:`Case "${s.params.caseId}" not found`});let o=await fe(a);return n.send(o)}catch(i){return w(n,500,"Failed to get case",i)}}),r.patch("/suites/:suiteId/cases/:caseId",async(s,n)=>{let i=Jh.safeParse(s.body);if(!i.success)return n.status(400).send({error:"Invalid case data",details:i.error.issues});try{let a=await t(s.params.suiteId);if(!a)return n.status(404).send({error:`Suite "${s.params.suiteId}" not found`});let o=await Pt(a,s.params.caseId);if(!o)return n.status(404).send({error:`Case "${s.params.caseId}" not found`});let c=await We(o,i.data);return n.send(c)}catch(a){return w(n,500,"Failed to update case",a)}}),r.delete("/suites/:suiteId/cases/:caseId",async(s,n)=>{try{let i=await t(s.params.suiteId);if(!i)return n.status(404).send({error:`Suite "${s.params.suiteId}" not found`});let a=await Pt(i,s.params.caseId);return a?(await an(a),n.status(204).send()):n.status(404).send({error:`Case "${s.params.caseId}" not found`})}catch(i){return w(n,500,"Failed to delete case",i)}})}var Cl,Q,Wh,Xh,Jh,Yh,Qh,Rl=b(()=>{"use strict";g();Cl=x(require("path")),Q=require("zod");Kr();Rt();et();de();Wh=Q.z.object({name:Q.z.string().min(1),description:Q.z.string().default(""),tags:Q.z.array(Q.z.string()).default([]),priority:Q.z.enum(["critical","high","medium","low"]).default("medium"),steps:Q.z.array(Q.z.object({instruction:Q.z.string().min(1)})).default([])}),Xh=Q.z.object({type:Q.z.enum(["inline","json-file","csv-file"]),path:Q.z.string().optional(),data:Q.z.array(Q.z.record(Q.z.string())).optional()}).optional().nullable(),Jh=Q.z.object({name:Q.z.string().min(1).optional(),description:Q.z.string().optional(),tags:Q.z.array(Q.z.string()).optional(),priority:Q.z.enum(["critical","high","medium","low"]).optional(),timeout:Q.z.number().int().positive().optional(),dataSource:Xh,lighthouseCategories:Q.z.array(Q.z.enum(["performance","accessibility","seo"])).optional()}),Yh=Q.z.object({ids:Q.z.array(Q.z.string().min(1)).min(1)}),Qh=Q.z.object({ids:Q.z.array(Q.z.string().min(1)).min(1),targetSuiteId:Q.z.string().min(1)})});function ty(r){return typeof r=="object"&&r.secret===!0}function kl(r){return ty(r)?r.value:r}var Rr,Zh,ey,Ba,Tl=b(()=>{"use strict";g();Rr=require("zod"),Zh=Rr.z.object({value:Rr.z.string(),secret:Rr.z.literal(!0)}),ey=Rr.z.union([Rr.z.string(),Zh]),Ba=Rr.z.record(Rr.z.string(),ey)});function $l(r){return`${r}.env.json`}async function kr(r){if(!await Wr.default.pathExists(r))return{};let e=await ve(r),t=Ba.safeParse(e);if(!t.success){let s=t.error.issues.map(n=>` \u2022 ${n.path.join(".")}: ${n.message}`).join(`
|
|
101
|
+
`);throw new te(`Invalid variables file at "${r}":
|
|
102
|
+
${s}`,"variables","INVALID_VARIABLES")}return t.data}async function Wn(r,e){let t=Ba.safeParse(e);if(!t.success){let s=t.error.issues.map(n=>` \u2022 ${n.path.join(".")}: ${n.message}`).join(`
|
|
103
|
+
`);throw new te(`Cannot write invalid variables to "${r}":
|
|
104
|
+
${s}`,"variables","INVALID_VARIABLES")}await re(r,t.data),Al.debug({path:r},"Variables written")}async function Xn(r){let e=kt.default.join(r,Xr,Kn);return kr(e)}async function Jn(r,e){let t=kt.default.join(r,Xr,$l(e));return kr(t)}async function ft(r,e){let t=await Xn(r),s=e?await Jn(r,e):{},n={...t,...s},i={};for(let[a,o]of Object.entries(n)){let c=process.env[`AM_${a}`];c!==void 0&&c!==""?(Al.debug({key:a},`Variable overridden by AM_${a} CI secret`),i[a]=c):i[a]=kl(o)}return i}async function Ua(r,e,t){let s=kt.default.join(r,Xr,Kn);await Wr.default.ensureDir(kt.default.dirname(s));let n=await kr(s);n[e]=t,await Wn(s,n)}async function _l(r,e){let t=kt.default.join(r,Xr,Kn),s=await kr(t);if(!(e in s))throw new ye(`Variable "${e}" not found in global variables.`,t,"VARIABLE_NOT_FOUND");delete s[e],await Wn(t,s)}function Ll(r){return!r||r.toUpperCase()===Ol}function Dl(r,e){return kt.default.join(r,Xr,$l(e))}function qn(r){return typeof r=="string"?{value:r,secret:!1}:{value:r.value,secret:!0}}async function Nl(r,e){let t=await Xn(r),s={};for(let a of e)s[a]=await Jn(r,a);let n=new Set(Object.keys(t));for(let a of e)for(let o of Object.keys(s[a]))n.add(o);let i=[];for(let a of[...n].sort()){let o=!1,c=t[a]!==void 0?qn(t[a]):{value:"",secret:!1};c.secret&&(o=!0);let l={};for(let u of e){let d=s[u][a];if(d!==void 0){let m=qn(d);l[u]=m.value,m.secret&&(o=!0)}else l[u]=""}i.push({key:a,secret:o,global:c.value,values:l})}return i}async function Fl(r,e,t,s){if(Ll(e)){if(qn(s).value===""){await on(r,e,t).catch(()=>{});return}return Ua(r,t,s)}let n=Dl(r,e);await Wr.default.ensureDir(kt.default.dirname(n));let i=await kr(n);qn(s).value===""?delete i[t]:i[t]=s,await Wn(n,i)}async function on(r,e,t){let s=Ll(e)?kt.default.join(r,Xr,Kn):Dl(r,e);if(!await Wr.default.pathExists(s))return;let n=await kr(s);t in n&&(delete n[t],await Wn(s,n))}async function Ml(r,e,t){await on(r,Ol,e);for(let s of t)await on(r,s,e)}async function jl(r){let e=kt.default.join(r,Xr);return await Wr.default.pathExists(e)?(await Wr.default.readdir(e,{withFileTypes:!0})).filter(s=>s.isFile()&&s.name.endsWith(".json")).map(s=>kt.default.join(e,s.name)):[]}var kt,Wr,Al,Xr,Kn,Ol,Tr=b(()=>{"use strict";g();kt=x(require("path")),Wr=x(require("fs-extra"));Tl();ge();j();ot();Al=I("variable-store"),Xr="variables",Kn="global.json";Ol="GLOBAL"});function ry(r){let e=r.trim();return/^https?:\/\//i.test(e)?e:e.startsWith("/")?`{{BASE_URL}}${e}`:/^[\w-]+\.[\w.-]+/.test(e)?`https://${e}`:e}function Jr(r){return(r??"").trim().replace(/^(?:the|a|an)\s+/i,"").replace(/\s+(?:field|input|box|textbox|dropdown|drop-down|menu|list|selector|element)$/i,"").trim()}function Ha(r){let e=Jr(r);return/\b(?:pass(?:word|code|phrase)?|pwd)\b/i.test(e)?`page.getByLabel('${e}')`:/\b(?:number|amount|quantity|qty|age|count)\b/i.test(e)?`page.getByRole('spinbutton', { name: '${e}' })`:`page.getByRole('textbox', { name: '${e}' })`}function Va(r,e,t){let s=Jr(e),i=[`await ${s?`page.getByLabel('${s}')`:`page.locator('input[type="file"]')`}.setInputFiles('${r}');`];return t?i.push(`await expect(page.getByText('${t}')).toBeVisible();`):i.push("await page.waitForLoadState('networkidle');"),i.join(`
|
|
105
|
+
`)}function Bl(r,e){let t=Jr(r);return["const downloadPromise = page.waitForEvent('download');",`await ${e==="button"?`page.getByRole('button', { name: '${t}' })`:e==="link"?`page.getByRole('link', { name: '${t}' })`:`page.getByText('${t}')`}.click();`,"const download = await downloadPromise;","await download.path();"].join(`
|
|
106
|
+
`)}var Ul,Yn,Hl=b(()=>{"use strict";g();Ul=[{regex:/^(?:go to|navigate to|open|visit)\s+(.+)$/i,gen:r=>`await page.goto('${ry(r[1]??"")}');`},{regex:/^(?:go|navigate)\s+back$/i,gen:()=>"await page.goBack();"},{regex:/^refresh\s+(?:the\s+)?page$/i,gen:()=>"await page.reload();"},{regex:/^download\s+(?:the\s+)?(?:file\s+)?(?:by\s+clicking\s+)?["']?(.+?)["']?(\s+button|\s+link)?$/i,gen:r=>Bl((r[1]??"").trim(),(r[2]??"").trim().toLowerCase())},{regex:/^click\s+(?:the\s+)?(?:on\s+)?["']?(.+?)["']?(\s+button|\s+link)?\s+(?:to\s+download|and\s+wait\s+for\s+(?:the\s+)?download)$/i,gen:r=>Bl((r[1]??"").trim(),(r[2]??"").trim().toLowerCase())},{regex:/^(?:select|choose|click|tick|pick)\s+(?:the\s+)?(?:on\s+)?["']?(.+?)["']?\s+radio(?:\s+button)?$/i,gen:r=>`await page.getByRole('radio', { name: '${Jr(r[1]??"")}' }).check();`},{regex:/^click\s+(?:the\s+)?(?:on\s+)?["'](.+?)["'](\s+button|\s+link|\s+tab|\s+menu)?$/i,gen:r=>{let e=r[1]??"",t=(r[2]??"").trim().toLowerCase();return t==="button"?`await page.getByRole('button', { name: '${e}' }).click();`:t==="link"?`await page.getByRole('link', { name: '${e}' }).click();`:`await page.getByText('${e}').click();`}},{regex:/^click\s+(?:the\s+)?(?:on\s+)?(\w[\w\s]{0,30}?)(\s+button|\s+link|\s+tab)?$/i,gen:r=>{let e=(r[1]??"").trim(),t=(r[2]??"").trim().toLowerCase();return t==="button"?`await page.getByRole('button', { name: '${e}' }).click();`:t==="link"?`await page.getByRole('link', { name: '${e}' }).click();`:`await page.getByText('${e}').click();`}},{regex:/^double\s+click\s+(?:the\s+)?(?:on\s+)?["']?(.+?)["']?$/i,gen:r=>`await page.getByText('${r[1]}').dblclick();`},{regex:/^right\s+click\s+(?:the\s+)?(?:on\s+)?["']?(.+?)["']?$/i,gen:r=>`await page.getByText('${r[1]}').click({ button: 'right' });`},{regex:/^(?:type|enter|fill|input)\s+["']?(.+?)["']?\s+(?:in|into)\s+(?:the\s+)?(.+)$/i,gen:r=>`await ${Ha(r[2]??"")}.fill('${r[1]}');`},{regex:/^clear\s+(?:the\s+)?(.+?)(?:\s+field)?$/i,gen:r=>`await ${Ha(r[1]??"")}.clear();`},{regex:/^upload\s+(?:the\s+)?(?:file\s+)?["']?(.+?)["']?\s+(?:to|into|in|on)\s+(?:the\s+)?(.+?)\s+and\s+wait\s+for\s+["'](.+?)["']$/i,gen:r=>Va(r[1]??"",r[2]??"",r[3]??"")},{regex:/^upload\s+(?:the\s+)?(?:file\s+)?["']?(.+?)["']?\s+(?:to|into|in|on)\s+(?:the\s+)?(.+)$/i,gen:r=>Va(r[1]??"",r[2]??"","")},{regex:/^upload\s+(?:the\s+)?(?:file\s+)?["']?(.+?)["']?$/i,gen:r=>Va(r[1]??"","","")},{regex:/^(?:verify|check|assert|ensure|confirm)\s+(?:that\s+)?["'](.+?)["']\s+(?:is\s+)?(?:visible|shown|displayed|present|appears)$/i,gen:r=>`await expect(page.getByText('${r[1]}')).toBeVisible();`},{regex:/^(?:verify|check|assert)\s+(?:that\s+)?["'](.+?)["']\s+(?:is\s+)?(?:not visible|hidden|not shown|gone|disappeared|not present)$/i,gen:r=>`await expect(page.getByText('${r[1]}')).not.toBeVisible();`},{regex:/^(?:verify|check)\s+(?:the\s+)?(?:page\s+)?title\s+(?:is|contains)\s+["'](.+?)["']$/i,gen:r=>`await expect(page).toHaveTitle(/${r[1]}/);`},{regex:/^(?:verify|check)\s+(?:the\s+)?url\s+(?:is|contains|matches)\s+["'](.+?)["']$/i,gen:r=>`await expect(page).toHaveURL(/${(r[1]??"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}/);`},{regex:/^(?:verify|check)\s+(?:the\s+)?["'](.+?)["']\s+(?:field|input)\s+(?:has value|contains|equals)\s+["'](.+?)["']$/i,gen:r=>`await expect(page.getByLabel('${r[1]}')).toHaveValue('${r[2]}');`},{regex:/^soft\s+(?:verify|check|assert|ensure|confirm)\s+(?:that\s+)?["'](.+?)["']\s+(?:is\s+)?(?:visible|shown|displayed|present|appears)$/i,gen:r=>`await expect.soft(page.getByText('${r[1]}')).toBeVisible();`},{regex:/^soft\s+(?:verify|check|assert)\s+(?:that\s+)?["'](.+?)["']\s+(?:is\s+)?(?:not visible|hidden|not shown|gone|disappeared|not present)$/i,gen:r=>`await expect.soft(page.getByText('${r[1]}')).not.toBeVisible();`},{regex:/^soft\s+(?:verify|check)\s+(?:the\s+)?(?:page\s+)?title\s+(?:is|contains)\s+["'](.+?)["']$/i,gen:r=>`await expect.soft(page).toHaveTitle(/${r[1]}/);`},{regex:/^soft\s+(?:verify|check)\s+(?:the\s+)?url\s+(?:is|contains|matches)\s+["'](.+?)["']$/i,gen:r=>`await expect.soft(page).toHaveURL(/${(r[1]??"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}/);`},{regex:/^soft\s+(?:verify|check)\s+(?:the\s+)?["'](.+?)["']\s+(?:field|input)\s+(?:has value|contains|equals)\s+["'](.+?)["']$/i,gen:r=>`await expect.soft(page.getByLabel('${r[1]}')).toHaveValue('${r[2]}');`},{regex:/^press\s+(.+)$/i,gen:r=>`await page.keyboard.press('${(r[1]??"").trim()}');`},{regex:/^(?:type|enter|fill)\s+["']?(.+?)["']?\s+(?:and\s+)?press\s+enter$/i,gen:r=>`await page.keyboard.type('${r[1]}');
|
|
107
|
+
await page.keyboard.press('Enter');`},{regex:/^select\s+["'](.+?)["']\s+from\s+(?:the\s+)?(.+)$/i,gen:r=>`await page.getByRole('combobox', { name: '${Jr(r[2]??"")}' }).selectOption('${r[1]}');`},{regex:/^check\s+(?:the\s+)?["']?(.+?)["']?(?:\s+checkbox)?$/i,gen:r=>`await page.getByRole('checkbox', { name: '${Jr(r[1]??"")}' }).check();`},{regex:/^uncheck\s+(?:the\s+)?["']?(.+?)["']?(?:\s+checkbox)?$/i,gen:r=>`await page.getByRole('checkbox', { name: '${Jr(r[1]??"")}' }).uncheck();`},{regex:/^wait\s+(?:for\s+)?(\d+)\s+seconds?$/i,gen:r=>`await page.waitForTimeout(${parseInt(r[1]??"1",10)*1e3});`},{regex:/^wait\s+(?:for\s+)?(?:the\s+)?page\s+(?:to\s+)?load$/i,gen:()=>"await page.waitForLoadState('networkidle');"},{regex:/^scroll\s+(down|up)$/i,gen:r=>`await page.evaluate(() => window.scrollBy(0, ${(r[1]??"").toLowerCase()==="down"?500:-500}));`},{regex:/^scroll\s+to\s+(top|bottom)$/i,gen:r=>`await page.evaluate(() => window.scrollTo(0, ${(r[1]??"").toLowerCase()==="top"?0:"document.body.scrollHeight"}));`},{regex:/^hover\s+(?:over\s+)?(?:the\s+)?["']?(.+?)["']?$/i,gen:r=>`await page.getByText('${r[1]}').hover();`},{regex:/^take\s+(?:a\s+)?screenshot$/i,gen:()=>`await page.screenshot({ path: 'results/screenshots/step_${Date.now()}.png', fullPage: true });`},{regex:/^(?:focus|tab to)\s+(?:the\s+)?(.+?)(?:\s+field)?$/i,gen:r=>`await ${Ha(r[1]??"")}.focus();`}],Yn=class{tryMatch(e){for(let t of Ul){let s=e.match(t.regex);if(s!==null)return t.gen(s)}return null}get patternCount(){return Ul.length}}});function sy(r){return(0,Vl.createHash)("sha256").update(r,"utf8").digest("hex")}function ny(r){try{return new URL(r).pathname.split("/").map(s=>/^\d+$/.test(s)||/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s)||/^[a-z0-9-]{8,}$/i.test(s)&&/-/.test(s)?"*":s).join("/")}catch{return r}}function Gl(r,e){let t=r.toLowerCase().replace(/\s+/g," ").trim(),s=ny(e);return sy(`${t}|${s}`).slice(0,16)}var Vl,zl=b(()=>{"use strict";g();Vl=require("crypto")});var ql,Ga,vs,iy,ay,xs,za=b(()=>{"use strict";g();ql=x(require("path")),Ga=x(require("fs-extra"));zl();j();ot();vs=I("code-cache"),iy=ql.default.join("results",".code-cache.json"),ay=3e4,xs=class{constructor(e=iy){this.persistPath=e}persistPath;cache=new Map;dirty=!1;persistTimer=null;generateKey(e,t){return Gl(e,t)}get(e){let t=this.cache.get(e);return t?t.failCount>=2?(this.cache.delete(e),this.dirty=!0,vs.debug({key:e},"Cache entry invalidated after 2 failures"),null):(t.lastUsedAt=Date.now(),t):null}set(e,t,s){this.cache.set(e,{code:t,urlPattern:s,successCount:0,failCount:0,createdAt:Date.now(),lastUsedAt:Date.now()}),this.dirty=!0}recordPass(e){let t=this.cache.get(e);t&&(t.successCount++,t.failCount=0,this.dirty=!0)}recordFail(e){let t=this.cache.get(e);t&&(t.failCount++,t.failCount>=2&&(this.cache.delete(e),vs.debug({key:e},"Cache entry removed after 2 failures")),this.dirty=!0)}get size(){return this.cache.size}async persist(){if(this.dirty)try{let e=Object.fromEntries(this.cache.entries());await re(this.persistPath,e),this.dirty=!1,vs.debug({entries:this.cache.size,path:this.persistPath},"Cache persisted")}catch(e){vs.warn({err:e},"Failed to persist code cache")}}async load(){if(await Ga.default.pathExists(this.persistPath))try{let e=await Ga.default.readJson(this.persistPath);this.cache.clear();for(let[t,s]of Object.entries(e))this.cache.set(t,s);vs.debug({entries:this.cache.size,path:this.persistPath},"Cache loaded")}catch(e){vs.warn({err:e},"Failed to load code cache \u2014 starting fresh")}}startAutoSave(){if(this.persistTimer)return;this.persistTimer=setInterval(()=>{this.persist()},ay),this.persistTimer.unref&&this.persistTimer.unref();let e=()=>{this.persist()};process.once("exit",e),process.once("SIGINT",e),process.once("SIGTERM",e)}stopAutoSave(){this.persistTimer&&(clearInterval(this.persistTimer),this.persistTimer=null)}}});var oy,Qn,Kl=b(()=>{"use strict";g();oy=[/table|grid|chart|graph|calendar|datepicker/i,/drag|drop|resize|reorder|sortable/i,/verify.*(count|greater|less|between|exactly|more than|fewer)/i,/wait\s+until|disappears|loading\s+(?:is\s+)?(?:done|complete|finished)/i,/(?:first|second|third|last|nth|\d+(?:st|nd|rd|th))\s+(?:item|row|card|element|result)/i,/if\s+|conditional|depending|based on/i,/iframe|shadow\s*dom|popup|modal|dialog|alert/i,/upload|download|file|attachment/i,/hover\s+.*then|right.click|double.click/i,/api|network|request|response|intercept/i,/(?:store|save|capture|extract)\s+.*(?:variable|value|text)/i,/multi.?select|autocomplete|typeahead|combobox/i],Qn=class{classify(e){let t=oy.filter(s=>s.test(e)).length;return t>=2?"complex":t===1?"medium":"simple"}}});var qa,Zn,Wl=b(()=>{"use strict";g();Vt();j();qa=I("batch-processor"),Zn=class{constructor(e,t){this.templateEngine=e;this.cache=t}templateEngine;cache;async batchGenerate(e,t){let s=new Map,n=[];for(let o=0;o<e.length;o++){let{instruction:c,context:l}=e[o],u=this.templateEngine.tryMatch(c);if(u!==null){s.set(o,u);continue}let d=this.cache.generateKey(c,l.url),m=this.cache.get(d);if(m!==null){s.set(o,m.code);continue}n.push({index:o,instruction:c,context:l})}if(qa.debug({total:e.length,local:s.size,needsAI:n.length},"Batch resolution"),n.length===0)return s;let i=n.map(o=>({instruction:o.instruction,context:o.context})),a=await t.batchGenerateCode(i);for(let o=0;o<n.length;o++){let c=n[o],l=a[o];if(!l){qa.warn({instruction:c.instruction},"AI returned empty code for step");continue}let u;try{u=Ke(l)}catch(m){qa.warn({instruction:c.instruction,err:m},"Skipping unsafe/invalid batch result");continue}s.set(c.index,u);let d=this.cache.generateKey(c.instruction,c.context.url);this.cache.set(d,u,c.context.url)}return s}}});function cy(r){return r.includes("'")?`"${r.replace(/"/g,'\\"')}"`:`'${r.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}'`}function Xl(r){return r.split("||").map(e=>e.trim()).filter(Boolean).map(e=>`frameLocator(${cy(e)})`).join(".")+"."}function ly(r){let e=new Map;for(let t of r.split(`
|
|
108
|
+
`)){if(!t.trim())continue;let s=t.match(/frame="([^"]*)"/),n=s?s[1]:null,i=[],a=t.match(/^[\w-]+\s+"([^"]*)"/);a&&i.push(a[1]);for(let o of t.matchAll(/(?:text|label|aria-label|placeholder)="([^"]*)"/g))i.push(o[1]);for(let o of i){let c=o.trim().toLowerCase();if(!c)continue;let l=e.get(c);l?l.frame!==n&&(l.ambiguous=!0):e.set(c,{frame:n,ambiguous:!1})}}return e}function Jl(r,e){if(!e)return null;let t=r.get(e.trim().toLowerCase());return!t||t.ambiguous||!t.frame?null:t.frame}function Yl(r,e){if(!r||!e||!e.includes("frame=")||!r.includes("page.getBy"))return r;let t=ly(e);if(t.size===0)return r;let s=r;return s=s.replace(/\bpage\.getByRole\((['"])[^'"]+\1\s*,\s*\{[^}]*\}/g,n=>{let i=n.match(/name:\s*(['"])([^'"]*)\1/),a=Jl(t,i?.[2]);return a?n.replace(/^page\./,`page.${Xl(a)}`):n}),s=s.replace(/\bpage\.getBy(?:Text|Label|Placeholder)\((['"])([^'"]+)\1/g,(n,i,a)=>{let o=Jl(t,a);return o?n.replace(/^page\./,`page.${Xl(o)}`):n}),s}var Ql=b(()=>{"use strict";g()});function py(r){return(r.match(/^(\s*)/)?.[1]??"").replace(/\t/g," ").length}function my(r,e){if(r)return`iframe[name='${r}']`;let t=e.match(/title=([^\s\]]+)/i);return t?`iframe[title='${t[1]}']`:"iframe"}function ei(r){if(!r||!r.trim())return{title:"",interactiveElements:"",accessibilityTree:""};let e=r.split(`
|
|
109
|
+
`),t="",s=[],n=[];for(let l of e){let u=l.trim();if(!u||u==="-")continue;let d=py(l);for(;n.length>0&&d<=n[n.length-1].indent;)n.pop();let m=u.match(/^-?\s*Page\s+"([^"]*)"/i);if(m){t=m[1]??"";continue}let p=u.match(/^-?\s*([\w-]+)(?:\s+"([^"]*)")?(?:\s+\[([^\]]*)\])?/);if(!p)continue;let f=p[1].toLowerCase(),h=p[2]??"",S=p[3]??"";if(f==="iframe"||f==="frame"){let k=n.length>0?n[n.length-1].chain:"",T=my(h,S),P=k?`${k}||${T}`:T;n.push({indent:d,chain:P});continue}if(h.length>80&&(h=h.slice(0,79)+"\u2026"),!h&&!S)continue;let C=uy.has(f),v=dy.has(f);if(C||v){let k=n.length>0?n[n.length-1].chain:null;s.push({role:f,name:h,attrs:S,isInteractive:C,frame:k})}}let i=s.filter(l=>l.isInteractive),a=s.filter(l=>!l.isInteractive),c=[...i,...a].slice(0,120).map(l=>{let u=`${l.role} "${l.name}"`;return l.attrs&&(u+=` [${l.attrs}]`),l.frame&&(u+=` frame="${l.frame}"`),u});return{title:t,interactiveElements:c.join(`
|
|
110
|
+
`),accessibilityTree:r.slice(0,8e3)}}var uy,dy,Ka=b(()=>{"use strict";g();uy=new Set(["button","link","textbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","searchbox","slider","spinbutton","switch","tab","treeitem"]),dy=new Set(["heading","img","navigation","dialog","alert","alertdialog","status","progressbar","table","row","cell","columnheader","rowheader"])});function Zl(r,e){let t=`Current page accessibility snapshot:
|
|
111
111
|
${e.slice(0,6e3)}
|
|
112
112
|
|
|
113
113
|
Step to execute: "${r}"
|
|
114
114
|
|
|
115
|
-
Plan the MCP browser actions needed. Return JSON with the "actions" array.`;return{system:
|
|
115
|
+
Plan the MCP browser actions needed. Return JSON with the "actions" array.`;return{system:gy,user:t}}var gy,eu=b(()=>{"use strict";g();gy=`You are a browser automation expert. Given a test step instruction and an accessibility snapshot of the current page, plan the exact browser actions needed to accomplish the step.
|
|
116
116
|
|
|
117
117
|
\u2501\u2501\u2501 OUTPUT FORMAT \u2501\u2501\u2501
|
|
118
118
|
Return ONLY valid JSON \u2014 no markdown, no explanations.
|
|
@@ -151,12 +151,12 @@ Match elements from the snapshot by their role and name:
|
|
|
151
151
|
- "Click Login button" \u2192 find button with name containing "Login"
|
|
152
152
|
- "Select country" \u2192 find combobox/listbox with name containing "country"
|
|
153
153
|
|
|
154
|
-
Always use the ref value from the snapshot (e.g., "s1e4") \u2014 this is the unique element identifier.`});async function
|
|
155
|
-
`)}function
|
|
156
|
-
`).length,title:i.title},"MCP: enriched context with live page elements"),{...t,title:i.title||t.title,interactiveElements:i.interactiveElements||t.interactiveElements,accessibilityTree:i.accessibilityTree}}catch(s){let n=s instanceof Error?s.message:String(s);return
|
|
157
|
-
`)||null}catch(s){let n=s instanceof Error?s.message:String(s);return
|
|
158
|
-
${
|
|
159
|
-
`):" None",s=Object.keys(e.variables).length>0?Object.entries(e.variables).map(([
|
|
154
|
+
Always use the ref value from the snapshot (e.g., "s1e4") \u2014 this is the unique element identifier.`});async function tu(r,e,t){try{let{system:s,user:n}=Zl(e,t),i={url:"",title:"",interactiveElements:"",htmlSnapshot:"",previousSteps:[{instruction:"SYSTEM",code:s}],variables:{}},o=(await r.generateCode(`[MCP_ACTION_PLAN] ${n}`,i)).replace(/```json?\s*/g,"").replace(/```/g,"").trim(),c=JSON.parse(o);if(!c.actions||!Array.isArray(c.actions))return Wa.warn("AI returned invalid action plan \u2014 no actions array"),[];let l=c.actions.filter(u=>u.tool&&typeof u.tool=="string"&&u.args);return Wa.info({instruction:e,actionCount:l.length},"MCP action plan created"),l}catch(s){let n=s instanceof Error?s.message:String(s);return Wa.warn({err:n,instruction:e},"Failed to plan MCP actions"),[]}}var Wa,ru=b(()=>{"use strict";g();eu();j();Wa=I("action-mapper")});function nu(r){let e=[];for(let t of r){let s=fy(t);s&&e.push(s)}return e.length===0?(su.warn("No actions could be converted to script"),""):e.join(`
|
|
155
|
+
`)}function fy(r){let e=r.args??{};switch(r.tool){case"browser_navigate":{let t=String(e.url??"");return t?`await page.goto('${cr(t)}', { waitUntil: 'networkidle' });`:null}case"browser_click":{let t=ti(e);return t?`await ${t}.click();`:null}case"browser_type":{let t=ti(e),s=String(e.text??"");return!t||!s?null:`await ${t}.fill('${cr(s)}');`}case"browser_select_option":{let t=ti(e),s=e.values;if(!t||!s?.length)return null;let n=s.map(i=>`'${cr(i)}'`).join(", ");return`await ${t}.selectOption([${n}]);`}case"browser_hover":{let t=ti(e);return t?`await ${t}.hover();`:null}case"browser_press_key":{let t=String(e.key??"");return t?`await page.keyboard.press('${cr(t)}');`:null}case"browser_wait":return`await page.waitForTimeout(${Number(e.time??1e3)});`;default:return su.debug({tool:r.tool},"Unknown MCP action \u2014 skipping"),null}}function ti(r){let e=String(r.element??"");if(!e)return null;let t=typeof r.frame=="string"?r.frame.trim():"",s=`page.${hy(t)}`,n=e.match(/^(\w+)\s+['"]([^'"]*)['"]/);if(n){let a=n[1].toLowerCase(),o=n[2],c={button:"button",link:"link",textbox:"textbox",checkbox:"checkbox",radio:"radio",combobox:"combobox",listbox:"listbox",menuitem:"menuitem",option:"option",searchbox:"searchbox",slider:"slider",spinbutton:"spinbutton",switch:"switch",tab:"tab",heading:"heading"};if((a==="textbox"||a==="searchbox")&&o&&/\b(?:pass(?:word|code|phrase)?|pwd)\b/i.test(o))return`${s}getByLabel('${cr(o)}')`;let l=c[a];if(l&&o)return`${s}getByRole('${l}', { name: '${cr(o)}' })`;if((a==="textbox"||a==="searchbox")&&o)return`${s}getByLabel('${cr(o)}')`}let i=e.match(/['"]([^'"]+)['"]/);return i?`${s}getByText('${cr(i[1])}')`:null}function hy(r){return r?r.split("||").map(e=>e.trim()).filter(Boolean).map(e=>`frameLocator(${yy(e)})`).join(".")+".":""}function yy(r){return r.includes("'")?`"${r.replace(/"/g,'\\"')}"`:`'${cr(r)}'`}function cr(r){return r.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}var su,iu=b(()=>{"use strict";g();j();su=I("action-to-script")});var je,ri,au=b(()=>{"use strict";g();Hl();za();Kl();Wl();Vt();Ql();j();Kr();Ka();ru();iu();je=I("smart-router"),ri=class{templateEngine;cache;classifier;batchProcessor;primaryProvider;fastProvider;mcpSession;actThenScript;ragManager;stats={template:0,cache:0,rag:0,batch:0,fast:0,primary:0,total:0};constructor(e){this.primaryProvider=e.primaryProvider,this.fastProvider=e.fastProvider??null,this.mcpSession=e.mcpSession??null,this.actThenScript=e.actThenScript??!1,this.ragManager=e.ragManager??null,this.cache=e.cache??new xs,this.templateEngine=new Yn,this.classifier=new Qn,this.batchProcessor=new Zn(this.templateEngine,this.cache)}async generate(e,t){if(this.stats.total++,!(gt(e)==="api")){let S=this.templateEngine.tryMatch(e);if(S!==null)return this.stats.template++,je.debug({instruction:e},"Template match"),{code:fs(S),strategy:"template",cost:0,model:"local"}}let n=this.cache.generateKey(e,t.url),i=this.cache.get(n);if(i!==null)return this.stats.cache++,je.debug({instruction:e},"Cache hit"),{code:gs(e,fs(i.code)),strategy:"cache",cost:0,model:"local"};let a=[],o=[];if(this.ragManager)try{let S=this.ragManager.codeCorpus.retrieve(e,t.url);if(S.length>0&&S[0].score>=.9){let C=S[0];this.stats.rag++,je.info({instruction:e,score:C.score,matchedInstruction:C.instruction},"RAG direct hit");let v=gs(e,fs(C.code));return this.cache.set(n,v,t.url),{code:v,strategy:"rag",cost:0,model:"local"}}a=S.filter(C=>C.score>=.65),a.length>0&&je.debug({instruction:e,examples:a.length,topScore:a[0].score},"RAG examples found"),o=this.ragManager.errorCatalog.getWarnings(t.url),o.length>0&&je.debug({url:t.url,warnings:o.length},"RAG error warnings")}catch(S){je.debug({err:S},"RAG retrieval failed \u2014 continuing without memory")}let c=this.mcpSession?await this.enrichWithMcp(e,t):t,l=a.length>0||o.length>0?{...c,_ragExamples:a,_errorWarnings:o}:c;if(this.actThenScript&&this.mcpSession&&c.accessibilityTree){let S=await this.tryActThenScript(e,c);if(S!==null)return this.stats.primary++,this.cache.set(n,S,t.url),{code:S,strategy:"primary",cost:.003,model:"mcp-act"};je.debug({instruction:e},"Act-then-script failed \u2014 falling back to AI generation")}let u=this.classifier.classify(e),d=this.fastProvider!==null&&u!=="complex",m=d?this.fastProvider:this.primaryProvider,p=d?"fast":"primary";je.debug({instruction:e,complexity:u,strategy:p,ragExamples:a.length,errorWarnings:o.length},"AI call");let f=await m.generateCode(e,l),h=Yl(f,c.interactiveElements);return this.cache.set(n,h,t.url),d?this.stats.fast++:this.stats.primary++,{code:h,strategy:p,cost:d?3e-4:.005,model:m.model}}async batchGenerate(e){let t=await this.batchProcessor.batchGenerate(e,this.primaryProvider);return this.stats.batch+=e.length,e.map((s,n)=>({code:t.get(n)??"",strategy:"batch",cost:.001,model:this.primaryProvider.model}))}recordPass(e,t){let s=this.cache.generateKey(e,t);this.cache.recordPass(s)}recordFail(e,t){let s=this.cache.generateKey(e,t);this.cache.recordFail(s)}getStats(){let e=this.stats.template+this.stats.cache+this.stats.rag;return{...this.stats,freeCalls:e,freePercent:(e/Math.max(this.stats.total,1)*100).toFixed(1),cacheEntries:this.cache.size}}getCache(){return this.cache}getPrimaryProvider(){return this.primaryProvider}getMcpSession(){return this.mcpSession}getRagManager(){return this.ragManager}async enrichWithMcp(e,t){try{let s=this.mcpSession;t.url&&(!s.currentUrl||!this.isSameOriginPath(s.currentUrl,t.url))&&(je.debug({url:t.url},"MCP: navigating to target URL"),await s.navigate(t.url));let n=await s.snapshot();if(!n)return je.debug("MCP: snapshot returned null \u2014 using blind context"),t;let i=ei(n);return je.info({elements:i.interactiveElements.split(`
|
|
156
|
+
`).length,title:i.title},"MCP: enriched context with live page elements"),{...t,title:i.title||t.title,interactiveElements:i.interactiveElements||t.interactiveElements,accessibilityTree:i.accessibilityTree}}catch(s){let n=s instanceof Error?s.message:String(s);return je.warn({err:n},"MCP enrichment failed \u2014 using blind context"),t}}async tryActThenScript(e,t){try{let s=this.mcpSession,n=t.accessibilityTree??"";if(!n)return null;let i=await tu(this.primaryProvider,e,n);if(i.length===0)return null;for(let c of i){let l=c.args??{},u=null;switch(c.tool){case"browser_click":u=await s.click(String(l.element??""),String(l.ref??""));break;case"browser_type":u=await s.fill(String(l.element??""),String(l.ref??""),String(l.text??""));break;case"browser_select_option":u=await s.selectOption(String(l.element??""),String(l.ref??""),l.values??[]);break;case"browser_hover":u=await s.hover(String(l.element??""),String(l.ref??""));break;case"browser_press_key":u=await s.pressKey(String(l.key??""));break;case"browser_navigate":u=await s.navigate(String(l.url??""));break;default:je.debug({tool:c.tool},"Unknown MCP action in act-then-script");continue}if(u===null)return je.debug({tool:c.tool,instruction:e},"MCP action failed during act-then-script"),null}let a=nu(i);if(!a)return null;let o=fs(a);return je.info({instruction:e,actions:i.length},"Act-then-script succeeded"),o}catch(s){let n=s instanceof Error?s.message:String(s);return je.warn({err:n,instruction:e},"Act-then-script failed"),null}}isSameOriginPath(e,t){try{let s=new URL(e),n=new URL(t);return s.origin===n.origin&&s.pathname===n.pathname}catch{return e===t}}}});var ou,cu,si,ni,lu=b(()=>{"use strict";g();ou=require("@modelcontextprotocol/sdk/client/index.js"),cu=require("@modelcontextprotocol/sdk/client/stdio.js");j();si=I("mcp-client"),ni=class{client=null;transport=null;_connected=!1;headless;actionTimeout;constructor(e={}){this.headless=e.headless??!0,this.actionTimeout=e.actionTimeout??15e3}get connected(){return this._connected}async connect(){if(this._connected)return;let e=["@playwright/mcp@latest"];this.headless&&e.push("--headless"),this.transport=new cu.StdioClientTransport({command:"npx",args:e}),this.client=new ou.Client({name:"assuremind-studio",version:"1.0.0"},{capabilities:{}}),await this.client.connect(this.transport),this._connected=!0,si.info({headless:this.headless},"MCP client connected")}async disconnect(){if(!(!this._connected||!this.client)){try{await this.client.close()}catch{}this.client=null,this.transport=null,this._connected=!1,si.info("MCP client disconnected")}}async navigate(e){return this.callTool("browser_navigate",{url:e})}async snapshot(){return this.callTool("browser_snapshot",{})}async click(e,t){return this.callTool("browser_click",{element:e,ref:t})}async fill(e,t,s){return this.callTool("browser_type",{element:e,ref:t,text:s})}async selectOption(e,t,s){return this.callTool("browser_select_option",{element:e,ref:t,values:s})}async hover(e,t){return this.callTool("browser_hover",{element:e,ref:t})}async pressKey(e){return this.callTool("browser_press_key",{key:e})}async screenshot(){return this.callTool("browser_take_screenshot",{})}async consoleLogs(){return this.callTool("browser_console_messages",{})}async networkRequests(){return this.callTool("browser_network_requests",{})}async waitAndSnapshot(e){return e?this.callTool("browser_wait",{time:e}):this.callTool("browser_wait",{time:1e3})}async callTool(e,t){if(!this._connected||!this.client)return si.warn({tool:e},"MCP callTool called but client not connected"),null;try{let s=await Promise.race([this.client.callTool({name:e,arguments:t}),new Promise((n,i)=>setTimeout(()=>i(new Error(`MCP tool "${e}" timed out after ${this.actionTimeout}ms`)),this.actionTimeout))]);return s&&Array.isArray(s.content)&&s.content.filter(i=>i.type==="text").map(i=>i.text).join(`
|
|
157
|
+
`)||null}catch(s){let n=s instanceof Error?s.message:String(s);return si.warn({tool:e,error:n},"MCP tool call failed"),null}}}});var uu,Tt,cn=b(()=>{"use strict";g();lu();j();uu=I("mcp-session"),Tt=class{client;idleTimeout;idleTimer=null;mutex=Promise.resolve();_currentUrl=null;constructor(e={}){this.client=new ni(e),this.idleTimeout=e.idleTimeout??3e4}get currentUrl(){return this._currentUrl}get connected(){return this.client.connected}async ensureConnected(){this.resetIdleTimer(),this.client.connected||await this.client.connect()}release(){this.startIdleTimer()}async disconnect(){this.clearIdleTimer(),this._currentUrl=null,await this.client.disconnect()}async navigate(e){return this.withMutex(async()=>{if(await this.ensureConnected(),this._currentUrl&&this.isSameUrl(this._currentUrl,e))return uu.debug({url:e},"Already on URL \u2014 skipping navigation"),this.client.snapshot();let t=await this.client.navigate(e);return t!==null&&(this._currentUrl=e),t})}async snapshot(){return this.withMutex(async()=>(await this.ensureConnected(),this.client.snapshot()))}async click(e,t){return this.withMutex(async()=>(await this.ensureConnected(),this.client.click(e,t)))}async fill(e,t,s){return this.withMutex(async()=>(await this.ensureConnected(),this.client.fill(e,t,s)))}async selectOption(e,t,s){return this.withMutex(async()=>(await this.ensureConnected(),this.client.selectOption(e,t,s)))}async hover(e,t){return this.withMutex(async()=>(await this.ensureConnected(),this.client.hover(e,t)))}async pressKey(e){return this.withMutex(async()=>(await this.ensureConnected(),this.client.pressKey(e)))}async screenshot(){return this.withMutex(async()=>(await this.ensureConnected(),this.client.screenshot()))}async consoleLogs(){return this.withMutex(async()=>(await this.ensureConnected(),this.client.consoleLogs()))}async networkRequests(){return this.withMutex(async()=>(await this.ensureConnected(),this.client.networkRequests()))}async waitAndSnapshot(e){return this.withMutex(async()=>(await this.ensureConnected(),this.client.waitAndSnapshot(e)))}async withMutex(e){let t,s=new Promise(i=>{t=i}),n=this.mutex;this.mutex=s,await n,this.resetIdleTimer();try{return await e()}finally{t()}}isSameUrl(e,t){try{let s=new URL(e),n=new URL(t);return s.origin===n.origin&&s.pathname===n.pathname}catch{return e===t}}startIdleTimer(){this.clearIdleTimer(),this.idleTimer=setTimeout(async()=>{uu.info("MCP session idle \u2014 disconnecting"),await this.disconnect()},this.idleTimeout)}resetIdleTimer(){this.clearIdleTimer()}clearIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}}});var q,du,wy,pu,by,mu,vy,gu,fu,hu,Xa=b(()=>{"use strict";g();q=require("zod"),du=q.z.object({instruction:q.z.string().min(1)}),wy=q.z.object({name:q.z.string().min(1),description:q.z.string().default(""),tags:q.z.array(q.z.string()).default([]),steps:q.z.array(du)}),pu=q.z.object({name:q.z.string().min(1),description:q.z.string().default(""),tags:q.z.array(q.z.string()).default([]),cases:q.z.array(wy)}),by=q.z.object({index:q.z.number().int().min(0),code:q.z.string()}),mu=q.z.object({results:q.z.array(by)}),vy=q.z.object({name:q.z.string().min(1),description:q.z.string().default(""),tags:q.z.array(q.z.string()).default([])}),gu=q.z.object({name:q.z.string().min(1),description:q.z.string().default(""),tags:q.z.array(q.z.string()).default([]),cases:q.z.array(vy)}),fu=q.z.object({steps:q.z.array(du)}),hu=q.z.object({x:q.z.number(),y:q.z.number(),action:q.z.string()})});function yu(r,e){let t=e.previousSteps.length>0?e.previousSteps.slice(-5).map((p,f)=>` ${f+1}. "${p.instruction}"
|
|
158
|
+
${p.code}`).join(`
|
|
159
|
+
`):" None",s=Object.keys(e.variables).length>0?Object.entries(e.variables).map(([p,f])=>` {{${p}}} = ${f}`).join(`
|
|
160
160
|
`):" None",i=!!(e.interactiveElements&&e.accessibilityTree)?`Interactive elements on page (LIVE from actual page \u2014 use exact roles/names):
|
|
161
161
|
${e.interactiveElements}
|
|
162
162
|
|
|
@@ -166,13 +166,13 @@ ${e.interactiveElements||" None captured"}`,a=e.accessibilityTree?`
|
|
|
166
166
|
Full accessibility tree (for additional context):
|
|
167
167
|
${e.accessibilityTree.slice(0,4e3)}`:"",o=e,c=o._ragExamples??[],l=o._errorWarnings??[],u=c.length>0?`
|
|
168
168
|
Similar past steps that worked (use as reference \u2014 adapt, don't copy blindly):
|
|
169
|
-
${c.slice(0,3).map((
|
|
170
|
-
${
|
|
169
|
+
${c.slice(0,3).map((p,f)=>` ${f+1}. "${p.instruction}" (similarity: ${(p.score*100).toFixed(0)}%)
|
|
170
|
+
${p.code}`).join(`
|
|
171
171
|
`)}`:"",d=l.length>0?`
|
|
172
172
|
Known issues on this page (avoid these patterns):
|
|
173
|
-
${l.slice(0,3).map((
|
|
174
|
-
Suggested fix: ${
|
|
175
|
-
`)}`:"",
|
|
173
|
+
${l.slice(0,3).map((p,f)=>` ${f+1}. ${p.errorPattern} (occurred ${p.frequency}x)${p.suggestedFix?`
|
|
174
|
+
Suggested fix: ${p.suggestedFix}`:""}`).join(`
|
|
175
|
+
`)}`:"",m=`Current page:
|
|
176
176
|
URL: ${e.url}
|
|
177
177
|
Title: ${e.title}
|
|
178
178
|
|
|
@@ -184,7 +184,7 @@ ${t}
|
|
|
184
184
|
Runtime variables:
|
|
185
185
|
${s}
|
|
186
186
|
|
|
187
|
-
Step to implement: "${r}"${u}${d}`;return{system:
|
|
187
|
+
Step to implement: "${r}"${u}${d}`;return{system:xy,user:m}}var xy,wu=b(()=>{"use strict";g();xy=`You are a Playwright test automation expert. Convert plain English test steps into executable Playwright TypeScript code.
|
|
188
188
|
|
|
189
189
|
\u2501\u2501\u2501 OUTPUT RULES \u2501\u2501\u2501
|
|
190
190
|
1. Return ONLY raw executable code \u2014 no markdown, no \`\`\`, no explanations
|
|
@@ -418,13 +418,13 @@ For keyboard-specific steps (press key, keyboard shortcut, type text):
|
|
|
418
418
|
await page.getByLabel('Search').press('Enter');
|
|
419
419
|
await page.getByRole('textbox').press('Control+A');
|
|
420
420
|
|
|
421
|
-
Use page.keyboard.press() for single keys or shortcuts, page.keyboard.type() when the step says "type using keyboard".`});function
|
|
421
|
+
Use page.keyboard.press() for single keys or shortcuts, page.keyboard.type() when the step says "type using keyboard".`});function bu(r){return{system:Sy,user:`User story:
|
|
422
422
|
${r}
|
|
423
423
|
|
|
424
424
|
Generate the suite outline (no steps) following this schema:
|
|
425
|
-
${
|
|
425
|
+
${Ey}
|
|
426
426
|
|
|
427
|
-
Return only the JSON object.`}}function
|
|
427
|
+
Return only the JSON object.`}}function vu(r,e,t,s){return{system:Iy,user:`Suite: "${r}"
|
|
428
428
|
Test case: "${e}"
|
|
429
429
|
Description: "${t}"
|
|
430
430
|
|
|
@@ -432,17 +432,17 @@ Context (original user story):
|
|
|
432
432
|
${s}
|
|
433
433
|
|
|
434
434
|
Generate the steps for this test case following this schema:
|
|
435
|
-
${
|
|
435
|
+
${Cy}
|
|
436
436
|
|
|
437
|
-
Return only the JSON object.`}}function
|
|
437
|
+
Return only the JSON object.`}}function xu(r){let e=`User story:
|
|
438
438
|
${r}
|
|
439
439
|
|
|
440
440
|
Generate a JSON test suite following this exact schema:
|
|
441
|
-
${
|
|
441
|
+
${Ry}
|
|
442
442
|
|
|
443
|
-
Important: return only the JSON object, nothing else.`;return{system:
|
|
443
|
+
Important: return only the JSON object, nothing else.`;return{system:Py,user:e}}var Sy,Ey,Iy,Cy,Py,Ry,Su=b(()=>{"use strict";g();Sy=`You are a QA test planning expert.
|
|
444
444
|
Given a user story, produce a test suite outline \u2014 the suite metadata and a list of test cases.
|
|
445
|
-
Do NOT include steps yet. Return ONLY valid JSON, no markdown, no code fences.`,
|
|
445
|
+
Do NOT include steps yet. Return ONLY valid JSON, no markdown, no code fences.`,Ey=`{
|
|
446
446
|
"name": "Suite name (e.g. Authentication)",
|
|
447
447
|
"description": "What this suite covers",
|
|
448
448
|
"tags": ["smoke", "regression"],
|
|
@@ -453,13 +453,13 @@ Do NOT include steps yet. Return ONLY valid JSON, no markdown, no code fences.`,
|
|
|
453
453
|
"tags": ["smoke"]
|
|
454
454
|
}
|
|
455
455
|
]
|
|
456
|
-
}`;
|
|
456
|
+
}`;Iy=`You are a QA test automation expert.
|
|
457
457
|
Given a test case name and description, write the detailed plain-English steps needed to execute it.
|
|
458
458
|
Rules:
|
|
459
459
|
1. Return ONLY valid JSON \u2014 no markdown, no code fences
|
|
460
460
|
2. Each step must be a single atomic action or assertion
|
|
461
461
|
3. Be concrete \u2014 reference actual UI elements by their visible labels or text
|
|
462
|
-
4. Use {{VARIABLE}} tokens for sensitive values (e.g. {{PASSWORD}}, {{USERNAME}})`,
|
|
462
|
+
4. Use {{VARIABLE}} tokens for sensitive values (e.g. {{PASSWORD}}, {{USERNAME}})`,Cy=`{
|
|
463
463
|
"steps": [
|
|
464
464
|
{ "instruction": "Navigate to the login page" },
|
|
465
465
|
{ "instruction": "Enter 'admin' in the Username field" },
|
|
@@ -467,7 +467,7 @@ Rules:
|
|
|
467
467
|
{ "instruction": "Click the 'Login' button" },
|
|
468
468
|
{ "instruction": "Verify the Dashboard heading is visible" }
|
|
469
469
|
]
|
|
470
|
-
}`;
|
|
470
|
+
}`;Py=`You are a QA test planning expert. Convert user stories into structured test suites.
|
|
471
471
|
|
|
472
472
|
RULES:
|
|
473
473
|
1. Return ONLY valid JSON \u2014 no markdown, no explanations, no code fences
|
|
@@ -475,7 +475,7 @@ RULES:
|
|
|
475
475
|
3. Steps must be atomic \u2014 one action or assertion per step
|
|
476
476
|
4. Cover the happy path, then common edge cases and negative scenarios
|
|
477
477
|
5. Test case names must be descriptive and start with a verb (e.g. "Login with valid credentials")
|
|
478
|
-
6. Steps should be concrete \u2014 reference actual UI elements by their labels or text`,
|
|
478
|
+
6. Steps should be concrete \u2014 reference actual UI elements by their labels or text`,Ry=`{
|
|
479
479
|
"name": "Suite name (e.g. Authentication)",
|
|
480
480
|
"description": "What this suite covers",
|
|
481
481
|
"tags": ["smoke", "regression"],
|
|
@@ -493,7 +493,7 @@ RULES:
|
|
|
493
493
|
]
|
|
494
494
|
}
|
|
495
495
|
]
|
|
496
|
-
}`});function
|
|
496
|
+
}`});function Eu(r,e,t,s,n){let a=!!(s.interactiveElements&&s.accessibilityTree)?`Current interactive elements (LIVE from page snapshot \u2014 use exact names):
|
|
497
497
|
${s.interactiveElements}`:`Current interactive elements:
|
|
498
498
|
${s.interactiveElements||" None captured"}`,o=s.accessibilityTree?`
|
|
499
499
|
Accessibility tree:
|
|
@@ -520,7 +520,7 @@ ${a}
|
|
|
520
520
|
HTML snapshot (partial):
|
|
521
521
|
${s.htmlSnapshot.slice(0,3e3)}${o}${c}
|
|
522
522
|
|
|
523
|
-
Generate new Playwright code that achieves: "${r}"`;return{system:
|
|
523
|
+
Generate new Playwright code that achieves: "${r}"`;return{system:ky,user:l}}var ky,Iu=b(()=>{"use strict";g();ky=`You are a Playwright test self-healing expert. A test step has failed due to an infrastructure issue (broken selector, element moved, DOM changed) and you must generate new code that will work with the current page state.
|
|
524
524
|
|
|
525
525
|
RULES:
|
|
526
526
|
1. Return ONLY raw executable code \u2014 no markdown, no explanations, no code fences
|
|
@@ -538,7 +538,7 @@ RULES:
|
|
|
538
538
|
expect(page.frameLocator('#wrong').getByText('Docs')).toBeVisible()), fix ONLY the
|
|
539
539
|
structural/locator path \u2014 the frame selector or container. PRESERVE the matcher
|
|
540
540
|
(toBeVisible/toHaveText/...) AND the content being matched (e.g. keep getByText('Docs')
|
|
541
|
-
verbatim). Never swap 'Docs' for another element to force a pass.`});function
|
|
541
|
+
verbatim). Never swap 'Docs' for another element to force a pass.`});function Cu(r,e){let t=r.map(c=>` ${c.index}. "${c.instruction}"`).join(`
|
|
542
542
|
`),s=Object.keys(e.variables).length>0?Object.entries(e.variables).map(([c,l])=>` {{${c}}} = ${l}`).join(`
|
|
543
543
|
`):" None",i=!!(e.interactiveElements&&e.accessibilityTree)?`Interactive elements on page (LIVE from actual page \u2014 use exact roles/names):
|
|
544
544
|
${e.interactiveElements}
|
|
@@ -569,7 +569,7 @@ Return JSON:
|
|
|
569
569
|
]
|
|
570
570
|
}
|
|
571
571
|
|
|
572
|
-
Return one result per step. Include ALL ${r.length} steps.`;return{system:
|
|
572
|
+
Return one result per step. Include ALL ${r.length} steps.`;return{system:Ty,user:o}}var Ty,Pu=b(()=>{"use strict";g();Ty=`You are a Playwright test automation expert. Generate executable Playwright code for multiple test steps in a single JSON response.
|
|
573
573
|
|
|
574
574
|
\u2501\u2501\u2501 OUTPUT FORMAT \u2501\u2501\u2501
|
|
575
575
|
1. Return ONLY valid JSON \u2014 no markdown, no \`\`\`, no explanations
|
|
@@ -654,8 +654,8 @@ Not visible: await expect(page.getByText('Error')).not.toBeVisible()
|
|
|
654
654
|
\u2501\u2501\u2501 SOFT ASSERTIONS (test continues on failure) \u2501\u2501\u2501
|
|
655
655
|
When instruction starts with "Soft verify/assert/check", use expect.soft():
|
|
656
656
|
await expect.soft(page.getByText('Welcome')).toBeVisible()
|
|
657
|
-
await expect.soft(page).toHaveURL(/dashboard/)`});async function
|
|
658
|
-
Return JSON: { "x": <pixels from left>, "y": <pixels from top>, "action": "click|fill|hover" }`,n=await this.chatWithVision(s,e);return this.parseScreenshotAnalysis(n)}async analyzeWithSoM(e,t,s){if(!this.supportsVision||!this.chatWithVision)throw new
|
|
657
|
+
await expect.soft(page).toHaveURL(/dashboard/)`});async function Ay(r,e){let t=new Array(r.length),s=0;async function n(){for(;s<r.length;){let a=s++;t[a]=await r[a]()}}let i=Array.from({length:Math.min(e,r.length)},n);return await Promise.all(i),t}function ln(r){let e=ms(r).trim();try{return JSON.parse(e),null}catch{}let t=[],s=!1,n=!1;for(let a of e){if(n){n=!1;continue}if(a==="\\"&&s){n=!0;continue}if(a==='"'){s=!s;continue}s||(a==="{"||a==="["?t.push(a==="{"?"}":"]"):(a==="}"||a==="]")&&t.pop())}if(t.length===0)return null;let i=e;s&&(i+='"'),i+=t.reverse().join("");try{return JSON.parse(i),i}catch{return null}}var Be,Ar=b(()=>{"use strict";g();Xa();wu();Su();Iu();Pu();Vt();ge();Xa();Vt();Be=class{timeoutMs;maxRetries;temperature;constructor(e){this.timeoutMs=e.timeoutMs??3e4,this.maxRetries=e.maxRetries??2,this.temperature=e.temperature??.1}async generateCode(e,t){let{system:s,user:n}=yu(e,t),i=await this.chatWithRetry([{role:"system",content:s},{role:"user",content:n}]),a=Ke(i);return gs(e,a)}async generateTestSuite(e){try{return await this.generateTestSuiteTwoPass(e)}catch{return await this.generateTestSuiteSinglePass(e)}}async generateTestSuiteTwoPass(e){let t=bu(e),s=await this.chatWithRetry([{role:"system",content:t.system},{role:"user",content:t.user}]),n=Bn(s),i=ln(n)??n,a=gu.safeParse(JSON.parse(i));if(!a.success)throw new te("Outline parse failed","outline","INVALID_OUTLINE");let o=a.data,c=await Ay(o.cases.map(l=>async()=>{let u=vu(o.name,l.name,l.description,e),d=await this.chatWithRetry([{role:"system",content:u.system},{role:"user",content:u.user}]),m=Bn(d),p=ln(m)??m,f=fu.safeParse(JSON.parse(p));return f.success?f.data.steps:[]}),5);return{name:o.name,description:o.description,tags:o.tags,cases:o.cases.map((l,u)=>({name:l.name,description:l.description,tags:l.tags,steps:(c[u]??[]).map(d=>({instruction:d.instruction,generatedCode:"",strategy:"primary"}))}))}}async generateTestSuiteSinglePass(e){let{system:t,user:s}=xu(e),n=[{role:"system",content:t},{role:"user",content:s}],i=await this.chatWithRetry(n);for(let a=0;a<2;a++){let o=ln(i);try{return this.parseGeneratedSuite(o??i)}catch(c){let l=i.trimEnd();if(!(!l.endsWith("}")&&!l.endsWith("]}")))throw c;n.push({role:"assistant",content:i}),n.push({role:"user",content:"Your JSON was cut off. Continue the JSON exactly from where you stopped \u2014 output only the continuation, no preamble."}),i+=await this.chatWithRetry(n)}}return this.parseGeneratedSuite(ln(i)??i)}async healSelector(e,t,s,n){let{system:i,user:a}=Eu(e,t,s,n),o=await this.chatWithRetry([{role:"system",content:i},{role:"user",content:a}]);return Ke(o)}async batchGenerateCode(e){if(e.length===0)return[];let t=e.map((o,c)=>({index:c,instruction:o.instruction})),{system:s,user:n}=Cu(t,e[0].context),i=await this.chatWithRetry([{role:"system",content:s},{role:"user",content:n}]);return(await this.parseBatchResponse(i,e.length)).map((o,c)=>gs(e[c]?.instruction??"",o))}async analyzeScreenshot(e,t){if(!this.supportsVision||!this.chatWithVision)throw new V(`Provider "${this.name}" does not support vision. Use Claude, GPT-4o, or Gemini for visual/coordinate-based healing.`,this.name,"VISION_NOT_SUPPORTED");let s=`Identify the UI element to interact with for this step: "${t}"
|
|
658
|
+
Return JSON: { "x": <pixels from left>, "y": <pixels from top>, "action": "click|fill|hover" }`,n=await this.chatWithVision(s,e);return this.parseScreenshotAnalysis(n)}async analyzeWithSoM(e,t,s){if(!this.supportsVision||!this.chatWithVision)throw new V(`Provider "${this.name}" does not support vision.`,this.name,"VISION_NOT_SUPPORTED");let n=`You are a Playwright test automation expert analyzing an annotated browser screenshot.
|
|
659
659
|
|
|
660
660
|
Each interactive element on the page has been highlighted with a colored numbered box.
|
|
661
661
|
|
|
@@ -678,48 +678,48 @@ Rules:
|
|
|
678
678
|
Examples:
|
|
679
679
|
await page.getByRole('link', { name: 'Admin' }).click()
|
|
680
680
|
await page.getByPlaceholder('Username').fill('Admin')
|
|
681
|
-
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible()`,i=await this.chatWithVision(n,e);return
|
|
682
|
-
`);throw new
|
|
683
|
-
${a}`,"generatedSuite","INVALID_SUITE_SCHEMA")}return{name:i.data.name,description:i.data.description,tags:i.data.tags,cases:i.data.cases.map(a=>({name:a.name,description:a.description,tags:a.tags,steps:a.steps.map(o=>({instruction:o.instruction,generatedCode:"",strategy:"primary"}))}))}}parseBatchResponse(e,t){let s=es(e),n=s.match(/\{[\s\S]*\}/);n&&(s=n[0]);let i=new Array(t).fill(""),a=!1;try{let l=JSON.parse(s),u=Zl.safeParse(l);if(u.success){a=!0;for(let d of u.data.results)if(d.index<t)try{i[d.index]=De(d.code)}catch{}return i}}catch{}if(a)return i;let o=/\/\/\s*STEP[_\s]?(\d+)\s*\n([\s\S]*?)(?=\/\/\s*STEP[_\s]?\d+|$)/g,c;for(;(c=o.exec(s))!==null;){let l=parseInt(c[1],10);if(l<t)try{i[l]=De(c[2].trim())}catch{}}return i}parseScreenshotAnalysis(e){let t=es(e);try{let s=JSON.parse(t),n=ru.safeParse(s);if(n.success)return n.data}catch{}throw new K(`Could not parse screenshot analysis response: ${t.slice(0,200)}`,"screenshotAnalysis","INVALID_SCREENSHOT_ANALYSIS")}}});var gu={};Q(gu,{AnthropicProvider:()=>Fa});var mu,Fa,fu=b(()=>{"use strict";m();mu=x(require("@anthropic-ai/sdk"));gr();re();Fa=class extends Ae{name="anthropic";model;supportsVision=!0;client;constructor(e){super(e),this.model=e.model,this.client=new mu.default({apiKey:e.apiKey,timeout:this.timeoutMs,maxRetries:0})}async chat(e){let t=e.find(n=>n.role==="system")?.content,s=e.filter(n=>n.role!=="system");try{let i=(await this.client.messages.create({model:this.model,max_tokens:8192,temperature:this.temperature,system:t,messages:s.map(a=>({role:a.role,content:a.content}))})).content[0];if(!i||i.type!=="text")throw new B("Anthropic returned an empty or non-text response.",this.name,"EMPTY_RESPONSE");return i.text}catch(n){if(n instanceof B)throw n;let i=n instanceof Error?n.message:String(n);throw new B(`Anthropic API error: ${i}`,this.name,"API_ERROR")}}async chatWithVision(e,t){try{let n=(await this.client.messages.create({model:this.model,max_tokens:512,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:"image/png",data:t.toString("base64")}},{type:"text",text:e}]}]})).content[0];if(!n||n.type!=="text")throw new B("Anthropic vision returned empty response.",this.name);return n.text}catch(s){throw s instanceof B?s:new B(`Anthropic vision error: ${s instanceof Error?s.message:String(s)}`,this.name,"VISION_ERROR")}}async validate(){return this.client.apiKey?super.validate():{valid:!1,error:"ANTHROPIC_API_KEY is not set"}}}});var yu={};Q(yu,{OpenAIProvider:()=>Ma});var hu,Ma,wu=b(()=>{"use strict";m();hu=x(require("openai"));gr();re();Ma=class extends Ae{name="openai";model;supportsVision=!0;client;constructor(e){super(e),this.model=e.model,this.client=new hu.default({apiKey:e.apiKey,timeout:this.timeoutMs,maxRetries:0})}async chat(e){try{let s=(await this.client.chat.completions.create({model:this.model,messages:e.map(n=>({role:n.role,content:n.content})),temperature:this.temperature,max_tokens:8192})).choices[0]?.message?.content;if(!s)throw new B("OpenAI returned an empty response.",this.name,"EMPTY_RESPONSE");return s}catch(t){throw t instanceof B?t:new B(`OpenAI API error: ${t instanceof Error?t.message:String(t)}`,this.name,"API_ERROR")}}async chatWithVision(e,t){try{let n=(await this.client.chat.completions.create({model:this.model,max_tokens:512,messages:[{role:"user",content:[{type:"image_url",image_url:{url:`data:image/png;base64,${t.toString("base64")}`}},{type:"text",text:e}]}]})).choices[0]?.message?.content;if(!n)throw new B("OpenAI vision returned empty response.",this.name);return n}catch(s){throw s instanceof B?s:new B(`OpenAI vision error: ${s instanceof Error?s.message:String(s)}`,this.name,"VISION_ERROR")}}async validate(){return this.client.apiKey?super.validate():{valid:!1,error:"OPENAI_API_KEY is not set"}}}});var vu={};Q(vu,{GoogleProvider:()=>ja});var bu,ja,xu=b(()=>{"use strict";m();bu=require("@google/generative-ai");gr();re();ja=class extends Ae{name="google";model;supportsVision=!0;genAI;constructor(e){super(e),this.model=e.model,this.genAI=new bu.GoogleGenerativeAI(e.apiKey)}async chat(e){try{let t=e.find(p=>p.role==="system"),s=e.filter(p=>p.role!=="system"),n=s.at(-1)?.content??"",i=n.includes("Return only the JSON")||n.includes("Return ONLY valid JSON")||n.includes("return only the JSON")||n.includes("Return only a single line")||(t?.content?.includes("Return ONLY valid JSON")??!1),a=this.genAI.getGenerativeModel({model:this.model,systemInstruction:t?.content,generationConfig:{temperature:this.temperature,maxOutputTokens:8192,...i?{responseMimeType:"application/json"}:{}}}),o=s.slice(0,-1).map(p=>({role:p.role==="assistant"?"model":"user",parts:[{text:p.content}]})),c=s.at(-1);if(!c)throw new B("No user message provided.",this.name);let d=(await a.startChat({history:o}).sendMessage(c.content)).response.text();if(!d)throw new B("Google Gemini returned empty response.",this.name,"EMPTY_RESPONSE");return d}catch(t){throw t instanceof B?t:new B(`Google Gemini error: ${t instanceof Error?t.message:String(t)}`,this.name,"API_ERROR")}}async chatWithVision(e,t){try{return(await this.genAI.getGenerativeModel({model:this.model}).generateContent([e,{inlineData:{data:t.toString("base64"),mimeType:"image/png"}}])).response.text()}catch(s){throw new B(`Google vision error: ${s instanceof Error?s.message:String(s)}`,this.name,"VISION_ERROR")}}async validate(){return this.genAI?super.validate():{valid:!1,error:"GOOGLE_API_KEY is not set"}}}});var Eu={};Q(Eu,{AzureOpenAIProvider:()=>Ba});var Su,Ba,Cu=b(()=>{"use strict";m();Su=x(require("openai"));gr();re();Ba=class extends Ae{name="azure-openai";model;supportsVision=!0;client;constructor(e){super(e),this.model=e.deployment,this.client=new Su.default({apiKey:e.apiKey,baseURL:`${e.endpoint}/openai/deployments/${e.deployment}`,defaultQuery:{"api-version":e.apiVersion??"2024-10-21"},defaultHeaders:{"api-key":e.apiKey},timeout:this.timeoutMs,maxRetries:0})}async chat(e){try{let s=(await this.client.chat.completions.create({model:this.model,messages:e.map(n=>({role:n.role,content:n.content})),temperature:this.temperature,max_tokens:8192})).choices[0]?.message?.content;if(!s)throw new B("Azure OpenAI returned empty response.",this.name,"EMPTY_RESPONSE");return s}catch(t){throw t instanceof B?t:new B(`Azure OpenAI error: ${t instanceof Error?t.message:String(t)}`,this.name,"API_ERROR")}}async validate(){return this.client.apiKey?super.validate():{valid:!1,error:"AZURE_OPENAI_API_KEY is not set"}}}});var Iu={};Q(Iu,{BedrockProvider:()=>Ua});var zn,Ua,Pu=b(()=>{"use strict";m();zn=require("@aws-sdk/client-bedrock-runtime");gr();re();Ua=class extends Ae{name="bedrock";model;supportsVision=!1;client;constructor(e){super(e),this.model=e.model,this.client=new zn.BedrockRuntimeClient({region:e.region,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey,...e.sessionToken?{sessionToken:e.sessionToken}:{}}})}async chat(e){try{return this.model.includes("claude")?await this.chatClaude(e):await this.chatClaude(e)}catch(t){throw t instanceof B?t:new B(`Bedrock error: ${t instanceof Error?t.message:String(t)}`,this.name,"API_ERROR")}}async chatClaude(e){let t=e.find(u=>u.role==="system")?.content,s=e.filter(u=>u.role!=="system"),n={anthropic_version:"bedrock-2023-05-31",max_tokens:8192,temperature:this.temperature,messages:s.map(u=>({role:u.role,content:u.content}))};t&&(n.system=t);let i=new zn.InvokeModelCommand({modelId:this.model,body:JSON.stringify(n),contentType:"application/json",accept:"application/json"}),a=await this.client.send(i),o=new TextDecoder().decode(a.body),l=JSON.parse(o).content[0]?.text;if(!l)throw new B("Bedrock returned empty response.",this.name,"EMPTY_RESPONSE");return l}async validate(){return this.client.config.credentials?super.validate():{valid:!1,error:"AWS credentials are not configured"}}}});var Ru,Ue,Dr=b(()=>{"use strict";m();Ru=x(require("openai"));gr();re();Ue=class extends Ae{name;model;supportsVision=!1;client;constructor(e,t){super(t),this.name=e,this.model=t.model,this.client=new Ru.default({apiKey:t.apiKey,baseURL:t.baseUrl,timeout:this.timeoutMs,maxRetries:0})}async chat(e){try{let s=(await this.client.chat.completions.create({model:this.model,messages:e.map(n=>({role:n.role,content:n.content})),temperature:this.temperature,max_tokens:8192})).choices[0]?.message?.content;if(!s)throw new B(`Provider "${this.name}" returned an empty response.`,this.name,"EMPTY_RESPONSE");return s}catch(t){if(t instanceof B)throw t;let s=t instanceof Error?t.message:String(t);throw new B(`Provider "${this.name}" API error: ${s}`,this.name,"API_ERROR")}}async validate(){return!this.client.apiKey||this.client.apiKey==="none"?{valid:!1,error:`API key is not configured for provider "${this.name}"`}:super.validate()}}});var Tu={};Q(Tu,{DeepSeekProvider:()=>Ha});var Ha,ku=b(()=>{"use strict";m();Dr();Ha=class extends Ue{name="deepseek";constructor(e){let t={apiKey:e.apiKey,baseUrl:"https://api.deepseek.com/v1",model:e.model??"deepseek-chat",timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("deepseek",t)}}});var Au={};Q(Au,{GroqProvider:()=>Va});var Va,$u=b(()=>{"use strict";m();Dr();Va=class extends Ue{name="groq";constructor(e){let t={apiKey:e.apiKey,baseUrl:"https://api.groq.com/openai/v1",model:e.model??"llama-3.3-70b-versatile",timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("groq",t)}}});var _u={};Q(_u,{TogetherProvider:()=>Ga});var Ga,Ou=b(()=>{"use strict";m();Dr();Ga=class extends Ue{name="together";constructor(e){let t={apiKey:e.apiKey,baseUrl:"https://api.together.xyz/v1",model:e.model??"meta-llama/Llama-3.3-70B-Instruct-Turbo",timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("together",t)}}});var Lu={};Q(Lu,{QwenProvider:()=>za});var za,Du=b(()=>{"use strict";m();Dr();za=class extends Ue{name="qwen";constructor(e){let t={apiKey:e.apiKey,baseUrl:e.baseUrl??"https://dashscope-intl.aliyuncs.com/compatible-mode/v1",model:e.model??"qwen-max",timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("qwen",t)}}});var Nu={};Q(Nu,{PerplexityProvider:()=>qa});var qa,Fu=b(()=>{"use strict";m();Dr();qa=class extends Ue{name="perplexity";constructor(e){let t={apiKey:e.apiKey,baseUrl:"https://api.perplexity.ai",model:e.model??"sonar-pro",timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("perplexity",t)}}});var ju={};Q(ju,{OllamaProvider:()=>Ka});var Mu,Ka,Bu=b(()=>{"use strict";m();Mu=x(require("openai"));gr();re();Ka=class extends Ae{name="ollama";model;supportsVision=!1;baseUrl;client;constructor(e={}){super({model:e.model??"llama3.3",timeoutMs:e.timeoutMs??12e4,maxRetries:e.maxRetries??1}),this.model=e.model??"llama3.3",this.baseUrl=`${e.baseUrl??"http://localhost:11434"}/v1`,this.client=new Mu.default({apiKey:"ollama",baseURL:this.baseUrl,timeout:this.timeoutMs,maxRetries:0})}async chat(e){try{let s=(await this.client.chat.completions.create({model:this.model,messages:e.map(n=>({role:n.role,content:n.content})),temperature:this.temperature})).choices[0]?.message?.content;if(!s)throw new B("Ollama returned an empty response.",this.name,"EMPTY_RESPONSE");return s}catch(t){if(t instanceof B)throw t;let s=t instanceof Error?t.message:String(t);throw s.includes("ECONNREFUSED")||s.includes("fetch failed")?new B(`Cannot connect to Ollama at ${this.baseUrl}. Make sure Ollama is running: start it with "ollama serve" and confirm it's on port 11434.`,this.name,"CONNECTION_REFUSED"):s.includes("model")&&(s.includes("not found")||s.includes("pull"))?new B(`Ollama model "${this.model}" is not available locally. Pull it first: ollama pull ${this.model}`,this.name,"MODEL_NOT_FOUND"):new B(`Ollama error: ${s}`,this.name,"API_ERROR")}}async validate(){try{return await this.chat([{role:"user",content:"Reply with the single word: ready"}]),{valid:!0}}catch(e){return{valid:!1,error:e instanceof Error?e.message:String(e)}}}}});var Uu={};Q(Uu,{CustomProvider:()=>Wa});var Wa,Hu=b(()=>{"use strict";m();Dr();Wa=class extends Ue{name="custom";constructor(e){let t={apiKey:e.apiKey,baseUrl:e.baseUrl,model:e.model,timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("custom",t)}}});var Vu={};Q(Vu,{createProvider:()=>Nr,createSmartRouter:()=>st,generateSuiteFromStory:()=>en,generateSuitesFromStories:()=>Ja});function Nr(r,e){let t=lr(),s=r??String(t.AI_PROVIDER),n=t.AI_TIMEOUT*1e3,i=t.AI_MAX_RETRIES,a=e?.temperature;switch(s){case"anthropic":{let{AnthropicProvider:o}=(fu(),et(gu));return new o({apiKey:String(t.ANTHROPIC_API_KEY??""),model:String(t.ANTHROPIC_MODEL??"claude-sonnet-4-20250514"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"openai":{let{OpenAIProvider:o}=(wu(),et(yu));return new o({apiKey:String(t.OPENAI_API_KEY??""),model:String(t.OPENAI_MODEL??"gpt-4o"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"google":{let{GoogleProvider:o}=(xu(),et(vu));return new o({apiKey:String(t.GOOGLE_API_KEY??""),model:String(t.GOOGLE_MODEL??"gemini-2.5-flash"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"azure-openai":{let{AzureOpenAIProvider:o}=(Cu(),et(Eu));return new o({apiKey:String(t.AZURE_OPENAI_API_KEY??""),endpoint:String(t.AZURE_OPENAI_ENDPOINT??""),deployment:String(t.AZURE_OPENAI_DEPLOYMENT??""),apiVersion:String(t.AZURE_OPENAI_API_VERSION??"2024-10-21"),model:String(t.AZURE_OPENAI_DEPLOYMENT??""),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"bedrock":{let{BedrockProvider:o}=(Pu(),et(Iu));return new o({accessKeyId:String(t.AWS_ACCESS_KEY_ID??""),secretAccessKey:String(t.AWS_SECRET_ACCESS_KEY??""),...t.AWS_SESSION_TOKEN?{sessionToken:String(t.AWS_SESSION_TOKEN)}:{},region:String(t.AWS_REGION??"us-east-1"),model:String(t.BEDROCK_MODEL??"anthropic.claude-sonnet-4-20250514-v1:0"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"deepseek":{let{DeepSeekProvider:o}=(ku(),et(Tu));return new o({apiKey:String(t.DEEPSEEK_API_KEY??""),model:String(t.DEEPSEEK_MODEL??"deepseek-chat"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"groq":{let{GroqProvider:o}=($u(),et(Au));return new o({apiKey:String(t.GROQ_API_KEY??""),model:String(t.GROQ_MODEL??"llama-3.3-70b-versatile"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"together":{let{TogetherProvider:o}=(Ou(),et(_u));return new o({apiKey:String(t.TOGETHER_API_KEY??""),model:String(t.TOGETHER_MODEL??"meta-llama/Llama-3.3-70B-Instruct-Turbo"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"qwen":{let{QwenProvider:o}=(Du(),et(Lu));return new o({apiKey:String(t.QWEN_API_KEY??""),model:String(t.QWEN_MODEL??"qwen-max"),baseUrl:t.QWEN_BASE_URL,timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"perplexity":{let{PerplexityProvider:o}=(Fu(),et(Nu));return new o({apiKey:String(t.PERPLEXITY_API_KEY??""),model:String(t.PERPLEXITY_MODEL??"sonar-pro"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"ollama":{let{OllamaProvider:o}=(Bu(),et(ju));return new o({model:String(t.OLLAMA_MODEL??"llama3.3"),baseUrl:t.OLLAMA_BASE_URL,timeoutMs:Math.max(n,12e4),maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"custom":{let{CustomProvider:o}=(Hu(),et(Uu));return new o({apiKey:String(t.CUSTOM_API_KEY??""),baseUrl:String(t.CUSTOM_BASE_URL??""),model:String(t.CUSTOM_MODEL??""),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}default:throw new fe(`Unknown AI provider: "${s}". Supported: anthropic, openai, google, azure-openai, bedrock, deepseek, groq, together, qwen, perplexity, ollama, custom`,"UNKNOWN_PROVIDER")}}function st(r,e,t,s){let n=lr(),i=Nr(),a;if(n.AI_TIERED_ENABLED){let l=n.AI_TIERED_FAST_PROVIDER;if(l)try{a=Nr(l),Ne.info({fast:l,primary:String(n.AI_PROVIDER)},"Tiered mode active")}catch(u){Ne.warn({err:u},"Failed to create fast provider \u2014 tiered mode disabled")}}let o=r?qn.default.join(r,"results",".code-cache.json"):void 0,c=new os(o);return new Hn({primaryProvider:i,fastProvider:a,cache:c,mcpSession:e,actThenScript:t,ragManager:s})}async function en(r,e){let t=Nr();Ne.info({provider:t.name,model:t.model},"Generating suite from story");let s=await t.generateTestSuite(r),n=e.suiteName??s.name,{suiteDir:i,suiteId:a}=await Ws(e.outputDir,{name:n,description:s.description,tags:s.tags,type:e.type??"ui"});Ne.info({suiteDir:i,suiteId:a,cases:s.cases.length},"Suite directory created");for(let l of s.cases)await is(i,{name:l.name,description:l.description,tags:l.tags,priority:"medium",steps:l.steps.map((u,d)=>({id:`step-${d+1}`,order:d+1,instruction:u.instruction,generatedCode:u.generatedCode||"",strategy:u.strategy??"primary",stepType:tt(u.instruction),lastHealed:null}))});Ne.info({suiteId:a,cases:s.cases.length},"Auto-generating code for all steps");let o;try{let l=await G(e.rootDir),u=await rt(e.rootDir).catch(()=>({}));l.mcp?.enabled&&(o=new ht({headless:l.mcp.headless,actionTimeout:l.mcp.actionTimeout,idleTimeout:l.mcp.idleTimeout}),Ne.info({headless:l.mcp.headless},"MCP session created for suite generation"));let d=st(e.rootDir,o,l.mcp?.actThenScript),p=await zt(i);for(let g of p){let f=await se(g),h=f.steps.filter(P=>!P.generatedCode.trim());if(h.length===0)continue;let S=h.map(P=>({instruction:P.instruction,context:{url:l.baseUrl,title:"",interactiveElements:"",htmlSnapshot:"",previousSteps:f.steps.filter(j=>j.order<P.order&&j.generatedCode).map(j=>({instruction:j.instruction,code:j.generatedCode})),variables:u}})),I=await d.batchGenerate(S),v=new Map(h.map((P,j)=>[P.id,I[j]])),k=0,T=f.steps.map(P=>{let j=v.get(P.id);return j&&j.code.trim()?(k++,{...P,generatedCode:j.code,strategy:j.strategy}):P});k>0&&(await Be(g,{steps:T}),Ne.info({caseName:f.name,generated:k,total:f.steps.length},"Code generated for case"))}await d.getCache().persist(),Ne.info({suiteId:a},"Auto code generation complete")}catch(l){Ne.warn({err:l,suiteId:a},"Auto code generation failed \u2014 steps saved without code")}finally{o&&(o.release(),Ne.debug("MCP session released after suite generation"))}let c=qn.default.join(e.rootDir,"results",`generated-suite-${kt(n)}.json`);return await Xa.default.ensureDir(qn.default.dirname(c)),await Xa.default.writeJson(c,{story:r,generated:s,suiteDir:i,suiteId:a},{spaces:2}),Ne.info({reportPath:c,suiteId:a},"Generation complete"),{suiteId:a,suiteName:n}}async function Ja(r,e,t,s=3){let{randomUUID:n}=await import("crypto"),i=n(),a=r.length,o=0,c=0,l=[];t?.broadcast("story:bulk-start",{jobId:i,total:a}),Ne.info({jobId:i,total:a,concurrency:s},"Bulk story generation started");let u=0,d=[...r.entries()],p=[];async function g(){for(;d.length>0;){let h=d.shift();if(!h)break;let[S,I]=h;t?.broadcast("story:bulk-progress",{jobId:i,total:a,completed:o,failed:c,currentIndex:S,currentStory:I.story.slice(0,80)});try{await en(I.story,{rootDir:e.rootDir,outputDir:e.outputDir,suiteName:I.suiteName}),o++,Ne.info({jobId:i,index:S,completed:o,total:a},"Story processed")}catch(v){c++;let k=v instanceof Error?v.message:String(v);l.push({index:S,story:I.story.slice(0,120),error:k}),Ne.warn({jobId:i,index:S,error:k},"Story generation failed \u2014 continuing batch")}t?.broadcast("story:bulk-progress",{jobId:i,total:a,completed:o,failed:c,currentIndex:S,currentStory:I.story.slice(0,80)}),d.length>0&&await new Promise(v=>setTimeout(v,350))}}for(let h=0;h<Math.min(s,a);h++)p.push(g());await Promise.all(p);let f={jobId:i,total:a,completed:o,failed:c,failedItems:l};return t?.broadcast("story:bulk-done",f),Ne.info({jobId:i,completed:o,failed:c,total:a},"Bulk story generation finished"),f}var qn,Xa,Ne,fr=b(()=>{"use strict";m();qn=x(require("path")),Xa=x(require("fs-extra"));ur();re();F();ql();_a();Qs();gt();Ke();Te();mr();At();$r();Ne=C("router")});async function qu(r){let e=r.rootDir,t=Gu.default.join(e,"tests");async function s(n,i){let a=await de(t,n);return a?mt(a,i):null}r.post("/suites/:suiteId/cases/:caseId/steps",async(n,i)=>{let a=xy.safeParse(n.body);if(!a.success)return i.status(400).send({error:"Invalid step data",details:a.error.issues});try{let o=await s(n.params.suiteId,n.params.caseId);if(!o)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=await se(o),l=a.data.order??c.steps.length+1,u={id:`step-${(0,zu.randomUUID)().slice(0,8)}`,order:l,instruction:a.data.instruction,generatedCode:a.data.generatedCode??"",strategy:a.data.generatedCode?a.data.strategy??"recorder":"primary",lastHealed:null,...a.data.soft?{soft:!0}:{}},d=[...c.steps.map(g=>g.order>=l?{...g,order:g.order+1}:g),u].sort((g,f)=>g.order-f.order),p=await Be(o,{steps:d});return i.status(201).send(p)}catch(o){return w(i,500,"Failed to add step",o)}}),r.delete("/suites/:suiteId/cases/:caseId/steps/:stepId",async(n,i)=>{try{let a=await s(n.params.suiteId,n.params.caseId);if(!a)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=(await se(a)).steps.filter(u=>u.id!==n.params.stepId).map((u,d)=>({...u,order:d+1})),l=await Be(a,{steps:c});return i.send(l)}catch(a){return w(i,500,"Failed to delete step",a)}}),r.post("/suites/:suiteId/cases/:caseId/steps/:stepId/generate",async(n,i)=>{let a;try{let o=await s(n.params.suiteId,n.params.caseId);if(!o)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=await se(o),l=c.steps.find(v=>v.id===n.params.stepId);if(!l)return i.status(404).send({error:`Step "${n.params.stepId}" not found`});let u=await rt(e).catch(()=>({})),d=await G(e),p=n.body?.useMcp??d.mcp?.enabled;p&&(a=new ht({headless:d.mcp?.headless??!0,actionTimeout:d.mcp?.actionTimeout??15e3,idleTimeout:d.mcp?.idleTimeout??3e4}));let g=st(e,a,p?d.mcp?.actThenScript:!1),f=await g.generate(l.instruction,{url:d.baseUrl,title:"",interactiveElements:"",htmlSnapshot:"",previousSteps:c.steps.filter(v=>v.order<l.order&&v.generatedCode).map(v=>({instruction:v.instruction,code:v.generatedCode})),variables:u}),h=f.code;l.soft&&h&&(h=h.replace(/\bexpect\(/g,"expect.soft("));let S=c.steps.map(v=>v.id===l.id?{...v,generatedCode:h,strategy:f.strategy}:v);await g.getCache().persist();let I=await Be(o,{steps:S});return i.send({step:I.steps.find(v=>v.id===l.id),strategy:f.strategy,cost:f.cost,mcpUsed:!!a})}catch(o){return w(i,500,"Failed to generate code for step",o)}finally{a?.release()}}),r.post("/suites/:suiteId/cases/:caseId/steps/generate-all",async(n,i)=>{let a;try{let o=await s(n.params.suiteId,n.params.caseId);if(!o)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=await se(o),l=c.steps.filter(T=>!T.generatedCode.trim());if(l.length===0)return i.send({message:"All steps already have code",generated:0});let u=await rt(e).catch(()=>({})),d=await G(e),p=n.body?.useMcp??d.mcp?.enabled;p&&(a=new ht({headless:d.mcp?.headless??!0,actionTimeout:d.mcp?.actionTimeout??15e3,idleTimeout:d.mcp?.idleTimeout??3e4}));let g=st(e,a,p?d.mcp?.actThenScript:!1),f=l.map(T=>({instruction:T.instruction,context:{url:d.baseUrl,title:"",interactiveElements:"",htmlSnapshot:"",previousSteps:c.steps.filter(P=>P.order<T.order&&P.generatedCode).map(P=>({instruction:P.instruction,code:P.generatedCode})),variables:u}})),h=await g.batchGenerate(f);await g.getCache().persist();let S=new Map(l.map((T,P)=>[T.id,h[P]])),I=0,v=c.steps.map(T=>{let P=S.get(T.id);if(P&&P.code.trim()){I++;let j=T.soft?P.code.replace(/\bexpect\(/g,"expect.soft("):P.code;return{...T,generatedCode:j,strategy:P.strategy}}return T});if(I===0)return i.status(500).send({error:"AI code generation produced no results",detail:"The AI provider responded but returned empty code for all steps. Check your API key, model name, and server terminal logs for details.",code:"GENERATION_EMPTY"});let k=await Be(o,{steps:v});return i.send({generated:I,case:k,mcpUsed:!!a})}catch(o){return w(i,500,"Failed to generate code for steps",o)}finally{a?.release()}}),r.patch("/suites/:suiteId/cases/:caseId/steps/reorder",async(n,i)=>{let a=Sy.safeParse(n.body);if(!a.success)return i.status(400).send({error:"Invalid reorder data",details:a.error.issues});try{let o=await s(n.params.suiteId,n.params.caseId);if(!o)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=await se(o),l=new Map(c.steps.map(p=>[p.id,p])),u=a.data.stepIds.map((p,g)=>{let f=l.get(p);return f?{...f,order:g+1}:null}).filter(p=>p!==null),d=await Be(o,{steps:u});return i.send(d)}catch(o){return w(i,500,"Failed to reorder steps",o)}}),r.patch("/suites/:suiteId/cases/:caseId/steps/:stepId",async(n,i)=>{let a=Ey.safeParse(n.body);if(!a.success)return i.status(400).send({error:"Invalid step data",details:a.error.issues});try{let o=await s(n.params.suiteId,n.params.caseId);if(!o)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=await se(o);if(!c.steps.find(p=>p.id===n.params.stepId))return i.status(404).send({error:`Step "${n.params.stepId}" not found`});let u=c.steps.map(p=>{if(p.id!==n.params.stepId)return p;let g={};return a.data.instruction!==void 0&&(g.instruction=a.data.instruction),a.data.timeout!==void 0&&(g.timeout=a.data.timeout??void 0),a.data.stepType!==void 0&&(g.stepType=a.data.stepType),a.data.retries!==void 0&&(g.retries=a.data.retries),a.data.mockUrl!==void 0&&(g.mockUrl=a.data.mockUrl),a.data.mockResponse!==void 0&&(g.mockResponse=a.data.mockResponse),a.data.mockStatus!==void 0&&(g.mockStatus=a.data.mockStatus),a.data.auditUrl!==void 0&&(g.auditUrl=a.data.auditUrl),a.data.lighthouseCategories!==void 0&&(g.lighthouseCategories=a.data.lighthouseCategories),a.data.runAudit!==void 0&&(g.runAudit=a.data.runAudit),a.data.soft!==void 0&&(g.soft=a.data.soft,p.generatedCode&&(a.data.soft?g.generatedCode=p.generatedCode.replace(/\bexpect\(/g,"expect.soft("):g.generatedCode=p.generatedCode.replace(/\bexpect\.soft\(/g,"expect("))),a.data.visualCheck!==void 0&&(g.visualCheck=a.data.visualCheck),{...p,...g}}),d=await Be(o,{steps:u});return i.send(d)}catch(o){return w(i,500,"Failed to update step",o)}})}var Gu,zu,ne,xy,Sy,Ey,Ku=b(()=>{"use strict";m();Gu=x(require("path")),zu=require("crypto"),ne=require("zod");gt();Ke();mr();Te();fr();Qs();te();xy=ne.z.object({instruction:ne.z.string().min(1),order:ne.z.number().int().positive().optional(),generatedCode:ne.z.string().optional(),strategy:ne.z.enum(["template","cache","rag","batch","fast","primary","recorder"]).optional(),soft:ne.z.boolean().optional()}),Sy=ne.z.object({stepIds:ne.z.array(ne.z.string().min(1))}),Ey=ne.z.object({instruction:ne.z.string().min(1).optional(),timeout:ne.z.number().int().positive().optional().nullable(),stepType:ne.z.enum(["ui","api","mock"]).optional(),retries:ne.z.number().int().min(0).optional(),mockUrl:ne.z.string().optional(),mockResponse:ne.z.string().optional(),mockStatus:ne.z.number().int().optional(),auditUrl:ne.z.string().optional(),lighthouseCategories:ne.z.array(ne.z.enum(["performance","accessibility","seo"])).optional(),runAudit:ne.z.boolean().optional(),soft:ne.z.boolean().optional(),visualCheck:ne.z.boolean().optional()})});async function hr(r){let e=Ya.default.join(r,Yu);if(!await Qa.default.pathExists(e))return{...Wu};let t=await ue(e),s=va.safeParse(t);return s.success?s.data:(Ju.warn({path:e,errors:s.error.issues},"Invalid hooks.json \u2014 returning defaults"),{...Wu})}async function Fr(r,e){await Qa.default.ensureDir(r);let t=Ya.default.join(r,Yu),s=va.safeParse(e);if(!s.success){let n=s.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
|
|
684
|
-
`);throw new
|
|
685
|
-
${n}`,"hooks","INVALID_HOOKS")}await
|
|
686
|
-
`);throw new
|
|
687
|
-
${i}`,"healingReport","INVALID_HEALING_REPORT")}let s=
|
|
688
|
-
`);throw new
|
|
689
|
-
${a}`,"healingReport","INVALID_HEALING_REPORT")}return i.data}async function nt(r){let e=Fe.default.join(r,Ot),t=Br(e);if(!await jr.default.pathExists(t))return[];let s=await ue(t),n=Kn.array().safeParse(s);return n.success?n.data:(yr.warn({path:t},"Pending healing file is malformed \u2014 resetting"),[])}function Ay(r){if(r.length<=ud)return r;let e=[...r].sort((n,i)=>{let a=dd.indexOf(n.status),o=dd.indexOf(i.status);return a!==o?a-o:n.timestamp<i.timestamp?-1:1}),t=r.length-ud,s=new Set(e.slice(0,t).map(n=>n.id));return yr.info({excess:t,deleted:s.size},"Auto-pruning healing events to enforce cap"),r.filter(n=>!s.has(n.id))}async function gd(r,e){let t=Kn.safeParse(e);if(!t.success)throw new K(`Invalid healing event: ${t.error.message}`,"healingEvent","INVALID_HEALING_EVENT");let s=Fe.default.join(r,Ot);await jr.default.ensureDir(s);let n=await nt(r);n.push(t.data),await W(Br(s),Ay(n))}async function tn(r,e){let t=await nt(r),s=t.findIndex(i=>i.id===e);if(s===-1)throw new oe(`Healing event "${e}" not found in pending list.`,e,"HEALING_EVENT_NOT_FOUND");t[s]={...t[s],status:"accepted",acceptedAt:new Date().toISOString()};let n=Fe.default.join(r,Ot);return await W(Br(n),t),yr.info({eventId:e},"Healing event accepted"),t[s]}async function Jn(r,e){let t=await nt(r),s=t.findIndex(i=>i.id===e);if(s===-1)throw new oe(`Healing event "${e}" not found in pending list.`,e,"HEALING_EVENT_NOT_FOUND");t[s]={...t[s],status:"rejected",rejectedAt:new Date().toISOString()};let n=Fe.default.join(r,Ot);return await W(Br(n),t),yr.info({eventId:e},"Healing event rejected"),t[s]}async function fd(r,e){let s=(await nt(r)).filter(i=>i.id!==e),n=Fe.default.join(r,Ot);await W(Br(n),s),yr.info({eventId:e},"Healing event deleted")}async function hd(r,e){let t=await nt(r),s=new Set(e),n=t.filter(o=>!s.has(o.id)),i=t.length-n.length,a=Fe.default.join(r,Ot);return await W(Br(a),n),yr.info({count:i},"Healing events bulk-deleted"),i}async function yd(r){let t=(await nt(r)).length,s=Fe.default.join(r,Ot);return await W(Br(s),[]),yr.info({count:t},"All healing events cleared"),t}async function wd(r){let e=await nt(r),t=e.filter(i=>i.status==="pending").length,s=e.filter(i=>i.status==="accepted").length,n=e.filter(i=>i.status==="rejected").length;return{pending:t,accepted:s,rejected:n,total:e.length}}async function bd(r){let e=Fe.default.join(r,Ot);if(!await jr.default.pathExists(e))return[];let s=(await jr.default.readdir(e,{withFileTypes:!0})).filter(i=>i.isFile()&&i.name.startsWith(Wn)&&i.name.endsWith(".json")).map(i=>i.name.slice(Wn.length,-5));return(await Promise.all(s.map(async i=>{let a=await jr.default.stat(Fe.default.join(e,`${Wn}${i}.json`));return{id:i,mtime:a.mtimeMs}}))).sort((i,a)=>a.mtime-i.mtime).map(i=>i.id)}var Fe,jr,yr,Ot,ky,Wn,ud,dd,Yn=b(()=>{"use strict";m();Fe=x(require("path")),jr=x(require("fs-extra"));ld();re();F();ze();yr=C("healing-store"),Ot=Fe.default.join("results","healing"),ky="pending.json",Wn="healing-report-";ud=50,dd=["pending","rejected","accepted"]});async function Sd(r){let e=r.rootDir,t=xd.default.join(e,"tests");r.get("/healing",async(s,n)=>{try{let i=await bd(e),a=[];for(let o of i){let c=await Xn(e,o);a.push(...c.events)}return n.send(a)}catch(i){return w(n,500,"Failed to list healing events",i)}}),r.get("/healing/pending",async(s,n)=>{try{let i=await nt(e);return n.send(i)}catch(i){return w(n,500,"Failed to read pending healing events",i)}}),r.get("/healing/stats",async(s,n)=>{try{let i=await wd(e);return n.send(i)}catch(i){return w(n,500,"Failed to get healing stats",i)}}),r.patch("/healing/:id/accept",async(s,n)=>{try{let i=await tn(e,s.params.id);return await eo(t,i.caseId,i.stepId,i.healedCode),vd.info({id:s.params.id,caseId:i.caseId,stepId:i.stepId},"Healing accepted and applied"),n.send(i)}catch(i){return w(n,500,"Failed to accept healing event",i)}}),r.patch("/healing/:id/reject",async(s,n)=>{try{let i=await Jn(e,s.params.id);return n.send(i)}catch(i){return w(n,500,"Failed to reject healing event",i)}}),r.post("/healing/accept-all",async(s,n)=>{try{let i=await nt(e),a=[];for(let o of i)try{await tn(e,o.id),await eo(t,o.caseId,o.stepId,o.healedCode),a.push({id:o.id,status:"accepted"}),vd.info({id:o.id},"Bulk healing accepted")}catch(c){a.push({id:o.id,status:"failed",error:c instanceof Error?c.message:String(c)})}return n.send({accepted:a.filter(o=>o.status==="accepted").length,results:a})}catch(i){return w(n,500,"Failed to accept all healing events",i)}}),r.post("/healing/clear-all",async(s,n)=>{try{let i=await yd(e);return n.send({deleted:i})}catch(i){return w(n,500,"Failed to clear healing events",i)}}),r.post("/healing/delete-bulk",async(s,n)=>{let i=_y.safeParse(s.body);if(!i.success)return n.status(400).send({error:"Invalid request",details:i.error.issues});try{let a=await hd(e,i.data.ids);return n.send({deleted:a})}catch(a){return w(n,500,"Failed to delete healing events",a)}}),r.delete("/healing/:id",async(s,n)=>{try{return await fd(e,s.params.id),n.status(204).send()}catch(i){return w(n,500,"Failed to delete healing event",i)}}),r.post("/healing/apply-from-report",async(s,n)=>{let i=$y.safeParse(s.body);if(!i.success)return n.status(400).send({error:"Invalid request",details:i.error.issues});try{let{default:a}=await import("fs-extra"),o=await a.readJson(i.data.reportPath);if(!Array.isArray(o.events))return n.status(400).send({error:"Invalid healing report format"});let c=[];for(let l of o.events)try{await eo(t,l.caseId,l.stepId,l.healedCode),c.push({id:l.id,status:"applied"})}catch(u){c.push({id:l.id,status:"failed",error:u instanceof Error?u.message:String(u)})}return n.send({applied:c.filter(l=>l.status==="applied").length,results:c})}catch(a){return w(n,500,"Failed to apply healing report",a)}})}async function eo(r,e,t,s){let n=await Oy(r,e);if(!n)throw new Error(`Case "${e}" not found in tests directory`);let i=await se(n);if(!i.steps.find(c=>c.id===t))throw new Error(`Step "${t}" not found in case "${e}"`);let o=i.steps.map(c=>c.id===t?{...c,generatedCode:s,lastHealed:new Date().toISOString()}:c);await Be(n,{steps:o})}async function Oy(r,e){let{default:t}=await import("fs-extra"),{listSuiteDirs:s}=await Promise.resolve().then(()=>(Ke(),Ia)),n=await s(r);for(let i of n){let a=await mt(i,e);if(a)return a}return null}var xd,ls,vd,$y,_y,Ed=b(()=>{"use strict";m();xd=x(require("path")),ls=require("zod");Yn();gt();te();F();vd=C("routes/healing"),$y=ls.z.object({reportPath:ls.z.string().min(1)}),_y=ls.z.object({ids:ls.z.array(ls.z.string().min(1)).min(1)})});var R,Qn,Ly,Dy,yt,Cd,Ny,Fy,My,jy,to,Id=b(()=>{"use strict";m();R=require("zod"),Qn=R.z.enum(["pending","running","passed","failed","skipped"]),Ly=R.z.object({method:R.z.string(),url:R.z.string(),headers:R.z.record(R.z.string()).optional(),body:R.z.string().optional()}).optional(),Dy=R.z.object({status:R.z.number(),statusText:R.z.string(),headers:R.z.record(R.z.string()).optional(),body:R.z.string().optional(),duration:R.z.number()}).optional(),yt=R.z.number().nullish().transform(r=>r??void 0),Cd=R.z.object({id:R.z.string(),title:R.z.string(),passed:R.z.boolean(),partial:R.z.boolean().optional(),na:R.z.boolean().optional()}),Ny=R.z.object({url:R.z.string(),stepIndex:R.z.number().int(),stepInstruction:R.z.string(),score:yt,a11yScore:yt,seoScore:yt,fcp:yt,lcp:yt,cls:yt,ttfb:yt,tbt:yt,si:yt,tti:yt,inp:yt,a11yAudits:R.z.array(Cd).optional(),seoAudits:R.z.array(Cd).optional(),lighthouseError:R.z.string().optional()}),Fy=R.z.object({stepId:R.z.string(),instruction:R.z.string(),status:Qn,code:R.z.string(),error:R.z.string().optional(),duration:R.z.number(),screenshotPath:R.z.string().optional(),healed:R.z.boolean().optional(),healedCode:R.z.string().optional(),stepType:R.z.enum(["ui","api","mock"]).optional(),apiRequest:Ly,apiResponse:Dy,navigatedToUrl:R.z.string().optional(),auditUrl:R.z.string().optional()}),My=R.z.object({caseId:R.z.string(),caseName:R.z.string(),status:Qn,steps:R.z.array(Fy),duration:R.z.number(),browser:R.z.string(),device:R.z.string().optional(),startedAt:R.z.string().datetime(),finishedAt:R.z.string().datetime(),videoPath:R.z.string().optional(),tracePath:R.z.string().optional(),downloadPaths:R.z.array(R.z.string()).default([]),dataRowIndex:R.z.number().int().optional(),dataRow:R.z.record(R.z.string()).optional(),pageLoads:R.z.array(Ny).default([])}),jy=R.z.object({suiteId:R.z.string(),suiteName:R.z.string(),suiteType:R.z.enum(["ui","api","audit","performance"]).optional(),status:Qn,cases:R.z.array(My),duration:R.z.number(),browser:R.z.string(),startedAt:R.z.string().datetime(),finishedAt:R.z.string().datetime()}),to=R.z.object({runId:R.z.string(),status:Qn,environment:R.z.string().optional(),suites:R.z.array(jy),duration:R.z.number(),startedAt:R.z.string().datetime(),finishedAt:R.z.string().datetime(),totalTests:R.z.number().int(),passed:R.z.number().int(),failed:R.z.number().int(),skipped:R.z.number().int(),logFilePath:R.z.string().optional()})});function so(r,e){return Hr.default.join(r,Pd,`${e}.json`)}async function Rd(r,e){let t=to.safeParse(e);if(!t.success){let n=t.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
|
|
690
|
-
`);throw new
|
|
691
|
-
${n}`,"result","INVALID_RESULT")}let s=
|
|
692
|
-
`);throw new
|
|
693
|
-
${i}`,"result","INVALID_RESULT")}return n.data}async function no(r){let e=Hr.default.join(r,Zn,Pd);if(!await Ur.default.pathExists(e))return[];let s=(await Ur.default.readdir(e,{withFileTypes:!0})).filter(i=>i.isFile()&&i.name.endsWith(".json")).map(i=>i.name.replace(".json",""));return(await Promise.all(s.map(async i=>{let a=await Ur.default.stat(Hr.default.join(e,`${i}.json`));return{id:i,mtime:a.mtimeMs}}))).sort((i,a)=>a.mtime-i.mtime).map(i=>i.id)}async function ei(r,e=20){let t=await no(r),s=[];for(let n of t.slice(0,e))try{let i=await Vr(r,n);s.push({runId:i.runId,status:i.status,environment:i.environment,startedAt:i.startedAt,finishedAt:i.finishedAt,totalTests:i.totalTests,passed:i.passed,failed:i.failed,skipped:i.skipped,duration:i.duration,suiteIds:i.suites.map(a=>a.suiteId)})}catch(i){ro.warn({runId:n,err:i},"Failed to read run result \u2014 skipping")}return s}async function Td(r,e){let t=so(Hr.default.join(r,Zn),e);if(!await Ur.default.pathExists(t))throw new oe(`Run result not found for runId "${e}".`,t,"RESULT_NOT_FOUND");await Ur.default.remove(t),ro.info({runId:e},"Run result deleted")}var Hr,Ur,ro,Zn,Pd,rn=b(()=>{"use strict";m();Hr=x(require("path")),Ur=x(require("fs-extra"));Id();re();F();ze();ro=C("result-store"),Zn="results",Pd="runs"});function kd(r){let t=r.toLowerCase().replace(/\{\{[^}]+\}\}/g," __var__ ").replace(/"([^"]+)"/g,(n,i)=>` ${i} `).replace(/'([^']+)'/g,(n,i)=>` ${i} `).replace(/[^a-z0-9\s-]/g," ").trim().split(/\s+/).filter(n=>n.length>1&&!By.has(n)),s=[...t];for(let n=0;n<t.length-1;n++)s.push(`${t[n]}_${t[n+1]}`);return s}function Uy(r){return(0,Ad.createHash)("md5").update(r).digest().readUInt32LE(0)%io}var Ad,ri,io,By,ti,$d=b(()=>{"use strict";m();Ad=require("crypto"),ri=require("fs-extra"),io=256,By=new Set(["the","a","an","is","are","was","were","be","been","being","have","has","had","do","does","did","will","would","could","should","may","might","shall","can","need","dare","ought","to","of","in","for","on","with","at","by","from","as","into","through","during","before","after","above","below","between","out","off","over","under","again","further","then","once","that","this","these","those","and","but","or","nor","not","so","very","just","than","too","also","it","its"]);ti=class{constructor(e){this.vocabPath=e}vocabPath;vocab={docCount:0,termDocFreq:{}};dirty=!1;async load(){try{this.vocab=await(0,ri.readJson)(this.vocabPath)}catch{this.vocab={docCount:0,termDocFreq:{}}}}async save(){this.dirty&&(await(0,ri.outputJson)(this.vocabPath,this.vocab,{spaces:0}),this.dirty=!1)}addDocument(e){let t=new Set(kd(e));this.vocab.docCount++;for(let s of t)this.vocab.termDocFreq[s]=(this.vocab.termDocFreq[s]??0)+1;this.dirty=!0}addDocuments(e){for(let t of e)this.addDocument(t)}embed(e){let t=kd(e);if(t.length===0)return new Array(io).fill(0);let s={};for(let o of t)s[o]=(s[o]??0)+1;let n=new Array(io).fill(0),i=Math.max(this.vocab.docCount,1);for(let[o,c]of Object.entries(s)){let l=c/t.length,u=this.vocab.termDocFreq[o]??0,d=Math.log(1+i/(1+u)),p=l*d,g=Uy(o);n[g]+=p}let a=Math.sqrt(n.reduce((o,c)=>o+c*c,0));if(a>0)for(let o=0;o<n.length;o++)n[o]/=a;return n}get documentCount(){return this.vocab.docCount}}});function _d(r,e){if(r.length!==e.length||r.length===0)return 0;let t=0,s=0,n=0;for(let a=0;a<r.length;a++)t+=r[a]*e[a],s+=r[a]*r[a],n+=e[a]*e[a];let i=Math.sqrt(s)*Math.sqrt(n);return i===0?0:t/i}var Od=b(()=>{"use strict";m()});var ds,us,Ld=b(()=>{"use strict";m();ds=require("fs-extra");Od();us=class{constructor(e,t=500){this.filePath=e;this.maxEntries=t}filePath;maxEntries;documents=[];dirty=!1;async load(){if(await(0,ds.pathExists)(this.filePath))try{let e=await(0,ds.readJson)(this.filePath);this.documents=e.documents??[]}catch{this.documents=[]}}async persist(){if(!this.dirty)return;let e={version:1,documents:this.documents};await(0,ds.outputJson)(this.filePath,e,{spaces:0}),this.dirty=!1}add(e,t,s,n){this.documents=this.documents.filter(a=>a.id!==e);let i=Date.now();this.documents.push({id:e,text:t,vector:s,metadata:n,createdAt:i,updatedAt:i}),this.dirty=!0,this.documents.length>this.maxEntries&&this.prune()}update(e,t){let s=this.documents.find(n=>n.id===e);return s?(s.metadata={...s.metadata,...t},s.updatedAt=Date.now(),this.dirty=!0,!0):!1}remove(e){let t=this.documents.length;return this.documents=this.documents.filter(s=>s.id!==e),this.documents.length!==t?(this.dirty=!0,!0):!1}get(e){return this.documents.find(t=>t.id===e)}has(e){return this.documents.some(t=>t.id===e)}get size(){return this.documents.length}search(e,t=5,s=.5){let n=[];for(let i of this.documents){let a=_d(e,i.vector);a>=s&&n.push({id:i.id,text:i.text,metadata:i.metadata,score:a})}return n.sort((i,a)=>{if(Math.abs(i.score-a.score)>.001)return a.score-i.score;let o=this.documents.find(l=>l.id===i.id);return this.documents.find(l=>l.id===a.id).updatedAt-o.updatedAt}),n.slice(0,t)}prune(){let e=[...this.documents].sort((s,n)=>s.updatedAt-n.updatedAt),t=e.length-this.maxEntries;if(t>0){let s=new Set(e.slice(0,t).map(n=>n.id));this.documents=this.documents.filter(n=>!s.has(n.id))}}}});function Vy(r,e){let t=`${r.toLowerCase().trim()}|${ao(e)}`;return(0,Dd.createHash)("sha256").update(t).digest("hex").slice(0,16)}function ao(r){if(!r)return"";try{let e=new URL(r),t=e.pathname.replace(/\/\d+/g,"/*");return`${e.hostname}${t}`}catch{return r}}function Gy(r,e,t){let s=e+t;if(s===0)return r;let n=e/s;return r*(1+.05*n)}function zy(r){return{id:r.id,instruction:r.metadata.instruction,code:r.metadata.code,urlPattern:r.metadata.urlPattern,score:r.score,successCount:r.metadata.successCount,failCount:r.metadata.failCount}}var Dd,Hy,si,Nd=b(()=>{"use strict";m();Dd=require("crypto"),Hy=2,si=class{constructor(e,t){this.store=e;this.embedder=t}store;embedder;ingest(e,t,s){let n=Vy(e,s),i=ao(s),a=`${e} ${i}`,o=this.store.get(n);if(o){this.store.update(n,{...o.metadata,code:t,successCount:o.metadata.successCount+1,consecutiveFails:0});return}this.embedder.addDocument(a);let c=this.embedder.embed(a);this.store.add(n,a,c,{instruction:e,code:t,urlPattern:i,successCount:1,failCount:0,consecutiveFails:0})}retrieve(e,t="",s=3,n=.65){if(this.store.size===0)return[];let i=ao(t),a=`${e} ${i}`,o=this.embedder.embed(a),l=this.store.search(o,s*2,n).map(u=>({...u,score:Gy(u.score,u.metadata.successCount,u.metadata.failCount)}));return l.sort((u,d)=>d.score-u.score),l.slice(0,s).map(zy)}recordOutcome(e,t){let s=this.store.get(e);if(s)if(t)this.store.update(e,{...s.metadata,successCount:s.metadata.successCount+1,consecutiveFails:0});else{let n=s.metadata.consecutiveFails+1;if(n>=Hy){this.store.remove(e);return}this.store.update(e,{...s.metadata,failCount:s.metadata.failCount+1,consecutiveFails:n})}}get size(){return this.store.size}}});function qy(r,e){let t=`${r.toLowerCase().trim()}|${ni(e)}`;return(0,Fd.createHash)("sha256").update(t).digest("hex").slice(0,16)}function ni(r){return r.replace(/\d+ms/g,"Nms").replace(/\d+/g,"N").replace(/["'][^"']*["']/g,"'...'").replace(/\s+/g," ").trim().slice(0,200)}function Ky(r){if(!r)return"";try{let e=new URL(r);return`${e.hostname}${e.pathname.replace(/\/\d+/g,"/*")}`}catch{return r}}function Wy(r){return{id:r.id,instruction:r.metadata.instruction,error:r.metadata.error,failedCode:r.metadata.failedCode,healedCode:r.metadata.healedCode,strategy:r.metadata.strategy,level:r.metadata.level,score:r.score,successCount:r.metadata.successCount}}var Fd,ii,Md=b(()=>{"use strict";m();Fd=require("crypto"),ii=class{constructor(e,t){this.store=e;this.embedder=t}store;embedder;ingest(e){let t=qy(e.instruction,e.error),s=Ky(e.url),n=`${e.instruction} ${ni(e.error)}`,i=this.store.get(t);if(i){this.store.update(t,{...i.metadata,healedCode:e.healedCode,strategy:e.strategy,level:e.level,successCount:i.metadata.successCount+1});return}this.embedder.addDocument(n);let a=this.embedder.embed(n);this.store.add(t,n,a,{instruction:e.instruction,error:ni(e.error),failedCode:e.failedCode,healedCode:e.healedCode,strategy:e.strategy,level:e.level,urlPattern:s,successCount:1})}retrieve(e,t,s=3,n=.6){if(this.store.size===0)return[];let i=`${e} ${ni(t)}`,a=this.embedder.embed(i);return this.store.search(a,s,n).map(Wy)}get size(){return this.store.size}}});function Xy(r,e){let t=`${r}|${e}`;return(0,Bd.createHash)("sha256").update(t).digest("hex").slice(0,16)}function Jy(r){return r.replace(/\d+ms/g,"Nms").replace(/\d+/g,"N").replace(/["'][^"']*["']/g,"'...'").replace(/\s+/g," ").trim().slice(0,200)}function jd(r){if(!r)return"";try{let e=new URL(r);return`${e.hostname}${e.pathname.replace(/\/\d+/g,"/*")}`}catch{return r}}function Yy(r){let e=r.metadata.commonFixes;return{errorPattern:r.metadata.errorPattern,urlPattern:r.metadata.urlPattern,frequency:r.metadata.frequency,suggestedFix:e.length>0?e[e.length-1]:null,score:r.score}}var Bd,ai,Ud=b(()=>{"use strict";m();Bd=require("crypto"),ai=class{constructor(e,t){this.store=e;this.embedder=t}store;embedder;record(e,t,s){let n=Jy(e),i=jd(t),a=Xy(n,i),o=`${n} ${i}`,c=this.store.get(a);if(c){let u=[...c.metadata.commonFixes];s&&!u.includes(s)&&(u.push(s),u.length>5&&u.shift()),this.store.update(a,{...c.metadata,frequency:c.metadata.frequency+1,lastSeen:Date.now(),commonFixes:u});return}this.embedder.addDocument(o);let l=this.embedder.embed(o);this.store.add(a,o,l,{errorPattern:n,urlPattern:i,frequency:1,lastSeen:Date.now(),commonFixes:s?[s]:[]})}getWarnings(e,t=5){if(this.store.size===0)return[];let s=jd(e),n=this.embedder.embed(s),a=this.store.search(n,t*2,.3).filter(o=>o.metadata.frequency>=2);return a.sort((o,c)=>c.metadata.frequency-o.metadata.frequency),a.slice(0,t).map(Yy)}get size(){return this.store.size}}});var ps,Hd,oo,oi,Vd=b(()=>{"use strict";m();ps=require("path"),Hd=require("fs-extra");$d();Ld();Nd();Md();Ud();F();oo=C("rag"),oi=class r{codeCorpus;healingCorpus;errorCatalog;embedder;codeStore;healingStore;errorStore;constructor(e,t,s,n){this.embedder=e,this.codeStore=t,this.healingStore=s,this.errorStore=n,this.codeCorpus=new si(t,e),this.healingCorpus=new ii(s,e),this.errorCatalog=new ai(n,e)}static async create(e,t){let s=(0,ps.join)(e,"results",".rag");await(0,Hd.ensureDir)(s),oo.info({ragDir:s},"Initializing RAG manager");let n=new ti((0,ps.join)(s,"idf-vocab.json"));await n.load();let i=new us((0,ps.join)(s,"code-corpus.json"),t.codeCorpus.maxEntries),a=new us((0,ps.join)(s,"healing-corpus.json"),t.healingCorpus.maxEntries),o=new us((0,ps.join)(s,"error-catalog.json"),t.errorCatalog.maxEntries);return await Promise.all([i.load(),a.load(),o.load()]),oo.info({codeCorpus:i.size,healingCorpus:a.size,errorCatalog:o.size,idfDocuments:n.documentCount},"RAG corpora loaded"),new r(n,i,a,o)}async persist(){await Promise.all([this.embedder.save(),this.codeStore.persist(),this.healingStore.persist(),this.errorStore.persist()]),oo.debug("RAG corpora persisted")}getStats(){return{codeCorpus:this.codeStore.size,healingCorpus:this.healingStore.size,errorCatalog:this.errorStore.size,idfVocab:this.embedder.documentCount}}}});function Zy(r){let e=[];for(let t of Qy){t.regex.lastIndex=0;let s;for(;(s=t.regex.exec(r))!==null;)t.type==="getByRole"?e.push({selector:s[0],type:t.type,role:s[1],name:s[2]}):e.push({selector:s[0],type:t.type,name:s[1]})}return e}function ew(r,e,t){let s=r.toLowerCase();for(let n of e){let i=n.match(/"([^"]+)"/);if(i&&i[1].toLowerCase()===s&&(!t||n.toLowerCase().startsWith(t.toLowerCase())))return n}for(let n of e){let i=n.match(/"([^"]+)"/);if(i){let a=i[1].toLowerCase();if((a.includes(s)||s.includes(a))&&(!t||n.toLowerCase().startsWith(t.toLowerCase())))return n}}if(t){let n=e.filter(i=>i.toLowerCase().startsWith(t.toLowerCase()));if(n.length>0){let i=0,a;for(let o of n){let c=o.match(/"([^"]+)"/);if(c){let l=tw(s,c[1].toLowerCase());l>i&&(i=l,a=o)}}if(i>.3)return a}}}function tw(r,e){if(r===e)return 1;let t=r.length>e.length?r:e,s=r.length>e.length?e:r;if(t.length===0)return 1;let n=0;for(let i=0;i<s.length;i++)t.includes(s[i])&&n++;return n/t.length}async function zd(r,e,t){let s=Zy(t);if(s.length===0)return{url:e,ok:!0,total:0,matched:0,missing:0,checks:[]};await r.navigate(e);let n=await r.snapshot();if(!n)return Gd.warn({url:e},"Proactive check: snapshot returned null \u2014 skipping validation"),{url:e,ok:!0,total:s.length,matched:0,missing:0,checks:[]};let a=Bn(n).interactiveElements.split(`
|
|
694
|
-
`).filter(Boolean),o=[];for(let u of s){let d=u.name??"",
|
|
681
|
+
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible()`,i=await this.chatWithVision(n,e);return Ke(i)}async validate(){try{return await this.chat([{role:"user",content:"Reply with the single word: ready"}]),{valid:!0}}catch(e){return{valid:!1,error:e instanceof Error?e.message:String(e)}}}async chatWithRetry(e,t=this.maxRetries){let s;for(let n=0;n<=t;n++)try{return await this.chat(e)}catch(i){s=i instanceof Error?i:new Error(String(i)),n<t&&await new Promise(a=>setTimeout(a,1e3*(n+1)))}throw new V(`Provider "${this.name}" failed after ${t+1} attempt(s): ${s?.message??"Unknown error"}`,this.name,"PROVIDER_EXHAUSTED")}parseGeneratedSuite(e){let t=Bn(e),s=ln(t)??t,n;try{n=JSON.parse(s)}catch{throw new te(`Provider "${this.name}" returned invalid JSON for test suite generation. Raw response: ${t.slice(0,200)}`,"generatedSuite","INVALID_SUITE_JSON")}let i=pu.safeParse(n);if(!i.success){let a=i.error.issues.map(o=>` \u2022 ${o.path.join(".")}: ${o.message}`).join(`
|
|
682
|
+
`);throw new te(`AI-generated suite has invalid structure:
|
|
683
|
+
${a}`,"generatedSuite","INVALID_SUITE_SCHEMA")}return{name:i.data.name,description:i.data.description,tags:i.data.tags,cases:i.data.cases.map(a=>({name:a.name,description:a.description,tags:a.tags,steps:a.steps.map(o=>({instruction:o.instruction,generatedCode:"",strategy:"primary"}))}))}}parseBatchResponse(e,t){let s=ms(e),n=s.match(/\{[\s\S]*\}/);n&&(s=n[0]);let i=new Array(t).fill(""),a=!1;try{let l=JSON.parse(s),u=mu.safeParse(l);if(u.success){a=!0;for(let d of u.data.results)if(d.index<t)try{i[d.index]=Ke(d.code)}catch{}return i}}catch{}if(a)return i;let o=/\/\/\s*STEP[_\s]?(\d+)\s*\n([\s\S]*?)(?=\/\/\s*STEP[_\s]?\d+|$)/g,c;for(;(c=o.exec(s))!==null;){let l=parseInt(c[1],10);if(l<t)try{i[l]=Ke(c[2].trim())}catch{}}return i}parseScreenshotAnalysis(e){let t=ms(e);try{let s=JSON.parse(t),n=hu.safeParse(s);if(n.success)return n.data}catch{}throw new te(`Could not parse screenshot analysis response: ${t.slice(0,200)}`,"screenshotAnalysis","INVALID_SCREENSHOT_ANALYSIS")}}});var ku={};ie(ku,{AnthropicProvider:()=>Ja});var Ru,Ja,Tu=b(()=>{"use strict";g();Ru=x(require("@anthropic-ai/sdk"));Ar();ge();Ja=class extends Be{name="anthropic";model;supportsVision=!0;client;constructor(e){super(e),this.model=e.model,this.client=new Ru.default({apiKey:e.apiKey,timeout:this.timeoutMs,maxRetries:0})}async chat(e){let t=e.find(n=>n.role==="system")?.content,s=e.filter(n=>n.role!=="system");try{let i=(await this.client.messages.create({model:this.model,max_tokens:8192,temperature:this.temperature,system:t,messages:s.map(a=>({role:a.role,content:a.content}))})).content[0];if(!i||i.type!=="text")throw new V("Anthropic returned an empty or non-text response.",this.name,"EMPTY_RESPONSE");return i.text}catch(n){if(n instanceof V)throw n;let i=n instanceof Error?n.message:String(n);throw new V(`Anthropic API error: ${i}`,this.name,"API_ERROR")}}async chatWithVision(e,t){try{let n=(await this.client.messages.create({model:this.model,max_tokens:512,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:"image/png",data:t.toString("base64")}},{type:"text",text:e}]}]})).content[0];if(!n||n.type!=="text")throw new V("Anthropic vision returned empty response.",this.name);return n.text}catch(s){throw s instanceof V?s:new V(`Anthropic vision error: ${s instanceof Error?s.message:String(s)}`,this.name,"VISION_ERROR")}}async validate(){return this.client.apiKey?super.validate():{valid:!1,error:"ANTHROPIC_API_KEY is not set"}}}});var $u={};ie($u,{OpenAIProvider:()=>Ya});var Au,Ya,_u=b(()=>{"use strict";g();Au=x(require("openai"));Ar();ge();Ya=class extends Be{name="openai";model;supportsVision=!0;client;constructor(e){super(e),this.model=e.model,this.client=new Au.default({apiKey:e.apiKey,timeout:this.timeoutMs,maxRetries:0})}async chat(e){try{let s=(await this.client.chat.completions.create({model:this.model,messages:e.map(n=>({role:n.role,content:n.content})),temperature:this.temperature,max_tokens:8192})).choices[0]?.message?.content;if(!s)throw new V("OpenAI returned an empty response.",this.name,"EMPTY_RESPONSE");return s}catch(t){throw t instanceof V?t:new V(`OpenAI API error: ${t instanceof Error?t.message:String(t)}`,this.name,"API_ERROR")}}async chatWithVision(e,t){try{let n=(await this.client.chat.completions.create({model:this.model,max_tokens:512,messages:[{role:"user",content:[{type:"image_url",image_url:{url:`data:image/png;base64,${t.toString("base64")}`}},{type:"text",text:e}]}]})).choices[0]?.message?.content;if(!n)throw new V("OpenAI vision returned empty response.",this.name);return n}catch(s){throw s instanceof V?s:new V(`OpenAI vision error: ${s instanceof Error?s.message:String(s)}`,this.name,"VISION_ERROR")}}async validate(){return this.client.apiKey?super.validate():{valid:!1,error:"OPENAI_API_KEY is not set"}}}});var Lu={};ie(Lu,{GoogleProvider:()=>Qa});var Ou,Qa,Du=b(()=>{"use strict";g();Ou=require("@google/generative-ai");Ar();ge();Qa=class extends Be{name="google";model;supportsVision=!0;genAI;constructor(e){super(e),this.model=e.model,this.genAI=new Ou.GoogleGenerativeAI(e.apiKey)}async chat(e){try{let t=e.find(m=>m.role==="system"),s=e.filter(m=>m.role!=="system"),n=s.at(-1)?.content??"",i=n.includes("Return only the JSON")||n.includes("Return ONLY valid JSON")||n.includes("return only the JSON")||n.includes("Return only a single line")||(t?.content?.includes("Return ONLY valid JSON")??!1),a=this.genAI.getGenerativeModel({model:this.model,systemInstruction:t?.content,generationConfig:{temperature:this.temperature,maxOutputTokens:8192,...i?{responseMimeType:"application/json"}:{}}}),o=s.slice(0,-1).map(m=>({role:m.role==="assistant"?"model":"user",parts:[{text:m.content}]})),c=s.at(-1);if(!c)throw new V("No user message provided.",this.name);let d=(await a.startChat({history:o}).sendMessage(c.content)).response.text();if(!d)throw new V("Google Gemini returned empty response.",this.name,"EMPTY_RESPONSE");return d}catch(t){throw t instanceof V?t:new V(`Google Gemini error: ${t instanceof Error?t.message:String(t)}`,this.name,"API_ERROR")}}async chatWithVision(e,t){try{return(await this.genAI.getGenerativeModel({model:this.model}).generateContent([e,{inlineData:{data:t.toString("base64"),mimeType:"image/png"}}])).response.text()}catch(s){throw new V(`Google vision error: ${s instanceof Error?s.message:String(s)}`,this.name,"VISION_ERROR")}}async validate(){return this.genAI?super.validate():{valid:!1,error:"GOOGLE_API_KEY is not set"}}}});var Fu={};ie(Fu,{AzureOpenAIProvider:()=>Za});var Nu,Za,Mu=b(()=>{"use strict";g();Nu=x(require("openai"));Ar();ge();Za=class extends Be{name="azure-openai";model;supportsVision=!0;client;constructor(e){super(e),this.model=e.deployment,this.client=new Nu.default({apiKey:e.apiKey,baseURL:`${e.endpoint}/openai/deployments/${e.deployment}`,defaultQuery:{"api-version":e.apiVersion??"2024-10-21"},defaultHeaders:{"api-key":e.apiKey},timeout:this.timeoutMs,maxRetries:0})}async chat(e){try{let s=(await this.client.chat.completions.create({model:this.model,messages:e.map(n=>({role:n.role,content:n.content})),temperature:this.temperature,max_tokens:8192})).choices[0]?.message?.content;if(!s)throw new V("Azure OpenAI returned empty response.",this.name,"EMPTY_RESPONSE");return s}catch(t){throw t instanceof V?t:new V(`Azure OpenAI error: ${t instanceof Error?t.message:String(t)}`,this.name,"API_ERROR")}}async validate(){return this.client.apiKey?super.validate():{valid:!1,error:"AZURE_OPENAI_API_KEY is not set"}}}});var ju={};ie(ju,{BedrockProvider:()=>eo});var ii,eo,Bu=b(()=>{"use strict";g();ii=require("@aws-sdk/client-bedrock-runtime");Ar();ge();eo=class extends Be{name="bedrock";model;supportsVision=!1;client;constructor(e){super(e),this.model=e.model,this.client=new ii.BedrockRuntimeClient({region:e.region,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey,...e.sessionToken?{sessionToken:e.sessionToken}:{}}})}async chat(e){try{return this.model.includes("claude")?await this.chatClaude(e):await this.chatClaude(e)}catch(t){throw t instanceof V?t:new V(`Bedrock error: ${t instanceof Error?t.message:String(t)}`,this.name,"API_ERROR")}}async chatClaude(e){let t=e.find(u=>u.role==="system")?.content,s=e.filter(u=>u.role!=="system"),n={anthropic_version:"bedrock-2023-05-31",max_tokens:8192,temperature:this.temperature,messages:s.map(u=>({role:u.role,content:u.content}))};t&&(n.system=t);let i=new ii.InvokeModelCommand({modelId:this.model,body:JSON.stringify(n),contentType:"application/json",accept:"application/json"}),a=await this.client.send(i),o=new TextDecoder().decode(a.body),l=JSON.parse(o).content[0]?.text;if(!l)throw new V("Bedrock returned empty response.",this.name,"EMPTY_RESPONSE");return l}async validate(){return this.client.config.credentials?super.validate():{valid:!1,error:"AWS credentials are not configured"}}}});var Uu,rt,Yr=b(()=>{"use strict";g();Uu=x(require("openai"));Ar();ge();rt=class extends Be{name;model;supportsVision=!1;client;constructor(e,t){super(t),this.name=e,this.model=t.model,this.client=new Uu.default({apiKey:t.apiKey,baseURL:t.baseUrl,timeout:this.timeoutMs,maxRetries:0})}async chat(e){try{let s=(await this.client.chat.completions.create({model:this.model,messages:e.map(n=>({role:n.role,content:n.content})),temperature:this.temperature,max_tokens:8192})).choices[0]?.message?.content;if(!s)throw new V(`Provider "${this.name}" returned an empty response.`,this.name,"EMPTY_RESPONSE");return s}catch(t){if(t instanceof V)throw t;let s=t instanceof Error?t.message:String(t);throw new V(`Provider "${this.name}" API error: ${s}`,this.name,"API_ERROR")}}async validate(){return!this.client.apiKey||this.client.apiKey==="none"?{valid:!1,error:`API key is not configured for provider "${this.name}"`}:super.validate()}}});var Hu={};ie(Hu,{DeepSeekProvider:()=>to});var to,Vu=b(()=>{"use strict";g();Yr();to=class extends rt{name="deepseek";constructor(e){let t={apiKey:e.apiKey,baseUrl:"https://api.deepseek.com/v1",model:e.model??"deepseek-chat",timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("deepseek",t)}}});var Gu={};ie(Gu,{GroqProvider:()=>ro});var ro,zu=b(()=>{"use strict";g();Yr();ro=class extends rt{name="groq";constructor(e){let t={apiKey:e.apiKey,baseUrl:"https://api.groq.com/openai/v1",model:e.model??"llama-3.3-70b-versatile",timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("groq",t)}}});var qu={};ie(qu,{TogetherProvider:()=>so});var so,Ku=b(()=>{"use strict";g();Yr();so=class extends rt{name="together";constructor(e){let t={apiKey:e.apiKey,baseUrl:"https://api.together.xyz/v1",model:e.model??"meta-llama/Llama-3.3-70B-Instruct-Turbo",timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("together",t)}}});var Wu={};ie(Wu,{QwenProvider:()=>no});var no,Xu=b(()=>{"use strict";g();Yr();no=class extends rt{name="qwen";constructor(e){let t={apiKey:e.apiKey,baseUrl:e.baseUrl??"https://dashscope-intl.aliyuncs.com/compatible-mode/v1",model:e.model??"qwen-max",timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("qwen",t)}}});var Ju={};ie(Ju,{PerplexityProvider:()=>io});var io,Yu=b(()=>{"use strict";g();Yr();io=class extends rt{name="perplexity";constructor(e){let t={apiKey:e.apiKey,baseUrl:"https://api.perplexity.ai",model:e.model??"sonar-pro",timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("perplexity",t)}}});var Zu={};ie(Zu,{OllamaProvider:()=>ao});var Qu,ao,ed=b(()=>{"use strict";g();Qu=x(require("openai"));Ar();ge();ao=class extends Be{name="ollama";model;supportsVision=!1;baseUrl;client;constructor(e={}){super({model:e.model??"llama3.3",timeoutMs:e.timeoutMs??12e4,maxRetries:e.maxRetries??1}),this.model=e.model??"llama3.3",this.baseUrl=`${e.baseUrl??"http://localhost:11434"}/v1`,this.client=new Qu.default({apiKey:"ollama",baseURL:this.baseUrl,timeout:this.timeoutMs,maxRetries:0})}async chat(e){try{let s=(await this.client.chat.completions.create({model:this.model,messages:e.map(n=>({role:n.role,content:n.content})),temperature:this.temperature})).choices[0]?.message?.content;if(!s)throw new V("Ollama returned an empty response.",this.name,"EMPTY_RESPONSE");return s}catch(t){if(t instanceof V)throw t;let s=t instanceof Error?t.message:String(t);throw s.includes("ECONNREFUSED")||s.includes("fetch failed")?new V(`Cannot connect to Ollama at ${this.baseUrl}. Make sure Ollama is running: start it with "ollama serve" and confirm it's on port 11434.`,this.name,"CONNECTION_REFUSED"):s.includes("model")&&(s.includes("not found")||s.includes("pull"))?new V(`Ollama model "${this.model}" is not available locally. Pull it first: ollama pull ${this.model}`,this.name,"MODEL_NOT_FOUND"):new V(`Ollama error: ${s}`,this.name,"API_ERROR")}}async validate(){try{return await this.chat([{role:"user",content:"Reply with the single word: ready"}]),{valid:!0}}catch(e){return{valid:!1,error:e instanceof Error?e.message:String(e)}}}}});var td={};ie(td,{CustomProvider:()=>oo});var oo,rd=b(()=>{"use strict";g();Yr();oo=class extends rt{name="custom";constructor(e){let t={apiKey:e.apiKey,baseUrl:e.baseUrl,model:e.model,timeoutMs:e.timeoutMs,maxRetries:e.maxRetries};super("custom",t)}}});var sd={};ie(sd,{createProvider:()=>Qr,createSmartRouter:()=>ht,generateSuiteFromStory:()=>un,generateSuitesFromStories:()=>lo});function Qr(r,e){let t=Cr(),s=r??String(t.AI_PROVIDER),n=t.AI_TIMEOUT*1e3,i=t.AI_MAX_RETRIES,a=e?.temperature;switch(s){case"anthropic":{let{AnthropicProvider:o}=(Tu(),mt(ku));return new o({apiKey:String(t.ANTHROPIC_API_KEY??""),model:String(t.ANTHROPIC_MODEL??"claude-sonnet-4-20250514"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"openai":{let{OpenAIProvider:o}=(_u(),mt($u));return new o({apiKey:String(t.OPENAI_API_KEY??""),model:String(t.OPENAI_MODEL??"gpt-4o"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"google":{let{GoogleProvider:o}=(Du(),mt(Lu));return new o({apiKey:String(t.GOOGLE_API_KEY??""),model:String(t.GOOGLE_MODEL??"gemini-2.5-flash"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"azure-openai":{let{AzureOpenAIProvider:o}=(Mu(),mt(Fu));return new o({apiKey:String(t.AZURE_OPENAI_API_KEY??""),endpoint:String(t.AZURE_OPENAI_ENDPOINT??""),deployment:String(t.AZURE_OPENAI_DEPLOYMENT??""),apiVersion:String(t.AZURE_OPENAI_API_VERSION??"2024-10-21"),model:String(t.AZURE_OPENAI_DEPLOYMENT??""),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"bedrock":{let{BedrockProvider:o}=(Bu(),mt(ju));return new o({accessKeyId:String(t.AWS_ACCESS_KEY_ID??""),secretAccessKey:String(t.AWS_SECRET_ACCESS_KEY??""),...t.AWS_SESSION_TOKEN?{sessionToken:String(t.AWS_SESSION_TOKEN)}:{},region:String(t.AWS_REGION??"us-east-1"),model:String(t.BEDROCK_MODEL??"anthropic.claude-sonnet-4-20250514-v1:0"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"deepseek":{let{DeepSeekProvider:o}=(Vu(),mt(Hu));return new o({apiKey:String(t.DEEPSEEK_API_KEY??""),model:String(t.DEEPSEEK_MODEL??"deepseek-chat"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"groq":{let{GroqProvider:o}=(zu(),mt(Gu));return new o({apiKey:String(t.GROQ_API_KEY??""),model:String(t.GROQ_MODEL??"llama-3.3-70b-versatile"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"together":{let{TogetherProvider:o}=(Ku(),mt(qu));return new o({apiKey:String(t.TOGETHER_API_KEY??""),model:String(t.TOGETHER_MODEL??"meta-llama/Llama-3.3-70B-Instruct-Turbo"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"qwen":{let{QwenProvider:o}=(Xu(),mt(Wu));return new o({apiKey:String(t.QWEN_API_KEY??""),model:String(t.QWEN_MODEL??"qwen-max"),baseUrl:t.QWEN_BASE_URL,timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"perplexity":{let{PerplexityProvider:o}=(Yu(),mt(Ju));return new o({apiKey:String(t.PERPLEXITY_API_KEY??""),model:String(t.PERPLEXITY_MODEL??"sonar-pro"),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"ollama":{let{OllamaProvider:o}=(ed(),mt(Zu));return new o({model:String(t.OLLAMA_MODEL??"llama3.3"),baseUrl:t.OLLAMA_BASE_URL,timeoutMs:Math.max(n,12e4),maxRetries:i,...a!==void 0?{temperature:a}:{}})}case"custom":{let{CustomProvider:o}=(rd(),mt(td));return new o({apiKey:String(t.CUSTOM_API_KEY??""),baseUrl:String(t.CUSTOM_BASE_URL??""),model:String(t.CUSTOM_MODEL??""),timeoutMs:n,maxRetries:i,...a!==void 0?{temperature:a}:{}})}default:throw new Re(`Unknown AI provider: "${s}". Supported: anthropic, openai, google, azure-openai, bedrock, deepseek, groq, together, qwen, perplexity, ollama, custom`,"UNKNOWN_PROVIDER")}}function ht(r,e,t,s){let n=Cr(),i=Qr(),a;if(n.AI_TIERED_ENABLED){let l=n.AI_TIERED_FAST_PROVIDER;if(l)try{a=Qr(l),Xe.info({fast:l,primary:String(n.AI_PROVIDER)},"Tiered mode active")}catch(u){Xe.warn({err:u},"Failed to create fast provider \u2014 tiered mode disabled")}}let o=r?ai.default.join(r,"results",".code-cache.json"):void 0,c=new xs(o);return new ri({primaryProvider:i,fastProvider:a,cache:c,mcpSession:e,actThenScript:t,ragManager:s})}async function un(r,e){let t=Qr();Xe.info({provider:t.name,model:t.model},"Generating suite from story");let s=await t.generateTestSuite(r),n=e.suiteName??s.name,{suiteDir:i,suiteId:a}=await nn(e.outputDir,{name:n,description:s.description,tags:s.tags,type:e.type??"ui"});Xe.info({suiteDir:i,suiteId:a,cases:s.cases.length},"Suite directory created");for(let l of s.cases)await bs(i,{name:l.name,description:l.description,tags:l.tags,priority:"medium",steps:l.steps.map((u,d)=>({id:`step-${d+1}`,order:d+1,instruction:u.instruction,generatedCode:u.generatedCode||"",strategy:u.strategy??"primary",stepType:gt(u.instruction),lastHealed:null}))});Xe.info({suiteId:a,cases:s.cases.length},"Auto-generating code for all steps");let o;try{let l=await J(e.rootDir),u=await ft(e.rootDir).catch(()=>({}));l.mcp?.enabled&&(o=new Tt({headless:l.mcp.headless,actionTimeout:l.mcp.actionTimeout,idleTimeout:l.mcp.idleTimeout}),Xe.info({headless:l.mcp.headless},"MCP session created for suite generation"));let d=ht(e.rootDir,o,l.mcp?.actThenScript),m=await or(i);for(let p of m){let f=await fe(p),h=f.steps.filter(P=>!P.generatedCode.trim());if(h.length===0)continue;let S=h.map(P=>({instruction:P.instruction,context:{url:l.baseUrl,title:"",interactiveElements:"",htmlSnapshot:"",previousSteps:f.steps.filter(G=>G.order<P.order&&G.generatedCode).map(G=>({instruction:G.instruction,code:G.generatedCode})),variables:u}})),C=await d.batchGenerate(S),v=new Map(h.map((P,G)=>[P.id,C[G]])),k=0,T=f.steps.map(P=>{let G=v.get(P.id);return G&&G.code.trim()?(k++,{...P,generatedCode:G.code,strategy:G.strategy}):P});k>0&&(await We(p,{steps:T}),Xe.info({caseName:f.name,generated:k,total:f.steps.length},"Code generated for case"))}await d.getCache().persist(),Xe.info({suiteId:a},"Auto code generation complete")}catch(l){Xe.warn({err:l,suiteId:a},"Auto code generation failed \u2014 steps saved without code")}finally{o&&(o.release(),Xe.debug("MCP session released after suite generation"))}let c=ai.default.join(e.rootDir,"results",`generated-suite-${Ht(n)}.json`);return await co.default.ensureDir(ai.default.dirname(c)),await co.default.writeJson(c,{story:r,generated:s,suiteDir:i,suiteId:a},{spaces:2}),Xe.info({reportPath:c,suiteId:a},"Generation complete"),{suiteId:a,suiteName:n}}async function lo(r,e,t,s=3){let{randomUUID:n}=await import("crypto"),i=n(),a=r.length,o=0,c=0,l=[];t?.broadcast("story:bulk-start",{jobId:i,total:a}),Xe.info({jobId:i,total:a,concurrency:s},"Bulk story generation started");let u=0,d=[...r.entries()],m=[];async function p(){for(;d.length>0;){let h=d.shift();if(!h)break;let[S,C]=h;t?.broadcast("story:bulk-progress",{jobId:i,total:a,completed:o,failed:c,currentIndex:S,currentStory:C.story.slice(0,80)});try{await un(C.story,{rootDir:e.rootDir,outputDir:e.outputDir,suiteName:C.suiteName}),o++,Xe.info({jobId:i,index:S,completed:o,total:a},"Story processed")}catch(v){c++;let k=v instanceof Error?v.message:String(v);l.push({index:S,story:C.story.slice(0,120),error:k}),Xe.warn({jobId:i,index:S,error:k},"Story generation failed \u2014 continuing batch")}t?.broadcast("story:bulk-progress",{jobId:i,total:a,completed:o,failed:c,currentIndex:S,currentStory:C.story.slice(0,80)}),d.length>0&&await new Promise(v=>setTimeout(v,350))}}for(let h=0;h<Math.min(s,a);h++)m.push(p());await Promise.all(m);let f={jobId:i,total:a,completed:o,failed:c,failedItems:l};return t?.broadcast("story:bulk-done",f),Xe.info({jobId:i,completed:o,failed:c,total:a},"Bulk story generation finished"),f}var ai,co,Xe,$r=b(()=>{"use strict";g();ai=x(require("path")),co=x(require("fs-extra"));Pr();ge();j();au();za();cn();Rt();et();Me();Tr();Vt();Kr();Xe=I("router")});async function ad(r){let e=r.rootDir,t=nd.default.join(e,"tests");async function s(n,i){let a=await we(t,n);return a?Pt(a,i):null}r.post("/suites/:suiteId/cases/:caseId/steps",async(n,i)=>{let a=$y.safeParse(n.body);if(!a.success)return i.status(400).send({error:"Invalid step data",details:a.error.issues});try{let o=await s(n.params.suiteId,n.params.caseId);if(!o)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=await fe(o),l=a.data.order??c.steps.length+1,u={id:`step-${(0,id.randomUUID)().slice(0,8)}`,order:l,instruction:a.data.instruction,generatedCode:a.data.generatedCode??"",strategy:a.data.generatedCode?a.data.strategy??"recorder":"primary",lastHealed:null,...a.data.soft?{soft:!0}:{}},d=[...c.steps.map(p=>p.order>=l?{...p,order:p.order+1}:p),u].sort((p,f)=>p.order-f.order),m=await We(o,{steps:d});return i.status(201).send(m)}catch(o){return w(i,500,"Failed to add step",o)}}),r.delete("/suites/:suiteId/cases/:caseId/steps/:stepId",async(n,i)=>{try{let a=await s(n.params.suiteId,n.params.caseId);if(!a)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=(await fe(a)).steps.filter(u=>u.id!==n.params.stepId).map((u,d)=>({...u,order:d+1})),l=await We(a,{steps:c});return i.send(l)}catch(a){return w(i,500,"Failed to delete step",a)}}),r.post("/suites/:suiteId/cases/:caseId/steps/:stepId/generate",async(n,i)=>{let a;try{let o=await s(n.params.suiteId,n.params.caseId);if(!o)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=await fe(o),l=c.steps.find(v=>v.id===n.params.stepId);if(!l)return i.status(404).send({error:`Step "${n.params.stepId}" not found`});let u=await ft(e).catch(()=>({})),d=await J(e),m=n.body?.useMcp??d.mcp?.enabled;m&&(a=new Tt({headless:d.mcp?.headless??!0,actionTimeout:d.mcp?.actionTimeout??15e3,idleTimeout:d.mcp?.idleTimeout??3e4}));let p=ht(e,a,m?d.mcp?.actThenScript:!1),f=await p.generate(l.instruction,{url:d.baseUrl,title:"",interactiveElements:"",htmlSnapshot:"",previousSteps:c.steps.filter(v=>v.order<l.order&&v.generatedCode).map(v=>({instruction:v.instruction,code:v.generatedCode})),variables:u}),h=f.code;l.soft&&h&&(h=h.replace(/\bexpect\(/g,"expect.soft("));let S=c.steps.map(v=>v.id===l.id?{...v,generatedCode:h,strategy:f.strategy}:v);await p.getCache().persist();let C=await We(o,{steps:S});return i.send({step:C.steps.find(v=>v.id===l.id),strategy:f.strategy,cost:f.cost,mcpUsed:!!a})}catch(o){return w(i,500,"Failed to generate code for step",o)}finally{a?.release()}}),r.post("/suites/:suiteId/cases/:caseId/steps/generate-all",async(n,i)=>{let a;try{let o=await s(n.params.suiteId,n.params.caseId);if(!o)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=await fe(o),l=c.steps.filter(T=>!T.generatedCode.trim());if(l.length===0)return i.send({message:"All steps already have code",generated:0});let u=await ft(e).catch(()=>({})),d=await J(e),m=n.body?.useMcp??d.mcp?.enabled;m&&(a=new Tt({headless:d.mcp?.headless??!0,actionTimeout:d.mcp?.actionTimeout??15e3,idleTimeout:d.mcp?.idleTimeout??3e4}));let p=ht(e,a,m?d.mcp?.actThenScript:!1),f=l.map(T=>({instruction:T.instruction,context:{url:d.baseUrl,title:"",interactiveElements:"",htmlSnapshot:"",previousSteps:c.steps.filter(P=>P.order<T.order&&P.generatedCode).map(P=>({instruction:P.instruction,code:P.generatedCode})),variables:u}})),h=await p.batchGenerate(f);await p.getCache().persist();let S=new Map(l.map((T,P)=>[T.id,h[P]])),C=0,v=c.steps.map(T=>{let P=S.get(T.id);if(P&&P.code.trim()){C++;let G=T.soft?P.code.replace(/\bexpect\(/g,"expect.soft("):P.code;return{...T,generatedCode:G,strategy:P.strategy}}return T});if(C===0)return i.status(500).send({error:"AI code generation produced no results",detail:"The AI provider responded but returned empty code for all steps. Check your API key, model name, and server terminal logs for details.",code:"GENERATION_EMPTY"});let k=await We(o,{steps:v});return i.send({generated:C,case:k,mcpUsed:!!a})}catch(o){return w(i,500,"Failed to generate code for steps",o)}finally{a?.release()}}),r.patch("/suites/:suiteId/cases/:caseId/steps/reorder",async(n,i)=>{let a=_y.safeParse(n.body);if(!a.success)return i.status(400).send({error:"Invalid reorder data",details:a.error.issues});try{let o=await s(n.params.suiteId,n.params.caseId);if(!o)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=await fe(o),l=new Map(c.steps.map(m=>[m.id,m])),u=a.data.stepIds.map((m,p)=>{let f=l.get(m);return f?{...f,order:p+1}:null}).filter(m=>m!==null),d=await We(o,{steps:u});return i.send(d)}catch(o){return w(i,500,"Failed to reorder steps",o)}}),r.patch("/suites/:suiteId/cases/:caseId/steps/:stepId",async(n,i)=>{let a=Oy.safeParse(n.body);if(!a.success)return i.status(400).send({error:"Invalid step data",details:a.error.issues});try{let o=await s(n.params.suiteId,n.params.caseId);if(!o)return i.status(404).send({error:`Case "${n.params.caseId}" not found`});let c=await fe(o);if(!c.steps.find(m=>m.id===n.params.stepId))return i.status(404).send({error:`Step "${n.params.stepId}" not found`});let u=c.steps.map(m=>{if(m.id!==n.params.stepId)return m;let p={};return a.data.instruction!==void 0&&(p.instruction=a.data.instruction),a.data.timeout!==void 0&&(p.timeout=a.data.timeout??void 0),a.data.stepType!==void 0&&(p.stepType=a.data.stepType),a.data.retries!==void 0&&(p.retries=a.data.retries),a.data.mockUrl!==void 0&&(p.mockUrl=a.data.mockUrl),a.data.mockResponse!==void 0&&(p.mockResponse=a.data.mockResponse),a.data.mockStatus!==void 0&&(p.mockStatus=a.data.mockStatus),a.data.auditUrl!==void 0&&(p.auditUrl=a.data.auditUrl),a.data.lighthouseCategories!==void 0&&(p.lighthouseCategories=a.data.lighthouseCategories),a.data.runAudit!==void 0&&(p.runAudit=a.data.runAudit),a.data.soft!==void 0&&(p.soft=a.data.soft,m.generatedCode&&(a.data.soft?p.generatedCode=m.generatedCode.replace(/\bexpect\(/g,"expect.soft("):p.generatedCode=m.generatedCode.replace(/\bexpect\.soft\(/g,"expect("))),a.data.visualCheck!==void 0&&(p.visualCheck=a.data.visualCheck),{...m,...p}}),d=await We(o,{steps:u});return i.send(d)}catch(o){return w(i,500,"Failed to update step",o)}})}var nd,id,he,$y,_y,Oy,od=b(()=>{"use strict";g();nd=x(require("path")),id=require("crypto"),he=require("zod");Rt();et();Tr();Me();$r();cn();de();$y=he.z.object({instruction:he.z.string().min(1),order:he.z.number().int().positive().optional(),generatedCode:he.z.string().optional(),strategy:he.z.enum(["template","cache","rag","batch","fast","primary","recorder"]).optional(),soft:he.z.boolean().optional()}),_y=he.z.object({stepIds:he.z.array(he.z.string().min(1))}),Oy=he.z.object({instruction:he.z.string().min(1).optional(),timeout:he.z.number().int().positive().optional().nullable(),stepType:he.z.enum(["ui","api","mock"]).optional(),retries:he.z.number().int().min(0).optional(),mockUrl:he.z.string().optional(),mockResponse:he.z.string().optional(),mockStatus:he.z.number().int().optional(),auditUrl:he.z.string().optional(),lighthouseCategories:he.z.array(he.z.enum(["performance","accessibility","seo"])).optional(),runAudit:he.z.boolean().optional(),soft:he.z.boolean().optional(),visualCheck:he.z.boolean().optional()})});var pd={};ie(pd,{addHookStep:()=>mo,deleteHookStep:()=>go,readHooks:()=>ut,reorderHookSteps:()=>fo,updateHookStep:()=>ho,writeHooks:()=>zt});async function ut(r){let e=uo.default.join(r,dd);if(!await po.default.pathExists(e))return{...cd};let t=await ve(e),s=La.safeParse(t);return s.success?s.data:(ud.warn({path:e,errors:s.error.issues},"Invalid hooks.json \u2014 returning defaults"),{...cd})}async function zt(r,e){await po.default.ensureDir(r);let t=uo.default.join(r,dd),s=La.safeParse(e);if(!s.success){let n=s.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
|
|
684
|
+
`);throw new te(`Cannot write invalid hooks to "${t}":
|
|
685
|
+
${n}`,"hooks","INVALID_HOOKS")}await re(t,s.data),ud.debug({path:t},"Hooks written")}async function mo(r,e,t,s){let n=await ut(r),i=n[e],a=s??i.length+1,o={id:`step-${(0,ld.randomUUID)().slice(0,8)}`,order:a,instruction:t,generatedCode:"",strategy:"primary",lastHealed:null},c=[...i.map(l=>l.order>=a?{...l,order:l.order+1}:l),o].sort((l,u)=>l.order-u.order);return n[e]=c,await zt(r,n),n}async function go(r,e,t){let s=await ut(r);return s[e]=s[e].filter(n=>n.id!==t).map((n,i)=>({...n,order:i+1})),await zt(r,s),s}async function fo(r,e,t){let s=await ut(r),n=new Map(s[e].map(i=>[i.id,i]));return s[e]=t.map((i,a)=>{let o=n.get(i);return o?{...o,order:a+1}:null}).filter(i=>i!==null),await zt(r,s),s}async function ho(r,e,t,s){let n=await ut(r);return n[e]=n[e].map(i=>{if(i.id!==t)return i;let a={};return s.instruction!==void 0&&(a.instruction=s.instruction),s.timeout!==void 0&&(a.timeout=s.timeout??void 0),{...i,...a}}),await zt(r,n),n}var uo,po,ld,ud,dd,cd,dn=b(()=>{"use strict";g();uo=x(require("path")),po=x(require("fs-extra")),ld=require("crypto");sn();ge();j();ot();ud=I("hooks-store"),dd="hooks.json",cd={before_all:[],before_each:[],after_each:[],after_all:[]}});function Ss(r){let e=yl.safeParse(r);return e.success?e.data:null}async function gd(r){let e=r.rootDir,t=md.default.join(e,"tests");async function s(n){return we(t,n)}r.get("/suites/:suiteId/hooks",async(n,i)=>{try{let a=await s(n.params.suiteId);if(!a)return i.status(404).send({error:"Suite not found"});let o=await ut(a);return i.send(o)}catch(a){return w(i,500,"Failed to read hooks",a)}}),r.post("/suites/:suiteId/hooks/:hookType/steps",async(n,i)=>{let a=Ss(n.params.hookType);if(!a)return i.status(400).send({error:`Invalid hook type: ${n.params.hookType}`});let o=Ly.safeParse(n.body);if(!o.success)return i.status(400).send({error:"Invalid step data",details:o.error.issues});try{let c=await s(n.params.suiteId);if(!c)return i.status(404).send({error:"Suite not found"});let l=await mo(c,a,o.data.instruction,o.data.order);return i.status(201).send(l)}catch(c){return w(i,500,"Failed to add hook step",c)}}),r.post("/suites/:suiteId/hooks/:hookType/steps/generate-all",async(n,i)=>{let a=Ss(n.params.hookType);if(!a)return i.status(400).send({error:`Invalid hook type: ${n.params.hookType}`});try{let o=await s(n.params.suiteId);if(!o)return i.status(404).send({error:"Suite not found"});let c=await ut(o),l=c[a],u=l.filter(v=>!v.generatedCode.trim());if(u.length===0)return i.send({message:"All steps already have code",generated:0,hooks:c});let d=await ft(e).catch(()=>({})),m=await Promise.resolve().then(()=>(Me(),Ta)).then(v=>v.readConfig(e)),p=ht(e),f=u.map(v=>({instruction:v.instruction,context:{url:m.baseUrl,title:"",interactiveElements:"",htmlSnapshot:"",previousSteps:l.filter(k=>k.order<v.order&&k.generatedCode).map(k=>({instruction:k.instruction,code:k.generatedCode})),variables:d}})),h=await p.batchGenerate(f);await p.getCache().persist();let S=new Map(u.map((v,k)=>[v.id,h[k]])),C=0;return c[a]=l.map(v=>{let k=S.get(v.id);return k&&k.code.trim()?(C++,{...v,generatedCode:k.code,strategy:k.strategy}):v}),C===0?i.status(500).send({error:"AI code generation produced no results",code:"GENERATION_EMPTY"}):(await zt(o,c),i.send({generated:C,hooks:c}))}catch(o){return w(i,500,"Failed to generate code for hook steps",o)}}),r.patch("/suites/:suiteId/hooks/:hookType/steps/reorder",async(n,i)=>{let a=Ss(n.params.hookType);if(!a)return i.status(400).send({error:`Invalid hook type: ${n.params.hookType}`});let o=Dy.safeParse(n.body);if(!o.success)return i.status(400).send({error:"Invalid reorder data",details:o.error.issues});try{let c=await s(n.params.suiteId);if(!c)return i.status(404).send({error:"Suite not found"});let l=await fo(c,a,o.data.stepIds);return i.send(l)}catch(c){return w(i,500,"Failed to reorder hook steps",c)}}),r.delete("/suites/:suiteId/hooks/:hookType/steps/:stepId",async(n,i)=>{let a=Ss(n.params.hookType);if(!a)return i.status(400).send({error:`Invalid hook type: ${n.params.hookType}`});try{let o=await s(n.params.suiteId);if(!o)return i.status(404).send({error:"Suite not found"});let c=await go(o,a,n.params.stepId);return i.send(c)}catch(o){return w(i,500,"Failed to delete hook step",o)}}),r.patch("/suites/:suiteId/hooks/:hookType/steps/:stepId",async(n,i)=>{let a=Ss(n.params.hookType);if(!a)return i.status(400).send({error:`Invalid hook type: ${n.params.hookType}`});let o=Ny.safeParse(n.body);if(!o.success)return i.status(400).send({error:"Invalid step data",details:o.error.issues});try{let c=await s(n.params.suiteId);if(!c)return i.status(404).send({error:"Suite not found"});let l=await ho(c,a,n.params.stepId,o.data);return i.send(l)}catch(c){return w(i,500,"Failed to update hook step",c)}}),r.post("/suites/:suiteId/hooks/:hookType/steps/:stepId/generate",async(n,i)=>{let a=Ss(n.params.hookType);if(!a)return i.status(400).send({error:`Invalid hook type: ${n.params.hookType}`});try{let o=await s(n.params.suiteId);if(!o)return i.status(404).send({error:"Suite not found"});let c=await ut(o),l=c[a],u=l.find(h=>h.id===n.params.stepId);if(!u)return i.status(404).send({error:`Step "${n.params.stepId}" not found`});let d=await ft(e).catch(()=>({})),m=ht(e),p=await Promise.resolve().then(()=>(Me(),Ta)).then(h=>h.readConfig(e)),f=await m.generate(u.instruction,{url:p.baseUrl,title:"",interactiveElements:"",htmlSnapshot:"",previousSteps:l.filter(h=>h.order<u.order&&h.generatedCode).map(h=>({instruction:h.instruction,code:h.generatedCode})),variables:d});return c[a]=l.map(h=>h.id===u.id?{...h,generatedCode:f.code,strategy:f.strategy}:h),await m.getCache().persist(),await zt(o,c),i.send({step:c[a].find(h=>h.id===u.id),strategy:f.strategy,cost:f.cost})}catch(o){return w(i,500,"Failed to generate code for hook step",o)}})}var md,qt,Ly,Dy,Ny,fd=b(()=>{"use strict";g();md=x(require("path")),qt=require("zod");et();dn();Tr();$r();de();sn();Ly=qt.z.object({instruction:qt.z.string().min(1),order:qt.z.number().int().positive().optional()}),Dy=qt.z.object({stepIds:qt.z.array(qt.z.string().min(1))}),Ny=qt.z.object({instruction:qt.z.string().min(1).optional(),timeout:qt.z.number().int().positive().optional().nullable()})});async function yd(r){let e=r.rootDir;r.get("/variables",async(t,s)=>{try{let n=await Xn(e);try{let i=await J(e),a=i.environment??"stage",c=i.environmentUrls?.[a]||i.baseUrl;return delete n.BASE_URL,s.send({BASE_URL:c,...n,_system:{BASE_URL:{source:"settings",environment:a,readOnly:!0}}})}catch{return s.send(n)}}catch(n){return w(s,500,"Failed to read variables",n)}}),r.get("/variables/matrix",async(t,s)=>{try{let i=(await J(e)).environments??[],a=await Nl(e,i);return s.send({environments:i,entries:a})}catch(n){return w(s,500,"Failed to read variable matrix",n)}}),r.put("/variables/:key/scope/:scope",async(t,s)=>{if(t.params.key==="BASE_URL")return s.status(400).send({error:"BASE_URL is managed in Settings \u2192 Environment."});let n=hd.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid variable value",details:n.error.issues});try{return await Fl(e,t.params.scope,t.params.key,n.data.value),s.send({key:t.params.key,scope:t.params.scope,value:n.data.value})}catch(i){return w(s,500,"Failed to set variable",i)}}),r.delete("/variables/:key/scope/:scope",async(t,s)=>{try{return await on(e,t.params.scope,t.params.key),s.status(204).send()}catch(n){return w(s,500,"Failed to delete variable value",n)}}),r.delete("/variables/:key/everywhere",async(t,s)=>{if(t.params.key==="BASE_URL")return s.status(400).send({error:"BASE_URL is managed in Settings \u2192 Environment."});try{let n=await J(e);return await Ml(e,t.params.key,n.environments??[]),s.status(204).send()}catch(n){return w(s,500,"Failed to delete variable",n)}}),r.get("/variables/env/:env",async(t,s)=>{try{let n=await Jn(e,t.params.env);return s.send(n)}catch(n){return w(s,500,`Failed to read variables for env "${t.params.env}"`,n)}}),r.put("/variables/:key",async(t,s)=>{if(t.params.key==="BASE_URL")return s.status(400).send({error:"BASE_URL is managed in Settings \u2192 Environment. Update it there."});let n=hd.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid variable value",details:n.error.issues});try{return await Ua(e,t.params.key,n.data.value),s.send({key:t.params.key,value:n.data.value})}catch(i){return w(s,500,"Failed to set variable",i)}}),r.delete("/variables/:key",async(t,s)=>{if(t.params.key==="BASE_URL")return s.status(400).send({error:"BASE_URL is managed in Settings \u2192 Environment. It cannot be deleted."});try{return await _l(e,t.params.key),s.status(204).send()}catch(n){return w(s,500,"Failed to delete variable",n)}})}var Zr,hd,wd=b(()=>{"use strict";g();Zr=require("zod");Tr();Me();de();hd=Zr.z.object({value:Zr.z.union([Zr.z.string(),Zr.z.object({value:Zr.z.string(),secret:Zr.z.literal(!0)})])})});var le,Fy,My,oi,yo,bd=b(()=>{"use strict";g();le=require("zod"),Fy=le.z.enum(["pending","accepted","rejected"]),My=le.z.enum(["retry","regenerate","multi-selector","visual","decompose","manual"]),oi=le.z.object({id:le.z.string(),runId:le.z.string(),suiteId:le.z.string(),caseId:le.z.string(),stepId:le.z.string(),stepInstruction:le.z.string(),failedCode:le.z.string(),healedCode:le.z.string(),error:le.z.string(),strategy:My,level:le.z.number().int().min(1).max(6),status:Fy,pageUrl:le.z.string(),timestamp:le.z.string().datetime(),acceptedAt:le.z.string().datetime().optional(),rejectedAt:le.z.string().datetime().optional()}),yo=le.z.object({runId:le.z.string(),generatedAt:le.z.string().datetime(),totalHeals:le.z.number().int(),accepted:le.z.number().int(),rejected:le.z.number().int(),pending:le.z.number().int(),events:le.z.array(oi)})});function Sd(r,e){return Je.default.join(r,`${ci}${e}.json`)}function ts(r){return Je.default.join(r,jy)}async function Ed(r,e){let t=yo.safeParse(e);if(!t.success){let i=t.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
|
|
686
|
+
`);throw new te(`Cannot write invalid healing report:
|
|
687
|
+
${i}`,"healingReport","INVALID_HEALING_REPORT")}let s=Je.default.join(r,Kt),n=Sd(s,e.runId);await re(n,t.data),_r.info({runId:e.runId,totalHeals:e.totalHeals},"Healing report saved")}async function li(r,e){let t=Je.default.join(r,Kt),s=Sd(t,e);if(!await es.default.pathExists(s))throw new ye(`Healing report not found for runId "${e}".`,s,"HEALING_REPORT_NOT_FOUND");let n=await ve(s),i=yo.safeParse(n);if(!i.success){let a=i.error.issues.map(o=>` \u2022 ${o.path.join(".")}: ${o.message}`).join(`
|
|
688
|
+
`);throw new te(`Invalid healing report at "${s}":
|
|
689
|
+
${a}`,"healingReport","INVALID_HEALING_REPORT")}return i.data}async function yt(r){let e=Je.default.join(r,Kt),t=ts(e);if(!await es.default.pathExists(t))return[];let s=await ve(t),n=oi.array().safeParse(s);return n.success?n.data:(_r.warn({path:t},"Pending healing file is malformed \u2014 resetting"),[])}function By(r){if(r.length<=vd)return r;let e=[...r].sort((n,i)=>{let a=xd.indexOf(n.status),o=xd.indexOf(i.status);return a!==o?a-o:n.timestamp<i.timestamp?-1:1}),t=r.length-vd,s=new Set(e.slice(0,t).map(n=>n.id));return _r.info({excess:t,deleted:s.size},"Auto-pruning healing events to enforce cap"),r.filter(n=>!s.has(n.id))}async function Id(r,e){let t=oi.safeParse(e);if(!t.success)throw new te(`Invalid healing event: ${t.error.message}`,"healingEvent","INVALID_HEALING_EVENT");let s=Je.default.join(r,Kt);await es.default.ensureDir(s);let n=await yt(r);n.push(t.data),await re(ts(s),By(n))}async function pn(r,e){let t=await yt(r),s=t.findIndex(i=>i.id===e);if(s===-1)throw new ye(`Healing event "${e}" not found in pending list.`,e,"HEALING_EVENT_NOT_FOUND");t[s]={...t[s],status:"accepted",acceptedAt:new Date().toISOString()};let n=Je.default.join(r,Kt);return await re(ts(n),t),_r.info({eventId:e},"Healing event accepted"),t[s]}async function ui(r,e){let t=await yt(r),s=t.findIndex(i=>i.id===e);if(s===-1)throw new ye(`Healing event "${e}" not found in pending list.`,e,"HEALING_EVENT_NOT_FOUND");t[s]={...t[s],status:"rejected",rejectedAt:new Date().toISOString()};let n=Je.default.join(r,Kt);return await re(ts(n),t),_r.info({eventId:e},"Healing event rejected"),t[s]}async function Cd(r,e){let s=(await yt(r)).filter(i=>i.id!==e),n=Je.default.join(r,Kt);await re(ts(n),s),_r.info({eventId:e},"Healing event deleted")}async function Pd(r,e){let t=await yt(r),s=new Set(e),n=t.filter(o=>!s.has(o.id)),i=t.length-n.length,a=Je.default.join(r,Kt);return await re(ts(a),n),_r.info({count:i},"Healing events bulk-deleted"),i}async function Rd(r){let t=(await yt(r)).length,s=Je.default.join(r,Kt);return await re(ts(s),[]),_r.info({count:t},"All healing events cleared"),t}async function kd(r){let e=await yt(r),t=e.filter(i=>i.status==="pending").length,s=e.filter(i=>i.status==="accepted").length,n=e.filter(i=>i.status==="rejected").length;return{pending:t,accepted:s,rejected:n,total:e.length}}async function Td(r){let e=Je.default.join(r,Kt);if(!await es.default.pathExists(e))return[];let s=(await es.default.readdir(e,{withFileTypes:!0})).filter(i=>i.isFile()&&i.name.startsWith(ci)&&i.name.endsWith(".json")).map(i=>i.name.slice(ci.length,-5));return(await Promise.all(s.map(async i=>{let a=await es.default.stat(Je.default.join(e,`${ci}${i}.json`));return{id:i,mtime:a.mtimeMs}}))).sort((i,a)=>a.mtime-i.mtime).map(i=>i.id)}var Je,es,_r,Kt,jy,ci,vd,xd,di=b(()=>{"use strict";g();Je=x(require("path")),es=x(require("fs-extra"));bd();ge();j();ot();_r=I("healing-store"),Kt=Je.default.join("results","healing"),jy="pending.json",ci="healing-report-";vd=50,xd=["pending","rejected","accepted"]});async function _d(r){let e=r.rootDir,t=$d.default.join(e,"tests");r.get("/healing",async(s,n)=>{try{let i=await Td(e),a=[];for(let o of i){let c=await li(e,o);a.push(...c.events)}return n.send(a)}catch(i){return w(n,500,"Failed to list healing events",i)}}),r.get("/healing/pending",async(s,n)=>{try{let i=await yt(e);return n.send(i)}catch(i){return w(n,500,"Failed to read pending healing events",i)}}),r.get("/healing/stats",async(s,n)=>{try{let i=await kd(e);return n.send(i)}catch(i){return w(n,500,"Failed to get healing stats",i)}}),r.patch("/healing/:id/accept",async(s,n)=>{try{let i=await pn(e,s.params.id);return await wo(t,i.caseId,i.stepId,i.healedCode),Ad.info({id:s.params.id,caseId:i.caseId,stepId:i.stepId},"Healing accepted and applied"),n.send(i)}catch(i){return w(n,500,"Failed to accept healing event",i)}}),r.patch("/healing/:id/reject",async(s,n)=>{try{let i=await ui(e,s.params.id);return n.send(i)}catch(i){return w(n,500,"Failed to reject healing event",i)}}),r.post("/healing/accept-all",async(s,n)=>{try{let i=await yt(e),a=[];for(let o of i)try{await pn(e,o.id),await wo(t,o.caseId,o.stepId,o.healedCode),a.push({id:o.id,status:"accepted"}),Ad.info({id:o.id},"Bulk healing accepted")}catch(c){a.push({id:o.id,status:"failed",error:c instanceof Error?c.message:String(c)})}return n.send({accepted:a.filter(o=>o.status==="accepted").length,results:a})}catch(i){return w(n,500,"Failed to accept all healing events",i)}}),r.post("/healing/clear-all",async(s,n)=>{try{let i=await Rd(e);return n.send({deleted:i})}catch(i){return w(n,500,"Failed to clear healing events",i)}}),r.post("/healing/delete-bulk",async(s,n)=>{let i=Hy.safeParse(s.body);if(!i.success)return n.status(400).send({error:"Invalid request",details:i.error.issues});try{let a=await Pd(e,i.data.ids);return n.send({deleted:a})}catch(a){return w(n,500,"Failed to delete healing events",a)}}),r.delete("/healing/:id",async(s,n)=>{try{return await Cd(e,s.params.id),n.status(204).send()}catch(i){return w(n,500,"Failed to delete healing event",i)}}),r.post("/healing/apply-from-report",async(s,n)=>{let i=Uy.safeParse(s.body);if(!i.success)return n.status(400).send({error:"Invalid request",details:i.error.issues});try{let{default:a}=await import("fs-extra"),o=await a.readJson(i.data.reportPath);if(!Array.isArray(o.events))return n.status(400).send({error:"Invalid healing report format"});let c=[];for(let l of o.events)try{await wo(t,l.caseId,l.stepId,l.healedCode),c.push({id:l.id,status:"applied"})}catch(u){c.push({id:l.id,status:"failed",error:u instanceof Error?u.message:String(u)})}return n.send({applied:c.filter(l=>l.status==="applied").length,results:c})}catch(a){return w(n,500,"Failed to apply healing report",a)}})}function Gy(r){if(!r.startsWith("hook_"))return null;let e=r.slice(5);for(let t of Vy)if(e.startsWith(`${t}_`))return{hookType:t,suiteId:e.slice(t.length+1)};return null}async function wo(r,e,t,s){let n=Gy(e);if(n){await zy(r,n.hookType,n.suiteId,t,s);return}let i=await qy(r,e);if(!i)throw new Error(`Case "${e}" not found in tests directory`);let a=await fe(i);if(a.steps.some(l=>l.id===t)){let l=a.steps.map(u=>u.id===t?{...u,generatedCode:s,lastHealed:new Date().toISOString()}:u);await We(i,{steps:l});return}let o=a.caseHooks?.before?.some(l=>l.id===t)??!1,c=a.caseHooks?.after?.some(l=>l.id===t)??!1;if(o||c){let l={before:(a.caseHooks?.before??[]).map(u=>u.id===t?{...u,generatedCode:s}:u),after:(a.caseHooks?.after??[]).map(u=>u.id===t?{...u,generatedCode:s}:u)};await We(i,{caseHooks:l});return}throw new Error(`Step "${t}" not found in case "${e}"`)}async function zy(r,e,t,s,n){let{findSuiteDirById:i}=await Promise.resolve().then(()=>(et(),Vn)),{readHooks:a,writeHooks:o}=await Promise.resolve().then(()=>(dn(),pd)),c=await i(r,t);if(!c)throw new Error(`Suite "${t}" not found for ${e} hook heal`);let l=await a(c),u=l[e];if(!u.some(d=>d.id===s))throw new Error(`Hook step "${s}" not found in ${e} of suite "${t}"`);l[e]=u.map(d=>d.id===s?{...d,generatedCode:n,lastHealed:new Date().toISOString()}:d),await o(c,l)}async function qy(r,e){let{default:t}=await import("fs-extra"),{listSuiteDirs:s}=await Promise.resolve().then(()=>(et(),Vn)),n=await s(r);for(let i of n){let a=await Pt(i,e);if(a)return a}return null}var $d,Es,Ad,Uy,Hy,Vy,Od=b(()=>{"use strict";g();$d=x(require("path")),Es=require("zod");di();Rt();de();j();Ad=I("routes/healing"),Uy=Es.z.object({reportPath:Es.z.string().min(1)}),Hy=Es.z.object({ids:Es.z.array(Es.z.string().min(1)).min(1)});Vy=["before_all","before_each","after_each","after_all"]});var R,pi,Ky,Wy,At,Ld,Xy,Jy,Yy,Qy,bo,Dd=b(()=>{"use strict";g();R=require("zod"),pi=R.z.enum(["pending","running","passed","failed","skipped"]),Ky=R.z.object({method:R.z.string(),url:R.z.string(),headers:R.z.record(R.z.string()).optional(),body:R.z.string().optional()}).optional(),Wy=R.z.object({status:R.z.number(),statusText:R.z.string(),headers:R.z.record(R.z.string()).optional(),body:R.z.string().optional(),duration:R.z.number()}).optional(),At=R.z.number().nullish().transform(r=>r??void 0),Ld=R.z.object({id:R.z.string(),title:R.z.string(),passed:R.z.boolean(),partial:R.z.boolean().optional(),na:R.z.boolean().optional()}),Xy=R.z.object({url:R.z.string(),stepIndex:R.z.number().int(),stepInstruction:R.z.string(),score:At,a11yScore:At,seoScore:At,fcp:At,lcp:At,cls:At,ttfb:At,tbt:At,si:At,tti:At,inp:At,a11yAudits:R.z.array(Ld).optional(),seoAudits:R.z.array(Ld).optional(),lighthouseError:R.z.string().optional()}),Jy=R.z.object({stepId:R.z.string(),instruction:R.z.string(),status:pi,code:R.z.string(),error:R.z.string().optional(),duration:R.z.number(),screenshotPath:R.z.string().optional(),healed:R.z.boolean().optional(),healedCode:R.z.string().optional(),stepType:R.z.enum(["ui","api","mock"]).optional(),apiRequest:Ky,apiResponse:Wy,navigatedToUrl:R.z.string().optional(),auditUrl:R.z.string().optional()}),Yy=R.z.object({caseId:R.z.string(),caseName:R.z.string(),status:pi,steps:R.z.array(Jy),duration:R.z.number(),browser:R.z.string(),device:R.z.string().optional(),startedAt:R.z.string().datetime(),finishedAt:R.z.string().datetime(),videoPath:R.z.string().optional(),tracePath:R.z.string().optional(),downloadPaths:R.z.array(R.z.string()).default([]),dataRowIndex:R.z.number().int().optional(),dataRow:R.z.record(R.z.string()).optional(),pageLoads:R.z.array(Xy).default([])}),Qy=R.z.object({suiteId:R.z.string(),suiteName:R.z.string(),suiteType:R.z.enum(["ui","api","audit","performance"]).optional(),status:pi,cases:R.z.array(Yy),duration:R.z.number(),browser:R.z.string(),startedAt:R.z.string().datetime(),finishedAt:R.z.string().datetime()}),bo=R.z.object({runId:R.z.string(),status:pi,environment:R.z.string().optional(),suites:R.z.array(Qy),duration:R.z.number(),startedAt:R.z.string().datetime(),finishedAt:R.z.string().datetime(),totalTests:R.z.number().int(),passed:R.z.number().int(),failed:R.z.number().int(),skipped:R.z.number().int(),healed:R.z.number().int().optional(),logFilePath:R.z.string().optional()})});function mi(r,e){return Lr.default.join(r,Fd,`${e}.json`)}async function Md(r,e){let t=bo.safeParse(e);if(!t.success){let n=t.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
|
|
690
|
+
`);throw new te(`Cannot write invalid run result:
|
|
691
|
+
${n}`,"result","INVALID_RESULT")}let s=mi(Lr.default.join(r,gn),e.runId);await re(s,t.data),mn.info({runId:e.runId,status:e.status},"Run result saved"),await Zy(r).catch(n=>{mn.warn({err:n},"Failed to prune old run results")})}async function Zy(r){let t=(await gi(r)).slice(Nd);if(t.length===0)return;let s=Lr.default.join(r,gn);for(let n of t)await Or.default.remove(mi(s,n)).catch(()=>{});mn.info({pruned:t.length,retained:Nd},"Pruned old run results")}async function rs(r,e){let t=mi(Lr.default.join(r,gn),e);if(!await Or.default.pathExists(t))throw new ye(`Run result not found for runId "${e}".`,t,"RESULT_NOT_FOUND");let s=await ve(t),n=bo.safeParse(s);if(!n.success){let i=n.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
|
|
692
|
+
`);throw new te(`Invalid run result at "${t}":
|
|
693
|
+
${i}`,"result","INVALID_RESULT")}return n.data}async function gi(r){let e=Lr.default.join(r,gn,Fd);if(!await Or.default.pathExists(e))return[];let s=(await Or.default.readdir(e,{withFileTypes:!0})).filter(i=>i.isFile()&&i.name.endsWith(".json")).map(i=>i.name.replace(".json",""));return(await Promise.all(s.map(async i=>{let a=await Or.default.stat(Lr.default.join(e,`${i}.json`));return{id:i,mtime:a.mtimeMs}}))).sort((i,a)=>a.mtime-i.mtime).map(i=>i.id)}async function fi(r,e=20){let t=await gi(r),s=[];for(let n of t.slice(0,e))try{let i=await rs(r,n);s.push({runId:i.runId,status:i.status,environment:i.environment,startedAt:i.startedAt,finishedAt:i.finishedAt,totalTests:i.totalTests,passed:i.passed,failed:i.failed,skipped:i.skipped,duration:i.duration,suiteIds:i.suites.map(a=>a.suiteId)})}catch(i){mn.warn({runId:n,err:i},"Failed to read run result \u2014 skipping")}return s}async function jd(r,e){let t=mi(Lr.default.join(r,gn),e);if(!await Or.default.pathExists(t))throw new ye(`Run result not found for runId "${e}".`,t,"RESULT_NOT_FOUND");await Or.default.remove(t),mn.info({runId:e},"Run result deleted")}var Lr,Or,mn,gn,Fd,Nd,fn=b(()=>{"use strict";g();Lr=x(require("path")),Or=x(require("fs-extra"));Dd();ge();j();ot();mn=I("result-store"),gn="results",Fd="runs",Nd=15});function Bd(r){let t=r.toLowerCase().replace(/\{\{[^}]+\}\}/g," __var__ ").replace(/"([^"]+)"/g,(n,i)=>` ${i} `).replace(/'([^']+)'/g,(n,i)=>` ${i} `).replace(/[^a-z0-9\s-]/g," ").trim().split(/\s+/).filter(n=>n.length>1&&!ew.has(n)),s=[...t];for(let n=0;n<t.length-1;n++)s.push(`${t[n]}_${t[n+1]}`);return s}function tw(r){return(0,Ud.createHash)("md5").update(r).digest().readUInt32LE(0)%vo}var Ud,yi,vo,ew,hi,Hd=b(()=>{"use strict";g();Ud=require("crypto"),yi=require("fs-extra"),vo=256,ew=new Set(["the","a","an","is","are","was","were","be","been","being","have","has","had","do","does","did","will","would","could","should","may","might","shall","can","need","dare","ought","to","of","in","for","on","with","at","by","from","as","into","through","during","before","after","above","below","between","out","off","over","under","again","further","then","once","that","this","these","those","and","but","or","nor","not","so","very","just","than","too","also","it","its"]);hi=class{constructor(e){this.vocabPath=e}vocabPath;vocab={docCount:0,termDocFreq:{}};dirty=!1;async load(){try{this.vocab=await(0,yi.readJson)(this.vocabPath)}catch{this.vocab={docCount:0,termDocFreq:{}}}}async save(){this.dirty&&(await(0,yi.outputJson)(this.vocabPath,this.vocab,{spaces:0}),this.dirty=!1)}addDocument(e){let t=new Set(Bd(e));this.vocab.docCount++;for(let s of t)this.vocab.termDocFreq[s]=(this.vocab.termDocFreq[s]??0)+1;this.dirty=!0}addDocuments(e){for(let t of e)this.addDocument(t)}embed(e){let t=Bd(e);if(t.length===0)return new Array(vo).fill(0);let s={};for(let o of t)s[o]=(s[o]??0)+1;let n=new Array(vo).fill(0),i=Math.max(this.vocab.docCount,1);for(let[o,c]of Object.entries(s)){let l=c/t.length,u=this.vocab.termDocFreq[o]??0,d=Math.log(1+i/(1+u)),m=l*d,p=tw(o);n[p]+=m}let a=Math.sqrt(n.reduce((o,c)=>o+c*c,0));if(a>0)for(let o=0;o<n.length;o++)n[o]/=a;return n}get documentCount(){return this.vocab.docCount}}});function Vd(r,e){if(r.length!==e.length||r.length===0)return 0;let t=0,s=0,n=0;for(let a=0;a<r.length;a++)t+=r[a]*e[a],s+=r[a]*r[a],n+=e[a]*e[a];let i=Math.sqrt(s)*Math.sqrt(n);return i===0?0:t/i}var Gd=b(()=>{"use strict";g()});var Cs,Is,zd=b(()=>{"use strict";g();Cs=require("fs-extra");Gd();Is=class{constructor(e,t=500){this.filePath=e;this.maxEntries=t}filePath;maxEntries;documents=[];dirty=!1;async load(){if(await(0,Cs.pathExists)(this.filePath))try{let e=await(0,Cs.readJson)(this.filePath);this.documents=e.documents??[]}catch{this.documents=[]}}async persist(){if(!this.dirty)return;let e={version:1,documents:this.documents};await(0,Cs.outputJson)(this.filePath,e,{spaces:0}),this.dirty=!1}add(e,t,s,n){this.documents=this.documents.filter(a=>a.id!==e);let i=Date.now();this.documents.push({id:e,text:t,vector:s,metadata:n,createdAt:i,updatedAt:i}),this.dirty=!0,this.documents.length>this.maxEntries&&this.prune()}update(e,t){let s=this.documents.find(n=>n.id===e);return s?(s.metadata={...s.metadata,...t},s.updatedAt=Date.now(),this.dirty=!0,!0):!1}remove(e){let t=this.documents.length;return this.documents=this.documents.filter(s=>s.id!==e),this.documents.length!==t?(this.dirty=!0,!0):!1}get(e){return this.documents.find(t=>t.id===e)}has(e){return this.documents.some(t=>t.id===e)}get size(){return this.documents.length}search(e,t=5,s=.5){let n=[];for(let i of this.documents){let a=Vd(e,i.vector);a>=s&&n.push({id:i.id,text:i.text,metadata:i.metadata,score:a})}return n.sort((i,a)=>{if(Math.abs(i.score-a.score)>.001)return a.score-i.score;let o=this.documents.find(l=>l.id===i.id);return this.documents.find(l=>l.id===a.id).updatedAt-o.updatedAt}),n.slice(0,t)}prune(){let e=[...this.documents].sort((s,n)=>s.updatedAt-n.updatedAt),t=e.length-this.maxEntries;if(t>0){let s=new Set(e.slice(0,t).map(n=>n.id));this.documents=this.documents.filter(n=>!s.has(n.id))}}}});function sw(r,e){let t=`${r.toLowerCase().trim()}|${xo(e)}`;return(0,qd.createHash)("sha256").update(t).digest("hex").slice(0,16)}function xo(r){if(!r)return"";try{let e=new URL(r),t=e.pathname.replace(/\/\d+/g,"/*");return`${e.hostname}${t}`}catch{return r}}function nw(r,e,t){let s=e+t;if(s===0)return r;let n=e/s;return r*(1+.05*n)}function iw(r){return{id:r.id,instruction:r.metadata.instruction,code:r.metadata.code,urlPattern:r.metadata.urlPattern,score:r.score,successCount:r.metadata.successCount,failCount:r.metadata.failCount}}var qd,rw,wi,Kd=b(()=>{"use strict";g();qd=require("crypto"),rw=2,wi=class{constructor(e,t){this.store=e;this.embedder=t}store;embedder;ingest(e,t,s){let n=sw(e,s),i=xo(s),a=`${e} ${i}`,o=this.store.get(n);if(o){this.store.update(n,{...o.metadata,code:t,successCount:o.metadata.successCount+1,consecutiveFails:0});return}this.embedder.addDocument(a);let c=this.embedder.embed(a);this.store.add(n,a,c,{instruction:e,code:t,urlPattern:i,successCount:1,failCount:0,consecutiveFails:0})}retrieve(e,t="",s=3,n=.65){if(this.store.size===0)return[];let i=xo(t),a=`${e} ${i}`,o=this.embedder.embed(a),l=this.store.search(o,s*2,n).map(u=>({...u,score:nw(u.score,u.metadata.successCount,u.metadata.failCount)}));return l.sort((u,d)=>d.score-u.score),l.slice(0,s).map(iw)}recordOutcome(e,t){let s=this.store.get(e);if(s)if(t)this.store.update(e,{...s.metadata,successCount:s.metadata.successCount+1,consecutiveFails:0});else{let n=s.metadata.consecutiveFails+1;if(n>=rw){this.store.remove(e);return}this.store.update(e,{...s.metadata,failCount:s.metadata.failCount+1,consecutiveFails:n})}}get size(){return this.store.size}}});function aw(r,e){let t=`${r.toLowerCase().trim()}|${bi(e)}`;return(0,Wd.createHash)("sha256").update(t).digest("hex").slice(0,16)}function bi(r){return r.replace(/\d+ms/g,"Nms").replace(/\d+/g,"N").replace(/["'][^"']*["']/g,"'...'").replace(/\s+/g," ").trim().slice(0,200)}function ow(r){if(!r)return"";try{let e=new URL(r);return`${e.hostname}${e.pathname.replace(/\/\d+/g,"/*")}`}catch{return r}}function cw(r){return{id:r.id,instruction:r.metadata.instruction,error:r.metadata.error,failedCode:r.metadata.failedCode,healedCode:r.metadata.healedCode,strategy:r.metadata.strategy,level:r.metadata.level,score:r.score,successCount:r.metadata.successCount}}var Wd,vi,Xd=b(()=>{"use strict";g();Wd=require("crypto"),vi=class{constructor(e,t){this.store=e;this.embedder=t}store;embedder;ingest(e){let t=aw(e.instruction,e.error),s=ow(e.url),n=`${e.instruction} ${bi(e.error)}`,i=this.store.get(t);if(i){this.store.update(t,{...i.metadata,healedCode:e.healedCode,strategy:e.strategy,level:e.level,successCount:i.metadata.successCount+1});return}this.embedder.addDocument(n);let a=this.embedder.embed(n);this.store.add(t,n,a,{instruction:e.instruction,error:bi(e.error),failedCode:e.failedCode,healedCode:e.healedCode,strategy:e.strategy,level:e.level,urlPattern:s,successCount:1})}retrieve(e,t,s=3,n=.6){if(this.store.size===0)return[];let i=`${e} ${bi(t)}`,a=this.embedder.embed(i);return this.store.search(a,s,n).map(cw)}get size(){return this.store.size}}});function lw(r,e){let t=`${r}|${e}`;return(0,Yd.createHash)("sha256").update(t).digest("hex").slice(0,16)}function uw(r){return r.replace(/\d+ms/g,"Nms").replace(/\d+/g,"N").replace(/["'][^"']*["']/g,"'...'").replace(/\s+/g," ").trim().slice(0,200)}function Jd(r){if(!r)return"";try{let e=new URL(r);return`${e.hostname}${e.pathname.replace(/\/\d+/g,"/*")}`}catch{return r}}function dw(r){let e=r.metadata.commonFixes;return{errorPattern:r.metadata.errorPattern,urlPattern:r.metadata.urlPattern,frequency:r.metadata.frequency,suggestedFix:e.length>0?e[e.length-1]:null,score:r.score}}var Yd,xi,Qd=b(()=>{"use strict";g();Yd=require("crypto"),xi=class{constructor(e,t){this.store=e;this.embedder=t}store;embedder;record(e,t,s){let n=uw(e),i=Jd(t),a=lw(n,i),o=`${n} ${i}`,c=this.store.get(a);if(c){let u=[...c.metadata.commonFixes];s&&!u.includes(s)&&(u.push(s),u.length>5&&u.shift()),this.store.update(a,{...c.metadata,frequency:c.metadata.frequency+1,lastSeen:Date.now(),commonFixes:u});return}this.embedder.addDocument(o);let l=this.embedder.embed(o);this.store.add(a,o,l,{errorPattern:n,urlPattern:i,frequency:1,lastSeen:Date.now(),commonFixes:s?[s]:[]})}getWarnings(e,t=5){if(this.store.size===0)return[];let s=Jd(e),n=this.embedder.embed(s),a=this.store.search(n,t*2,.3).filter(o=>o.metadata.frequency>=2);return a.sort((o,c)=>c.metadata.frequency-o.metadata.frequency),a.slice(0,t).map(dw)}get size(){return this.store.size}}});var Ps,Zd,So,Si,ep=b(()=>{"use strict";g();Ps=require("path"),Zd=require("fs-extra");Hd();zd();Kd();Xd();Qd();j();So=I("rag"),Si=class r{codeCorpus;healingCorpus;errorCatalog;embedder;codeStore;healingStore;errorStore;constructor(e,t,s,n){this.embedder=e,this.codeStore=t,this.healingStore=s,this.errorStore=n,this.codeCorpus=new wi(t,e),this.healingCorpus=new vi(s,e),this.errorCatalog=new xi(n,e)}static async create(e,t){let s=(0,Ps.join)(e,"results",".rag");await(0,Zd.ensureDir)(s),So.info({ragDir:s},"Initializing RAG manager");let n=new hi((0,Ps.join)(s,"idf-vocab.json"));await n.load();let i=new Is((0,Ps.join)(s,"code-corpus.json"),t.codeCorpus.maxEntries),a=new Is((0,Ps.join)(s,"healing-corpus.json"),t.healingCorpus.maxEntries),o=new Is((0,Ps.join)(s,"error-catalog.json"),t.errorCatalog.maxEntries);return await Promise.all([i.load(),a.load(),o.load()]),So.info({codeCorpus:i.size,healingCorpus:a.size,errorCatalog:o.size,idfDocuments:n.documentCount},"RAG corpora loaded"),new r(n,i,a,o)}async persist(){await Promise.all([this.embedder.save(),this.codeStore.persist(),this.healingStore.persist(),this.errorStore.persist()]),So.debug("RAG corpora persisted")}getStats(){return{codeCorpus:this.codeStore.size,healingCorpus:this.healingStore.size,errorCatalog:this.errorStore.size,idfVocab:this.embedder.documentCount}}}});function mw(r){let e=[];for(let t of pw){t.regex.lastIndex=0;let s;for(;(s=t.regex.exec(r))!==null;)t.type==="getByRole"?e.push({selector:s[0],type:t.type,role:s[1],name:s[2]}):e.push({selector:s[0],type:t.type,name:s[1]})}return e}function gw(r,e,t){let s=r.toLowerCase();for(let n of e){let i=n.match(/"([^"]+)"/);if(i&&i[1].toLowerCase()===s&&(!t||n.toLowerCase().startsWith(t.toLowerCase())))return n}for(let n of e){let i=n.match(/"([^"]+)"/);if(i){let a=i[1].toLowerCase();if((a.includes(s)||s.includes(a))&&(!t||n.toLowerCase().startsWith(t.toLowerCase())))return n}}if(t){let n=e.filter(i=>i.toLowerCase().startsWith(t.toLowerCase()));if(n.length>0){let i=0,a;for(let o of n){let c=o.match(/"([^"]+)"/);if(c){let l=fw(s,c[1].toLowerCase());l>i&&(i=l,a=o)}}if(i>.3)return a}}}function fw(r,e){if(r===e)return 1;let t=r.length>e.length?r:e,s=r.length>e.length?e:r;if(t.length===0)return 1;let n=0;for(let i=0;i<s.length;i++)t.includes(s[i])&&n++;return n/t.length}async function rp(r,e,t){let s=mw(t);if(s.length===0)return{url:e,ok:!0,total:0,matched:0,missing:0,checks:[]};await r.navigate(e);let n=await r.snapshot();if(!n)return tp.warn({url:e},"Proactive check: snapshot returned null \u2014 skipping validation"),{url:e,ok:!0,total:s.length,matched:0,missing:0,checks:[]};let a=ei(n).interactiveElements.split(`
|
|
694
|
+
`).filter(Boolean),o=[];for(let u of s){let d=u.name??"",m=!1;for(let f of a){let h=f.match(/"([^"]+)"/);if(h&&h[1].toLowerCase()===d.toLowerCase())if(u.role){if(f.toLowerCase().startsWith(u.role.toLowerCase())){m=!0;break}}else{m=!0;break}}let p={selector:u.selector,type:u.type,found:m};if(!m){let f=gw(d,a,u.role);f&&(p.suggestion=f)}o.push(p)}let c=o.filter(u=>u.found).length,l=o.filter(u=>!u.found).length;return tp.info({url:e,total:o.length,matched:c,missing:l},"Proactive selector check complete"),{url:e,ok:l===0,total:o.length,matched:c,missing:l,checks:o}}var tp,pw,sp=b(()=>{"use strict";g();Ka();j();tp=I("proactive-checker"),pw=[{regex:/getByRole\(\s*['"](\w+)['"]\s*,\s*\{[^}]*name:\s*['"]([^'"]+)['"]/g,type:"getByRole"},{regex:/getByLabel\(\s*['"]([^'"]+)['"]/g,type:"getByLabel"},{regex:/getByPlaceholder\(\s*['"]([^'"]+)['"]/g,type:"getByPlaceholder"},{regex:/getByText\(\s*['"]([^'"]+)['"]/g,type:"getByText"},{regex:/getByTestId\(\s*['"]([^'"]+)['"]/g,type:"getByTestId"}]});async function hw(r){if(!await Ei.default.pathExists(r))return r;let e=Dr.default.dirname(r),t=Dr.default.extname(r),s=Dr.default.basename(r,t);for(let n=1;n<1e3;n++){let i=Dr.default.join(e,`${s} (${n})${t}`);if(!await Ei.default.pathExists(i))return i}return r}var Dr,Ei,$t,lr,Ii,np=b(()=>{"use strict";g();Dr=x(require("path")),Ei=x(require("fs-extra")),$t=require("playwright");j();lr=I("browser-manager");Ii=class{browsers=new Map;pendingDownloads=new WeakMap;capturedDownloads=new WeakMap;async launchBrowser(e,t){let s=this.browsers.get(e);if(s)return s;let i=await(e==="firefox"?$t.firefox:e==="webkit"?$t.webkit:$t.chromium).launch({headless:t.headless});return this.browsers.set(e,i),lr.info({browser:e,headless:t.headless},"Browser launched"),i}async newContext(e,t,s,n,i){let a=t.device&&$t.devices[t.device]?$t.devices[t.device]:null;t.device&&!a&&lr.warn({device:t.device},"Unknown Playwright device \u2014 falling back to configured viewport");let o={...a??{},viewport:a?.viewport??t.viewport??{width:1280,height:720},storageState:i??void 0,acceptDownloads:!0};t.video!=="off"&&s&&(o.recordVideo={dir:s});let c=await e.newContext(o);return n&&(this.pendingDownloads.set(c,[]),this.capturedDownloads.set(c,[]),c.on("download",l=>{let u=(async()=>{try{await Ei.default.ensureDir(n);let d=l.suggestedFilename()||"download",m=Dr.default.basename(d).replace(/[^a-zA-Z0-9._-]+/g,"_")||"download",p=await hw(Dr.default.join(n,m));await l.saveAs(p),this.capturedDownloads.get(c)?.push(p),lr.info({file:Dr.default.basename(p)},"Download captured")}catch(d){lr.debug({err:d},"Failed to capture download")}})();this.pendingDownloads.get(c)?.push(u)})),await c.addInitScript(()=>{let l=`
|
|
695
695
|
outline: 3px solid #3b82f6 !important;
|
|
696
696
|
outline-offset: 3px !important;
|
|
697
697
|
box-shadow: 0 0 0 6px rgba(59,130,246,0.20) !important;
|
|
698
698
|
transition: outline 0.08s ease, box-shadow 0.08s ease !important;
|
|
699
|
-
`,
|
|
700
|
-
<div style="flex:1;display:flex;flex-direction:column;align-items:center;padding:10px 8px;border-radius:10px;background:${
|
|
699
|
+
`,u=null;function d(){u&&(u.style.cssText=u.style.cssText.replace(/outline[^;]*;?/g,"").replace(/box-shadow[^;]*;?/g,"").replace(/transition[^;]*;?/g,""),u=null)}function m(p){!p||!(p instanceof HTMLElement)||(d(),u=p,u.style.cssText+=l,setTimeout(d,1800))}document.addEventListener("focus",p=>m(p.target),!0),document.addEventListener("mousedown",p=>m(p.target),!0)}),t.trace!=="off"&&await c.tracing.start({screenshots:!0,snapshots:!0,sources:!1}),c}async newPage(e){let t=await e.newPage(),s=[/Failed to load resource/i,/net::ERR_/i,/the server responded with a status of/i,/Access to .* has been blocked by CORS/i,/Mixed Content:/i,/favicon\.ico/i],n=[];return t.on("console",i=>{let a=i.text();s.some(o=>o.test(a))||n.push({type:i.type(),text:a,timestamp:Date.now()})}),t.on("pageerror",i=>{i.message?.includes("appendChild")&&i.message?.includes("null")||n.push({type:"error",text:`[PageError] ${i.message}`,timestamp:Date.now()})}),t.__consoleLogs=n,t}drainConsoleLogs(e){let t=e.__consoleLogs;if(!t)return[];let s=[...t];return t.length=0,s}apiContexts=[];async createApiOnlyPage(e){let t=await $t.request.newContext({baseURL:e||void 0});this.apiContexts.push(t);let s={request:t,url:()=>e??"",title:async()=>"",goto:async()=>null,waitForLoadState:async()=>{},setDefaultTimeout:()=>{},setDefaultNavigationTimeout:()=>{},screenshot:async()=>Buffer.alloc(0),video:()=>null,close:async()=>{}};return lr.info("Created API-only request context (zero browser)"),s}async closeApiContexts(){for(let e of this.apiContexts)await e.dispose().catch(()=>{});this.apiContexts=[]}async closeContext(e,t,s){let n=this.pendingDownloads.get(e);if(n&&n.length>0){let i=new Promise(a=>setTimeout(a,15e3));await Promise.race([Promise.allSettled(n),i]),this.pendingDownloads.delete(e)}if(t.trace!=="off")try{s?await e.tracing.stop({path:s}):await e.tracing.stop()}catch(i){lr.debug({err:i},"Tracing stop failed \u2014 ignoring")}await e.close().catch(i=>{lr.debug({err:i},"Context close failed \u2014 ignoring")})}takeDownloads(e){let t=this.capturedDownloads.get(e)??[];return this.capturedDownloads.delete(e),[...t]}async closeAll(){await this.closeApiContexts();for(let[e,t]of this.browsers)try{await t.close(),lr.debug({browser:e},"Browser closed")}catch(s){lr.warn({err:s,browser:e},"Error closing browser")}this.browsers.clear()}get openBrowserCount(){return this.browsers.size}}});var ip,yw,Ci,ap=b(()=>{"use strict";g();j();ip=I("healing-budget"),yw=[/timeout/i,/ECONNREFUSED/i,/ENOTFOUND/i,/ETIMEDOUT/i,/net::ERR/i,/ERR_NAME_NOT_RESOLVED/i,/DNS/i,/socket hang up/i],Ci=class{constructor(e){this.dailyBudget=e}dailyBudget;spent=0;healFailures=new Map;pageFailures=new Map;shouldHeal(e){if(this.spent>=this.dailyBudget)return ip.warn({spent:this.spent,budget:this.dailyBudget},"Daily healing budget exhausted"),{heal:!1,reason:"Daily budget exhausted"};let t=this.healFailures.get(e.stepId)??0;if(t>=3)return{heal:!1,reason:`Persistent failure \u2014 step "${e.stepId}" has failed healing ${t} times. Needs manual fix.`};if(this.isEnvironmental(e.error))return{heal:!1,reason:"Environmental error \u2014 retry instead"};let s=this.pageFailures.get(e.pageUrl)??[];return s.some(n=>n.stepId===e.stepId)||(s.push(e),this.pageFailures.set(e.pageUrl,s)),s.length>=3?{heal:!0,strategy:"batch",reason:`Batch heal \u2014 ${s.length} failures on ${e.pageUrl}, fix once for all`}:{heal:!0,strategy:"individual",reason:"Standard heal"}}recordSpend(e){this.spent+=e,ip.debug({spend:e,total:this.spent},"Healing budget spend recorded")}recordHealFailure(e){this.healFailures.set(e,(this.healFailures.get(e)??0)+1)}get totalSpent(){return this.spent}get remainingBudget(){return Math.max(0,this.dailyBudget-this.spent)}isEnvironmental(e){return yw.some(t=>t.test(e))}}});var op,Pi,Ri,cp=b(()=>{"use strict";g();op=require("crypto");di();j();Pi=I("healing-report"),Ri=class{events=[];record(e){let t={id:(0,op.randomUUID)(),runId:e.runId,suiteId:e.suiteId,caseId:e.caseId,stepId:e.stepId,stepInstruction:e.stepInstruction,failedCode:e.failedCode,healedCode:e.healedCode,error:e.error,strategy:e.strategy,level:e.level,status:"pending",pageUrl:e.pageUrl,timestamp:new Date().toISOString()};this.events.push(t),Pi.info({stepId:e.stepId,strategy:e.strategy,level:e.level},"Healing event recorded")}get eventCount(){return this.events.length}async flush(e,t){if(this.events.length===0)return;let s={runId:t,generatedAt:new Date().toISOString(),totalHeals:this.events.length,accepted:0,rejected:0,pending:this.events.length,events:[...this.events]};try{await Ed(e,s),Pi.info({runId:t,count:this.events.length},"Healing report written")}catch(n){Pi.error({err:n,runId:t},"Failed to write healing report")}for(let n of this.events)try{await Id(e,n)}catch(i){Pi.warn({err:i,eventId:n.id},"Failed to append pending healing event")}}}});function ww(r){let e=r.slice(r.lastIndexOf(".")).toLowerCase();return{".pdf":"application/pdf",".csv":"text/csv",".txt":"text/plain",".json":"application/json",".xml":"application/xml",".html":"text/html",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".svg":"image/svg+xml",".zip":"application/zip",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".xls":"application/vnd.ms-excel",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document"}[e]??"application/octet-stream"}function lp(r){switch(r){case"passed":return"passed";case"failed":return"failed";case"skipped":return"skipped";default:return"broken"}}function bw(r){return r==null?"\u2014":r<1e3?`${Math.round(r)}ms`:`${(r/1e3).toFixed(2)}s`}function up(r,e){if(e==null)return"na";let t={fcp:[1800,3e3],lcp:[2500,4e3],cls:[.1,.25],ttfb:[800,1800],tbt:[200,600],si:[3400,5800],tti:[3800,7300],inp:[200,500]},[s,n]=t[r];return e<s?"good":e<n?"average":"poor"}function vw(r){return r==null?"#94a3b8":r>=90?"#10b981":r>=50?"#f59e0b":"#ef4444"}function Io(r,e,t){let s={good:"#10b981",average:"#f59e0b",poor:"#ef4444",na:"#94a3b8"},n=[{key:"fcp",abbr:"FCP",label:"First Contentful Paint"},{key:"lcp",abbr:"LCP",label:"Largest Contentful Paint"},{key:"cls",abbr:"CLS",label:"Cumulative Layout Shift"},{key:"ttfb",abbr:"TTFB",label:"Time to First Byte"},{key:"tbt",abbr:"TBT",label:"Total Blocking Time"},{key:"si",abbr:"SI",label:"Speed Index"},{key:"tti",abbr:"TTI",label:"Time to Interactive"},{key:"inp",abbr:"INP",label:"Interaction to Next Paint"}],i=(u,d)=>u==="cls"&&d!=null?d.toFixed(3):bw(d),a=(u,d)=>{let m=vw(u),p=2*Math.PI*24,f=u!=null?u/100*p:0,h=u==null?"\u2014":u>=90?"Good":u>=50?"Needs Work":"Poor";return`
|
|
700
|
+
<div style="flex:1;display:flex;flex-direction:column;align-items:center;padding:10px 8px;border-radius:10px;background:${m}12;border:2px solid ${m}44;">
|
|
701
701
|
<div style="position:relative;width:58px;height:58px;">
|
|
702
702
|
<svg width="58" height="58" viewBox="0 0 60 60" style="transform:rotate(-90deg)">
|
|
703
|
-
<circle cx="30" cy="30" r="24" fill="none" stroke="${
|
|
704
|
-
<circle cx="30" cy="30" r="24" fill="none" stroke="${
|
|
705
|
-
stroke-dasharray="${
|
|
703
|
+
<circle cx="30" cy="30" r="24" fill="none" stroke="${m}" stroke-width="5" stroke-opacity=".15"/>
|
|
704
|
+
<circle cx="30" cy="30" r="24" fill="none" stroke="${m}" stroke-width="5"
|
|
705
|
+
stroke-dasharray="${p.toFixed(2)}" stroke-dashoffset="${(p-f).toFixed(2)}" stroke-linecap="round"/>
|
|
706
706
|
</svg>
|
|
707
|
-
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:900;color:${
|
|
707
|
+
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:900;color:${m};">${u??"\u2014"}</div>
|
|
708
708
|
</div>
|
|
709
|
-
<div style="font-size:9px;font-weight:700;color:${
|
|
709
|
+
<div style="font-size:9px;font-weight:700;color:${m};margin-top:4px;">${h}</div>
|
|
710
710
|
<div style="font-size:9px;color:#64748b;margin-top:1px;text-align:center;">${d}</div>
|
|
711
|
-
</div>`},o=e.map(u=>{let d=u.lighthouseError?`<div style="padding:12px 16px;background:#fef3c7;color:#92400e;font-size:12px;">\u26A0 ${u.lighthouseError}</div>`:"",
|
|
712
|
-
<div style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:${
|
|
713
|
-
<div style="font-size:14px;font-weight:800;color:${
|
|
714
|
-
</div>`}).join(""),
|
|
711
|
+
</div>`},o=e.map(u=>{let d=u.lighthouseError?`<div style="padding:12px 16px;background:#fef3c7;color:#92400e;font-size:12px;">\u26A0 ${u.lighthouseError}</div>`:"",m=u.lighthouseError?"":n.map(({key:v,abbr:k})=>{let T=up(v,u[v]),P=T==="good"?"#ecfdf5":T==="average"?"#fffbeb":T==="poor"?"#fef2f2":"#f8fafc",G=s[T];return`<div style="flex:1;min-width:90px;background:${P};border:1px solid ${G}33;border-radius:8px;padding:8px 10px;text-align:center;">
|
|
712
|
+
<div style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:${G};opacity:.8;margin-bottom:3px;">${k}</div>
|
|
713
|
+
<div style="font-size:14px;font-weight:800;color:${G};font-variant-numeric:tabular-nums;">${i(v,u[v])}</div>
|
|
714
|
+
</div>`}).join(""),p=u.lighthouseError?"":`
|
|
715
715
|
<div style="border-top:1px solid #f1f5f9;padding:10px 16px;display:grid;grid-template-columns:1fr 1fr;gap:3px 24px;">
|
|
716
|
-
${n.map(({key:v,abbr:k,label:T})=>{let P=
|
|
716
|
+
${n.map(({key:v,abbr:k,label:T})=>{let P=up(v,u[v]);return`<div style="display:flex;align-items:center;gap:5px;font-size:9px;color:#64748b;padding:1px 0;">
|
|
717
717
|
<span style="color:${P==="good"?"#10b981":P==="average"?"#f59e0b":P==="poor"?"#ef4444":"#94a3b8"};font-size:11px;line-height:1;flex-shrink:0;">\u25CF</span>
|
|
718
718
|
<span style="font-weight:700;color:#475569;">${k}</span>
|
|
719
719
|
<span style="opacity:.7;">${T}</span>
|
|
720
720
|
</div>`}).join("")}
|
|
721
|
-
</div>`,f=(v,k)=>{if(!v||v.length===0)return"";let T=v.map(P=>{let
|
|
722
|
-
<span style="font-weight:700;flex-shrink:0;">${
|
|
721
|
+
</div>`,f=(v,k)=>{if(!v||v.length===0)return"";let T=v.map(P=>{let G=P.na?"#94a3b8":P.passed?"#10b981":"#ef4444",ne=P.na?"\u2013":P.passed?"\u2713":"\u2717";return`<div style="display:flex;align-items:center;gap:5px;font-size:9px;color:${G};padding:1px 0;">
|
|
722
|
+
<span style="font-weight:700;flex-shrink:0;">${ne}</span>
|
|
723
723
|
<span style="color:#475569;">${P.title}</span>
|
|
724
724
|
</div>`}).join("");return`<div>
|
|
725
725
|
<div style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#94a3b8;margin-bottom:4px;">${k}</div>
|
|
@@ -728,13 +728,13 @@ ${i}`,"result","INVALID_RESULT")}return n.data}async function no(r){let e=Hr.def
|
|
|
728
728
|
<div style="padding:16px;">
|
|
729
729
|
<div style="display:flex;gap:10px;margin-bottom:14px;">${a(u.score,"Performance")}</div>
|
|
730
730
|
<div style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#94a3b8;margin-bottom:8px;">Core Web Vitals</div>
|
|
731
|
-
<div style="display:flex;flex-wrap:wrap;gap:6px;">${
|
|
731
|
+
<div style="display:flex;flex-wrap:wrap;gap:6px;">${m}</div>
|
|
732
732
|
</div>
|
|
733
|
-
${
|
|
733
|
+
${p}`:"",S=t==="accessibility"&&!u.lighthouseError?`
|
|
734
734
|
<div style="padding:16px;">
|
|
735
735
|
<div style="display:flex;gap:10px;margin-bottom:14px;">${a(u.a11yScore,"Accessibility")}</div>
|
|
736
736
|
${f(u.a11yAudits,"Accessibility Audits")}
|
|
737
|
-
</div>`:"",
|
|
737
|
+
</div>`:"",C=t==="seo"&&!u.lighthouseError?`
|
|
738
738
|
<div style="padding:16px;">
|
|
739
739
|
<div style="display:flex;gap:10px;margin-bottom:14px;">${a(u.seoScore,"SEO")}</div>
|
|
740
740
|
${f(u.seoAudits,"SEO Audits")}
|
|
@@ -746,7 +746,7 @@ ${i}`,"result","INVALID_RESULT")}return n.data}async function no(r){let e=Hr.def
|
|
|
746
746
|
<div style="font-size:11px;font-family:monospace;color:#6366f1;font-weight:600;">${u.url}</div>
|
|
747
747
|
</div>
|
|
748
748
|
${d}
|
|
749
|
-
${h}${S}${
|
|
749
|
+
${h}${S}${C}
|
|
750
750
|
</div>`}).join(""),c=new Date().toLocaleString(),l={performance:{icon:"\u26A1",color:"#7c3aed",title:"Performance Report",subtitle:"Core Web Vitals \xB7 Speed Metrics"},accessibility:{icon:"\u267F",color:"#2563eb",title:"Accessibility Report",subtitle:"WCAG Audit Checks"},seo:{icon:"\u{1F50D}",color:"#059669",title:"SEO Report",subtitle:"Search Engine Optimisation Checks"}}[t];return`<!DOCTYPE html>
|
|
751
751
|
<html lang="en">
|
|
752
752
|
<head>
|
|
@@ -786,17 +786,17 @@ ${i}`,"result","INVALID_RESULT")}return n.data}async function no(r){let e=Hr.def
|
|
|
786
786
|
<p style="font-size:9px;color:#cbd5e1;text-align:center;margin-top:16px;">Powered by Google Lighthouse \xB7 Desktop preset \xB7 Assuremind Studio</p>
|
|
787
787
|
</div>
|
|
788
788
|
</body>
|
|
789
|
-
</html>`}var
|
|
790
|
-
`);await br.default.writeFile(Me.default.join(this.resultsDir,"environment.properties"),t,"utf-8");let s;if(e.logFilePath)try{s=Me.default.basename(e.logFilePath),await this.copyAttachment(e.logFilePath).catch(()=>{}),co.debug({logFile:s},"Log file copied to Allure results")}catch{co.debug("Failed to copy log file to Allure results")}let n=0;for(let i of e.suites)for(let a of i.cases)await this.writeTestResult(i,a,s),n++;co.info({dir:this.resultsDir,written:n},"Allure results written")}async writeTestResult(e,t,s){let n=(0,ep.randomUUID)(),i=new Date(t.startedAt).getTime(),a=new Date(t.finishedAt).getTime(),o=t.steps.find(p=>p.status==="failed"),c=[];if(t.videoPath){let p=Me.default.basename(t.videoPath),f=Me.default.extname(p).toLowerCase()===".mp4"?"video/mp4":"video/webm";c.push({name:"Video Recording",source:p,type:f})}if(t.tracePath){let p=Me.default.basename(t.tracePath);c.push({name:"Playwright Trace",source:p,type:"application/zip"})}s&&c.push({name:"Run Log",source:s,type:"text/plain"});for(let p of t.downloadPaths??[]){let g=Me.default.basename(p);c.push({name:`Downloaded: ${g}`,source:g,type:nw(g)})}let l=t.pageLoads??[];if(l.length>0){let p=l.some(h=>!h.lighthouseError&&h.score!=null),g=l.some(h=>!h.lighthouseError&&(h.a11yScore!=null||(h.a11yAudits?.length??0)>0)),f=l.some(h=>!h.lighthouseError&&(h.seoScore!=null||(h.seoAudits?.length??0)>0));if(p){let h=lo(t.caseName,l,"performance"),S=`${n}-perf.html`;await br.default.writeFile(Me.default.join(this.resultsDir,S),h,"utf-8"),c.push({name:"\u26A1 Performance Report",source:S,type:"text/html"})}if(g){let h=lo(t.caseName,l,"accessibility"),S=`${n}-a11y.html`;await br.default.writeFile(Me.default.join(this.resultsDir,S),h,"utf-8"),c.push({name:"\u267F Accessibility Report",source:S,type:"text/html"})}if(f){let h=lo(t.caseName,l,"seo"),S=`${n}-seo.html`;await br.default.writeFile(Me.default.join(this.resultsDir,S),h,"utf-8"),c.push({name:"\u{1F50D} SEO Report",source:S,type:"text/html"})}}let u={uuid:n,historyId:`${e.suiteId}::${t.caseId}::${e.browser}`,testCaseId:`${e.suiteId}::${t.caseId}`,fullName:`${e.suiteName} > ${t.caseName}`,name:t.caseName,status:Qd(t.status),stage:"finished",start:i,stop:a,labels:[{name:"suite",value:e.suiteName},{name:"browser",value:e.browser},{name:"framework",value:"assuremind"},{name:"tag",value:`ENV: ${this.environment.toUpperCase()}`}],links:[],parameters:[],attachments:c,steps:t.steps.map(p=>this.buildStepResult(p,i)),...o?.error&&{statusDetails:{message:o.error}}},d=Me.default.join(this.resultsDir,`${n}-result.json`);await br.default.writeJson(d,u,{spaces:2});for(let p of t.steps)p.screenshotPath&&await this.copyAttachment(p.screenshotPath).catch(()=>{});t.videoPath&&await this.copyAttachment(t.videoPath).catch(()=>{}),t.tracePath&&await this.copyAttachment(t.tracePath).catch(()=>{});for(let p of t.downloadPaths??[])await this.copyAttachment(p).catch(()=>{})}buildStepResult(e,t){let s=t,n=t+e.duration,i=[];if(e.screenshotPath){let a=Me.default.basename(e.screenshotPath);i.push({name:"Screenshot",source:a,type:"image/png"})}return{name:e.instruction,status:Qd(e.status),stage:"finished",start:s,stop:n,attachments:i,parameters:[],steps:[]}}async copyAttachment(e){let t=Me.default.join(this.resultsDir,Me.default.basename(e));await br.default.copy(e,t,{overwrite:!0})}}});function cw(){try{let e=(0,np.createRequire)(y??__filename).resolve("allure-commandline/package.json"),t=ms.default.dirname(e);return ms.default.join(t,"dist","bin",po?"allure.bat":"allure")}catch{return ms.default.resolve(__dirname,po?"../node_modules/allure-commandline/dist/bin/allure.bat":"../node_modules/.bin/allure")}}async function ip(r,e){let t=ms.default.join(r,"results","allure-results",e),s=ms.default.join(r,"results","allure-report",e);if(!await uo.default.pathExists(t))return sn.debug({resultsDir:t},"No allure-results dir for run \u2014 skipping report generation"),!1;if((await uo.default.readdir(t)).length===0)return sn.debug({runId:e},"allure-results is empty \u2014 skipping report generation"),!1;try{return await ow(lw,["generate",t,"--output",s,"--clean","--single-file"],po?{shell:!0}:{}),sn.info({reportDir:s,runId:e},"Allure HTML report generated"),!0}catch(i){let a=i instanceof Error?i.message:String(i);return a.includes("java")||a.includes("Java")||a.includes("JAVA")?sn.warn("Allure report generation skipped \u2014 Java is not installed. Install Java (https://adoptium.net) to enable Allure HTML reports."):sn.warn({err:a,runId:e},"Allure report generation failed"),!1}}var ms,uo,rp,sp,np,ow,sn,po,lw,ap=b(()=>{"use strict";m();ms=x(require("path")),uo=x(require("fs-extra")),rp=require("child_process"),sp=require("util"),np=require("module");F();ow=(0,sp.promisify)(rp.execFile),sn=C("allure-generator"),po=process.platform==="win32";lw=cw()});function gi(){return[{category:"person",methods:[{name:"fullName",example:E.faker.person.fullName()},{name:"firstName",example:E.faker.person.firstName()},{name:"lastName",example:E.faker.person.lastName()},{name:"middleName",example:E.faker.person.middleName()},{name:"sex",example:E.faker.person.sex()},{name:"prefix",example:E.faker.person.prefix()},{name:"suffix",example:E.faker.person.suffix()},{name:"jobTitle",example:E.faker.person.jobTitle()},{name:"jobArea",example:E.faker.person.jobArea()},{name:"jobType",example:E.faker.person.jobType()},{name:"bio",example:E.faker.person.bio()}]},{category:"internet",methods:[{name:"email",example:E.faker.internet.email()},{name:"userName",example:E.faker.internet.username()},{name:"password",example:E.faker.internet.password()},{name:"url",example:E.faker.internet.url()},{name:"domainName",example:E.faker.internet.domainName()},{name:"domainWord",example:E.faker.internet.domainWord()},{name:"ip",example:E.faker.internet.ip()},{name:"ipv6",example:E.faker.internet.ipv6()},{name:"mac",example:E.faker.internet.mac()},{name:"userAgent",example:E.faker.internet.userAgent()},{name:"emoji",example:E.faker.internet.emoji()}]},{category:"phone",methods:[{name:"number",example:E.faker.phone.number()},{name:"imei",example:E.faker.phone.imei()}]},{category:"location",methods:[{name:"streetAddress",example:E.faker.location.streetAddress()},{name:"street",example:E.faker.location.street()},{name:"city",example:E.faker.location.city()},{name:"state",example:E.faker.location.state()},{name:"zipCode",example:E.faker.location.zipCode()},{name:"country",example:E.faker.location.country()},{name:"countryCode",example:E.faker.location.countryCode()},{name:"county",example:E.faker.location.county()},{name:"latitude",example:String(E.faker.location.latitude())},{name:"longitude",example:String(E.faker.location.longitude())},{name:"timeZone",example:E.faker.location.timeZone()},{name:"buildingNumber",example:E.faker.location.buildingNumber()}]},{category:"company",methods:[{name:"name",example:E.faker.company.name()},{name:"catchPhrase",example:E.faker.company.catchPhrase()},{name:"buzzPhrase",example:E.faker.company.buzzPhrase()},{name:"buzzNoun",example:E.faker.company.buzzNoun()},{name:"buzzVerb",example:E.faker.company.buzzVerb()},{name:"buzzAdjective",example:E.faker.company.buzzAdjective()}]},{category:"finance",methods:[{name:"accountNumber",example:E.faker.finance.accountNumber()},{name:"accountName",example:E.faker.finance.accountName()},{name:"creditCardNumber",example:E.faker.finance.creditCardNumber()},{name:"creditCardCVV",example:E.faker.finance.creditCardCVV()},{name:"currencyCode",example:E.faker.finance.currencyCode()},{name:"currencyName",example:E.faker.finance.currencyName()},{name:"amount",example:E.faker.finance.amount()},{name:"iban",example:E.faker.finance.iban()},{name:"bic",example:E.faker.finance.bic()},{name:"bitcoinAddress",example:E.faker.finance.bitcoinAddress()},{name:"transactionType",example:E.faker.finance.transactionType()}]},{category:"date",methods:[{name:"past",example:E.faker.date.past().toISOString()},{name:"future",example:E.faker.date.future().toISOString()},{name:"recent",example:E.faker.date.recent().toISOString()},{name:"soon",example:E.faker.date.soon().toISOString()},{name:"birthdate",example:E.faker.date.birthdate().toISOString()},{name:"month",example:E.faker.date.month()},{name:"weekday",example:E.faker.date.weekday()}]},{category:"lorem",methods:[{name:"word",example:E.faker.lorem.word()},{name:"words",example:E.faker.lorem.words()},{name:"sentence",example:E.faker.lorem.sentence()},{name:"sentences",example:E.faker.lorem.sentences()},{name:"paragraph",example:E.faker.lorem.paragraph()},{name:"slug",example:E.faker.lorem.slug()},{name:"lines",example:E.faker.lorem.lines(1)}]},{category:"string",methods:[{name:"uuid",example:E.faker.string.uuid()},{name:"alphanumeric",example:E.faker.string.alphanumeric(10)},{name:"numeric",example:E.faker.string.numeric(6)},{name:"alpha",example:E.faker.string.alpha(8)},{name:"nanoid",example:E.faker.string.nanoid()},{name:"hexadecimal",example:E.faker.string.hexadecimal({length:8})},{name:"sample",example:E.faker.string.sample(12)}]},{category:"number",methods:[{name:"int",example:String(E.faker.number.int({max:9999}))},{name:"float",example:String(E.faker.number.float({max:100,fractionDigits:2}))}]},{category:"datatype",methods:[{name:"boolean",example:String(E.faker.datatype.boolean())}]},{category:"image",methods:[{name:"avatar",example:E.faker.image.avatar()},{name:"url",example:E.faker.image.url()}]},{category:"color",methods:[{name:"human",example:E.faker.color.human()},{name:"rgb",example:E.faker.color.rgb()},{name:"hex",example:E.faker.color.rgb({format:"hex"})}]},{category:"commerce",methods:[{name:"productName",example:E.faker.commerce.productName()},{name:"price",example:E.faker.commerce.price()},{name:"department",example:E.faker.commerce.department()},{name:"productDescription",example:E.faker.commerce.productDescription().slice(0,60)+"..."},{name:"isbn",example:E.faker.commerce.isbn()}]},{category:"vehicle",methods:[{name:"vehicle",example:E.faker.vehicle.vehicle()},{name:"manufacturer",example:E.faker.vehicle.manufacturer()},{name:"model",example:E.faker.vehicle.model()},{name:"vin",example:E.faker.vehicle.vin()},{name:"vrm",example:E.faker.vehicle.vrm()}]},{category:"system",methods:[{name:"fileName",example:E.faker.system.fileName()},{name:"mimeType",example:E.faker.system.mimeType()},{name:"fileExt",example:E.faker.system.fileExt()},{name:"filePath",example:E.faker.system.filePath()},{name:"semver",example:E.faker.system.semver()}]}]}function Gr(r,e){let t=E.faker[r],s=uw[`${r}.${e}`]??e;if(!t||typeof t[s]!="function")return`[unknown: ${r}.${e}]`;let n=t[s]();return n instanceof Date?n.toISOString():String(n)}var E,uw,mo=b(()=>{"use strict";m();E=require("@faker-js/faker");uw={"internet.userName":"username"}});function cp(r){switch(r){case"FAKE_FIRST_NAME":return Gr("person","firstName");case"FAKE_LAST_NAME":return Gr("person","lastName");case"FAKE_AGE":return String(Math.floor(Math.random()*63)+18);case"FAKE_EMAIL":return Gr("internet","email");case"FAKE_PHONE":return Gr("phone","number");case"FAKE_PASSWORD":return Gr("internet","password")}let e=r.match(/^FAKE_NUMBER_(\d+)$/);if(e&&e[1]){let t=Math.max(1,parseInt(e[1],10)),s=t===1?0:Math.pow(10,t-1),n=Math.pow(10,t)-1;return String(Math.floor(Math.random()*(n-s+1))+s)}if(r.startsWith("FAKE_")){let t=r.slice(5),s=op[t];if(s)return Gr(s[0],s[1])}return null}var op,lp=b(()=>{"use strict";m();mo();op={};for(let r of gi())for(let e of r.methods){let t=`${r.category}_${e.name}`.toUpperCase();op[t]=[r.category,e.name]}});function Wt(r,e,t){let s=r.replace(/\{\{FILE:([^}]+)\}\}/g,(n,i)=>`fixtures/uploads/${i.trim().replace(/[^a-zA-Z0-9._-]+/g,"_")}`);return s=s.replace(new RegExp(dw,"g"),(n,i)=>{if(i==="BASE_URL")return t;let a=cp(i);return a!==null?a:e[i]??`{{${i}}}`}),s}var dw,go=b(()=>{"use strict";m();lp();dw="\\{\\{([A-Z0-9_]+)\\}\\}"});async function bt(r,e,t,s="domcontentloaded",n){t&&(e.setDefaultTimeout(t),e.setDefaultNavigationTimeout(t));let i=t?fo.expect.configure({timeout:t}):fo.expect,a=n??{setVariable:()=>{},getVariable:()=>{}},c=new pw("page","expect","context",r)(e,i,a);c.catch(()=>{}),await c;let l=t?Math.min(t/2,1e4):1e4;try{await e.waitForLoadState(s,{timeout:l})}catch{}}var fo,pw,fi=b(()=>{"use strict";m();fo=require("@playwright/test"),pw=Object.getPrototypeOf(async function(){}).constructor});async function mw(r,e){if(r===e)return null;let t=[],s=r;for(;s&&s!==e;){let n="iframe";try{let i=await s.frameElement();if(i){let a=await i.getAttribute("id").catch(()=>null),o=await i.getAttribute("name").catch(()=>null),c=await i.getAttribute("title").catch(()=>null);a?n=`#${a}`:o?n=`iframe[name='${o}']`:c&&(n=`iframe[title='${c}']`),await i.dispose().catch(()=>{})}}catch{}t.push(n),s=s.parentFrame()}return t.reverse().join("||")}async function nn(r,e,t){let s=r.url(),n=await r.title().catch(()=>""),i={maxElements:120,maxTextLen:80},a=await r.evaluate(up,i).catch(()=>[])??[],o=[],c=r.mainFrame();for(let d of r.frames())if(d!==c){if(o.length+a.length>=120)break;try{let p=await mw(d,c);if(p===null)continue;let g=await d.evaluate(up,i).catch(()=>[])??[];for(let f of g)o.push(`${f} frame="${p}"`)}catch{}}let l=[...a,...o].slice(0,120).join(`
|
|
791
|
-
`),u=await r.evaluate(({maxLen:d})=>document.body?.innerHTML?.slice(0,d)??"",{maxLen:8e3}).catch(()=>"");return{url:s,title:n,interactiveElements:l,htmlSnapshot:u,previousSteps:e,variables:t}}var
|
|
792
|
-
`);return{screenshot:s,elements:t,elementMap:n}}async function
|
|
793
|
-
`).map(t=>t.trim()).filter(t=>t.length>0&&t.startsWith("await"))}var
|
|
789
|
+
</html>`}var Ye,dp,Nr,Eo,ki,pp=b(()=>{"use strict";g();Ye=x(require("path")),dp=require("crypto"),Nr=x(require("fs-extra"));j();Eo=I("allure-reporter"),ki=class{resultsDir;environment;constructor(e,t,s="dev"){this.resultsDir=Ye.default.join(e,"results","allure-results",t),this.environment=s}async writeResults(e){await Nr.default.ensureDir(this.resultsDir);let t=[`Environment=${this.environment.toUpperCase()}`,"Framework=Assuremind"].join(`
|
|
790
|
+
`);await Nr.default.writeFile(Ye.default.join(this.resultsDir,"environment.properties"),t,"utf-8");let s;if(e.logFilePath)try{s=Ye.default.basename(e.logFilePath),await this.copyAttachment(e.logFilePath).catch(()=>{}),Eo.debug({logFile:s},"Log file copied to Allure results")}catch{Eo.debug("Failed to copy log file to Allure results")}let n=0;for(let i of e.suites)for(let a of i.cases)await this.writeTestResult(i,a,s),n++;Eo.info({dir:this.resultsDir,written:n},"Allure results written")}async writeTestResult(e,t,s){let n=(0,dp.randomUUID)(),i=new Date(t.startedAt).getTime(),a=new Date(t.finishedAt).getTime(),o=t.steps.find(m=>m.status==="failed"),c=[];if(t.videoPath){let m=Ye.default.basename(t.videoPath),f=Ye.default.extname(m).toLowerCase()===".mp4"?"video/mp4":"video/webm";c.push({name:"Video Recording",source:m,type:f})}if(t.tracePath){let m=Ye.default.basename(t.tracePath);c.push({name:"Playwright Trace",source:m,type:"application/zip"})}s&&c.push({name:"Run Log",source:s,type:"text/plain"});for(let m of t.downloadPaths??[]){let p=Ye.default.basename(m);c.push({name:`Downloaded: ${p}`,source:p,type:ww(p)})}let l=t.pageLoads??[];if(l.length>0){let m=l.some(h=>!h.lighthouseError&&h.score!=null),p=l.some(h=>!h.lighthouseError&&(h.a11yScore!=null||(h.a11yAudits?.length??0)>0)),f=l.some(h=>!h.lighthouseError&&(h.seoScore!=null||(h.seoAudits?.length??0)>0));if(m){let h=Io(t.caseName,l,"performance"),S=`${n}-perf.html`;await Nr.default.writeFile(Ye.default.join(this.resultsDir,S),h,"utf-8"),c.push({name:"\u26A1 Performance Report",source:S,type:"text/html"})}if(p){let h=Io(t.caseName,l,"accessibility"),S=`${n}-a11y.html`;await Nr.default.writeFile(Ye.default.join(this.resultsDir,S),h,"utf-8"),c.push({name:"\u267F Accessibility Report",source:S,type:"text/html"})}if(f){let h=Io(t.caseName,l,"seo"),S=`${n}-seo.html`;await Nr.default.writeFile(Ye.default.join(this.resultsDir,S),h,"utf-8"),c.push({name:"\u{1F50D} SEO Report",source:S,type:"text/html"})}}let u={uuid:n,historyId:`${e.suiteId}::${t.caseId}::${e.browser}`,testCaseId:`${e.suiteId}::${t.caseId}`,fullName:`${e.suiteName} > ${t.caseName}`,name:t.caseName,status:lp(t.status),stage:"finished",start:i,stop:a,labels:[{name:"suite",value:e.suiteName},{name:"browser",value:e.browser},{name:"framework",value:"assuremind"},{name:"tag",value:`ENV: ${this.environment.toUpperCase()}`}],links:[],parameters:[],attachments:c,steps:t.steps.map(m=>this.buildStepResult(m,i)),...o?.error&&{statusDetails:{message:o.error}}},d=Ye.default.join(this.resultsDir,`${n}-result.json`);await Nr.default.writeJson(d,u,{spaces:2});for(let m of t.steps)m.screenshotPath&&await this.copyAttachment(m.screenshotPath).catch(()=>{});t.videoPath&&await this.copyAttachment(t.videoPath).catch(()=>{}),t.tracePath&&await this.copyAttachment(t.tracePath).catch(()=>{});for(let m of t.downloadPaths??[])await this.copyAttachment(m).catch(()=>{})}buildStepResult(e,t){let s=t,n=t+e.duration,i=[];if(e.screenshotPath){let c=Ye.default.basename(e.screenshotPath);i.push({name:"Screenshot",source:c,type:"image/png"})}let a=[],o=e.instruction;return e.healed&&(o=`\u{1F527} [Auto-healed \u2014 approval needed] ${e.instruction}`,a.push({name:"\u26A0 Self-healing",value:"Auto-healed locator \u2014 approval needed (Studio \u2192 Self-Healing)"}),e.healedCode&&a.push({name:"Healed code",value:e.healedCode})),{name:o,status:lp(e.status),stage:"finished",start:s,stop:n,attachments:i,parameters:a,steps:[]}}async copyAttachment(e){let t=Ye.default.join(this.resultsDir,Ye.default.basename(e));await Nr.default.copy(e,t,{overwrite:!0})}}});function Sw(){try{let e=(0,fp.createRequire)(y??__filename).resolve("allure-commandline/package.json"),t=Rs.default.dirname(e);return Rs.default.join(t,"dist","bin",Po?"allure.bat":"allure")}catch{return Rs.default.resolve(__dirname,Po?"../node_modules/allure-commandline/dist/bin/allure.bat":"../node_modules/.bin/allure")}}async function hp(r,e){let t=Rs.default.join(r,"results","allure-results",e),s=Rs.default.join(r,"results","allure-report",e);if(!await Co.default.pathExists(t))return hn.debug({resultsDir:t},"No allure-results dir for run \u2014 skipping report generation"),!1;if((await Co.default.readdir(t)).length===0)return hn.debug({runId:e},"allure-results is empty \u2014 skipping report generation"),!1;try{return await xw(Ew,["generate",t,"--output",s,"--clean","--single-file"],Po?{shell:!0}:{}),hn.info({reportDir:s,runId:e},"Allure HTML report generated"),!0}catch(i){let a=i instanceof Error?i.message:String(i);return a.includes("java")||a.includes("Java")||a.includes("JAVA")?hn.warn("Allure report generation skipped \u2014 Java is not installed. Install Java (https://adoptium.net) to enable Allure HTML reports."):hn.warn({err:a,runId:e},"Allure report generation failed"),!1}}var Rs,Co,mp,gp,fp,xw,hn,Po,Ew,yp=b(()=>{"use strict";g();Rs=x(require("path")),Co=x(require("fs-extra")),mp=require("child_process"),gp=require("util"),fp=require("module");j();xw=(0,gp.promisify)(mp.execFile),hn=I("allure-generator"),Po=process.platform==="win32";Ew=Sw()});function Ti(){return[{category:"person",methods:[{name:"fullName",example:E.faker.person.fullName()},{name:"firstName",example:E.faker.person.firstName()},{name:"lastName",example:E.faker.person.lastName()},{name:"middleName",example:E.faker.person.middleName()},{name:"sex",example:E.faker.person.sex()},{name:"prefix",example:E.faker.person.prefix()},{name:"suffix",example:E.faker.person.suffix()},{name:"jobTitle",example:E.faker.person.jobTitle()},{name:"jobArea",example:E.faker.person.jobArea()},{name:"jobType",example:E.faker.person.jobType()},{name:"bio",example:E.faker.person.bio()}]},{category:"internet",methods:[{name:"email",example:E.faker.internet.email()},{name:"userName",example:E.faker.internet.username()},{name:"password",example:E.faker.internet.password()},{name:"url",example:E.faker.internet.url()},{name:"domainName",example:E.faker.internet.domainName()},{name:"domainWord",example:E.faker.internet.domainWord()},{name:"ip",example:E.faker.internet.ip()},{name:"ipv6",example:E.faker.internet.ipv6()},{name:"mac",example:E.faker.internet.mac()},{name:"userAgent",example:E.faker.internet.userAgent()},{name:"emoji",example:E.faker.internet.emoji()}]},{category:"phone",methods:[{name:"number",example:E.faker.phone.number()},{name:"imei",example:E.faker.phone.imei()}]},{category:"location",methods:[{name:"streetAddress",example:E.faker.location.streetAddress()},{name:"street",example:E.faker.location.street()},{name:"city",example:E.faker.location.city()},{name:"state",example:E.faker.location.state()},{name:"zipCode",example:E.faker.location.zipCode()},{name:"country",example:E.faker.location.country()},{name:"countryCode",example:E.faker.location.countryCode()},{name:"county",example:E.faker.location.county()},{name:"latitude",example:String(E.faker.location.latitude())},{name:"longitude",example:String(E.faker.location.longitude())},{name:"timeZone",example:E.faker.location.timeZone()},{name:"buildingNumber",example:E.faker.location.buildingNumber()}]},{category:"company",methods:[{name:"name",example:E.faker.company.name()},{name:"catchPhrase",example:E.faker.company.catchPhrase()},{name:"buzzPhrase",example:E.faker.company.buzzPhrase()},{name:"buzzNoun",example:E.faker.company.buzzNoun()},{name:"buzzVerb",example:E.faker.company.buzzVerb()},{name:"buzzAdjective",example:E.faker.company.buzzAdjective()}]},{category:"finance",methods:[{name:"accountNumber",example:E.faker.finance.accountNumber()},{name:"accountName",example:E.faker.finance.accountName()},{name:"creditCardNumber",example:E.faker.finance.creditCardNumber()},{name:"creditCardCVV",example:E.faker.finance.creditCardCVV()},{name:"currencyCode",example:E.faker.finance.currencyCode()},{name:"currencyName",example:E.faker.finance.currencyName()},{name:"amount",example:E.faker.finance.amount()},{name:"iban",example:E.faker.finance.iban()},{name:"bic",example:E.faker.finance.bic()},{name:"bitcoinAddress",example:E.faker.finance.bitcoinAddress()},{name:"transactionType",example:E.faker.finance.transactionType()}]},{category:"date",methods:[{name:"past",example:E.faker.date.past().toISOString()},{name:"future",example:E.faker.date.future().toISOString()},{name:"recent",example:E.faker.date.recent().toISOString()},{name:"soon",example:E.faker.date.soon().toISOString()},{name:"birthdate",example:E.faker.date.birthdate().toISOString()},{name:"month",example:E.faker.date.month()},{name:"weekday",example:E.faker.date.weekday()}]},{category:"lorem",methods:[{name:"word",example:E.faker.lorem.word()},{name:"words",example:E.faker.lorem.words()},{name:"sentence",example:E.faker.lorem.sentence()},{name:"sentences",example:E.faker.lorem.sentences()},{name:"paragraph",example:E.faker.lorem.paragraph()},{name:"slug",example:E.faker.lorem.slug()},{name:"lines",example:E.faker.lorem.lines(1)}]},{category:"string",methods:[{name:"uuid",example:E.faker.string.uuid()},{name:"alphanumeric",example:E.faker.string.alphanumeric(10)},{name:"numeric",example:E.faker.string.numeric(6)},{name:"alpha",example:E.faker.string.alpha(8)},{name:"nanoid",example:E.faker.string.nanoid()},{name:"hexadecimal",example:E.faker.string.hexadecimal({length:8})},{name:"sample",example:E.faker.string.sample(12)}]},{category:"number",methods:[{name:"int",example:String(E.faker.number.int({max:9999}))},{name:"float",example:String(E.faker.number.float({max:100,fractionDigits:2}))}]},{category:"datatype",methods:[{name:"boolean",example:String(E.faker.datatype.boolean())}]},{category:"image",methods:[{name:"avatar",example:E.faker.image.avatar()},{name:"url",example:E.faker.image.url()}]},{category:"color",methods:[{name:"human",example:E.faker.color.human()},{name:"rgb",example:E.faker.color.rgb()},{name:"hex",example:E.faker.color.rgb({format:"hex"})}]},{category:"commerce",methods:[{name:"productName",example:E.faker.commerce.productName()},{name:"price",example:E.faker.commerce.price()},{name:"department",example:E.faker.commerce.department()},{name:"productDescription",example:E.faker.commerce.productDescription().slice(0,60)+"..."},{name:"isbn",example:E.faker.commerce.isbn()}]},{category:"vehicle",methods:[{name:"vehicle",example:E.faker.vehicle.vehicle()},{name:"manufacturer",example:E.faker.vehicle.manufacturer()},{name:"model",example:E.faker.vehicle.model()},{name:"vin",example:E.faker.vehicle.vin()},{name:"vrm",example:E.faker.vehicle.vrm()}]},{category:"system",methods:[{name:"fileName",example:E.faker.system.fileName()},{name:"mimeType",example:E.faker.system.mimeType()},{name:"fileExt",example:E.faker.system.fileExt()},{name:"filePath",example:E.faker.system.filePath()},{name:"semver",example:E.faker.system.semver()}]}]}function ss(r,e){let t=E.faker[r],s=Iw[`${r}.${e}`]??e;if(!t||typeof t[s]!="function")return`[unknown: ${r}.${e}]`;let n=t[s]();return n instanceof Date?n.toISOString():String(n)}var E,Iw,Ro=b(()=>{"use strict";g();E=require("@faker-js/faker");Iw={"internet.userName":"username"}});function bp(r){switch(r){case"FAKE_FIRST_NAME":return ss("person","firstName");case"FAKE_LAST_NAME":return ss("person","lastName");case"FAKE_AGE":return String(Math.floor(Math.random()*63)+18);case"FAKE_EMAIL":return ss("internet","email");case"FAKE_PHONE":return ss("phone","number");case"FAKE_PASSWORD":return ss("internet","password")}let e=r.match(/^FAKE_NUMBER_(\d+)$/);if(e&&e[1]){let t=Math.max(1,parseInt(e[1],10)),s=t===1?0:Math.pow(10,t-1),n=Math.pow(10,t)-1;return String(Math.floor(Math.random()*(n-s+1))+s)}if(r.startsWith("FAKE_")){let t=r.slice(5),s=wp[t];if(s)return ss(s[0],s[1])}return null}var wp,vp=b(()=>{"use strict";g();Ro();wp={};for(let r of Ti())for(let e of r.methods){let t=`${r.category}_${e.name}`.toUpperCase();wp[t]=[r.category,e.name]}});function ur(r,e,t){let s=r.replace(/\{\{FILE:([^}]+)\}\}/g,(n,i)=>`fixtures/uploads/${i.trim().replace(/[^a-zA-Z0-9._-]+/g,"_")}`);return s=s.replace(new RegExp(Cw,"g"),(n,i)=>{if(i==="BASE_URL")return t;let a=bp(i);return a!==null?a:e[i]??`{{${i}}}`}),s}var Cw,ko=b(()=>{"use strict";g();vp();Cw="\\{\\{([A-Z0-9_]+)\\}\\}"});async function _t(r,e,t,s="domcontentloaded",n){t&&(e.setDefaultTimeout(t),e.setDefaultNavigationTimeout(t));let i=t?To.expect.configure({timeout:t}):To.expect,a=n??{setVariable:()=>{},getVariable:()=>{}},c=new Pw("page","expect","context",r)(e,i,a);c.catch(()=>{}),await c;let l=t?Math.min(t/2,1e4):1e4;try{await e.waitForLoadState(s,{timeout:l})}catch{}}var To,Pw,Ai=b(()=>{"use strict";g();To=require("@playwright/test"),Pw=Object.getPrototypeOf(async function(){}).constructor});async function Rw(r,e){if(r===e)return null;let t=[],s=r;for(;s&&s!==e;){let n="iframe";try{let i=await s.frameElement();if(i){let a=await i.getAttribute("id").catch(()=>null),o=await i.getAttribute("name").catch(()=>null),c=await i.getAttribute("title").catch(()=>null);a?n=`#${a}`:o?n=`iframe[name='${o}']`:c&&(n=`iframe[title='${c}']`),await i.dispose().catch(()=>{})}}catch{}t.push(n),s=s.parentFrame()}return t.reverse().join("||")}async function yn(r,e,t){let s=r.url(),n=await r.title().catch(()=>""),i={maxElements:120,maxTextLen:80},a=await r.evaluate(xp,i).catch(()=>[])??[],o=[],c=r.mainFrame();for(let d of r.frames())if(d!==c){if(o.length+a.length>=120)break;try{let m=await Rw(d,c);if(m===null)continue;let p=await d.evaluate(xp,i).catch(()=>[])??[];for(let f of p)o.push(`${f} frame="${m}"`)}catch{}}let l=[...a,...o].slice(0,120).join(`
|
|
791
|
+
`),u=await r.evaluate(({maxLen:d})=>document.body?.innerHTML?.slice(0,d)??"",{maxLen:8e3}).catch(()=>"");return{url:s,title:n,interactiveElements:l,htmlSnapshot:u,previousSteps:e,variables:t}}var xp,Sp=b(()=>{"use strict";g();xp=({maxElements:r,maxTextLen:e})=>{let t=["button",'[role="button"]',"a[href]",'input:not([type="hidden"])',"textarea","select",'[role="link"]','[role="tab"]','[role="checkbox"]','[role="radio"]','[role="menuitem"]','[role="option"]','[role="combobox"]','[role="listbox"]','[role="switch"]'];function s(l){let u=l.getAttribute("aria-labelledby");return u?u.split(/\s+/).map(d=>document.getElementById(d)?.textContent?.trim()??"").filter(Boolean).join(" "):""}function n(l){let u=l.getAttribute("id");if(u){let m=document.querySelector(`label[for="${u}"]`);if(m)return(m.textContent??"").trim().slice(0,60)}let d=l.closest("label");if(d){let m=d.cloneNode(!0);return m.querySelectorAll("input,textarea,select").forEach(p=>p.remove()),(m.textContent??"").trim().slice(0,60)}return""}function i(l){let u=window.getComputedStyle(l);if(u.display==="none"||u.visibility==="hidden"||u.opacity==="0")return!1;let d=l.getBoundingClientRect();return d.width>0&&d.height>0}function a(l){return l.getAttribute("data-testid")??l.getAttribute("data-cy")??l.getAttribute("data-qa")??l.getAttribute("data-test")??""}let o=new Set,c=[];for(let l of t){if(c.length>=r)break;for(let u of Array.from(document.querySelectorAll(l))){if(c.length>=r)break;if(o.has(u)||!i(u))continue;o.add(u);let d=u.tagName.toLowerCase(),m=u.getAttribute("role")??d,p=(u.textContent??"").trim().replace(/\s+/g," ").slice(0,e),f=u.getAttribute("aria-label")??"",h=s(u),S=n(u),C=a(u),v=u.getAttribute("name")??"",k=u.getAttribute("id")??"",T=u.getAttribute("placeholder")??"",P=u.getAttribute("type")??"",G=d==="a"?(u.getAttribute("href")??"").slice(0,60):"",ne=d==="select"||d==="input"?u.value?.slice(0,40):"",ae=[`<${d}>`];C&&ae.push(`data-testid="${C}"`),f&&ae.push(`aria-label="${f}"`),h&&ae.push(`aria-labelledby="${h}"`),S&&ae.push(`label="${S}"`),T&&ae.push(`placeholder="${T}"`),p&&ae.push(`text="${p}"`),P&&ae.push(`type="${P}"`),v&&ae.push(`name="${v}"`),k&&ae.push(`id="${k}"`),G&&ae.push(`href="${G}"`),ne&&ae.push(`value="${ne}"`),m!==d&&ae.push(`role="${m}"`),c.push(ae.join(" "))}}return c}});function kw(r){return r.testId?`page.getByTestId('${r.testId}')`:r.ariaLabel?`page.getByRole('${r.role||r.tag}', { name: '${r.ariaLabel}' })`:r.labelText&&(r.tag==="input"||r.tag==="textarea"||r.tag==="select")?`page.getByLabel('${r.labelText}')`:r.placeholder?`page.getByPlaceholder('${r.placeholder}')`:r.tag==="a"&&r.text?`page.getByRole('link', { name: '${r.text}' })`:(r.tag==="button"||r.role==="button")&&r.text?`page.getByRole('button', { name: '${r.text}' })`:r.role&&r.text?`page.getByRole('${r.role}', { name: '${r.text}' })`:r.text?`page.getByText('${r.text}')`:r.id?`page.locator('#${r.id}')`:r.name?`page.locator('[name="${r.name}"]')`:r.inputType?`page.locator('${r.tag}[type="${r.inputType}"]')`:`page.locator('${r.tag}')`}async function $o(r){let t=(await r.evaluate(()=>{let i=["button",'[role="button"]',"a[href]",'input:not([type="hidden"])',"textarea","select",'[role="link"]','[role="tab"]','[role="checkbox"]','[role="radio"]','[role="menuitem"]','[role="option"]','[role="combobox"]','[role="switch"]'];function a(m){let p=m.getAttribute("aria-labelledby");return p?p.split(/\s+/).map(f=>document.getElementById(f)?.textContent?.trim()??"").filter(Boolean).join(" "):""}function o(m){let p=m.getAttribute("id");if(p){let h=document.querySelector(`label[for="${p}"]`);if(h)return(h.textContent??"").trim().slice(0,60)}let f=m.closest("label");if(f){let h=f.cloneNode(!0);return h.querySelectorAll("input,textarea,select").forEach(S=>S.remove()),(h.textContent??"").trim().slice(0,60)}return""}let c=new Set,l=[],u=window.innerWidth,d=window.innerHeight;for(let m of i){for(let p of Array.from(document.querySelectorAll(m))){if(c.has(p))continue;let f=window.getComputedStyle(p);if(f.display==="none"||f.visibility==="hidden"||f.opacity==="0")continue;let h=p.getBoundingClientRect();if(h.width<=0||h.height<=0||h.right<0||h.bottom<0||h.left>u||h.top>d)continue;c.add(p);let S=p.tagName.toLowerCase(),C=p.getAttribute("role")??"",v=(p.textContent??"").trim().replace(/\s+/g," ").slice(0,60),k=p.getAttribute("aria-label")??"",T=a(p),P=o(p),G=p.getAttribute("data-testid")??p.getAttribute("data-cy")??p.getAttribute("data-qa")??p.getAttribute("data-test")??"",ne=p.getAttribute("placeholder")??"",ae=S==="a"?p.getAttribute("href")??"":"",me=p.getAttribute("id")??"",Qt=p.getAttribute("name")??"",Fe=p.getAttribute("type")??"";if(l.push({tag:S,role:C,text:v,ariaLabel:k,ariaLabelledBy:T,labelText:P,testId:G,placeholder:ne,href:ae,id:me,name:Qt,inputType:Fe,x:Math.round(h.left),y:Math.round(h.top),width:Math.round(h.width),height:Math.round(h.height)}),l.length>=80)break}if(l.length>=80)break}return l})).map((i,a)=>{let o=kw(i),c=i.ariaLabel||i.labelText||i.text||i.placeholder||i.testId||i.href||`${i.tag}[${a}]`,l=`[${a+1}] ${i.tag}${i.role?`(${i.role})`:""} "${c.slice(0,50)}" \u2192 ${o}`;return{index:a+1,tag:i.tag,role:i.role||i.tag,selector:o,description:l,x:i.x,y:i.y,width:i.width,height:i.height}});await r.evaluate(({els:i,overlayId:a})=>{let o=document.getElementById(a);o&&o.remove();let c="http://www.w3.org/2000/svg",l=document.createElementNS(c,"svg");l.id=a,l.setAttribute("xmlns",c),l.style.cssText=["position:fixed","top:0","left:0","width:100%","height:100%","pointer-events:none","z-index:2147483647"].join(";");let u=(d,m)=>{let p=d.toLowerCase(),f=m.toLowerCase();return p==="button"||f==="button"?"#FF6B6B":p==="a"||f==="link"?"#96CEB4":p==="input"||p==="textarea"?"#4ECDC4":p==="select"||f==="combobox"?"#45B7D1":f==="menuitem"||f==="tab"?"#DDA0DD":"#FFE66D"};for(let d of i){let m=u(d.tag,d.role),p=document.createElementNS(c,"rect");p.setAttribute("x",String(d.x+1)),p.setAttribute("y",String(d.y+1)),p.setAttribute("width",String(Math.max(d.width-2,4))),p.setAttribute("height",String(Math.max(d.height-2,4))),p.setAttribute("fill","none"),p.setAttribute("stroke",m),p.setAttribute("stroke-width","2"),p.setAttribute("stroke-dasharray","4,3"),l.appendChild(p);let f=d.index>=10?22:16,h=16,S=d.index<d.x-f?d.x-f:d.x,C=d.y<h?d.y:d.y-h+2,v=document.createElementNS(c,"rect");v.setAttribute("x",String(S)),v.setAttribute("y",String(C)),v.setAttribute("width",String(f)),v.setAttribute("height",String(h)),v.setAttribute("fill",m),v.setAttribute("rx","3"),l.appendChild(v);let k=document.createElementNS(c,"text");k.setAttribute("x",String(S+f/2)),k.setAttribute("y",String(C+12)),k.setAttribute("fill","#000"),k.setAttribute("font-size","11"),k.setAttribute("font-family","monospace"),k.setAttribute("font-weight","bold"),k.setAttribute("text-anchor","middle"),k.textContent=String(d.index),l.appendChild(k)}document.documentElement.appendChild(l)},{els:t.map(i=>({index:i.index,x:i.x,y:i.y,width:i.width,height:i.height,tag:i.tag,role:i.role})),overlayId:Ao});let s=await r.screenshot({type:"png",fullPage:!1});await r.evaluate(i=>document.getElementById(i)?.remove(),Ao);let n=t.map(i=>i.description).join(`
|
|
792
|
+
`);return{screenshot:s,elements:t,elementMap:n}}async function _o(r){await r.evaluate(e=>document.getElementById(e)?.remove(),Ao).catch(()=>{})}var Ao,Ep=b(()=>{"use strict";g();Ao="__testassuremind_som__"});function Ip(r){let e=r.replace(/^```[\w]*\n?/gm,"").replace(/^```\s*$/gm,"").trim();return e.includes("---")?e.split("---").map(t=>t.trim()).filter(t=>t.length>0):e.split(`
|
|
793
|
+
`).map(t=>t.trim()).filter(t=>t.length>0&&t.startsWith("await"))}var Ue,$i,Cp=b(()=>{"use strict";g();Sp();Ep();Vt();ko();j();Ai();Kr();Ue=I("self-healing"),$i=class{constructor(e,t,s,n=!1){this.provider=e;this.maxLevel=t;this.ragManager=s;this.healAssertionLocators=n}provider;maxLevel;ragManager;healAssertionLocators;async heal(e,t,s,n,i,a){if(t.failureKind==="assertion")return Ue.info({stepId:e.id},"Assertion failure \u2014 skipping all healing levels (possible application bug)"),null;if(t.failureKind==="assertion-locator-missing"){if(!this.healAssertionLocators)return Ue.info({stepId:e.id},"Assertion-locator heal disabled \u2014 skipping"),null;try{let c=await this.level2Regenerate(e,t,s,n,i,a);if(c!==null)return Ue.info({stepId:e.id},"Assertion locator healed (matcher preserved)"),{...c,strategy:"regenerate"}}catch(c){Ue.debug({err:c,stepId:e.id},"Assertion-locator heal attempt threw")}return null}let o=e.stepType==="api"||gt(e.instruction)==="api";for(let c=1;c<=this.maxLevel;c++){if(o&&(c===3||c===4)){Ue.debug({stepId:e.id,level:c},`Skipping DOM-specific level ${c} for API step`);continue}Ue.debug({stepId:e.id,level:c,isApi:o},`Attempting heal level ${c}`);try{let l=await this.tryLevel(c,e,t,s,n,i,a,o);if(l!==null)return Ue.info({stepId:e.id,level:c,strategy:l.strategy},"Healing succeeded"),l}catch(l){Ue.debug({err:l,level:c,stepId:e.id},`Level ${c} healing attempt threw`)}}return Ue.warn({stepId:e.id,maxLevel:this.maxLevel},"All healing levels exhausted"),null}async tryLevel(e,t,s,n,i,a,o,c=!1){switch(e){case 1:return this.level1Retry(s,i);case 2:return c?this.level2ApiRegenerate(t,s,i,a,o):this.level2Regenerate(t,s,n,i,a,o);case 3:return this.level3MultiSelector(t,s,n,i,a,o);case 4:return this.level4Visual(t,i,a,o);case 5:return this.level5Decompose(t,s,n,i,a,o);case 6:return this.level6Manual(t,s);default:return null}}async level1Retry(e,t){return await t.waitForLoadState("networkidle",{timeout:5e3}).catch(()=>{}),await t.waitForTimeout(1e3),await _t(e.failedCode,t),{code:e.failedCode,level:1,strategy:"retry"}}async level2Regenerate(e,t,s,n,i,a){if(this.provider.supportsVision)try{let d=await $o(n);Ue.debug({stepId:e.id},"Level 2: using SoM vision healing");let m=await this.provider.analyzeWithSoM(d.screenshot,d.elementMap,e.instruction),p=ur(Ke(m),i,a);return await _t(p,n),{code:p,level:2,strategy:"regenerate"}}catch(d){Ue.debug({stepId:e.id,err:d},"SoM vision failed, falling back to text regeneration"),await _o(n)}let o=await yn(n,s,i),c="";if(this.ragManager)try{let d=this.ragManager.healingCorpus.retrieve(e.instruction,t.error,2);d.length>0&&(c=`
|
|
794
794
|
|
|
795
795
|
Similar past fixes that worked:
|
|
796
|
-
`+d.map((p
|
|
797
|
-
Fix: ${
|
|
798
|
-
`)
|
|
799
|
-
`),level:5,strategy:"decompose"}}level6Manual(e,t){return
|
|
796
|
+
`+d.map((m,p)=>` ${p+1}. Error: ${m.error}
|
|
797
|
+
Fix: ${m.healedCode}`).join(`
|
|
798
|
+
`),Ue.debug({stepId:e.id,matches:d.length},"RAG healing corpus matches found"))}catch{}let l=await this.provider.healSelector(e.instruction+c,t.failedCode,t.error,o),u=ur(Ke(l),i,a);return await _t(u,n),{code:u,level:2,strategy:"regenerate"}}async level2ApiRegenerate(e,t,s,n,i){let a=await yn(s,[],n),o=`[API STEP] Fix this failing API test step. Only fix the REQUEST \u2014 the URL, headers, body, or HTTP method. NEVER change expect() assertions or expected status codes. Common fixable issues: wrong endpoint URL, missing auth header, incorrect request body schema. Keep all assertions exactly as they are. Step: ${e.instruction}`,c=await this.provider.healSelector(o,t.failedCode,t.error,a),l=ur(Ke(c),n,i);return await _t(l,s),{code:l,level:2,strategy:"regenerate"}}async level3MultiSelector(e,t,s,n,i,a){let o=await yn(n,s,i),c=`Generate 5 different Playwright selector strategies (one per line, separated by "---") for this step: ${e.instruction}`,l=await this.provider.healSelector(c,t.failedCode,t.error,o),u=Ip(l);for(let d of u)try{let m=ur(Ke(d),i,a);return await _t(m,n),{code:m,level:3,strategy:"multi-selector"}}catch{}return null}async level4Visual(e,t,s,n){if(!this.provider.supportsVision)return Ue.debug({stepId:e.id},"Skipping SoM visual healing \u2014 provider lacks vision"),null;try{let i=await $o(t);Ue.debug({stepId:e.id,elementCount:i.elements.length},"Level 4: SoM screenshot captured");let a=await this.provider.analyzeWithSoM(i.screenshot,i.elementMap,e.instruction),o=ur(Ke(a),s,n);return await _t(o,t),{code:o,level:4,strategy:"visual"}}catch(i){return await _o(t),Ue.debug({stepId:e.id,err:i},"Level 4 SoM failed"),null}}async level5Decompose(e,t,s,n,i,a){let o=await yn(n,s,i),c='Break this failing step into 3-5 sequential micro-actions (separate each with "---"): '+e.instruction,l=await this.provider.healSelector(c,t.failedCode,t.error,o),u=Ip(l),d=[];for(let m of u){let p=ur(Ke(m),i,a);await _t(p,n),d.push(p)}return{code:d.join(`
|
|
799
|
+
`),level:5,strategy:"decompose"}}level6Manual(e,t){return Ue.warn({stepId:e.id,instruction:e.instruction,error:t.error},"Level 6: manual intervention required \u2014 marking step for human review"),null}}});function Ow(r,e){for(let t of Tw)if(t.test(r)){for(let s of $w)if(s.test(r))return"assertion-locator-missing";return"assertion"}if(e&&_w.test(e))return"assertion-locator-missing";for(let t of Aw)if(t.test(r))return"infra";return"assertion"}async function _i(r,e,t){let s=Date.now(),n={...t.variables,...Object.fromEntries(t.sharedVariables)},i=r.generatedCode.includes("{{");i&&dt.warn({stepId:r.id,variableKeys:Object.keys(n),variableCount:Object.keys(n).length},"Interpolating variables into step code");let a=ur(r.generatedCode,n,t.config.baseUrl);if(i&&dt.warn({stepId:r.id,before:r.generatedCode,after:a},"Variable interpolation result"),!a.trim()&&r.stepType!=="mock")return dt.warn({stepId:r.id},"Step has no generated code \u2014 skipping"),Ot(r,"skipped",a,Date.now()-s);if(r.stepType==="mock"&&r.mockUrl&&r.mockResponse)try{await e.route(r.mockUrl,f=>f.fulfill({status:r.mockStatus??200,contentType:"application/json",body:r.mockResponse??""}));let p=Ot(r,"passed",a,Date.now()-s);return await e.unrouteAll().catch(()=>{}),p}catch(p){let f=p instanceof Error?p.message:String(p);return Ot(r,"failed",a,Date.now()-s,f)}let o={setVariable:(p,f)=>{t.sharedVariables.set(p,f),dt.info({stepId:r.id,key:p,value:f.slice(0,50)},"Runtime variable set")},getVariable:p=>t.sharedVariables.get(p)},c=r.retries??t.config.retries??0,l=r.timeout??t.config.timeout,u=r.stepType!=="api"?e.url():"",d;for(let p=0;p<=c;p++)try{await Lw(_t(a,e,l,t.config.pageLoad??"domcontentloaded",o),l);let f;if(r.stepType!=="api")try{let S=e.url();(u!==S||u==="about:blank"||u==="")&&S&&!S.startsWith("about:")&&(f=S)}catch{}let h;if(r.runAudit&&r.stepType!=="api")try{h=f??(e.url()||void 0)}catch{}if(t.ragManager&&a.trim())try{t.ragManager.codeCorpus.ingest(r.instruction,a,e.url())}catch{}if(t.config.screenshot==="on"){let S=await Pp(e,t,r.id,"pass");return{...Ot(r,"passed",a,Date.now()-s,void 0,S),navigatedToUrl:f,auditUrl:h}}return{...Ot(r,"passed",a,Date.now()-s),navigatedToUrl:f,auditUrl:h}}catch(f){if(d=f,p<c){dt.debug({stepId:r.id,attempt:p,stepRetries:c},"Step failed \u2014 retrying");continue}}let m=d;{let p=m instanceof Error?m.message:String(m);dt.debug({stepId:r.id,error:p},"Step failed on first attempt");let f=r.runAudit&&r.stepType!=="api"?(()=>{try{return e.url()||void 0}catch{return}})():void 0,h;t.config.screenshot!=="off"&&(h=await Pp(e,t,r.id,"fail"));let S=Ow(p,a);if(S==="assertion")return dt.info({stepId:r.id,error:p},"Assertion failure detected \u2014 NOT healing (possible application bug)"),{...Ot(r,"failed",a,Date.now()-s,p,h),...f?{auditUrl:f}:{}};if(S==="assertion-locator-missing"&&!t.config.healing.healAssertionLocators)return dt.info({stepId:r.id,error:p},"Assertion locator not found \u2014 healing disabled (enable healing.healAssertionLocators to fix locators inside assertions)"),{...Ot(r,"failed",a,Date.now()-s,p,h),...f?{auditUrl:f}:{}};if(!t.config.healing.enabled)return{...Ot(r,"failed",a,Date.now()-s,p,h),...f?{auditUrl:f}:{}};if(t.ragManager)try{t.ragManager.errorCatalog.record(p,e.url())}catch{}dt.debug({stepId:r.id,failureKind:S},"Infrastructure failure \u2014 attempting to heal");let C={stepId:r.id,instruction:r.instruction,error:p,pageUrl:e.url(),failedCode:a,failureKind:S},v=t.budget.shouldHeal(C);if(!v.heal)return dt.info({stepId:r.id,reason:v.reason},"Healing skipped"),{...Ot(r,"failed",a,Date.now()-s,p,h),...f?{auditUrl:f}:{}};let k=new $i(t.provider,t.config.healing.maxLevel,t.ragManager,t.config.healing.healAssertionLocators);try{let T=await k.heal(r,C,t.previousSteps,e,t.variables,t.config.baseUrl);if(T!==null){let P=T.code.trim()!==a.trim();if(P){if(t.budget.recordSpend(.005),t.ragManager)try{t.ragManager.healingCorpus.ingest({instruction:r.instruction,error:p,failedCode:a,healedCode:T.code,strategy:T.strategy,level:T.level,url:e.url()}),t.ragManager.errorCatalog.record(p,e.url(),T.code)}catch{}t.healingReporter.record({runId:t.runId,suiteId:t.suiteId,caseId:t.caseId,stepId:r.id,stepInstruction:r.instruction,failedCode:a,healedCode:T.code,error:p,strategy:T.strategy,level:T.level,pageUrl:e.url()})}return dt.info({stepId:r.id,level:T.level,strategy:T.strategy,codeChanged:P},P?"Step passed after healing (code NOT saved \u2014 awaits human review)":"Step passed after smart retry (no code change)"),Ot(r,"passed",a,Date.now()-s,void 0,h,P?T.code:void 0)}}catch(T){let P=T instanceof Error?T.message:String(T);dt.warn({stepId:r.id,error:P},"Healing threw an error"),t.budget.recordHealFailure(r.id)}return{...Ot(r,"failed",a,Date.now()-s,p,h),...f?{auditUrl:f}:{}}}}function Ot(r,e,t,s,n,i,a){let o=r.stepType??gt(r.instruction);return{stepId:r.id,instruction:r.instruction,status:e,code:t,duration:s,stepType:o,...n!==void 0&&{error:n},...i!==void 0&&{screenshotPath:i},...a!==void 0&&{healed:!0,healedCode:a}}}async function Pp(r,e,t,s){try{await r.waitForLoadState("load",{timeout:5e3}).catch(()=>{});let n=Oo.default.join(e.rootDir,"results","screenshots"),i=(c,l=28)=>c.replace(/[^a-zA-Z0-9_-]/g,"").slice(-l),a=`${i(e.runId)}_${i(e.caseId)}_${i(t)}_${s}.png`,o=Oo.default.join(n,a);return await r.screenshot({path:o,fullPage:!0}),o}catch(n){dt.debug({err:n},"Screenshot capture failed");return}}function Lw(r,e){return Promise.race([r,new Promise((t,s)=>setTimeout(()=>s(new Error(`Step timed out after ${e}ms`)),e))])}var Oo,dt,Tw,Aw,$w,_w,Rp=b(()=>{"use strict";g();Oo=x(require("path"));ko();Ai();Cp();Kr();j();Ai();dt=I("executor"),Tw=[/expect\(.*\)\.(toBe|toEqual|toContain|toMatch|toHaveText|toHaveValue|toBeChecked|toBeVisible|toBeHidden|toBeEnabled|toBeDisabled|toHaveCount|toHaveAttribute|toHaveClass|toHaveCSS|toHaveURL|toHaveTitle)/i,/AssertionError/i,/AssertError/i,/Expected.*received/i,/Expected.*but got/i,/expect\..*Error/i,/expected\s+\d{3}.*received\s+\d{3}/i,/expected\s+status\s+.*\d{3}/i,/toBe\(\d{3}\).*Received:\s*\d{3}/i,/Expected:.*\d{3}.*Received:.*\d{3}/i,/Error:.*expect.*to(Be|Equal|Contain|Match|Have)/i],Aw=[/locator\.(click|fill|type|check|selectOption|press).*timeout/i,/waiting for (locator|selector)/i,/element is not (visible|attached|enabled|stable)/i,/page\.(goto|waitForSelector|waitForURL|waitForNavigation).*timeout/i,/net::ERR_/i,/ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND/i,/Target closed|Target page, context or browser has been closed/i,/frame was detached/i,/navigation interrupted/i,/Timeout \d+ms exceeded/i,/Step timed out after \d+ms/i,/strict mode violation/i,/No element matches/i,/page\.request\.(get|post|put|delete|patch).*ECONNREFUSED/i,/socket hang up/i,/ERR_CONNECTION/i],$w=[/element\(s\) not found/i,/waiting for (locator|selector)/i,/no element(s)? (match|found)/i,/locator resolved to 0 elements/i,/frame.*not found|no frame/i],_w=/\bexpect\s*\(/i});async function Oi(r){await r.addInitScript(Dw)}var Dw,kp=b(()=>{"use strict";g();Dw=`(function () {
|
|
800
800
|
if (window.__tm_highlighter_installed__) return;
|
|
801
801
|
window.__tm_highlighter_installed__ = true;
|
|
802
802
|
|
|
@@ -938,13 +938,13 @@ Similar past fixes that worked:
|
|
|
938
938
|
}
|
|
939
939
|
}, { capture: true, passive: true });
|
|
940
940
|
|
|
941
|
-
})();`});async function
|
|
942
|
-
`).map(o=>o.trim()).filter(Boolean);if(n.length<2)return[];let i=n[0].split(",").map(o=>o.trim()),a=n.slice(1).map(o=>{let c=o.split(",").map(u=>u.trim()),l={};return i.forEach((u,d)=>{l[u]=c[d]??""}),l});return bp.info({path:t,rows:a.length},"Loaded CSV data rows"),a}default:return[]}}var xo,an,bp,xp=b(()=>{"use strict";m();xo=x(require("path")),an=x(require("fs-extra"));F();bp=C("data-loader")});function Sp(r){return Lt.default.join(r,".assuremind","visual-regression.json")}function Ep(r,e){return Lt.default.join(r,"results","visual-diffs",e,"summary.json")}function xw(r){return Lt.default.join(r,"results","visual-diffs")}function Cp(r){return Lt.default.join(r,"baselines")}async function on(r){let e=Sp(r);if(!await Xt.default.pathExists(e))return{...So};try{let t=await ue(e);return{...So,...t}}catch{return{...So}}}async function Ip(r,e){await W(Sp(r),e)}async function yi(r,e){await W(Ep(r,e.runId),e)}async function wi(r,e){return await ue(Ep(r,e))}async function Pp(r){let e=xw(r);if(!await Xt.default.pathExists(e))return[];let t=await Xt.default.readdir(e,{withFileTypes:!0}),s=[];for(let n of t){if(!n.isDirectory())continue;let i=Lt.default.join(e,n.name,"summary.json");if(await Xt.default.pathExists(i))try{let a=await ue(i),o=a.diffs??[];s.push({runId:a.runId,timestamp:a.timestamp,totalDiffs:o.length,passed:o.filter(c=>c.status==="passed").length,failed:o.filter(c=>c.status==="failed").length,newCount:o.filter(c=>c.status==="new").length,approved:o.filter(c=>c.status==="approved").length,rejected:o.filter(c=>c.status==="rejected").length})}catch{}}return s.sort((n,i)=>new Date(i.timestamp).getTime()-new Date(n.timestamp).getTime()),s}async function Rp(r,e,t,s){let n=await wi(r,e),i=n.diffs.find(a=>a.stepId===t);if(!i)throw new Error(`Diff entry not found for stepId "${t}" in run "${e}"`);return i.status=s,await yi(r,n),n}async function Tp(r,e){let t=await wi(r,e);for(let s of t.diffs)(s.status==="failed"||s.status==="new")&&(s.status="approved");return await yi(r,t),t}async function kp(r){let e=Cp(r);if(!await Xt.default.pathExists(e))return[];let t=[];async function s(n){let i=await Xt.default.readdir(n,{withFileTypes:!0});for(let a of i){let o=Lt.default.join(n,a.name);if(a.isDirectory())await s(o);else if(a.name.endsWith(".png")){let c=Lt.default.relative(e,o).replace(/\\/g,"/"),l=c.split("/"),u=await Xt.default.stat(o);t.push({path:o,relativePath:c,suite:l.length>1?l[0]:"",caseName:l.length>2?l[1]:"",fileName:a.name,sizeBytes:u.size})}}}return await s(e),t}async function Ap(r,e){let t=Cp(r),s=Lt.default.resolve(e);if(!s.startsWith(Lt.default.resolve(t)))throw new Error("Path is outside baselines directory");await Xt.default.remove(s)}var Lt,Xt,So,Eo=b(()=>{"use strict";m();Lt=x(require("path")),Xt=x(require("fs-extra"));ze();So={enabled:!1,defaultThreshold:.5,fullPage:!0,maskSelectors:[]}});async function $p(r){let e=await it.default.readFile(r);return new Promise((t,s)=>{new bi.PNG().parse(e,(i,a)=>{i?s(i):t(a)})})}function Sw(r){return bi.PNG.sync.write(r)}async function Op(r,e,t,s={}){let{threshold:n=.1,diffThreshold:i=.5}=s;if(!await it.default.pathExists(r))return await it.default.ensureDir(gs.default.dirname(r)),await it.default.copy(e,r),{match:!0,diffPercentage:0,diffPixels:0,totalPixels:0,baselinePath:r,actualPath:e};let[o,c]=await Promise.all([$p(r),$p(e)]);if(o.width!==c.width||o.height!==c.height){await it.default.ensureDir(gs.default.dirname(t));let k=t.replace(/\.png$/,"-baseline.png"),T=t.replace(/\.png$/,"-actual.png");await Promise.all([it.default.copy(r,k),it.default.copy(e,T)]);let P=Math.max(o.width*o.height,c.width*c.height);return{match:!1,diffPercentage:100,diffPixels:P,totalPixels:P,baselinePath:r,actualPath:e,error:`Size mismatch: baseline ${o.width}x${o.height} vs actual ${c.width}x${c.height}`}}let{width:l,height:u}=o,d=new bi.PNG({width:l,height:u}),p=(0,_p.default)(o.data,c.data,d.data,l,u,{threshold:n}),g=l*u,f=g>0?p/g*100:0,h=f<=i;await it.default.ensureDir(gs.default.dirname(t));let S=Sw(d),I=t.replace(/\.png$/,"-baseline.png"),v=t.replace(/\.png$/,"-actual.png");return await Promise.all([it.default.writeFile(t,S),it.default.copy(r,I),it.default.copy(e,v)]),{match:h,diffPercentage:Math.round(f*100)/100,diffPixels:p,totalPixels:g,diffImagePath:t,baselinePath:r,actualPath:e}}function Lp(r,e,t,s,n,i){return gs.default.join(r,"baselines",e,t,`${s}-${n}-${i.width}x${i.height}.png`)}function Dp(r,e,t,s,n){return gs.default.join(r,"results","visual-diffs",e,`${t}-${s}-${n.width}x${n.height}-diff.png`)}var _p,bi,it,gs,Np=b(()=>{"use strict";m();_p=x(require("pixelmatch")),bi=require("pngjs"),it=x(require("fs-extra")),gs=x(require("path"))});var Mp=It((iP,Fp)=>{"use strict";m();Fp.exports=r=>{if(typeof r!="string")throw new TypeError("Expected a string");return r.replace(/[|\\{}()[\]^$+*?.]/g,"\\$&").replace(/-/g,"\\x2d")}});var Bp=It((oP,jp)=>{"use strict";m();var fs=1e3,hs=fs*60,ys=hs*60,zr=ys*24,Ew=zr*7,Cw=zr*365.25;jp.exports=function(r,e){e=e||{};var t=typeof r;if(t==="string"&&r.length>0)return Iw(r);if(t==="number"&&isFinite(r))return e.long?Rw(r):Pw(r);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(r))};function Iw(r){if(r=String(r),!(r.length>100)){var e=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(r);if(e){var t=parseFloat(e[1]),s=(e[2]||"ms").toLowerCase();switch(s){case"years":case"year":case"yrs":case"yr":case"y":return t*Cw;case"weeks":case"week":case"w":return t*Ew;case"days":case"day":case"d":return t*zr;case"hours":case"hour":case"hrs":case"hr":case"h":return t*ys;case"minutes":case"minute":case"mins":case"min":case"m":return t*hs;case"seconds":case"second":case"secs":case"sec":case"s":return t*fs;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return t;default:return}}}}function Pw(r){var e=Math.abs(r);return e>=zr?Math.round(r/zr)+"d":e>=ys?Math.round(r/ys)+"h":e>=hs?Math.round(r/hs)+"m":e>=fs?Math.round(r/fs)+"s":r+"ms"}function Rw(r){var e=Math.abs(r);return e>=zr?vi(r,e,zr,"day"):e>=ys?vi(r,e,ys,"hour"):e>=hs?vi(r,e,hs,"minute"):e>=fs?vi(r,e,fs,"second"):r+" ms"}function vi(r,e,t,s){var n=e>=t*1.5;return Math.round(r/t)+" "+s+(n?"s":"")}});var Co=It((lP,Up)=>{"use strict";m();function Tw(r){t.debug=t,t.default=t,t.coerce=c,t.disable=a,t.enable=n,t.enabled=o,t.humanize=Bp(),t.destroy=l,Object.keys(r).forEach(u=>{t[u]=r[u]}),t.names=[],t.skips=[],t.formatters={};function e(u){let d=0;for(let p=0;p<u.length;p++)d=(d<<5)-d+u.charCodeAt(p),d|=0;return t.colors[Math.abs(d)%t.colors.length]}t.selectColor=e;function t(u){let d,p=null,g,f;function h(...S){if(!h.enabled)return;let I=h,v=Number(new Date),k=v-(d||v);I.diff=k,I.prev=d,I.curr=v,d=v,S[0]=t.coerce(S[0]),typeof S[0]!="string"&&S.unshift("%O");let T=0;S[0]=S[0].replace(/%([a-zA-Z%])/g,(j,J)=>{if(j==="%%")return"%";T++;let M=t.formatters[J];if(typeof M=="function"){let Ce=S[T];j=M.call(I,Ce),S.splice(T,1),T--}return j}),t.formatArgs.call(I,S),(I.log||t.log).apply(I,S)}return h.namespace=u,h.useColors=t.useColors(),h.color=t.selectColor(u),h.extend=s,h.destroy=t.destroy,Object.defineProperty(h,"enabled",{enumerable:!0,configurable:!1,get:()=>p!==null?p:(g!==t.namespaces&&(g=t.namespaces,f=t.enabled(u)),f),set:S=>{p=S}}),typeof t.init=="function"&&t.init(h),h}function s(u,d){let p=t(this.namespace+(typeof d>"u"?":":d)+u);return p.log=this.log,p}function n(u){t.save(u),t.namespaces=u,t.names=[],t.skips=[];let d=(typeof u=="string"?u:"").trim().replace(/\s+/g,",").split(",").filter(Boolean);for(let p of d)p[0]==="-"?t.skips.push(p.slice(1)):t.names.push(p)}function i(u,d){let p=0,g=0,f=-1,h=0;for(;p<u.length;)if(g<d.length&&(d[g]===u[p]||d[g]==="*"))d[g]==="*"?(f=g,h=p,g++):(p++,g++);else if(f!==-1)g=f+1,h++,p=h;else return!1;for(;g<d.length&&d[g]==="*";)g++;return g===d.length}function a(){let u=[...t.names,...t.skips.map(d=>"-"+d)].join(",");return t.enable(""),u}function o(u){for(let d of t.skips)if(i(u,d))return!1;for(let d of t.names)if(i(u,d))return!0;return!1}function c(u){return u instanceof Error?u.stack||u.message:u}function l(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")}return t.enable(t.load()),t}Up.exports=Tw});var Hp=It((He,xi)=>{"use strict";m();He.formatArgs=Aw;He.save=$w;He.load=_w;He.useColors=kw;He.storage=Ow();He.destroy=(()=>{let r=!1;return()=>{r||(r=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})();He.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function kw(){if(typeof window<"u"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs))return!0;if(typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;let r;return typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&(r=navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/))&&parseInt(r[1],10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}function Aw(r){if(r[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+r[0]+(this.useColors?"%c ":" ")+"+"+xi.exports.humanize(this.diff),!this.useColors)return;let e="color: "+this.color;r.splice(1,0,e,"color: inherit");let t=0,s=0;r[0].replace(/%[a-zA-Z%]/g,n=>{n!=="%%"&&(t++,n==="%c"&&(s=t))}),r.splice(s,0,e)}He.log=console.debug||console.log||(()=>{});function $w(r){try{r?He.storage.setItem("debug",r):He.storage.removeItem("debug")}catch{}}function _w(){let r;try{r=He.storage.getItem("debug")||He.storage.getItem("DEBUG")}catch{}return!r&&typeof process<"u"&&"env"in process&&(r=process.env.DEBUG),r}function Ow(){try{return localStorage}catch{}}xi.exports=Co()(He);var{formatters:Lw}=xi.exports;Lw.j=function(r){try{return JSON.stringify(r)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}});var Gp=It((pP,Vp)=>{"use strict";m();Vp.exports=(r,e=process.argv)=>{let t=r.startsWith("-")?"":r.length===1?"-":"--",s=e.indexOf(t+r),n=e.indexOf("--");return s!==-1&&(n===-1||s<n)}});var Kp=It((gP,qp)=>{"use strict";m();var Dw=require("os"),zp=require("tty"),at=Gp(),{env:xe}=process,vr;at("no-color")||at("no-colors")||at("color=false")||at("color=never")?vr=0:(at("color")||at("colors")||at("color=true")||at("color=always"))&&(vr=1);"FORCE_COLOR"in xe&&(xe.FORCE_COLOR==="true"?vr=1:xe.FORCE_COLOR==="false"?vr=0:vr=xe.FORCE_COLOR.length===0?1:Math.min(parseInt(xe.FORCE_COLOR,10),3));function Io(r){return r===0?!1:{level:r,hasBasic:!0,has256:r>=2,has16m:r>=3}}function Po(r,e){if(vr===0)return 0;if(at("color=16m")||at("color=full")||at("color=truecolor"))return 3;if(at("color=256"))return 2;if(r&&!e&&vr===void 0)return 0;let t=vr||0;if(xe.TERM==="dumb")return t;if(process.platform==="win32"){let s=Dw.release().split(".");return Number(s[0])>=10&&Number(s[2])>=10586?Number(s[2])>=14931?3:2:1}if("CI"in xe)return["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI","GITHUB_ACTIONS","BUILDKITE"].some(s=>s in xe)||xe.CI_NAME==="codeship"?1:t;if("TEAMCITY_VERSION"in xe)return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(xe.TEAMCITY_VERSION)?1:0;if(xe.COLORTERM==="truecolor")return 3;if("TERM_PROGRAM"in xe){let s=parseInt((xe.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(xe.TERM_PROGRAM){case"iTerm.app":return s>=3?3:2;case"Apple_Terminal":return 2}}return/-256(color)?$/i.test(xe.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(xe.TERM)||"COLORTERM"in xe?1:t}function Nw(r){let e=Po(r,r&&r.isTTY);return Io(e)}qp.exports={supportsColor:Nw,stdout:Io(Po(!0,zp.isatty(1))),stderr:Io(Po(!0,zp.isatty(2)))}});var Xp=It((Se,Ei)=>{"use strict";m();var Fw=require("tty"),Si=require("util");Se.init=Gw;Se.log=Uw;Se.formatArgs=jw;Se.save=Hw;Se.load=Vw;Se.useColors=Mw;Se.destroy=Si.deprecate(()=>{},"Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");Se.colors=[6,2,3,4,5,1];try{let r=Kp();r&&(r.stderr||r).level>=2&&(Se.colors=[20,21,26,27,32,33,38,39,40,41,42,43,44,45,56,57,62,63,68,69,74,75,76,77,78,79,80,81,92,93,98,99,112,113,128,129,134,135,148,149,160,161,162,163,164,165,166,167,168,169,170,171,172,173,178,179,184,185,196,197,198,199,200,201,202,203,204,205,206,207,208,209,214,215,220,221])}catch{}Se.inspectOpts=Object.keys(process.env).filter(r=>/^debug_/i.test(r)).reduce((r,e)=>{let t=e.substring(6).toLowerCase().replace(/_([a-z])/g,(n,i)=>i.toUpperCase()),s=process.env[e];return/^(yes|on|true|enabled)$/i.test(s)?s=!0:/^(no|off|false|disabled)$/i.test(s)?s=!1:s==="null"?s=null:s=Number(s),r[t]=s,r},{});function Mw(){return"colors"in Se.inspectOpts?!!Se.inspectOpts.colors:Fw.isatty(process.stderr.fd)}function jw(r){let{namespace:e,useColors:t}=this;if(t){let s=this.color,n="\x1B[3"+(s<8?s:"8;5;"+s),i=` ${n};1m${e} \x1B[0m`;r[0]=i+r[0].split(`
|
|
941
|
+
})();`});async function Ap(r,e){switch(e.type){case"inline":return e.data??[];case"json-file":{if(!e.path)throw new Error("JSON data source requires a path");let t=Lo.default.resolve(r,e.path);if(!await wn.default.pathExists(t))throw new Error(`Data file not found: ${t}`);let s=await wn.default.readJson(t);if(!Array.isArray(s))throw new Error("JSON data file must contain an array of objects");return Tp.info({path:t,rows:s.length},"Loaded JSON data rows"),s.map(n=>Object.fromEntries(Object.entries(n).map(([i,a])=>[i,String(a)])))}case"csv-file":{if(!e.path)throw new Error("CSV data source requires a path");let t=Lo.default.resolve(r,e.path);if(!await wn.default.pathExists(t))throw new Error(`Data file not found: ${t}`);let n=(await wn.default.readFile(t,"utf8")).trim().split(`
|
|
942
|
+
`).map(o=>o.trim()).filter(Boolean);if(n.length<2)return[];let i=n[0].split(",").map(o=>o.trim()),a=n.slice(1).map(o=>{let c=o.split(",").map(u=>u.trim()),l={};return i.forEach((u,d)=>{l[u]=c[d]??""}),l});return Tp.info({path:t,rows:a.length},"Loaded CSV data rows"),a}default:return[]}}var Lo,wn,Tp,$p=b(()=>{"use strict";g();Lo=x(require("path")),wn=x(require("fs-extra"));j();Tp=I("data-loader")});function _p(r){return Wt.default.join(r,".assuremind","visual-regression.json")}function Op(r,e){return Wt.default.join(r,"results","visual-diffs",e,"summary.json")}function Nw(r){return Wt.default.join(r,"results","visual-diffs")}function Lp(r){return Wt.default.join(r,"baselines")}async function bn(r){let e=_p(r);if(!await dr.default.pathExists(e))return{...Do};try{let t=await ve(e);return{...Do,...t}}catch{return{...Do}}}async function Dp(r,e){await re(_p(r),e)}async function Li(r,e){await re(Op(r,e.runId),e)}async function Di(r,e){return await ve(Op(r,e))}async function Np(r){let e=Nw(r);if(!await dr.default.pathExists(e))return[];let t=await dr.default.readdir(e,{withFileTypes:!0}),s=[];for(let n of t){if(!n.isDirectory())continue;let i=Wt.default.join(e,n.name,"summary.json");if(await dr.default.pathExists(i))try{let a=await ve(i),o=a.diffs??[];s.push({runId:a.runId,timestamp:a.timestamp,totalDiffs:o.length,passed:o.filter(c=>c.status==="passed").length,failed:o.filter(c=>c.status==="failed").length,newCount:o.filter(c=>c.status==="new").length,approved:o.filter(c=>c.status==="approved").length,rejected:o.filter(c=>c.status==="rejected").length})}catch{}}return s.sort((n,i)=>new Date(i.timestamp).getTime()-new Date(n.timestamp).getTime()),s}async function Fp(r,e,t,s){let n=await Di(r,e),i=n.diffs.find(a=>a.stepId===t);if(!i)throw new Error(`Diff entry not found for stepId "${t}" in run "${e}"`);return i.status=s,await Li(r,n),n}async function Mp(r,e){let t=await Di(r,e);for(let s of t.diffs)(s.status==="failed"||s.status==="new")&&(s.status="approved");return await Li(r,t),t}async function jp(r){let e=Lp(r);if(!await dr.default.pathExists(e))return[];let t=[];async function s(n){let i=await dr.default.readdir(n,{withFileTypes:!0});for(let a of i){let o=Wt.default.join(n,a.name);if(a.isDirectory())await s(o);else if(a.name.endsWith(".png")){let c=Wt.default.relative(e,o).replace(/\\/g,"/"),l=c.split("/"),u=await dr.default.stat(o);t.push({path:o,relativePath:c,suite:l.length>1?l[0]:"",caseName:l.length>2?l[1]:"",fileName:a.name,sizeBytes:u.size})}}}return await s(e),t}async function Bp(r,e){let t=Lp(r),s=Wt.default.resolve(e);if(!s.startsWith(Wt.default.resolve(t)))throw new Error("Path is outside baselines directory");await dr.default.remove(s)}var Wt,dr,Do,No=b(()=>{"use strict";g();Wt=x(require("path")),dr=x(require("fs-extra"));ot();Do={enabled:!1,defaultThreshold:.5,fullPage:!0,maskSelectors:[]}});async function Up(r){let e=await wt.default.readFile(r);return new Promise((t,s)=>{new Ni.PNG().parse(e,(i,a)=>{i?s(i):t(a)})})}function Fw(r){return Ni.PNG.sync.write(r)}async function Vp(r,e,t,s={}){let{threshold:n=.1,diffThreshold:i=.5}=s;if(!await wt.default.pathExists(r))return await wt.default.ensureDir(ks.default.dirname(r)),await wt.default.copy(e,r),{match:!0,diffPercentage:0,diffPixels:0,totalPixels:0,baselinePath:r,actualPath:e};let[o,c]=await Promise.all([Up(r),Up(e)]);if(o.width!==c.width||o.height!==c.height){await wt.default.ensureDir(ks.default.dirname(t));let k=t.replace(/\.png$/,"-baseline.png"),T=t.replace(/\.png$/,"-actual.png");await Promise.all([wt.default.copy(r,k),wt.default.copy(e,T)]);let P=Math.max(o.width*o.height,c.width*c.height);return{match:!1,diffPercentage:100,diffPixels:P,totalPixels:P,baselinePath:r,actualPath:e,error:`Size mismatch: baseline ${o.width}x${o.height} vs actual ${c.width}x${c.height}`}}let{width:l,height:u}=o,d=new Ni.PNG({width:l,height:u}),m=(0,Hp.default)(o.data,c.data,d.data,l,u,{threshold:n}),p=l*u,f=p>0?m/p*100:0,h=f<=i;await wt.default.ensureDir(ks.default.dirname(t));let S=Fw(d),C=t.replace(/\.png$/,"-baseline.png"),v=t.replace(/\.png$/,"-actual.png");return await Promise.all([wt.default.writeFile(t,S),wt.default.copy(r,C),wt.default.copy(e,v)]),{match:h,diffPercentage:Math.round(f*100)/100,diffPixels:m,totalPixels:p,diffImagePath:t,baselinePath:r,actualPath:e}}function Gp(r,e,t,s,n,i){return ks.default.join(r,"baselines",e,t,`${s}-${n}-${i.width}x${i.height}.png`)}function zp(r,e,t,s,n){return ks.default.join(r,"results","visual-diffs",e,`${t}-${s}-${n.width}x${n.height}-diff.png`)}var Hp,Ni,wt,ks,qp=b(()=>{"use strict";g();Hp=x(require("pixelmatch")),Ni=require("pngjs"),wt=x(require("fs-extra")),ks=x(require("path"))});var Wp=Mt((vP,Kp)=>{"use strict";g();Kp.exports=r=>{if(typeof r!="string")throw new TypeError("Expected a string");return r.replace(/[|\\{}()[\]^$+*?.]/g,"\\$&").replace(/-/g,"\\x2d")}});var Jp=Mt((SP,Xp)=>{"use strict";g();var Ts=1e3,As=Ts*60,$s=As*60,ns=$s*24,Mw=ns*7,jw=ns*365.25;Xp.exports=function(r,e){e=e||{};var t=typeof r;if(t==="string"&&r.length>0)return Bw(r);if(t==="number"&&isFinite(r))return e.long?Hw(r):Uw(r);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(r))};function Bw(r){if(r=String(r),!(r.length>100)){var e=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(r);if(e){var t=parseFloat(e[1]),s=(e[2]||"ms").toLowerCase();switch(s){case"years":case"year":case"yrs":case"yr":case"y":return t*jw;case"weeks":case"week":case"w":return t*Mw;case"days":case"day":case"d":return t*ns;case"hours":case"hour":case"hrs":case"hr":case"h":return t*$s;case"minutes":case"minute":case"mins":case"min":case"m":return t*As;case"seconds":case"second":case"secs":case"sec":case"s":return t*Ts;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return t;default:return}}}}function Uw(r){var e=Math.abs(r);return e>=ns?Math.round(r/ns)+"d":e>=$s?Math.round(r/$s)+"h":e>=As?Math.round(r/As)+"m":e>=Ts?Math.round(r/Ts)+"s":r+"ms"}function Hw(r){var e=Math.abs(r);return e>=ns?Fi(r,e,ns,"day"):e>=$s?Fi(r,e,$s,"hour"):e>=As?Fi(r,e,As,"minute"):e>=Ts?Fi(r,e,Ts,"second"):r+" ms"}function Fi(r,e,t,s){var n=e>=t*1.5;return Math.round(r/t)+" "+s+(n?"s":"")}});var Fo=Mt((IP,Yp)=>{"use strict";g();function Vw(r){t.debug=t,t.default=t,t.coerce=c,t.disable=a,t.enable=n,t.enabled=o,t.humanize=Jp(),t.destroy=l,Object.keys(r).forEach(u=>{t[u]=r[u]}),t.names=[],t.skips=[],t.formatters={};function e(u){let d=0;for(let m=0;m<u.length;m++)d=(d<<5)-d+u.charCodeAt(m),d|=0;return t.colors[Math.abs(d)%t.colors.length]}t.selectColor=e;function t(u){let d,m=null,p,f;function h(...S){if(!h.enabled)return;let C=h,v=Number(new Date),k=v-(d||v);C.diff=k,C.prev=d,C.curr=v,d=v,S[0]=t.coerce(S[0]),typeof S[0]!="string"&&S.unshift("%O");let T=0;S[0]=S[0].replace(/%([a-zA-Z%])/g,(G,ne)=>{if(G==="%%")return"%";T++;let ae=t.formatters[ne];if(typeof ae=="function"){let me=S[T];G=ae.call(C,me),S.splice(T,1),T--}return G}),t.formatArgs.call(C,S),(C.log||t.log).apply(C,S)}return h.namespace=u,h.useColors=t.useColors(),h.color=t.selectColor(u),h.extend=s,h.destroy=t.destroy,Object.defineProperty(h,"enabled",{enumerable:!0,configurable:!1,get:()=>m!==null?m:(p!==t.namespaces&&(p=t.namespaces,f=t.enabled(u)),f),set:S=>{m=S}}),typeof t.init=="function"&&t.init(h),h}function s(u,d){let m=t(this.namespace+(typeof d>"u"?":":d)+u);return m.log=this.log,m}function n(u){t.save(u),t.namespaces=u,t.names=[],t.skips=[];let d=(typeof u=="string"?u:"").trim().replace(/\s+/g,",").split(",").filter(Boolean);for(let m of d)m[0]==="-"?t.skips.push(m.slice(1)):t.names.push(m)}function i(u,d){let m=0,p=0,f=-1,h=0;for(;m<u.length;)if(p<d.length&&(d[p]===u[m]||d[p]==="*"))d[p]==="*"?(f=p,h=m,p++):(m++,p++);else if(f!==-1)p=f+1,h++,m=h;else return!1;for(;p<d.length&&d[p]==="*";)p++;return p===d.length}function a(){let u=[...t.names,...t.skips.map(d=>"-"+d)].join(",");return t.enable(""),u}function o(u){for(let d of t.skips)if(i(u,d))return!1;for(let d of t.names)if(i(u,d))return!0;return!1}function c(u){return u instanceof Error?u.stack||u.message:u}function l(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")}return t.enable(t.load()),t}Yp.exports=Vw});var Qp=Mt((st,Mi)=>{"use strict";g();st.formatArgs=zw;st.save=qw;st.load=Kw;st.useColors=Gw;st.storage=Ww();st.destroy=(()=>{let r=!1;return()=>{r||(r=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})();st.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function Gw(){if(typeof window<"u"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs))return!0;if(typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;let r;return typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&(r=navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/))&&parseInt(r[1],10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}function zw(r){if(r[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+r[0]+(this.useColors?"%c ":" ")+"+"+Mi.exports.humanize(this.diff),!this.useColors)return;let e="color: "+this.color;r.splice(1,0,e,"color: inherit");let t=0,s=0;r[0].replace(/%[a-zA-Z%]/g,n=>{n!=="%%"&&(t++,n==="%c"&&(s=t))}),r.splice(s,0,e)}st.log=console.debug||console.log||(()=>{});function qw(r){try{r?st.storage.setItem("debug",r):st.storage.removeItem("debug")}catch{}}function Kw(){let r;try{r=st.storage.getItem("debug")||st.storage.getItem("DEBUG")}catch{}return!r&&typeof process<"u"&&"env"in process&&(r=process.env.DEBUG),r}function Ww(){try{return localStorage}catch{}}Mi.exports=Fo()(st);var{formatters:Xw}=Mi.exports;Xw.j=function(r){try{return JSON.stringify(r)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}});var em=Mt((RP,Zp)=>{"use strict";g();Zp.exports=(r,e=process.argv)=>{let t=r.startsWith("-")?"":r.length===1?"-":"--",s=e.indexOf(t+r),n=e.indexOf("--");return s!==-1&&(n===-1||s<n)}});var sm=Mt((TP,rm)=>{"use strict";g();var Jw=require("os"),tm=require("tty"),bt=em(),{env:$e}=process,Fr;bt("no-color")||bt("no-colors")||bt("color=false")||bt("color=never")?Fr=0:(bt("color")||bt("colors")||bt("color=true")||bt("color=always"))&&(Fr=1);"FORCE_COLOR"in $e&&($e.FORCE_COLOR==="true"?Fr=1:$e.FORCE_COLOR==="false"?Fr=0:Fr=$e.FORCE_COLOR.length===0?1:Math.min(parseInt($e.FORCE_COLOR,10),3));function Mo(r){return r===0?!1:{level:r,hasBasic:!0,has256:r>=2,has16m:r>=3}}function jo(r,e){if(Fr===0)return 0;if(bt("color=16m")||bt("color=full")||bt("color=truecolor"))return 3;if(bt("color=256"))return 2;if(r&&!e&&Fr===void 0)return 0;let t=Fr||0;if($e.TERM==="dumb")return t;if(process.platform==="win32"){let s=Jw.release().split(".");return Number(s[0])>=10&&Number(s[2])>=10586?Number(s[2])>=14931?3:2:1}if("CI"in $e)return["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI","GITHUB_ACTIONS","BUILDKITE"].some(s=>s in $e)||$e.CI_NAME==="codeship"?1:t;if("TEAMCITY_VERSION"in $e)return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test($e.TEAMCITY_VERSION)?1:0;if($e.COLORTERM==="truecolor")return 3;if("TERM_PROGRAM"in $e){let s=parseInt(($e.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch($e.TERM_PROGRAM){case"iTerm.app":return s>=3?3:2;case"Apple_Terminal":return 2}}return/-256(color)?$/i.test($e.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test($e.TERM)||"COLORTERM"in $e?1:t}function Yw(r){let e=jo(r,r&&r.isTTY);return Mo(e)}rm.exports={supportsColor:Yw,stdout:Mo(jo(!0,tm.isatty(1))),stderr:Mo(jo(!0,tm.isatty(2)))}});var im=Mt((_e,Bi)=>{"use strict";g();var Qw=require("tty"),ji=require("util");_e.init=ib;_e.log=rb;_e.formatArgs=eb;_e.save=sb;_e.load=nb;_e.useColors=Zw;_e.destroy=ji.deprecate(()=>{},"Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");_e.colors=[6,2,3,4,5,1];try{let r=sm();r&&(r.stderr||r).level>=2&&(_e.colors=[20,21,26,27,32,33,38,39,40,41,42,43,44,45,56,57,62,63,68,69,74,75,76,77,78,79,80,81,92,93,98,99,112,113,128,129,134,135,148,149,160,161,162,163,164,165,166,167,168,169,170,171,172,173,178,179,184,185,196,197,198,199,200,201,202,203,204,205,206,207,208,209,214,215,220,221])}catch{}_e.inspectOpts=Object.keys(process.env).filter(r=>/^debug_/i.test(r)).reduce((r,e)=>{let t=e.substring(6).toLowerCase().replace(/_([a-z])/g,(n,i)=>i.toUpperCase()),s=process.env[e];return/^(yes|on|true|enabled)$/i.test(s)?s=!0:/^(no|off|false|disabled)$/i.test(s)?s=!1:s==="null"?s=null:s=Number(s),r[t]=s,r},{});function Zw(){return"colors"in _e.inspectOpts?!!_e.inspectOpts.colors:Qw.isatty(process.stderr.fd)}function eb(r){let{namespace:e,useColors:t}=this;if(t){let s=this.color,n="\x1B[3"+(s<8?s:"8;5;"+s),i=` ${n};1m${e} \x1B[0m`;r[0]=i+r[0].split(`
|
|
943
943
|
`).join(`
|
|
944
|
-
`+i),r.push(n+"m+"+
|
|
945
|
-
`)}function
|
|
946
|
-
`).map(e=>e.trim()).join(" ")};Wp.O=function(r){return this.inspectOpts.colors=this.useColors,Si.inspect(r,this.inspectOpts)}});var Jp=It((yP,Ro)=>{"use strict";m();typeof process>"u"||process.type==="renderer"||process.browser===!0||process.__nwjs?Ro.exports=Hp():Ro.exports=Xp()});function Ci(r){if(!r)throw new Error("name must be non-empty")}function zw(r,e){for(var t=0,s=r.length,n;t<s;)n=t+s>>>1,r[n].startTime<e.startTime?t=n+1:s=n;r.splice(t,0,e)}var Ee,em,Yp,To,Qp,Zp,Ri,Ti,cn,ki,Ii,Pi,tm=b(()=>{"use strict";m();Ee=typeof performance<"u"&&performance;Yp=process.hrtime,To=function(){var r=Yp();return r[0]*1e9+r[1]},Qp=To(),em=function(){return(To()-Qp)/1e6};Zp=Ee&&Ee.now?function(){return Ee.now()}:em;Ee&&Ee.mark&&Ee.measure&&Ee.getEntriesByName&&Ee.getEntriesByType&&Ee.clearMarks&&Ee.clearMeasures&&Ee.clearResourceTimings?(Ri=function(r){Ci(r),Ee.mark("start "+r)},Ti=function(r){Ci(r),Ee.mark("end "+r);var e=Ee.measure(r,"start "+r,"end "+r);if(e)return e;var t=Ee.getEntriesByName(r);return t[t.length-1]},cn=function(){return Ee.getEntriesByType("measure")},ki=function(){Ee.clearMarks(),Ee.clearMeasures()}):(Ii={},Pi=[],Ri=function(r){Ci(r);var e=Zp();Ii["$"+r]=e},Ti=function(r){Ci(r);var e=Zp(),t=Ii["$"+r];if(!t)throw new Error("no known mark: "+r);var s={startTime:t,name:r,duration:e-t,entryType:"measure"};return zw(Pi,s),s},cn=function(){return Pi},ki=function(){Ii={},Pi=[]})});var vs,nm,Sr,xr,ws,bs,ko,rm,Kw,sm,xs,ce,Ao=b(()=>{"use strict";m();vs=x(require("process"),1),nm=require("events"),Sr=x(Jp(),1);tm();xr=vs.default.platform==="win32",ws=vs.default.browser,bs={red:ws?"crimson":1,yellow:ws?"gold":3,cyan:ws?"darkturquoise":6,green:ws?"forestgreen":2,blue:ws?"steelblue":4,magenta:ws?"palevioletred":5};Sr.default.colors=[bs.cyan,bs.green,bs.blue,bs.magenta];ko=class extends nm.EventEmitter{constructor(e){super(e)}issueStatus(e,t){(e==="status"||e==="statusEnd")&&this.emit(e,[e,...t])}issueWarning(e,t){this.emit("warning",[e,...t])}},rm={},Kw=25,xs=class r{static _logToStdErr(e,t){r.loggerfn(e)(...t)}static loggerfn(e){e=`LH:${e}`;let t=rm[e];return t||(t=(0,Sr.default)(e),rm[e]=t,e.endsWith("error")?t.color=bs.red:e.endsWith("warn")&&(t.color=bs.yellow)),t}static setLevel(e){switch(sm=e,e){case"silent":Sr.default.enable("-LH:*");break;case"verbose":Sr.default.enable("LH:*");break;case"warn":Sr.default.enable("-LH:*, LH:*:warn, LH:*:error");break;case"error":Sr.default.enable("-LH:*, LH:*:error");break;default:Sr.default.enable("LH:*, -LH:*:verbose")}}static formatProtocol(e,t,s){let n=!vs.default||vs.default.browser?1/0:vs.default.stdout.columns,i=t.method||"?????",a=n-i.length-e.length-Kw,o=t.params&&i!=="IO.read"?JSON.stringify(t.params).substr(0,a):"";r._logToStdErr(`${e}:${s||""}`,[i,o])}static isVerbose(){return sm==="verbose"}static time({msg:e,id:t,args:s=[]},n="log"){Ri(t),r[n]("status",e,...s)}static timeEnd({msg:e,id:t,args:s=[]},n="verbose"){r[n]("statusEnd",e,...s),Ti(t)}static log(e,...t){return r.events.issueStatus(e,t),r._logToStdErr(e,t)}static warn(e,...t){return r.events.issueWarning(e,t),r._logToStdErr(`${e}:warn`,t)}static error(e,...t){return r._logToStdErr(`${e}:error`,t)}static verbose(e,...t){return r.events.issueStatus(e,t),r._logToStdErr(`${e}:verbose`,t)}static greenify(e){return`${r.green}${e}${r.reset}`}static redify(e){return`${r.red}${e}${r.reset}`}static get green(){return"\x1B[32m"}static get red(){return"\x1B[31m"}static get yellow(){return"\x1B[33m"}static get purple(){return"\x1B[95m"}static get reset(){return"\x1B[0m"}static get bold(){return"\x1B[1m"}static get dim(){return"\x1B[2m"}static get tick(){return xr?"\u221A":"\u2713"}static get cross(){return xr?"\xD7":"\u2718"}static get whiteSmallSquare(){return xr?"\u0387":"\u25AB"}static get heavyHorizontal(){return xr?"\u2500":"\u2501"}static get heavyVertical(){return xr?"\u2502 ":"\u2503 "}static get heavyUpAndRight(){return xr?"\u2514":"\u2517"}static get heavyVerticalAndRight(){return xr?"\u251C":"\u2523"}static get heavyDownAndHorizontal(){return xr?"\u252C":"\u2533"}static get doubleLightHorizontal(){return"\u2500\u2500"}};xs.events=new ko;xs.takeTimeEntries=()=>{let r=cn();return ki(),r};xs.getTimeEntries=()=>cn();ce=xs});var om=It((SP,am)=>{"use strict";m();var im=require("fs"),$o;function Ww(){try{return im.statSync("/.dockerenv"),!0}catch{return!1}}function Xw(){try{return im.readFileSync("/proc/self/cgroup","utf8").includes("docker")}catch{return!1}}am.exports=()=>($o===void 0&&($o=Ww()||Xw()),$o)});var um=It((CP,_o)=>{"use strict";m();var Jw=require("os"),Yw=require("fs"),cm=om(),lm=()=>{if(process.platform!=="linux")return!1;if(Jw.release().toLowerCase().includes("microsoft"))return!cm();try{return Yw.readFileSync("/proc/version","utf8").toLowerCase().includes("microsoft")?!cm():!1}catch{return!1}};process.env.__IS_WSL_TEST__?_o.exports=lm:_o.exports=lm()});function xt(r,e){return typeof r>"u"?e:r}async function gm(r){return new Promise(e=>setTimeout(e,r))}function Dt(){return mm.default?"wsl":process.platform}function fm(){switch(Dt()){case"darwin":case"linux":return eb();case"wsl":process.env.TEMP=Oo(`${process.env.PATH}`);case"win32":return tb();default:throw new ln}}function Qw(r=""){let e=/\/mnt\/([a-z])\//.exec(r);if(!e)return r;let t=e[1];return r.replace(`/mnt/${t}/`,`${t.toUpperCase()}:\\`).replace(/\//g,"\\")}function hm(r=""){if(/[a-z]:\\/iu.test(r))return r;try{return _i.default.execFileSync("wslpath",["-w",r]).toString().trim()}catch{return Qw(r)}}function Oi(r,e){try{return _i.default.execFileSync("wslpath",["-u",r]).toString().trim()}catch{return e}}function Zw(r){let t=/\/mnt\/([a-z])\/Users\/([^\/:]+)\/AppData\//.exec(r)||[];return`/mnt/${t[1]}/Users/${t[2]}/AppData/Local`}function Oo(r){let t=/\/([a-z])\/Users\/([^\/:]+)\/AppData\//.exec(r)||[];return Oi(`${t[1]}:\\Users\\${t[2]}\\AppData\\Local`,Zw(r))}function eb(){return _i.default.execSync("mktemp -d -t lighthouse.XXXXXXX").toString().trim()}function tb(){let r=process.env.TEMP||process.env.TMP||(process.env.SystemRoot||process.env.windir)+"\\temp",e=Math.floor(Math.random()*9e7+1e7),t=(0,dm.join)(r,"lighthouse."+e);return(0,pm.mkdirSync)(t,{recursive:!0}),t}var dm,_i,pm,mm,Ss,Ai,$i,ln,un,Lo=b(()=>{"use strict";m();dm=require("path"),_i=x(require("child_process"),1),pm=require("fs"),mm=x(um(),1);Ss=class extends Error{constructor(e="Unexpected error",t){return super(),this.message=e,this.code=t,this.stack=new Error().stack,this}},Ai=class extends Ss{constructor(){super(...arguments),this.message="The CHROME_PATH environment variable must be set to a Chrome/Chromium executable no older than Chrome stable.",this.code="ERR_LAUNCHER_PATH_NOT_SET"}},$i=class extends Ss{constructor(){super(...arguments),this.message="userDataDir must be false or a path.",this.code="ERR_LAUNCHER_INVALID_USER_DATA_DIRECTORY"}},ln=class extends Ss{constructor(){super(...arguments),this.message=`Platform ${Dt()} is not supported.`,this.code="ERR_LAUNCHER_UNSUPPORTED_PLATFORM"}},un=class extends Ss{constructor(){super(...arguments),this.message="No Chrome installations found.",this.code="ERR_LAUNCHER_NOT_INSTALLED"}}});var Li={};Q(Li,{darwin:()=>wm,darwinFast:()=>Fo,linux:()=>rb,win32:()=>bm,wsl:()=>sb});function Fo(){let r=[process.env.CHROME_PATH,process.env.LIGHTHOUSE_CHROMIUM_PATH,"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary","/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"];for(let e of r)if(e&&Er(e))return e;return wm()[0]}function wm(){let r=["/Contents/MacOS/Google Chrome Canary","/Contents/MacOS/Google Chrome"],e="/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister",t=[],s=Mo();s&&t.push(s),(0,Cs.execSync)(`${e} -dump | grep -i 'google chrome\\( canary\\)\\?\\.app' | awk '{$1=""; print $0}'`).toString().split(No).forEach(a=>{r.forEach(o=>{let c=ot.default.join(a.substring(0,a.indexOf(".app")+4).trim(),o);Er(c)&&t.indexOf(c)===-1&&t.push(c)})});let n=(0,Es.default)(process.env.HOME||(0,Do.homedir)()),i=[{regex:new RegExp(`^${n}/Applications/.*Chrome\\.app`),weight:50},{regex:new RegExp(`^${n}/Applications/.*Chrome Canary\\.app`),weight:51},{regex:/^\/Applications\/.*Chrome.app/,weight:100},{regex:/^\/Applications\/.*Chrome Canary.app/,weight:101},{regex:/^\/Volumes\/.*Chrome.app/,weight:-2},{regex:/^\/Volumes\/.*Chrome Canary.app/,weight:-1}];return process.env.LIGHTHOUSE_CHROMIUM_PATH&&i.unshift({regex:new RegExp((0,Es.default)(process.env.LIGHTHOUSE_CHROMIUM_PATH)),weight:150}),process.env.CHROME_PATH&&i.unshift({regex:new RegExp((0,Es.default)(process.env.CHROME_PATH)),weight:151}),vm(t,i)}function Mo(){if(Er(process.env.CHROME_PATH))return process.env.CHROME_PATH;if(Er(process.env.LIGHTHOUSE_CHROMIUM_PATH))return ce.warn("ChromeLauncher","LIGHTHOUSE_CHROMIUM_PATH is deprecated, use CHROME_PATH env variable instead."),process.env.LIGHTHOUSE_CHROMIUM_PATH}function rb(){let r=[],e=Mo();if(e&&r.push(e),[ot.default.join((0,Do.homedir)(),".local/share/applications/"),"/usr/share/applications/"].forEach(i=>{r=r.concat(ib(i))}),["google-chrome-stable","google-chrome","chromium-browser","chromium"].forEach(i=>{try{let a=(0,Cs.execFileSync)("which",[i],{stdio:"pipe"}).toString().split(No)[0];Er(a)&&r.push(a)}catch{}}),!r.length)throw new Ai;let n=[{regex:/chrome-wrapper$/,weight:51},{regex:/google-chrome-stable$/,weight:50},{regex:/google-chrome$/,weight:49},{regex:/chromium-browser$/,weight:48},{regex:/chromium$/,weight:47}];return process.env.LIGHTHOUSE_CHROMIUM_PATH&&n.unshift({regex:new RegExp((0,Es.default)(process.env.LIGHTHOUSE_CHROMIUM_PATH)),weight:100}),process.env.CHROME_PATH&&n.unshift({regex:new RegExp((0,Es.default)(process.env.CHROME_PATH)),weight:101}),vm(nb(r.filter(Boolean)),n)}function sb(){return process.env.LOCALAPPDATA=Oo(`${process.env.PATH}`),process.env.PROGRAMFILES=Oi("C:/Program Files","/mnt/c/Program Files"),process.env["PROGRAMFILES(X86)"]=Oi("C:/Program Files (x86)","/mnt/c/Program Files (x86)"),bm()}function bm(){let r=[],e=[`${ot.default.sep}Google${ot.default.sep}Chrome SxS${ot.default.sep}Application${ot.default.sep}chrome.exe`,`${ot.default.sep}Google${ot.default.sep}Chrome${ot.default.sep}Application${ot.default.sep}chrome.exe`],t=[process.env.LOCALAPPDATA,process.env.PROGRAMFILES,process.env["PROGRAMFILES(X86)"]].filter(Boolean),s=Mo();return s&&r.push(s),t.forEach(n=>e.forEach(i=>{let a=ot.default.join(n,i);Er(a)&&r.push(a)})),r}function vm(r,e){return r.map(s=>{for(let n of e)if(n.regex.test(s))return{path:s,weight:n.weight};return{path:s,weight:10}}).sort((s,n)=>n.weight-s.weight).map(s=>s.path)}function Er(r){if(!r)return!1;try{return ym.default.accessSync(r),!0}catch{return!1}}function nb(r){return Array.from(new Set(r))}function ib(r){let e=/(^[^ ]+).*/,t="^Exec=/.*/(google-chrome|chrome|chromium)-.*",s=[];if(Er(r)){let n;try{n=(0,Cs.execSync)(`grep -ER "${t}" ${r} | awk -F '=' '{print $2}'`,{stdio:"pipe"})}catch{n=(0,Cs.execSync)(`grep -Er "${t}" ${r} | awk -F '=' '{print $2}'`,{stdio:"pipe"})}n=n.toString().split(No).map(i=>i.replace(e,"$1")),n.forEach(i=>Er(i)&&s.push(i))}return s}var ym,ot,Do,Cs,Es,No,xm=b(()=>{"use strict";m();ym=x(require("fs"),1),ot=x(require("path"),1),Do=require("os"),Cs=require("child_process"),Es=x(Mp(),1);Ao();Lo();No=/\r?\n/});function Em(){return new Promise((r,e)=>{let t=(0,Sm.createServer)();t.listen(0),t.once("listening",()=>{let{port:s}=t.address();t.close(()=>r(s))}),t.once("error",e)})}var Sm,Cm=b(()=>{"use strict";m();Sm=require("http");});var jo,Im=b(()=>{"use strict";m();jo=["--disable-features="+["Translate","OptimizationHints","MediaRouter","DialMediaRouteProvider","CalculateNativeWinOcclusion","InterestFeedContentSuggestions","CertificateTransparencyComponentUpdater","AutofillServerCommunication","PrivacySandboxSettings4","RenderDocument"].join(","),"--disable-extensions","--disable-component-extensions-with-background-pages","--disable-background-networking","--disable-component-update","--disable-client-side-phishing-detection","--disable-sync","--metrics-recording-only","--disable-default-apps","--mute-audio","--no-default-browser-check","--no-first-run","--disable-backgrounding-occluded-windows","--disable-renderer-backgrounding","--disable-background-timer-throttling","--disable-ipc-flooding-protection","--password-store=basic","--use-mock-keychain","--force-fieldtrials=*BackgroundTracing/default/","--disable-hang-monitor","--disable-prompt-on-repost","--disable-domain-reliability","--propagate-iph-for-testing"]});async function db(r={}){r.handleSIGINT=xt(r.handleSIGINT,!0);let e=new dn(r);r.handleSIGINT&&Is.size===0&&process.on(Pm,Rm),Is.add(e),await e.launch();let t=()=>{Is.delete(e),Is.size===0&&process.removeListener(Pm,Rm),e.kill()};return{pid:e.pid,port:e.port,process:e.chromeProcess,remoteDebuggingPipes:e.remoteDebuggingPipes,kill:t}}function pb(){let r=dn.getFirstInstallation();if(!r)throw new un;return r}function km(){let r=[];for(let e of Is)try{e.kill(),Is.delete(e)}catch(t){r.push(t)}return r}var ab,Tm,Di,ob,cb,Pm,lb,ub,Is,Rm,dn,Am=b(()=>{"use strict";m();ab=x(require("fs"),1),Tm=x(require("net"),1);xm();Cm();Im();Lo();Di=require("child_process");Ao();ob=Dt()==="wsl",cb=Dt()==="win32",Pm="SIGINT",lb=130,ub=new Set(["darwin","linux","win32","wsl"]),Is=new Set,Rm=()=>{km(),process.exit(lb)};dn=class r{constructor(e={},t={}){if(this.opts=e,this.tmpDirandPidFileReady=!1,this.remoteDebuggingPipes=null,this.fs=t.fs||ab,this.spawn=t.spawn||Di.spawn,ce.setLevel(xt(this.opts.logLevel,"silent")),this.startingUrl=xt(this.opts.startingUrl,"about:blank"),this.chromeFlags=xt(this.opts.chromeFlags,[]),this.prefs=xt(this.opts.prefs,{}),this.requestedPort=xt(this.opts.port,0),this.portStrictMode=e.portStrictMode,this.chromePath=this.opts.chromePath,this.ignoreDefaultFlags=xt(this.opts.ignoreDefaultFlags,!1),this.connectionPollInterval=xt(this.opts.connectionPollInterval,500),this.maxConnectionRetries=xt(this.opts.maxConnectionRetries,50),this.envVars=xt(e.envVars,Object.assign({},process.env)),typeof this.opts.userDataDir=="boolean")if(!this.opts.userDataDir)this.useDefaultProfile=!0,this.userDataDir=void 0;else throw new $i;else this.useDefaultProfile=!1,this.userDataDir=this.opts.userDataDir;this.useRemoteDebuggingPipe=this.chromeFlags.some(s=>s.startsWith("--remote-debugging-pipe"))}get flags(){let e=this.ignoreDefaultFlags?[]:jo.slice();return this.port&&e.push(`--remote-debugging-port=${this.port}`),!this.ignoreDefaultFlags&&Dt()==="linux"&&e.push("--disable-setuid-sandbox"),this.useDefaultProfile||e.push(`--user-data-dir=${ob?hm(this.userDataDir):this.userDataDir}`),process.env.HEADLESS&&e.push("--headless"),e.push(...this.chromeFlags),e.push(this.startingUrl),e}static defaultFlags(){return jo.slice()}static getFirstInstallation(){return Dt()==="darwin"?Fo():Li[Dt()]()[0]}static getInstallations(){return Li[Dt()]()}makeTmpDir(){return fm()}prepare(){let e=Dt();if(!ub.has(e))throw new ln;this.userDataDir=this.userDataDir||this.makeTmpDir(),this.outFile=this.fs.openSync(`${this.userDataDir}/chrome-out.log`,"a"),this.errFile=this.fs.openSync(`${this.userDataDir}/chrome-err.log`,"a"),this.setBrowserPrefs(),this.pidFile=`${this.userDataDir}/chrome.pid`,ce.verbose("ChromeLauncher",`created ${this.userDataDir}`),this.tmpDirandPidFileReady=!0}setBrowserPrefs(){if(Object.keys(this.prefs).length===0)return;let e=`${this.userDataDir}/Default`;this.fs.existsSync(e)||this.fs.mkdirSync(e,{recursive:!0});let t=`${e}/Preferences`;try{if(this.fs.existsSync(t)){let s=this.fs.readFileSync(t,"utf-8"),n=JSON.parse(s);this.fs.writeFileSync(t,JSON.stringify({...n,...this.prefs}),"utf-8")}else this.fs.writeFileSync(t,JSON.stringify({...this.prefs}),"utf-8")}catch(s){ce.log("ChromeLauncher",`Failed to set browser prefs: ${s.message}`)}}async launch(){if(this.requestedPort!==0){this.port=this.requestedPort;try{await this.isDebuggerReady(),ce.log("ChromeLauncher",`Found existing Chrome already running using port ${this.port}, using that.`);return}catch{if(this.portStrictMode)throw new Error(`found no Chrome at port ${this.requestedPort}`);ce.log("ChromeLauncher",`No debugging port found on port ${this.port}, launching a new Chrome.`)}}if(this.chromePath===void 0){let e=r.getFirstInstallation();if(!e)throw new un;this.chromePath=e}return this.tmpDirandPidFileReady||this.prepare(),this.pid=await this.spawnProcess(this.chromePath),Promise.resolve()}async spawnProcess(e){let s=await(async()=>this.chromeProcess?(ce.log("ChromeLauncher",`Chrome already running with pid ${this.chromeProcess.pid}.`),this.chromeProcess.pid):(this.requestedPort===0&&(this.useRemoteDebuggingPipe?this.port=0:this.port=await Em()),ce.verbose("ChromeLauncher",`Launching with command:
|
|
947
|
-
"${e}" ${this.flags.join(" ")}`),this.chromeProcess=this.spawn(e,this.flags,{detached:process.platform!=="win32",stdio:this.useRemoteDebuggingPipe?["ignore",this.outFile,this.errFile,"pipe","pipe"]:["ignore",this.outFile,this.errFile],env:this.envVars}),this.chromeProcess.pid&&this.fs.writeFileSync(this.pidFile,this.chromeProcess.pid.toString()),this.useRemoteDebuggingPipe&&(this.remoteDebuggingPipes={incoming:this.chromeProcess.stdio[4],outgoing:this.chromeProcess.stdio[3]}),ce.verbose("ChromeLauncher",`Chrome running with pid ${this.chromeProcess.pid} on port ${this.port}.`),this.chromeProcess.pid))();return this.port!==0&&await this.waitUntilReady(),s}cleanup(e){e&&(e.removeAllListeners(),e.end(),e.destroy(),e.unref())}isDebuggerReady(){return new Promise((e,t)=>{let s=Tm.createConnection(this.port,"127.0.0.1");s.once("error",n=>{this.cleanup(s),t(n)}),s.once("connect",()=>{this.cleanup(s),e()})})}waitUntilReady(){let e=this;return new Promise((t,s)=>{let n=0,i="Waiting for browser.",a=()=>{n===0&&ce.log("ChromeLauncher",i),n++,i+="..",ce.log("ChromeLauncher",i),e.isDebuggerReady().then(()=>{ce.log("ChromeLauncher",i+`${ce.greenify(ce.tick)}`),t()}).catch(o=>{if(n>e.maxConnectionRetries){ce.error("ChromeLauncher",o.message);let c=this.fs.readFileSync(`${this.userDataDir}/chrome-err.log`,{encoding:"utf-8"});return ce.error("ChromeLauncher",`Logging contents of ${this.userDataDir}/chrome-err.log`),ce.error("ChromeLauncher",c),s(o)}gm(e.connectionPollInterval).then(a)})};a()})}kill(){if(this.chromeProcess){this.chromeProcess.on("close",()=>{delete this.chromeProcess,this.destroyTmp()}),ce.log("ChromeLauncher",`Killing Chrome instance ${this.chromeProcess.pid}`);try{if(cb){let e=(0,Di.spawnSync)(`taskkill /pid ${this.chromeProcess.pid} /T /F`,{shell:!0,encoding:"utf-8"}),{stderr:t}=e;t&&ce.error("ChromeLauncher","taskkill stderr",t)}else this.chromeProcess.pid&&process.kill(-this.chromeProcess.pid,"SIGKILL")}catch(e){let t=`Chrome could not be killed ${e.message}`;ce.warn("ChromeLauncher",t)}this.destroyTmp()}}destroyTmp(){if(this.outFile&&(this.fs.closeSync(this.outFile),delete this.outFile),this.userDataDir===void 0||this.opts.userDataDir!==void 0)return;this.errFile&&(this.fs.closeSync(this.errFile),delete this.errFile),(this.fs.rmSync||this.fs.rmdirSync)(this.userDataDir,{recursive:!0,force:!0,maxRetries:10})}}});var $m={};Q($m,{Launcher:()=>dn,getChromePath:()=>pb,killAll:()=>km,launch:()=>db});var _m=b(()=>{"use strict";m();Am()});var Fm={};Q(Fm,{getActiveRunId:()=>mb,runTests:()=>Ho,stopActiveRun:()=>Uo});function Dm(r){return r.steps.length===0?!1:r.steps.every(e=>(e.stepType??tt(e.instruction))==="api")}function Nm(r){return r.type==="api"}function Ni(r){return r.type==="audit"||r.type==="performance"}function Uo(){Fi&&Fi()}function mb(){return Bo}async function Ho(r,e,t,s){let n=(0,Lm.randomUUID)(),i=new Date().toISOString(),a=Jt.default.join(r,"tests"),o=!1;Bo=n,Fi=()=>{o=!0,le.info({runId:n},"Run stop requested")};let c=e.env??t.environment??"DEV",l=t.environmentUrls?.[c];l&&(t={...t,baseUrl:l}),le.info({runId:n,runConfig:e,environment:c,baseUrl:t.baseUrl},"Run started");let u=await Uc(r,n),d=(I,v,k)=>{let T=new Date().toISOString(),P=k?" "+JSON.stringify(k):"";u.write(`[${T}] [${I.toUpperCase()}] ${v}${P}`)};d("info","Run started",{runId:n,environment:c,baseUrl:t.baseUrl});let p=new li,g=new pi,f=new mi(r,n,c),h=await on(r),S=[];h.enabled&&(t.screenshot!=="on"&&(t={...t,screenshot:"on"}),d("info","Visual regression enabled \u2014 screenshots forced ON",{threshold:h.defaultThreshold,fullPage:h.fullPage}));try{let I=await fb(a,e);I.length===0&&le.warn({runConfig:e},"No tests matched the run config");let v=await hb(r,c),k=new Map,T=[];for(let _ of I){if(_.testCase.dataSource)try{let Z=await vp(r,_.testCase.dataSource);if(Z.length>0){Z.forEach((ie,we)=>T.push({..._,dataRow:ie,dataRowIndex:we})),le.info({caseId:_.testCase.id,rows:Z.length},"Expanded data-driven case");continue}}catch(Z){le.warn({err:Z,caseId:_.testCase.id},"Failed to load data rows \u2014 running case once")}T.push(_)}if(s?.broadcast("run:start",{runId:n,totalTests:T.length}),t.mcp?.proactiveHealing&&t.mcp?.enabled){le.info("Proactive healing enabled \u2014 validating selectors before run");let _=null;try{_=new ht({headless:t.mcp.headless,actionTimeout:t.mcp.actionTimeout,idleTimeout:t.mcp.idleTimeout});for(let Z of T)for(let ie of Z.testCase.steps){if(!ie.generatedCode.trim())continue;let we=await zd(_,t.baseUrl,ie.generatedCode);we.ok||(s?.broadcast("healing:proactive",{runId:n,caseId:Z.testCase.id,stepId:ie.id,instruction:ie.instruction,missing:we.checks.filter(be=>!be.found).map(be=>({selector:be.selector,suggestion:be.suggestion}))}),le.warn({stepId:ie.id,missing:we.missing},`Proactive check: ${we.missing} selector(s) may be broken`))}}catch(Z){le.warn({err:Z},"Proactive healing check failed \u2014 continuing with run")}finally{await _?.disconnect()}}let P;if(t.rag?.enabled)try{P=await oi.create(r,t.rag),le.info(P.getStats(),"RAG memory loaded"),d("info","RAG memory loaded",P.getStats())}catch(_){le.warn({err:_},"RAG initialization failed \u2014 continuing without memory")}let j=st(r,void 0,void 0,P),J=j.getPrimaryProvider(),M=new ui(t.healing.dailyBudget),Ce=t.browsers,lt=[],Ve=T.length>0&&T.every(({suite:_,testCase:Z})=>(Nm(_)||Dm(Z))&&!Ni(_));Ve&&(le.info("All test cases are API-only \u2014 no browser will be launched"),d("info","API-only run detected \u2014 skipping browser launch"));let Ye=new Map;if(Ve)for(let _ of Ce)Ye.set(_,null);else{let _=await Promise.all(Ce.map(async Z=>({name:Z,instance:await p.launchBrowser(Z,t)})));for(let{name:Z,instance:ie}of _)Ye.set(Z,ie)}let js=[];for(let _ of Ce){let Z=Ye.get(_)??null;for(let ie of T)js.push({browserName:_,browser:Z,...ie})}let nr=(await yb(js,t.parallel,async({browserName:_,browser:Z,suite:ie,testCase:we,dataRow:be,dataRowIndex:U})=>{if(o)return null;let ut=be?{...v,...be}:v,Qe=be?`${we.name} [row ${(U??0)+1}]`:we.name;s?.broadcast("run:case",{runId:n,suiteId:ie.id,caseId:`${we.id}_${_}`,caseName:Ce.length>1?`${Qe} [${_}]`:Qe,browser:_,status:"running"});let Y=`${we.id}_${_}`;d("info",`Case started: ${ie.name} > ${Qe}`,{suiteId:ie.id,caseId:we.id,browser:_});let q=await gb(r,n,ie,we,_,Z,t,ut,k,J,M,g,p,s,d,Y,P,h,S);return q.suiteType=ie.type,be!==void 0&&(q.caseResult.dataRowIndex=U,q.caseResult.dataRow=be,q.caseResult.caseName=Qe),s?.broadcast("run:case:done",{runId:n,suiteId:ie.id,caseId:we.id,status:q.caseResult.status}),d("info",`Case finished: ${Qe} [${_}] \u2014 ${q.caseResult.status.toUpperCase()}`,{duration:q.caseResult.duration,steps:q.caseResult.steps.length,passed:q.caseResult.steps.filter(ae=>ae.status==="passed").length,failed:q.caseResult.steps.filter(ae=>ae.status==="failed").length}),{...q,browserName:_}})).filter(Boolean);if(Ce.length>1)for(let _ of Ce){let Z=nr.filter(we=>we.browserName===_),ie=Om(Z,_);lt.push(...ie)}else{let _=Om(nr);lt.push(..._)}let Bs=new Date().toISOString(),Ie=wb(n,i,Bs,lt,c);if(Ie.logFilePath=u.logFilePath,d("info",`Run finished: ${Ie.status.toUpperCase()}`,{totalTests:Ie.totalTests,passed:Ie.passed,failed:Ie.failed,skipped:Ie.skipped,duration:Ie.duration}),u.close(),await Rd(r,Ie),t.reporting.allure&&(await f.writeResults(Ie),ip(r,n).catch(_=>{le.warn({err:_ instanceof Error?_.message:String(_),runId:n},"Allure report generation failed")})),t.reporting.json){let _=Jt.default.join(r,"results",`run-${n}.json`);await Cr.default.ensureDir(Jt.default.dirname(_)),await Cr.default.writeJson(_,Ie,{spaces:2})}if(h.enabled&&S.length>0&&(await yi(r,{runId:n,timestamp:Bs,diffs:S}),d("info",`Visual regression: ${S.length} comparisons saved`,{passed:S.filter(_=>_.status==="passed").length,failed:S.filter(_=>_.status==="failed").length,new:S.filter(_=>_.status==="new").length})),await g.flush(r,n),await j.getCache().persist(),P)try{await P.persist(),le.info(P.getStats(),"RAG memory persisted")}catch(_){le.warn({err:_},"RAG persistence failed")}return le.info({runId:n,passed:Ie.passed,failed:Ie.failed,total:Ie.totalTests,healingSpend:M.totalSpent},"Run finished"),Ie}finally{Fi=null,Bo=null,await p.closeAll(),o&&s?.broadcast("run:stopped",{runId:n})}}async function gb(r,e,t,s,n,i,a,o,c,l,u,d,p,g,f,h,S,I,v){let k=h??s.id,T=new Date().toISOString(),P=[],j=[],J=Nm(t)||Dm(s),M=Ni(t)?{...a,screenshot:"off",video:"off",trace:"off"}:a,Ce=!J&&M.trace!=="off"?Jt.default.join(r,"results","traces",`${e}_${s.id}_${n}.zip`):void 0,lt=null,Ve;if(J)Ve=await p.createApiOnlyPage(a.baseUrl),le.debug({caseName:s.name},"API-only case \u2014 zero browser, using standalone HTTP client");else{if(!i)throw new Error("Browser required for UI test cases");lt=await p.newContext(i,M,Jt.default.join(r,"results","videos"),Jt.default.join(r,"results","downloads",e)),Ve=await p.newPage(lt),await yp(Ve)}let Ye="passed",js=!1,Mt=[],nr,Bs=[],Ie=async be=>{let U=s.timeout??t.timeout??M.timeout,ut=U!==M.timeout?{...M,timeout:U}:M;try{await vo({...be,strategy:"primary",stepType:"ui",lastHealed:null},Ve,{runId:e,suiteId:t.id,caseId:s.id,browser:n,rootDir:r,previousSteps:j,variables:o,sharedVariables:c,config:ut,provider:l,budget:u,healingReporter:d})}catch{}};try{for(let U of s.caseHooks?.before??[])await Ie(U);for(let U of s.steps){if(js){P.push({stepId:U.id,instruction:U.instruction,status:"skipped",code:U.generatedCode,duration:0});continue}g?.broadcast("run:step",{runId:e,caseId:k,stepId:U.id,instruction:U.instruction,status:"running",duration:0});let ut=s.timeout??t.timeout??M.timeout,Qe=ut!==M.timeout?{...M,timeout:ut}:M,Y=await vo(U,Ve,{runId:e,suiteId:t.id,caseId:s.id,browser:n,rootDir:r,previousSteps:j,variables:o,sharedVariables:c,config:Qe,provider:l,budget:u,healingReporter:d,ragManager:S});if(g?.broadcast("run:step",{runId:e,caseId:k,stepId:Y.stepId,instruction:Y.instruction,status:Y.status,duration:Y.duration}),!J&&g){try{let Le=await Ve.screenshot({type:"jpeg",quality:60});g.broadcast("run:screenshot",{runId:e,caseId:k,stepId:Y.stepId,image:Le.toString("base64"),timestamp:Date.now()})}catch{}let ae=p.drainConsoleLogs(Ve);ae.length>0&&g.broadcast("run:console",{runId:e,caseId:k,stepId:Y.stepId,logs:ae})}let q=Y.status==="failed"?"error":"info";if(f?.(q,` Step ${U.order}: ${Y.instruction} \u2014 ${Y.status.toUpperCase()} (${Y.duration}ms)`,{...Y.error?{error:Y.error}:{},...Y.healed?{healed:!0}:{}}),P.push(Y),I?.enabled&&v&&!J&&U.visualCheck&&Y.status==="passed"&&Y.screenshotPath)try{let ae=a.viewport??{width:1280,height:720},Le=kt(t.name),jt=kt(s.name),Pr=Lp(r,Le,jt,U.id,n,ae),Us=Dp(r,e,U.id,n,ae),Ze=await Op(Pr,Y.screenshotPath,Us,{threshold:.1,diffThreshold:I.defaultThreshold}),Rr=Ze.totalPixels===0?"new":Ze.match?"passed":"failed";v.push({stepId:U.id,caseId:s.id,caseName:s.name,suiteName:t.name,suiteId:t.id,browser:n,viewport:`${ae.width}x${ae.height}`,diffPercentage:Ze.diffPercentage,threshold:I.defaultThreshold,status:Rr,baselinePath:Ze.baselinePath,actualPath:Ze.actualPath,diffPath:Ze.diffImagePath??Us}),f?.("info",` Visual: ${U.id} \u2014 ${Rr} (diff: ${Ze.diffPercentage}%)`,{baseline:Pr})}catch(ae){le.debug({err:ae,stepId:U.id},"Visual regression comparison failed")}Y.status==="passed"?j.push({instruction:U.instruction,code:Y.code}):Y.status==="failed"&&(Ye="failed",js=!0)}let be=(()=>{let U=s.lighthouseCategories;return U&&U.length>0?U:Ni(t)?["performance","accessibility","seo"]:null})();if(!J&&n==="chromium"&&i&&be!==null){let U=new Map(s.steps.map(q=>[q.id,q])),Qe=s.steps.some(q=>q.runAudit===!0)?P.map((q,ae)=>({s:q,idx:ae})).filter(({s:q})=>U.get(q.stepId)?.runAudit===!0&&!!(q.auditUrl??q.navigatedToUrl)):[],Y=be;if(Qe.length>0){f?.("info",`--- LIGHTHOUSE AUDIT [${Y.join(", ")}] ---`);try{let{default:q}=await import("lighthouse"),ae=await Promise.resolve().then(()=>(_m(),$m)),Le=ae.launch??ae.default?.launch,{chromium:jt}=await import("playwright"),Pr=jt.executablePath(),Us=Jt.default.join(r,"results",".lh-profiles");await Cr.default.ensureDir(Us);for(let Ze=0;Ze<Qe.length;Ze++){let{s:Rr,idx:yc}=Qe[Ze],Wr=Rr.auditUrl??Rr.navigatedToUrl;f?.("info",` Auditing: ${Wr}`);let ra=null,sa=Jt.default.join(Us,`audit-${Date.now()}-${Ze}`);try{await Cr.default.ensureDir(sa),ra=await Le({chromePath:Pr,chromeFlags:["--headless","--no-sandbox","--disable-gpu"],userDataDir:sa,logLevel:"silent"});let Xr=(await Promise.race([q(Wr,{port:ra.port,output:"json",logLevel:"silent",onlyCategories:Y,disableStorageReset:!0,screenEmulation:{disabled:!0},throttlingMethod:"simulate",formFactor:"desktop"}),new Promise((Jr,Ut)=>setTimeout(()=>Ut(new Error("Lighthouse audit timed out after 60s")),6e4))]))?.lhr,wc=Xr?.audits??{},bc=Xr?.categories??{},ir=Jr=>{let Ut=wc[Jr]?.numericValue;return Ut!=null&&isFinite(Ut)?Ut:void 0},na=Jr=>{let Ut=bc[Jr]?.score;return Ut!=null?Math.round(Ut*100):void 0},vc=Jr=>(bc[Jr]?.auditRefs??[]).filter(Tr=>(Tr.weight??0)>0).map(Tr=>{let wn=wc[Tr.id],ar=wn?.score??null,Hs=ar!==null&&ar>0&&ar<1;return{id:Tr.id,title:wn?.title??Tr.id,passed:ar===1,partial:Hs||void 0,na:ar===null||void 0}}).sort((Tr,wn)=>{let ar=Hs=>Hs.na?3:Hs.passed?4:Hs.partial?1:0;return ar(Tr)-ar(wn)}),Bt=Y.includes("performance"),xc=Y.includes("accessibility"),Sc=Y.includes("seo"),dt={url:Wr,stepIndex:yc,stepInstruction:Rr.instruction,score:Bt?na("performance"):void 0,a11yScore:xc?na("accessibility"):void 0,seoScore:Sc?na("seo"):void 0,fcp:Bt?ir("first-contentful-paint"):void 0,lcp:Bt?ir("largest-contentful-paint"):void 0,cls:Bt?ir("cumulative-layout-shift"):void 0,ttfb:Bt?ir("server-response-time"):void 0,tbt:Bt?ir("total-blocking-time"):void 0,si:Bt?ir("speed-index"):void 0,tti:Bt?ir("interactive"):void 0,inp:Bt?ir("interaction-to-next-paint"):void 0,a11yAudits:xc?vc("accessibility"):void 0,seoAudits:Sc?vc("seo"):void 0};Mt.push(dt),f?.("info",` Perf: ${dt.score??"--"} | A11y: ${dt.a11yScore??"--"} | SEO: ${dt.seoScore??"--"} | FCP: ${dt.fcp!=null?(dt.fcp/1e3).toFixed(2)+"s":"--"} | LCP: ${dt.lcp!=null?(dt.lcp/1e3).toFixed(2)+"s":"--"} | CLS: ${dt.cls?.toFixed(3)??"--"} | TTFB: ${dt.ttfb!=null?(dt.ttfb/1e3).toFixed(2)+"s":"--"}`)}catch(yn){let Xr=yn instanceof Error?yn.message:String(yn);le.warn({url:Wr,err:Xr},"Lighthouse audit failed for URL"),f?.("warn",` Lighthouse failed for ${Wr}: ${Xr}`),Mt.push({url:Wr,stepIndex:yc,stepInstruction:Rr.instruction,lighthouseError:Xr})}finally{try{await ra?.kill()}catch{}try{await Cr.default.remove(sa)}catch{}}}}catch(q){let ae=q instanceof Error?q.message:String(q);le.warn({err:ae},"Lighthouse import/init failed"),f?.("warn",` Lighthouse init failed: ${ae}`);let Le=new Set(Mt.map(jt=>jt.stepIndex));for(let{s:jt,idx:Pr}of Qe)Le.has(Pr)||Mt.push({url:jt.auditUrl??jt.navigatedToUrl,stepIndex:Pr,stepInstruction:jt.instruction,lighthouseError:`Lighthouse unavailable: ${ae}`})}f?.("info","------------------------------------")}Ni(t)&&Mt.length>0&&Ye==="passed"&&Mt.some(Le=>!Le.lighthouseError&&(Le.score!=null&&Le.score<50||Le.a11yScore!=null&&Le.a11yScore<50||Le.seoScore!=null&&Le.seoScore<50))&&(Ye="failed",f?.("warn"," Case FAILED: one or more Lighthouse scores below 50 (Poor threshold)"))}else!J&&n!=="chromium"&&(Mt=P.map((U,ut)=>({s:U,idx:ut})).filter(({s:U})=>!!U.navigatedToUrl).map(({s:U,idx:ut})=>({url:U.navigatedToUrl,stepIndex:ut,stepInstruction:U.instruction,lighthouseError:"Lighthouse only available for Chromium"})));for(let U of s.caseHooks?.after??[])await Ie(U);J||(nr=await Ve.video()?.path().catch(()=>{}))}finally{J||lt&&(await p.closeContext(lt,M,Ce),Bs=p.takeDownloads(lt),(M.video==="retain-on-failure"||M.video==="on-first-retry")&&Ye==="passed"&&nr&&await Cr.default.remove(nr).catch(()=>{}),(M.trace==="retain-on-failure"||M.trace==="on-first-retry")&&Ye==="passed"&&Ce&&await Cr.default.remove(Ce).catch(()=>{}))}let _,Z;if(!J){let be=M.video==="retain-on-failure"||M.video==="on-first-retry";_=M.video!=="off"&&nr?be&&Ye==="passed"?void 0:nr:void 0;let U=M.trace==="retain-on-failure"||M.trace==="on-first-retry";Z=M.trace==="off"||U&&Ye==="passed"?void 0:Ce}let ie=new Date().toISOString(),we=new Date(ie).getTime()-new Date(T).getTime();return{suiteId:t.id,suiteName:t.name,caseResult:{caseId:s.id,caseName:s.name,status:Ye,steps:P,duration:we,browser:n,device:a.device||void 0,startedAt:T,finishedAt:ie,videoPath:_,tracePath:Z,downloadPaths:Bs,pageLoads:Mt}}}async function fb(r,e){let t=await Ar(r),s=[];for(let n of t){if(e.type&&n.type!==e.type)continue;if(e.suite){let o=e.suite.toLowerCase(),c=n.name.toLowerCase().includes(o),l=n.id===e.suite;if(!c&&!l)continue}let i=await de(r,n.id);if(!i){le.warn({suiteId:n.id},"Suite directory not found \u2014 skipping");continue}let a=await We(i);for(let o of a){if(e.test){let c=e.test.toLowerCase(),l=o.name.toLowerCase().includes(c),u=o.id===e.test;if(!l&&!u)continue}e.tag&&!o.tags.includes(e.tag)||!e.all&&!e.type&&!e.suite&&!e.test&&!e.tag||s.push({suite:n,testCase:o})}}return s}async function hb(r,e){try{let t=await rt(r,e??"dev"),s=Object.keys(t).length;return le.info({count:s,env:e??"dev",rootDir:r},"Variables loaded"),t}catch(t){return le.warn({err:t instanceof Error?t.message:String(t)},"Failed to load variables \u2014 running without variable substitution"),{}}}async function yb(r,e,t){let s=[],n=[...r];async function i(){for(;n.length>0;){let o=n.shift();o!==void 0&&s.push(await t(o))}}let a=Array.from({length:Math.min(e,r.length||1)},()=>i());return await Promise.all(a),s}function Om(r,e){let t=new Map;for(let n of r){let i=n.suiteId,a=t.get(i)??[];a.push(n),t.set(i,a)}let s=[];for(let[n,i]of t){let a=i[0];if(!a)continue;let o=i.map(p=>p.caseResult),c=o.some(p=>p.status==="failed")?"failed":"passed",l=o[0]?.startedAt??new Date().toISOString(),u=o[o.length-1]?.finishedAt??new Date().toISOString(),d=new Date(u).getTime()-new Date(l).getTime();s.push({suiteId:n,suiteName:e?`${a.suiteName} [${e}]`:a.suiteName,suiteType:a.suiteType,status:c,cases:o,duration:d,browser:o[0]?.browser??"chromium",startedAt:l,finishedAt:u})}return s}function wb(r,e,t,s,n){let i=s.flatMap(d=>d.cases),a=i.filter(d=>d.status==="passed").length,o=i.filter(d=>d.status==="failed").length,c=i.filter(d=>d.status==="skipped").length,l=i.length,u=new Date(t).getTime()-new Date(e).getTime();return{runId:r,status:o>0?"failed":"passed",environment:n,suites:s,duration:u,startedAt:e,finishedAt:t,totalTests:l,passed:a,failed:o,skipped:c}}var Jt,Lm,Cr,le,Fi,Bo,Vo=b(()=>{"use strict";m();Jt=x(require("path")),Lm=require("crypto"),Cr=x(require("fs-extra"));Ke();gt();mr();rn();fr();Qs();Vd();qd();Kd();Xd();Yd();tp();ap();hp();wp();xp();$r();Eo();Np();At();F();le=C("runner");Fi=null,Bo=null});async function Mm(r){let e=r.rootDir;r.post("/run",async(t,s)=>{let n=Sb.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid run config",details:n.error.issues});try{let i=await G(e),a=n.data,o={...i,headless:a.headed!==void 0?!a.headed:!1,browsers:a.browsers?.length?a.browsers:i.browsers,parallel:a.parallel??i.parallel,screenshot:a.screenshot??i.screenshot,video:a.video??i.video,trace:a.trace??i.trace,device:a.device??i.device,...a.ci?{headless:!0}:{}},c=r.wsManager,l=new Date().toISOString();return Go.info({runConfig:a},"Run requested via API"),Ho(e,a,o,c).then(u=>{c.broadcast("run:complete",{runId:u.runId,passed:u.passed,failed:u.failed,skipped:u.skipped,duration:u.duration}),Go.info({runId:u.runId,status:u.status},"API-triggered run complete")}).catch(u=>{Go.error({err:u},"API-triggered run failed"),c.broadcast("run:error",{error:u instanceof Error?u.message:String(u)})}),s.status(202).send({status:"accepted",startedAt:l,message:"Run started \u2014 subscribe to WebSocket for progress"})}catch(i){return w(s,500,"Failed to start run",i)}}),r.post("/run/stop",async(t,s)=>(Uo(),s.status(202).send({status:"stopping"}))),r.get("/run",async(t,s)=>{let n=Eb.safeParse(t.query);if(!n.success)return s.status(400).send({error:"Invalid query",details:n.error.issues});try{let i=await ei(e,n.data.limit);return s.send(i)}catch(i){return w(s,500,"Failed to list run results",i)}}),r.get("/run/:runId",async(t,s)=>{try{let n=await Vr(e,t.params.runId);return s.send(n)}catch{return s.status(404).send({error:`Run "${t.params.runId}" not found`})}})}var pe,Go,bb,vb,xb,Sb,Eb,jm=b(()=>{"use strict";m();pe=require("zod");Vo();Te();rn();te();F();Go=C("routes/run"),bb=pe.z.enum(["off","on","only-on-failure"]),vb=pe.z.enum(["off","on","on-first-retry","retain-on-failure"]),xb=pe.z.enum(["off","on","on-first-retry","retain-on-failure"]),Sb=pe.z.object({all:pe.z.boolean().optional(),suite:pe.z.string().optional(),tag:pe.z.string().optional(),test:pe.z.string().optional(),browsers:pe.z.array(pe.z.string()).optional(),env:pe.z.string().optional(),parallel:pe.z.number().int().positive().optional(),headed:pe.z.boolean().optional(),ci:pe.z.boolean().optional(),reporter:pe.z.array(pe.z.string()).optional(),noHealing:pe.z.boolean().optional(),screenshot:bb.optional(),video:vb.optional(),trace:xb.optional(),device:pe.z.string().optional()}),Eb=pe.z.object({limit:pe.z.coerce.number().int().positive().max(100).default(20)})});async function zo(r){let e;if(r.startsWith("http://")||r.startsWith("https://")){let t=r;t.includes("#")&&(t=t.split("#")[0]);let s=t.replace(/\/$/,""),n=[t,`${t}.json`,`${s}/swagger.json`,`${s}/openapi.json`,`${s}/openapi.yaml`,`${s}/openapi.yml`,`${s}/swagger.yaml`,`${s}/api-docs`,`${s}/api-docs.json`,`${s}/v2/swagger.json`,`${s}/v2/openapi.yaml`,`${s}/v2/openapi.json`,`${s}/v3/api-docs`,`${s}/v3/openapi.yaml`,s.replace(/\/[^/]+$/,"")+"/swagger.json",s.replace(/\/[^/]+$/,"")+"/openapi.json",s.replace(/\/[^/]+$/,"")+"/openapi.yaml",s.replace(/\/[^/]+$/,"")+"/openapi.yml",s.replace(/\/[^/]+\/[^/]+$/,"")+"/openapi.yaml",s.replace(/\/[^/]+\/[^/]+$/,"")+"/swagger.json"];try{let c=await fetch(t,{headers:{Accept:"text/html, */*"},signal:AbortSignal.timeout(1e4)});if(c.ok){let l=await c.text();if(l.includes("<!")||l.includes("<html")){let u=[/Redoc\.init\(\s*['"]([^'"]+)['"]/,/spec-url=['"]([^'"]+)['"]/i,/url:\s*['"]([^'"]+\.(?:json|yaml|yml))['"]/,/specUrl:\s*['"]([^'"]+)['"]/,/["']([^"']*(?:openapi|swagger)[^"']*\.(?:json|yaml|yml))["']/i,/href=['"]([^'"]*\.(?:json|yaml|yml))['"][^>]*>.*?(?:openapi|swagger|spec|api-doc)/i];for(let d of u){let p=l.match(d);if(p?.[1]){let g=p[1];g.startsWith("/")?g=`${new URL(t).origin}${g}`:g.startsWith("http")||(g=new URL(g,t).href),n.unshift(g),pn.info({specUrl:g,pattern:d.source},"Extracted spec URL from HTML page")}}}}}catch{}let i=[...new Set(n)],a=!1,o=[];for(let c of i)try{pn.debug({tryUrl:c},"Trying to fetch OpenAPI spec");let l=await fetch(c,{headers:{Accept:"application/json, application/yaml, */*"},signal:AbortSignal.timeout(15e3)});if(l.ok){let u=l.headers.get("content-type")??"",d=await l.text(),p=u.includes("json")||d.trimStart().startsWith("{")||d.trimStart().startsWith("["),g=u.includes("yaml")||u.includes("yml")||c.endsWith(".yaml")||c.endsWith(".yml");if(p||g)try{if(e=p?JSON.parse(d):Mi.default.load(d),e&&(e.openapi||e.swagger||e.paths)){a=!0,pn.info({tryUrl:c,format:g?"YAML":"JSON"},"Successfully fetched OpenAPI spec");break}o.push(`${c}: Parsed but not a valid OpenAPI spec`)}catch{o.push(`${c}: Response is not valid ${g?"YAML":"JSON"}`)}else{if(!u.includes("html")&&!d.trimStart().startsWith("<!"))try{let f=Mi.default.load(d);if(f&&typeof f=="object"&&(f.openapi||f.swagger||f.paths)){e=f,a=!0,pn.info({tryUrl:c,format:"YAML (text/plain)"},"Successfully fetched OpenAPI spec");break}}catch{}o.push(`${c}: Response is HTML/text, not JSON or YAML (content-type: ${u})`)}}else o.push(`${c}: HTTP ${l.status}`)}catch(l){o.push(`${c}: ${l instanceof Error?l.message:String(l)}`)}if(!a)throw new Error(`Could not auto-discover the OpenAPI spec from this URL.
|
|
944
|
+
`+i),r.push(n+"m+"+Bi.exports.humanize(this.diff)+"\x1B[0m")}else r[0]=tb()+e+" "+r[0]}function tb(){return _e.inspectOpts.hideDate?"":new Date().toISOString()+" "}function rb(...r){return process.stderr.write(ji.formatWithOptions(_e.inspectOpts,...r)+`
|
|
945
|
+
`)}function sb(r){r?process.env.DEBUG=r:delete process.env.DEBUG}function nb(){return process.env.DEBUG}function ib(r){r.inspectOpts={};let e=Object.keys(_e.inspectOpts);for(let t=0;t<e.length;t++)r.inspectOpts[e[t]]=_e.inspectOpts[e[t]]}Bi.exports=Fo()(_e);var{formatters:nm}=Bi.exports;nm.o=function(r){return this.inspectOpts.colors=this.useColors,ji.inspect(r,this.inspectOpts).split(`
|
|
946
|
+
`).map(e=>e.trim()).join(" ")};nm.O=function(r){return this.inspectOpts.colors=this.useColors,ji.inspect(r,this.inspectOpts)}});var am=Mt((_P,Bo)=>{"use strict";g();typeof process>"u"||process.type==="renderer"||process.browser===!0||process.__nwjs?Bo.exports=Qp():Bo.exports=im()});function Ui(r){if(!r)throw new Error("name must be non-empty")}function ab(r,e){for(var t=0,s=r.length,n;t<s;)n=t+s>>>1,r[n].startTime<e.startTime?t=n+1:s=n;r.splice(t,0,e)}var Oe,um,om,Uo,cm,lm,Gi,zi,vn,qi,Hi,Vi,dm=b(()=>{"use strict";g();Oe=typeof performance<"u"&&performance;om=process.hrtime,Uo=function(){var r=om();return r[0]*1e9+r[1]},cm=Uo(),um=function(){return(Uo()-cm)/1e6};lm=Oe&&Oe.now?function(){return Oe.now()}:um;Oe&&Oe.mark&&Oe.measure&&Oe.getEntriesByName&&Oe.getEntriesByType&&Oe.clearMarks&&Oe.clearMeasures&&Oe.clearResourceTimings?(Gi=function(r){Ui(r),Oe.mark("start "+r)},zi=function(r){Ui(r),Oe.mark("end "+r);var e=Oe.measure(r,"start "+r,"end "+r);if(e)return e;var t=Oe.getEntriesByName(r);return t[t.length-1]},vn=function(){return Oe.getEntriesByType("measure")},qi=function(){Oe.clearMarks(),Oe.clearMeasures()}):(Hi={},Vi=[],Gi=function(r){Ui(r);var e=lm();Hi["$"+r]=e},zi=function(r){Ui(r);var e=lm(),t=Hi["$"+r];if(!t)throw new Error("no known mark: "+r);var s={startTime:t,name:r,duration:e-t,entryType:"measure"};return ab(Vi,s),s},vn=function(){return Vi},qi=function(){Hi={},Vi=[]})});var Ls,gm,jr,Mr,_s,Os,Ho,pm,cb,mm,Ds,be,Vo=b(()=>{"use strict";g();Ls=x(require("process"),1),gm=require("events"),jr=x(am(),1);dm();Mr=Ls.default.platform==="win32",_s=Ls.default.browser,Os={red:_s?"crimson":1,yellow:_s?"gold":3,cyan:_s?"darkturquoise":6,green:_s?"forestgreen":2,blue:_s?"steelblue":4,magenta:_s?"palevioletred":5};jr.default.colors=[Os.cyan,Os.green,Os.blue,Os.magenta];Ho=class extends gm.EventEmitter{constructor(e){super(e)}issueStatus(e,t){(e==="status"||e==="statusEnd")&&this.emit(e,[e,...t])}issueWarning(e,t){this.emit("warning",[e,...t])}},pm={},cb=25,Ds=class r{static _logToStdErr(e,t){r.loggerfn(e)(...t)}static loggerfn(e){e=`LH:${e}`;let t=pm[e];return t||(t=(0,jr.default)(e),pm[e]=t,e.endsWith("error")?t.color=Os.red:e.endsWith("warn")&&(t.color=Os.yellow)),t}static setLevel(e){switch(mm=e,e){case"silent":jr.default.enable("-LH:*");break;case"verbose":jr.default.enable("LH:*");break;case"warn":jr.default.enable("-LH:*, LH:*:warn, LH:*:error");break;case"error":jr.default.enable("-LH:*, LH:*:error");break;default:jr.default.enable("LH:*, -LH:*:verbose")}}static formatProtocol(e,t,s){let n=!Ls.default||Ls.default.browser?1/0:Ls.default.stdout.columns,i=t.method||"?????",a=n-i.length-e.length-cb,o=t.params&&i!=="IO.read"?JSON.stringify(t.params).substr(0,a):"";r._logToStdErr(`${e}:${s||""}`,[i,o])}static isVerbose(){return mm==="verbose"}static time({msg:e,id:t,args:s=[]},n="log"){Gi(t),r[n]("status",e,...s)}static timeEnd({msg:e,id:t,args:s=[]},n="verbose"){r[n]("statusEnd",e,...s),zi(t)}static log(e,...t){return r.events.issueStatus(e,t),r._logToStdErr(e,t)}static warn(e,...t){return r.events.issueWarning(e,t),r._logToStdErr(`${e}:warn`,t)}static error(e,...t){return r._logToStdErr(`${e}:error`,t)}static verbose(e,...t){return r.events.issueStatus(e,t),r._logToStdErr(`${e}:verbose`,t)}static greenify(e){return`${r.green}${e}${r.reset}`}static redify(e){return`${r.red}${e}${r.reset}`}static get green(){return"\x1B[32m"}static get red(){return"\x1B[31m"}static get yellow(){return"\x1B[33m"}static get purple(){return"\x1B[95m"}static get reset(){return"\x1B[0m"}static get bold(){return"\x1B[1m"}static get dim(){return"\x1B[2m"}static get tick(){return Mr?"\u221A":"\u2713"}static get cross(){return Mr?"\xD7":"\u2718"}static get whiteSmallSquare(){return Mr?"\u0387":"\u25AB"}static get heavyHorizontal(){return Mr?"\u2500":"\u2501"}static get heavyVertical(){return Mr?"\u2502 ":"\u2503 "}static get heavyUpAndRight(){return Mr?"\u2514":"\u2517"}static get heavyVerticalAndRight(){return Mr?"\u251C":"\u2523"}static get heavyDownAndHorizontal(){return Mr?"\u252C":"\u2533"}static get doubleLightHorizontal(){return"\u2500\u2500"}};Ds.events=new Ho;Ds.takeTimeEntries=()=>{let r=vn();return qi(),r};Ds.getTimeEntries=()=>vn();be=Ds});var ym=Mt((FP,hm)=>{"use strict";g();var fm=require("fs"),Go;function lb(){try{return fm.statSync("/.dockerenv"),!0}catch{return!1}}function ub(){try{return fm.readFileSync("/proc/self/cgroup","utf8").includes("docker")}catch{return!1}}hm.exports=()=>(Go===void 0&&(Go=lb()||ub()),Go)});var vm=Mt((jP,zo)=>{"use strict";g();var db=require("os"),pb=require("fs"),wm=ym(),bm=()=>{if(process.platform!=="linux")return!1;if(db.release().toLowerCase().includes("microsoft"))return!wm();try{return pb.readFileSync("/proc/version","utf8").toLowerCase().includes("microsoft")?!wm():!1}catch{return!1}};process.env.__IS_WSL_TEST__?zo.exports=bm:zo.exports=bm()});function Lt(r,e){return typeof r>"u"?e:r}async function Im(r){return new Promise(e=>setTimeout(e,r))}function Xt(){return Em.default?"wsl":process.platform}function Cm(){switch(Xt()){case"darwin":case"linux":return fb();case"wsl":process.env.TEMP=qo(`${process.env.PATH}`);case"win32":return hb();default:throw new xn}}function mb(r=""){let e=/\/mnt\/([a-z])\//.exec(r);if(!e)return r;let t=e[1];return r.replace(`/mnt/${t}/`,`${t.toUpperCase()}:\\`).replace(/\//g,"\\")}function Pm(r=""){if(/[a-z]:\\/iu.test(r))return r;try{return Xi.default.execFileSync("wslpath",["-w",r]).toString().trim()}catch{return mb(r)}}function Ji(r,e){try{return Xi.default.execFileSync("wslpath",["-u",r]).toString().trim()}catch{return e}}function gb(r){let t=/\/mnt\/([a-z])\/Users\/([^\/:]+)\/AppData\//.exec(r)||[];return`/mnt/${t[1]}/Users/${t[2]}/AppData/Local`}function qo(r){let t=/\/([a-z])\/Users\/([^\/:]+)\/AppData\//.exec(r)||[];return Ji(`${t[1]}:\\Users\\${t[2]}\\AppData\\Local`,gb(r))}function fb(){return Xi.default.execSync("mktemp -d -t lighthouse.XXXXXXX").toString().trim()}function hb(){let r=process.env.TEMP||process.env.TMP||(process.env.SystemRoot||process.env.windir)+"\\temp",e=Math.floor(Math.random()*9e7+1e7),t=(0,xm.join)(r,"lighthouse."+e);return(0,Sm.mkdirSync)(t,{recursive:!0}),t}var xm,Xi,Sm,Em,Ns,Ki,Wi,xn,Sn,Ko=b(()=>{"use strict";g();xm=require("path"),Xi=x(require("child_process"),1),Sm=require("fs"),Em=x(vm(),1);Ns=class extends Error{constructor(e="Unexpected error",t){return super(),this.message=e,this.code=t,this.stack=new Error().stack,this}},Ki=class extends Ns{constructor(){super(...arguments),this.message="The CHROME_PATH environment variable must be set to a Chrome/Chromium executable no older than Chrome stable.",this.code="ERR_LAUNCHER_PATH_NOT_SET"}},Wi=class extends Ns{constructor(){super(...arguments),this.message="userDataDir must be false or a path.",this.code="ERR_LAUNCHER_INVALID_USER_DATA_DIRECTORY"}},xn=class extends Ns{constructor(){super(...arguments),this.message=`Platform ${Xt()} is not supported.`,this.code="ERR_LAUNCHER_UNSUPPORTED_PLATFORM"}},Sn=class extends Ns{constructor(){super(...arguments),this.message="No Chrome installations found.",this.code="ERR_LAUNCHER_NOT_INSTALLED"}}});var Yi={};ie(Yi,{darwin:()=>km,darwinFast:()=>Jo,linux:()=>yb,win32:()=>Tm,wsl:()=>wb});function Jo(){let r=[process.env.CHROME_PATH,process.env.LIGHTHOUSE_CHROMIUM_PATH,"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary","/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"];for(let e of r)if(e&&Br(e))return e;return km()[0]}function km(){let r=["/Contents/MacOS/Google Chrome Canary","/Contents/MacOS/Google Chrome"],e="/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister",t=[],s=Yo();s&&t.push(s),(0,Ms.execSync)(`${e} -dump | grep -i 'google chrome\\( canary\\)\\?\\.app' | awk '{$1=""; print $0}'`).toString().split(Xo).forEach(a=>{r.forEach(o=>{let c=vt.default.join(a.substring(0,a.indexOf(".app")+4).trim(),o);Br(c)&&t.indexOf(c)===-1&&t.push(c)})});let n=(0,Fs.default)(process.env.HOME||(0,Wo.homedir)()),i=[{regex:new RegExp(`^${n}/Applications/.*Chrome\\.app`),weight:50},{regex:new RegExp(`^${n}/Applications/.*Chrome Canary\\.app`),weight:51},{regex:/^\/Applications\/.*Chrome.app/,weight:100},{regex:/^\/Applications\/.*Chrome Canary.app/,weight:101},{regex:/^\/Volumes\/.*Chrome.app/,weight:-2},{regex:/^\/Volumes\/.*Chrome Canary.app/,weight:-1}];return process.env.LIGHTHOUSE_CHROMIUM_PATH&&i.unshift({regex:new RegExp((0,Fs.default)(process.env.LIGHTHOUSE_CHROMIUM_PATH)),weight:150}),process.env.CHROME_PATH&&i.unshift({regex:new RegExp((0,Fs.default)(process.env.CHROME_PATH)),weight:151}),Am(t,i)}function Yo(){if(Br(process.env.CHROME_PATH))return process.env.CHROME_PATH;if(Br(process.env.LIGHTHOUSE_CHROMIUM_PATH))return be.warn("ChromeLauncher","LIGHTHOUSE_CHROMIUM_PATH is deprecated, use CHROME_PATH env variable instead."),process.env.LIGHTHOUSE_CHROMIUM_PATH}function yb(){let r=[],e=Yo();if(e&&r.push(e),[vt.default.join((0,Wo.homedir)(),".local/share/applications/"),"/usr/share/applications/"].forEach(i=>{r=r.concat(vb(i))}),["google-chrome-stable","google-chrome","chromium-browser","chromium"].forEach(i=>{try{let a=(0,Ms.execFileSync)("which",[i],{stdio:"pipe"}).toString().split(Xo)[0];Br(a)&&r.push(a)}catch{}}),!r.length)throw new Ki;let n=[{regex:/chrome-wrapper$/,weight:51},{regex:/google-chrome-stable$/,weight:50},{regex:/google-chrome$/,weight:49},{regex:/chromium-browser$/,weight:48},{regex:/chromium$/,weight:47}];return process.env.LIGHTHOUSE_CHROMIUM_PATH&&n.unshift({regex:new RegExp((0,Fs.default)(process.env.LIGHTHOUSE_CHROMIUM_PATH)),weight:100}),process.env.CHROME_PATH&&n.unshift({regex:new RegExp((0,Fs.default)(process.env.CHROME_PATH)),weight:101}),Am(bb(r.filter(Boolean)),n)}function wb(){return process.env.LOCALAPPDATA=qo(`${process.env.PATH}`),process.env.PROGRAMFILES=Ji("C:/Program Files","/mnt/c/Program Files"),process.env["PROGRAMFILES(X86)"]=Ji("C:/Program Files (x86)","/mnt/c/Program Files (x86)"),Tm()}function Tm(){let r=[],e=[`${vt.default.sep}Google${vt.default.sep}Chrome SxS${vt.default.sep}Application${vt.default.sep}chrome.exe`,`${vt.default.sep}Google${vt.default.sep}Chrome${vt.default.sep}Application${vt.default.sep}chrome.exe`],t=[process.env.LOCALAPPDATA,process.env.PROGRAMFILES,process.env["PROGRAMFILES(X86)"]].filter(Boolean),s=Yo();return s&&r.push(s),t.forEach(n=>e.forEach(i=>{let a=vt.default.join(n,i);Br(a)&&r.push(a)})),r}function Am(r,e){return r.map(s=>{for(let n of e)if(n.regex.test(s))return{path:s,weight:n.weight};return{path:s,weight:10}}).sort((s,n)=>n.weight-s.weight).map(s=>s.path)}function Br(r){if(!r)return!1;try{return Rm.default.accessSync(r),!0}catch{return!1}}function bb(r){return Array.from(new Set(r))}function vb(r){let e=/(^[^ ]+).*/,t="^Exec=/.*/(google-chrome|chrome|chromium)-.*",s=[];if(Br(r)){let n;try{n=(0,Ms.execSync)(`grep -ER "${t}" ${r} | awk -F '=' '{print $2}'`,{stdio:"pipe"})}catch{n=(0,Ms.execSync)(`grep -Er "${t}" ${r} | awk -F '=' '{print $2}'`,{stdio:"pipe"})}n=n.toString().split(Xo).map(i=>i.replace(e,"$1")),n.forEach(i=>Br(i)&&s.push(i))}return s}var Rm,vt,Wo,Ms,Fs,Xo,$m=b(()=>{"use strict";g();Rm=x(require("fs"),1),vt=x(require("path"),1),Wo=require("os"),Ms=require("child_process"),Fs=x(Wp(),1);Vo();Ko();Xo=/\r?\n/});function Om(){return new Promise((r,e)=>{let t=(0,_m.createServer)();t.listen(0),t.once("listening",()=>{let{port:s}=t.address();t.close(()=>r(s))}),t.once("error",e)})}var _m,Lm=b(()=>{"use strict";g();_m=require("http");});var Qo,Dm=b(()=>{"use strict";g();Qo=["--disable-features="+["Translate","OptimizationHints","MediaRouter","DialMediaRouteProvider","CalculateNativeWinOcclusion","InterestFeedContentSuggestions","CertificateTransparencyComponentUpdater","AutofillServerCommunication","PrivacySandboxSettings4","RenderDocument"].join(","),"--disable-extensions","--disable-component-extensions-with-background-pages","--disable-background-networking","--disable-component-update","--disable-client-side-phishing-detection","--disable-sync","--metrics-recording-only","--disable-default-apps","--mute-audio","--no-default-browser-check","--no-first-run","--disable-backgrounding-occluded-windows","--disable-renderer-backgrounding","--disable-background-timer-throttling","--disable-ipc-flooding-protection","--password-store=basic","--use-mock-keychain","--force-fieldtrials=*BackgroundTracing/default/","--disable-hang-monitor","--disable-prompt-on-repost","--disable-domain-reliability","--propagate-iph-for-testing"]});async function Pb(r={}){r.handleSIGINT=Lt(r.handleSIGINT,!0);let e=new En(r);r.handleSIGINT&&js.size===0&&process.on(Nm,Fm),js.add(e),await e.launch();let t=()=>{js.delete(e),js.size===0&&process.removeListener(Nm,Fm),e.kill()};return{pid:e.pid,port:e.port,process:e.chromeProcess,remoteDebuggingPipes:e.remoteDebuggingPipes,kill:t}}function Rb(){let r=En.getFirstInstallation();if(!r)throw new Sn;return r}function jm(){let r=[];for(let e of js)try{e.kill(),js.delete(e)}catch(t){r.push(t)}return r}var xb,Mm,Qi,Sb,Eb,Nm,Ib,Cb,js,Fm,En,Bm=b(()=>{"use strict";g();xb=x(require("fs"),1),Mm=x(require("net"),1);$m();Lm();Dm();Ko();Qi=require("child_process");Vo();Sb=Xt()==="wsl",Eb=Xt()==="win32",Nm="SIGINT",Ib=130,Cb=new Set(["darwin","linux","win32","wsl"]),js=new Set,Fm=()=>{jm(),process.exit(Ib)};En=class r{constructor(e={},t={}){if(this.opts=e,this.tmpDirandPidFileReady=!1,this.remoteDebuggingPipes=null,this.fs=t.fs||xb,this.spawn=t.spawn||Qi.spawn,be.setLevel(Lt(this.opts.logLevel,"silent")),this.startingUrl=Lt(this.opts.startingUrl,"about:blank"),this.chromeFlags=Lt(this.opts.chromeFlags,[]),this.prefs=Lt(this.opts.prefs,{}),this.requestedPort=Lt(this.opts.port,0),this.portStrictMode=e.portStrictMode,this.chromePath=this.opts.chromePath,this.ignoreDefaultFlags=Lt(this.opts.ignoreDefaultFlags,!1),this.connectionPollInterval=Lt(this.opts.connectionPollInterval,500),this.maxConnectionRetries=Lt(this.opts.maxConnectionRetries,50),this.envVars=Lt(e.envVars,Object.assign({},process.env)),typeof this.opts.userDataDir=="boolean")if(!this.opts.userDataDir)this.useDefaultProfile=!0,this.userDataDir=void 0;else throw new Wi;else this.useDefaultProfile=!1,this.userDataDir=this.opts.userDataDir;this.useRemoteDebuggingPipe=this.chromeFlags.some(s=>s.startsWith("--remote-debugging-pipe"))}get flags(){let e=this.ignoreDefaultFlags?[]:Qo.slice();return this.port&&e.push(`--remote-debugging-port=${this.port}`),!this.ignoreDefaultFlags&&Xt()==="linux"&&e.push("--disable-setuid-sandbox"),this.useDefaultProfile||e.push(`--user-data-dir=${Sb?Pm(this.userDataDir):this.userDataDir}`),process.env.HEADLESS&&e.push("--headless"),e.push(...this.chromeFlags),e.push(this.startingUrl),e}static defaultFlags(){return Qo.slice()}static getFirstInstallation(){return Xt()==="darwin"?Jo():Yi[Xt()]()[0]}static getInstallations(){return Yi[Xt()]()}makeTmpDir(){return Cm()}prepare(){let e=Xt();if(!Cb.has(e))throw new xn;this.userDataDir=this.userDataDir||this.makeTmpDir(),this.outFile=this.fs.openSync(`${this.userDataDir}/chrome-out.log`,"a"),this.errFile=this.fs.openSync(`${this.userDataDir}/chrome-err.log`,"a"),this.setBrowserPrefs(),this.pidFile=`${this.userDataDir}/chrome.pid`,be.verbose("ChromeLauncher",`created ${this.userDataDir}`),this.tmpDirandPidFileReady=!0}setBrowserPrefs(){if(Object.keys(this.prefs).length===0)return;let e=`${this.userDataDir}/Default`;this.fs.existsSync(e)||this.fs.mkdirSync(e,{recursive:!0});let t=`${e}/Preferences`;try{if(this.fs.existsSync(t)){let s=this.fs.readFileSync(t,"utf-8"),n=JSON.parse(s);this.fs.writeFileSync(t,JSON.stringify({...n,...this.prefs}),"utf-8")}else this.fs.writeFileSync(t,JSON.stringify({...this.prefs}),"utf-8")}catch(s){be.log("ChromeLauncher",`Failed to set browser prefs: ${s.message}`)}}async launch(){if(this.requestedPort!==0){this.port=this.requestedPort;try{await this.isDebuggerReady(),be.log("ChromeLauncher",`Found existing Chrome already running using port ${this.port}, using that.`);return}catch{if(this.portStrictMode)throw new Error(`found no Chrome at port ${this.requestedPort}`);be.log("ChromeLauncher",`No debugging port found on port ${this.port}, launching a new Chrome.`)}}if(this.chromePath===void 0){let e=r.getFirstInstallation();if(!e)throw new Sn;this.chromePath=e}return this.tmpDirandPidFileReady||this.prepare(),this.pid=await this.spawnProcess(this.chromePath),Promise.resolve()}async spawnProcess(e){let s=await(async()=>this.chromeProcess?(be.log("ChromeLauncher",`Chrome already running with pid ${this.chromeProcess.pid}.`),this.chromeProcess.pid):(this.requestedPort===0&&(this.useRemoteDebuggingPipe?this.port=0:this.port=await Om()),be.verbose("ChromeLauncher",`Launching with command:
|
|
947
|
+
"${e}" ${this.flags.join(" ")}`),this.chromeProcess=this.spawn(e,this.flags,{detached:process.platform!=="win32",stdio:this.useRemoteDebuggingPipe?["ignore",this.outFile,this.errFile,"pipe","pipe"]:["ignore",this.outFile,this.errFile],env:this.envVars}),this.chromeProcess.pid&&this.fs.writeFileSync(this.pidFile,this.chromeProcess.pid.toString()),this.useRemoteDebuggingPipe&&(this.remoteDebuggingPipes={incoming:this.chromeProcess.stdio[4],outgoing:this.chromeProcess.stdio[3]}),be.verbose("ChromeLauncher",`Chrome running with pid ${this.chromeProcess.pid} on port ${this.port}.`),this.chromeProcess.pid))();return this.port!==0&&await this.waitUntilReady(),s}cleanup(e){e&&(e.removeAllListeners(),e.end(),e.destroy(),e.unref())}isDebuggerReady(){return new Promise((e,t)=>{let s=Mm.createConnection(this.port,"127.0.0.1");s.once("error",n=>{this.cleanup(s),t(n)}),s.once("connect",()=>{this.cleanup(s),e()})})}waitUntilReady(){let e=this;return new Promise((t,s)=>{let n=0,i="Waiting for browser.",a=()=>{n===0&&be.log("ChromeLauncher",i),n++,i+="..",be.log("ChromeLauncher",i),e.isDebuggerReady().then(()=>{be.log("ChromeLauncher",i+`${be.greenify(be.tick)}`),t()}).catch(o=>{if(n>e.maxConnectionRetries){be.error("ChromeLauncher",o.message);let c=this.fs.readFileSync(`${this.userDataDir}/chrome-err.log`,{encoding:"utf-8"});return be.error("ChromeLauncher",`Logging contents of ${this.userDataDir}/chrome-err.log`),be.error("ChromeLauncher",c),s(o)}Im(e.connectionPollInterval).then(a)})};a()})}kill(){if(this.chromeProcess){this.chromeProcess.on("close",()=>{delete this.chromeProcess,this.destroyTmp()}),be.log("ChromeLauncher",`Killing Chrome instance ${this.chromeProcess.pid}`);try{if(Eb){let e=(0,Qi.spawnSync)(`taskkill /pid ${this.chromeProcess.pid} /T /F`,{shell:!0,encoding:"utf-8"}),{stderr:t}=e;t&&be.error("ChromeLauncher","taskkill stderr",t)}else this.chromeProcess.pid&&process.kill(-this.chromeProcess.pid,"SIGKILL")}catch(e){let t=`Chrome could not be killed ${e.message}`;be.warn("ChromeLauncher",t)}this.destroyTmp()}}destroyTmp(){if(this.outFile&&(this.fs.closeSync(this.outFile),delete this.outFile),this.userDataDir===void 0||this.opts.userDataDir!==void 0)return;this.errFile&&(this.fs.closeSync(this.errFile),delete this.errFile),(this.fs.rmSync||this.fs.rmdirSync)(this.userDataDir,{recursive:!0,force:!0,maxRetries:10})}}});var Um={};ie(Um,{Launcher:()=>En,getChromePath:()=>Rb,killAll:()=>jm,launch:()=>Pb});var Hm=b(()=>{"use strict";g();Bm()});var Wm={};ie(Wm,{getActiveRunId:()=>kb,runTests:()=>rc,stopActiveRun:()=>tc});function Km(r){return r.steps.length===0?!1:r.steps.every(e=>(e.stepType??gt(e.instruction))==="api")}function Zo(r){return r.type==="api"}function In(r){return r.type==="audit"||r.type==="performance"}function tc(){Zi&&Zi()}function kb(){return ec}async function rc(r,e,t,s){let n=(0,qm.randomUUID)(),i=new Date().toISOString(),a=nt.default.join(r,"tests"),o=!1;ec=n,Zi=()=>{o=!0,pe.info({runId:n},"Run stop requested")};let c=e.env??t.environment??"DEV",l=t.environmentUrls?.[c];l&&(t={...t,baseUrl:l}),pe.info({runId:n,runConfig:e,environment:c,baseUrl:t.baseUrl},"Run started");let u=await el(r,n),d=(C,v,k)=>{let T=new Date().toISOString(),P=k?" "+JSON.stringify(k):"";u.write(`[${T}] [${C.toUpperCase()}] ${v}${P}`)};d("info","Run started",{runId:n,environment:c,baseUrl:t.baseUrl});let m=new Ii,p=new Ri,f=new ki(r,n,c),h=await bn(r),S=[];h.enabled&&(t.screenshot!=="on"&&(t={...t,screenshot:"on"}),d("info","Visual regression enabled \u2014 screenshots forced ON",{threshold:h.defaultThreshold,fullPage:h.fullPage}));try{let C=await Tb(a,e);C.length===0&&pe.warn({runConfig:e},"No tests matched the run config");let v=await Ab(r,c),k=new Map,T=[];for(let $ of C){if($.testCase.dataSource)try{let M=await Ap(r,$.testCase.dataSource);if(M.length>0){M.forEach((Z,K)=>T.push({...$,dataRow:Z,dataRowIndex:K})),pe.info({caseId:$.testCase.id,rows:M.length},"Expanded data-driven case");continue}}catch(M){pe.warn({err:M,caseId:$.testCase.id},"Failed to load data rows \u2014 running case once")}T.push($)}if(s?.broadcast("run:start",{runId:n,totalTests:T.length}),t.mcp?.proactiveHealing&&t.mcp?.enabled){pe.info("Proactive healing enabled \u2014 validating selectors before run");let $=null;try{$=new Tt({headless:t.mcp.headless,actionTimeout:t.mcp.actionTimeout,idleTimeout:t.mcp.idleTimeout});for(let M of T)for(let Z of M.testCase.steps){if(!Z.generatedCode.trim())continue;let K=await rp($,t.baseUrl,Z.generatedCode);K.ok||(s?.broadcast("healing:proactive",{runId:n,caseId:M.testCase.id,stepId:Z.id,instruction:Z.instruction,missing:K.checks.filter(Le=>!Le.found).map(Le=>({selector:Le.selector,suggestion:Le.suggestion}))}),pe.warn({stepId:Z.id,missing:K.missing},`Proactive check: ${K.missing} selector(s) may be broken`))}}catch(M){pe.warn({err:M},"Proactive healing check failed \u2014 continuing with run")}finally{await $?.disconnect()}}let P;if(t.rag?.enabled)try{P=await Si.create(r,t.rag),pe.info(P.getStats(),"RAG memory loaded"),d("info","RAG memory loaded",P.getStats())}catch($){pe.warn({err:$},"RAG initialization failed \u2014 continuing without memory")}let G=ht(r,void 0,void 0,P),ne=G.getPrimaryProvider(),ae=new Ci(t.healing.dailyBudget),me=t.browsers,Qt=[],Fe=T.length>0&&T.every(({suite:$,testCase:M})=>(Zo($)||Km(M))&&!In($));Fe&&(pe.info("All test cases are API-only \u2014 no browser will be launched"),d("info","API-only run detected \u2014 skipping browser launch"));let oe=new Map;if(Fe)for(let $ of me)oe.set($,null);else{let $=await Promise.all(me.map(async M=>({name:M,instance:await m.launchBrowser(M,t)})));for(let{name:M,instance:Z}of $)oe.set(M,Z)}let Zt=new Map;for(let $ of T){let M=$.suite.id;Zt.has(M)||Zt.set(M,[]),Zt.get(M).push($)}let St=async($,M,Z,K,Le,br)=>{if(M.length===0)return;let Qe=`hook_${$}_${Z.id}`,Ge=$.replace(/_/g," ");s?.broadcast("run:case",{runId:n,suiteId:Z.id,caseId:Qe,caseName:`\u2699 ${Ge} \u2014 ${Z.name}`,browser:Le,status:"running"});let Ze="passed",De=null,Y,U,_=[];try{br?Y=br:K?(De=await m.newContext(K,{...t,video:"off",trace:"off"},nt.default.join(r,"results","videos"),nt.default.join(r,"results","downloads",n)),Y=await m.newPage(De),await Oi(Y)):Y=await m.createApiOnlyPage(t.baseUrl);let ce=[];for(let ee of M){let B=`\u2699 [${Ge}] ${ee.instruction}`,z=`hook-${$}-${ee.id}`;if(!ee.generatedCode.trim()){d("warn",`[${$}] Hook step has no generated code \u2014 skipped: ${ee.instruction}`,{suiteId:Z.id});continue}s?.broadcast("run:step",{runId:n,caseId:Qe,stepId:z,instruction:B,status:"running",duration:0});let W=Date.now();try{let H=await _i({...ee,strategy:"primary",stepType:"ui",lastHealed:null},Y,{runId:n,suiteId:Z.id,caseId:Qe,browser:Le,rootDir:r,previousSteps:ce,variables:v,sharedVariables:k,config:t,provider:ne,budget:ae,healingReporter:p});s?.broadcast("run:step",{runId:n,caseId:Qe,stepId:z,instruction:B,status:H.status,duration:H.duration}),H.status==="failed"&&(Ze="failed"),_.push({...H,stepId:z,instruction:B}),ce.push({instruction:ee.instruction,code:ee.generatedCode})}catch(H){Ze="failed",s?.broadcast("run:step",{runId:n,caseId:Qe,stepId:z,instruction:B,status:"failed",duration:Date.now()-W}),pe.warn({err:H instanceof Error?H.message:String(H),hookType:$,stepId:ee.id,suiteId:Z.id},"Suite hook step failed \u2014 continuing with next step"),d("warn",`[${$}] Hook step failed: ${ee.instruction}`,{suiteId:Z.id}),_.push({stepId:z,instruction:B,status:"failed",code:ee.generatedCode,duration:Date.now()-W,error:H instanceof Error?H.message:String(H)})}if(K&&s)try{let H=await Y.screenshot({type:"jpeg",quality:60});s.broadcast("run:screenshot",{runId:n,caseId:Qe,stepId:z,image:H.toString("base64"),timestamp:Date.now()})}catch{}}if(De)try{U=await De.storageState()}catch{}}catch(ce){Ze="failed",pe.warn({err:ce instanceof Error?ce.message:String(ce),hookType:$,suiteId:Z.id},"Suite hook block failed \u2014 continuing"),d("warn",`[${$}] Hook block failed`,{suiteId:Z.id})}finally{De&&await m.closeContext(De,t).catch(()=>{}),s?.broadcast("run:case:done",{runId:n,suiteId:Z.id,caseId:Qe,previewCaseId:Qe,status:Ze})}return{storageState:U,stepResults:_}};for(let[,$]of Zt){if(o)break;let{suite:M,suiteDir:Z}=$[0],K;try{K=await ut(Z)}catch(Ge){pe.warn({err:Ge,suiteId:M.id},"Failed to read hooks.json \u2014 skipping suite hooks"),K={before_all:[],before_each:[],after_each:[],after_all:[]}}let Le=[];if(!Zo(M)&&(K.before_all.length>0||K.after_all.length>0)){let Ge=nt.default.join(r,"results","videos"),Ze=nt.default.join(r,"results","downloads",n),De=In(M),Y=De?{...t,screenshot:"off",video:"off",trace:"off"}:t;for(let U of me){if(o)break;let _=oe.get(U)??null;if(!_)continue;let ce=!De&&Y.trace!=="off"?nt.default.join(r,"results","traces",`${n}_suite_${M.id}_${U}.zip`):void 0,ee=null,B,z=Le.length,W,H;try{ee=await m.newContext(_,Y,Ge,Ze),B=await m.newPage(ee),await Oi(B),K.before_all.length>0&&(d("info",`[before_all] Suite: ${M.name}`,{suiteId:M.id,steps:K.before_all.length}),W=await St("before_all",K.before_all,M,_,U,B));for(let ue of $){if(o)break;let{testCase:Ce,dataRow:Et,dataRowIndex:ze}=ue,er=Et?{...v,...Et}:v,vr=Et?`${Ce.name} [row ${(ze??0)+1}]`:Ce.name;s?.broadcast("run:case",{runId:n,suiteId:M.id,caseId:`${Ce.id}_${U}`,caseName:me.length>1?`${vr} [${U}]`:vr,browser:U,status:"running"});let tr=`${Ce.id}_${U}`;d("info",`Case started: ${M.name} > ${vr}`,{suiteId:M.id,caseId:Ce.id,browser:U});let qe=await Vm(r,n,M,Ce,U,_,t,er,k,ne,ae,p,m,s,d,tr,P,h,S,K,void 0,ee,B);qe.suiteType=M.type,Et!==void 0&&(qe.caseResult.dataRowIndex=ze,qe.caseResult.dataRow=Et,qe.caseResult.caseName=vr),s?.broadcast("run:case:done",{runId:n,suiteId:M.id,caseId:Ce.id,previewCaseId:tr,status:qe.caseResult.status}),d("info",`Case finished: ${vr} [${U}] \u2014 ${qe.caseResult.status.toUpperCase()}`,{duration:qe.caseResult.duration,steps:qe.caseResult.steps.length,passed:qe.caseResult.steps.filter(Vr=>Vr.status==="passed").length,failed:qe.caseResult.steps.filter(Vr=>Vr.status==="failed").length}),Le.push({...qe,browserName:U})}}finally{K.after_all.length>0&&B&&(d("info",`[after_all] Suite: ${M.name}`,{suiteId:M.id,steps:K.after_all.length}),H=await St("after_all",K.after_all,M,_,U,B)),ee&&await m.closeContext(ee,Y,ce).catch(()=>{})}Gm(Le.slice(z).map(ue=>ue.caseResult),W?.stepResults,H?.stepResults)}}else{let Ge=[];for(let Y of me){let U=oe.get(Y)??null;for(let _ of $)Ge.push({browserName:Y,browser:U,..._})}let Ze,De;if(K.before_all.length>0){let Y=me[0],U=oe.get(Y)??null;d("info",`[before_all] Suite: ${M.name}`,{suiteId:M.id,steps:K.before_all.length}),Ze=await St("before_all",K.before_all,M,U,Y)}try{Le=await $b(Ge,t.parallel,async({browserName:Y,browser:U,suite:_,testCase:ce,dataRow:ee,dataRowIndex:B})=>{if(o)return null;let z=ee?{...v,...ee}:v,W=ee?`${ce.name} [row ${(B??0)+1}]`:ce.name;s?.broadcast("run:case",{runId:n,suiteId:_.id,caseId:`${ce.id}_${Y}`,caseName:me.length>1?`${W} [${Y}]`:W,browser:Y,status:"running"});let H=`${ce.id}_${Y}`;d("info",`Case started: ${_.name} > ${W}`,{suiteId:_.id,caseId:ce.id,browser:Y});let ue=await Vm(r,n,_,ce,Y,U,t,z,k,ne,ae,p,m,s,d,H,P,h,S,K,void 0);return ue.suiteType=_.type,ee!==void 0&&(ue.caseResult.dataRowIndex=B,ue.caseResult.dataRow=ee,ue.caseResult.caseName=W),s?.broadcast("run:case:done",{runId:n,suiteId:_.id,caseId:ce.id,previewCaseId:H,status:ue.caseResult.status}),d("info",`Case finished: ${W} [${Y}] \u2014 ${ue.caseResult.status.toUpperCase()}`,{duration:ue.caseResult.duration,steps:ue.caseResult.steps.length,passed:ue.caseResult.steps.filter(Ce=>Ce.status==="passed").length,failed:ue.caseResult.steps.filter(Ce=>Ce.status==="failed").length}),{...ue,browserName:Y}})}finally{if(K.after_all.length>0){let Y=me[0],U=oe.get(Y)??null;d("info",`[after_all] Suite: ${M.name}`,{suiteId:M.id,steps:K.after_all.length}),De=await St("after_all",K.after_all,M,U,Y)}}Gm(Le.filter(Boolean).map(Y=>Y.caseResult),Ze?.stepResults,De?.stepResults)}let Qe=Le.filter(Boolean);if(me.length>1)for(let Ge of me){let Ze=Qe.filter(De=>De.browserName===Ge);Qt.push(...zm(Ze,Ge))}else Qt.push(...zm(Qe))}let it=new Date().toISOString(),Se=_b(n,i,it,Qt,c);if(Se.logFilePath=u.logFilePath,Se.healed=p.eventCount,d("info",`Run finished: ${Se.status.toUpperCase()}`,{totalTests:Se.totalTests,passed:Se.passed,failed:Se.failed,skipped:Se.skipped,duration:Se.duration}),u.close(),await Md(r,Se),t.reporting.allure&&(await f.writeResults(Se),hp(r,n).catch($=>{pe.warn({err:$ instanceof Error?$.message:String($),runId:n},"Allure report generation failed")})),t.reporting.json){let $=nt.default.join(r,"results",`run-${n}.json`);await Ur.default.ensureDir(nt.default.dirname($)),await Ur.default.writeJson($,Se,{spaces:2})}if(h.enabled&&S.length>0&&(await Li(r,{runId:n,timestamp:it,diffs:S}),d("info",`Visual regression: ${S.length} comparisons saved`,{passed:S.filter($=>$.status==="passed").length,failed:S.filter($=>$.status==="failed").length,new:S.filter($=>$.status==="new").length})),await p.flush(r,n),await G.getCache().persist(),P)try{await P.persist(),pe.info(P.getStats(),"RAG memory persisted")}catch($){pe.warn({err:$},"RAG persistence failed")}return pe.info({runId:n,passed:Se.passed,failed:Se.failed,total:Se.totalTests,healingSpend:ae.totalSpent},"Run finished"),Se}finally{Zi=null,ec=null,await m.closeAll(),o&&s?.broadcast("run:stopped",{runId:n})}}async function Vm(r,e,t,s,n,i,a,o,c,l,u,d,m,p,f,h,S,C,v,k,T,P,G){let ne=h??s.id,ae=new Date().toISOString(),me=[],Qt=[],Fe=Zo(t)||Km(s),oe=In(t)?{...a,screenshot:"off",video:"off",trace:"off"}:a,Zt=!Fe&&oe.trace!=="off"?nt.default.join(r,"results","traces",`${e}_${s.id}_${n}.zip`):void 0,St=null,it,Se=!Fe&&!!G;if(Fe)it=await m.createApiOnlyPage(a.baseUrl),pe.debug({caseName:s.name},"API-only case \u2014 zero browser, using standalone HTTP client");else if(Se)St=P??null,it=G;else{if(!i)throw new Error("Browser required for UI test cases");St=await m.newContext(i,oe,nt.default.join(r,"results","videos"),nt.default.join(r,"results","downloads",e),T?.storageState),it=await m.newPage(St),await Oi(it)}let $="passed",M=!1,Z=[],K,Le=[],br=async(U,_)=>{let ce=s.timeout??t.timeout??oe.timeout,ee=ce!==oe.timeout?{...oe,timeout:ce}:oe,B=`\u2699 [${_}] ${U.instruction}`,z=`hook-${_.replace(/\s+/g,"-")}-${U.id}`;if(!U.generatedCode.trim()){f?.("warn",`[${_}] Hook step has no generated code \u2014 skipped: ${U.instruction}`,{suiteId:t.id,caseId:s.id});return}p?.broadcast("run:step",{runId:e,caseId:ne,stepId:z,instruction:B,status:"running",duration:0}),f?.("info",`[${_}] ${U.instruction}`,{suiteId:t.id,caseId:s.id});let W=Date.now();try{let H=await _i({...U,strategy:"primary",stepType:"ui",lastHealed:null},it,{runId:e,suiteId:t.id,caseId:s.id,browser:n,rootDir:r,previousSteps:Qt,variables:o,sharedVariables:c,config:ee,provider:l,budget:u,healingReporter:d});p?.broadcast("run:step",{runId:e,caseId:ne,stepId:z,instruction:B,status:H.status,duration:H.duration}),await Qe(z),me.push({...H,stepId:z,instruction:B}),H.status==="failed"&&($="failed")}catch(H){p?.broadcast("run:step",{runId:e,caseId:ne,stepId:z,instruction:B,status:"failed",duration:Date.now()-W}),await Qe(z),f?.("warn",`[${_}] Hook step failed: ${U.instruction}`,{suiteId:t.id,caseId:s.id}),me.push({stepId:z,instruction:B,status:"failed",code:U.generatedCode,duration:Date.now()-W,error:H instanceof Error?H.message:String(H)}),$="failed"}},Qe=async U=>{if(!(Fe||!p))try{let _=await it.screenshot({type:"jpeg",quality:60});p.broadcast("run:screenshot",{runId:e,caseId:ne,stepId:U,image:_.toString("base64"),timestamp:Date.now()})}catch{}};try{for(let _ of k?.before_each??[])await br(_,"before each");for(let _ of s.caseHooks?.before??[])await br(_,"before");for(let _ of s.steps){if(M){me.push({stepId:_.id,instruction:_.instruction,status:"skipped",code:_.generatedCode,duration:0});continue}p?.broadcast("run:step",{runId:e,caseId:ne,stepId:_.id,instruction:_.instruction,status:"running",duration:0});let ce=s.timeout??t.timeout??oe.timeout,ee=ce!==oe.timeout?{...oe,timeout:ce}:oe,B=await _i(_,it,{runId:e,suiteId:t.id,caseId:s.id,browser:n,rootDir:r,previousSteps:Qt,variables:o,sharedVariables:c,config:ee,provider:l,budget:u,healingReporter:d,ragManager:S});if(p?.broadcast("run:step",{runId:e,caseId:ne,stepId:B.stepId,instruction:B.instruction,status:B.status,duration:B.duration}),!Fe&&p){try{let H=await it.screenshot({type:"jpeg",quality:60});p.broadcast("run:screenshot",{runId:e,caseId:ne,stepId:B.stepId,image:H.toString("base64"),timestamp:Date.now()})}catch{}let W=m.drainConsoleLogs(it);W.length>0&&p.broadcast("run:console",{runId:e,caseId:ne,stepId:B.stepId,logs:W})}let z=B.status==="failed"?"error":"info";if(f?.(z,` Step ${_.order}: ${B.instruction} \u2014 ${B.status.toUpperCase()} (${B.duration}ms)`,{...B.error?{error:B.error}:{},...B.healed?{healed:!0}:{}}),me.push(B),C?.enabled&&v&&!Fe&&_.visualCheck&&B.status==="passed"&&B.screenshotPath)try{let W=a.viewport??{width:1280,height:720},H=Ht(t.name),ue=Ht(s.name),Ce=Gp(r,H,ue,_.id,n,W),Et=zp(r,e,_.id,n,W),ze=await Vp(Ce,B.screenshotPath,Et,{threshold:.1,diffThreshold:C.defaultThreshold}),er=ze.totalPixels===0?"new":ze.match?"passed":"failed";v.push({stepId:_.id,caseId:s.id,caseName:s.name,suiteName:t.name,suiteId:t.id,browser:n,viewport:`${W.width}x${W.height}`,diffPercentage:ze.diffPercentage,threshold:C.defaultThreshold,status:er,baselinePath:ze.baselinePath,actualPath:ze.actualPath,diffPath:ze.diffImagePath??Et}),f?.("info",` Visual: ${_.id} \u2014 ${er} (diff: ${ze.diffPercentage}%)`,{baseline:Ce})}catch(W){pe.debug({err:W,stepId:_.id},"Visual regression comparison failed")}B.status==="passed"?Qt.push({instruction:_.instruction,code:B.code}):B.status==="failed"&&($="failed",M=!0)}let U=(()=>{let _=s.lighthouseCategories;return _&&_.length>0?_:In(t)?["performance","accessibility","seo"]:null})();if(!Fe&&n==="chromium"&&i&&U!==null){let _=new Map(s.steps.map(z=>[z.id,z])),ee=s.steps.some(z=>z.runAudit===!0)?me.map((z,W)=>({s:z,idx:W})).filter(({s:z})=>_.get(z.stepId)?.runAudit===!0&&!!(z.auditUrl??z.navigatedToUrl)):[],B=U;if(ee.length>0){f?.("info",`--- LIGHTHOUSE AUDIT [${B.join(", ")}] ---`);try{let{default:z}=await import("lighthouse"),W=await Promise.resolve().then(()=>(Hm(),Um)),H=W.launch??W.default?.launch,{chromium:ue}=await import("playwright"),Ce=ue.executablePath(),Et=nt.default.join(r,"results",".lh-profiles");await Ur.default.ensureDir(Et);for(let ze=0;ze<ee.length;ze++){let{s:er,idx:vr}=ee[ze],tr=er.auditUrl??er.navigatedToUrl;f?.("info",` Auditing: ${tr}`);let qe=null,Vr=nt.default.join(Et,`audit-${Date.now()}-${ze}`);try{await Ur.default.ensureDir(Vr),qe=await H({chromePath:Ce,chromeFlags:["--headless","--no-sandbox","--disable-gpu"],userDataDir:Vr,logLevel:"silent"});let cs=(await Promise.race([z(tr,{port:qe.port,output:"json",logLevel:"silent",onlyCategories:B,disableStorageReset:!0,screenEmulation:{disabled:!0},throttlingMethod:"simulate",formFactor:"desktop"}),new Promise((ls,sr)=>setTimeout(()=>sr(new Error("Lighthouse audit timed out after 60s")),6e4))]))?.lhr,$c=cs?.audits??{},_c=cs?.categories??{},xr=ls=>{let sr=$c[ls]?.numericValue;return sr!=null&&isFinite(sr)?sr:void 0},wa=ls=>{let sr=_c[ls]?.score;return sr!=null?Math.round(sr*100):void 0},Oc=ls=>(_c[ls]?.auditRefs??[]).filter(Gr=>(Gr.weight??0)>0).map(Gr=>{let $n=$c[Gr.id],Sr=$n?.score??null,Qs=Sr!==null&&Sr>0&&Sr<1;return{id:Gr.id,title:$n?.title??Gr.id,passed:Sr===1,partial:Qs||void 0,na:Sr===null||void 0}}).sort((Gr,$n)=>{let Sr=Qs=>Qs.na?3:Qs.passed?4:Qs.partial?1:0;return Sr(Gr)-Sr($n)}),rr=B.includes("performance"),Lc=B.includes("accessibility"),Dc=B.includes("seo"),It={url:tr,stepIndex:vr,stepInstruction:er.instruction,score:rr?wa("performance"):void 0,a11yScore:Lc?wa("accessibility"):void 0,seoScore:Dc?wa("seo"):void 0,fcp:rr?xr("first-contentful-paint"):void 0,lcp:rr?xr("largest-contentful-paint"):void 0,cls:rr?xr("cumulative-layout-shift"):void 0,ttfb:rr?xr("server-response-time"):void 0,tbt:rr?xr("total-blocking-time"):void 0,si:rr?xr("speed-index"):void 0,tti:rr?xr("interactive"):void 0,inp:rr?xr("interaction-to-next-paint"):void 0,a11yAudits:Lc?Oc("accessibility"):void 0,seoAudits:Dc?Oc("seo"):void 0};Z.push(It),f?.("info",` Perf: ${It.score??"--"} | A11y: ${It.a11yScore??"--"} | SEO: ${It.seoScore??"--"} | FCP: ${It.fcp!=null?(It.fcp/1e3).toFixed(2)+"s":"--"} | LCP: ${It.lcp!=null?(It.lcp/1e3).toFixed(2)+"s":"--"} | CLS: ${It.cls?.toFixed(3)??"--"} | TTFB: ${It.ttfb!=null?(It.ttfb/1e3).toFixed(2)+"s":"--"}`)}catch(An){let cs=An instanceof Error?An.message:String(An);pe.warn({url:tr,err:cs},"Lighthouse audit failed for URL"),f?.("warn",` Lighthouse failed for ${tr}: ${cs}`),Z.push({url:tr,stepIndex:vr,stepInstruction:er.instruction,lighthouseError:cs})}finally{try{await qe?.kill()}catch{}try{await Ur.default.remove(Vr)}catch{}}}}catch(z){let W=z instanceof Error?z.message:String(z);pe.warn({err:W},"Lighthouse import/init failed"),f?.("warn",` Lighthouse init failed: ${W}`);let H=new Set(Z.map(ue=>ue.stepIndex));for(let{s:ue,idx:Ce}of ee)H.has(Ce)||Z.push({url:ue.auditUrl??ue.navigatedToUrl,stepIndex:Ce,stepInstruction:ue.instruction,lighthouseError:`Lighthouse unavailable: ${W}`})}f?.("info","------------------------------------")}In(t)&&Z.length>0&&$==="passed"&&Z.some(H=>!H.lighthouseError&&(H.score!=null&&H.score<50||H.a11yScore!=null&&H.a11yScore<50||H.seoScore!=null&&H.seoScore<50))&&($="failed",f?.("warn"," Case FAILED: one or more Lighthouse scores below 50 (Poor threshold)"))}else!Fe&&n!=="chromium"&&(Z=me.map((_,ce)=>({s:_,idx:ce})).filter(({s:_})=>!!_.navigatedToUrl).map(({s:_,idx:ce})=>({url:_.navigatedToUrl,stepIndex:ce,stepInstruction:_.instruction,lighthouseError:"Lighthouse only available for Chromium"})))}finally{for(let U of s.caseHooks?.after??[])await br(U,"after");for(let U of k?.after_each??[])await br(U,"after each");!Fe&&!Se&&(K=await it.video()?.path().catch(()=>{})),Fe||Se||St&&(await m.closeContext(St,oe,Zt),Le=m.takeDownloads(St),(oe.video==="retain-on-failure"||oe.video==="on-first-retry")&&$==="passed"&&K&&await Ur.default.remove(K).catch(()=>{}),(oe.trace==="retain-on-failure"||oe.trace==="on-first-retry")&&$==="passed"&&Zt&&await Ur.default.remove(Zt).catch(()=>{}))}let Ge,Ze;if(!Fe&&!Se){let U=oe.video==="retain-on-failure"||oe.video==="on-first-retry";Ge=oe.video!=="off"&&K?U&&$==="passed"?void 0:K:void 0;let _=oe.trace==="retain-on-failure"||oe.trace==="on-first-retry";Ze=oe.trace==="off"||_&&$==="passed"?void 0:Zt}let De=new Date().toISOString(),Y=new Date(De).getTime()-new Date(ae).getTime();return{suiteId:t.id,suiteName:t.name,caseResult:{caseId:s.id,caseName:s.name,status:$,steps:me,duration:Y,browser:n,device:a.device||void 0,startedAt:ae,finishedAt:De,videoPath:Ge,tracePath:Ze,downloadPaths:Le,pageLoads:Z}}}async function Tb(r,e){let t=await qr(r),s=[];for(let n of t){if(e.type&&n.type!==e.type)continue;if(e.suite){let o=e.suite.toLowerCase(),c=n.name.toLowerCase().includes(o),l=n.id===e.suite;if(!c&&!l)continue}let i=await we(r,n.id);if(!i){pe.warn({suiteId:n.id},"Suite directory not found \u2014 skipping");continue}let a=await lt(i);for(let o of a){if(e.test){let c=e.test.toLowerCase(),l=o.name.toLowerCase().includes(c),u=o.id===e.test;if(!l&&!u)continue}e.tag&&!o.tags.includes(e.tag)||!e.all&&!e.type&&!e.suite&&!e.test&&!e.tag||s.push({suite:n,suiteDir:i,testCase:o})}}return s}async function Ab(r,e){try{let t=await ft(r,e??"dev"),s=Object.keys(t).length;return pe.info({count:s,env:e??"dev",rootDir:r},"Variables loaded"),t}catch(t){return pe.warn({err:t instanceof Error?t.message:String(t)},"Failed to load variables \u2014 running without variable substitution"),{}}}async function $b(r,e,t){let s=[],n=[...r];async function i(){for(;n.length>0;){let o=n.shift();o!==void 0&&s.push(await t(o))}}let a=Array.from({length:Math.min(e,r.length||1)},()=>i());return await Promise.all(a),s}function Gm(r,e,t){if(r.length!==0&&(e&&e.length>0&&(r[0].steps=[...e,...r[0].steps],e.some(s=>s.status==="failed")&&(r[0].status="failed")),t&&t.length>0)){let s=r[r.length-1];s.steps=[...s.steps,...t],t.some(n=>n.status==="failed")&&(s.status="failed")}}function zm(r,e){let t=new Map;for(let n of r){let i=n.suiteId,a=t.get(i)??[];a.push(n),t.set(i,a)}let s=[];for(let[n,i]of t){let a=i[0];if(!a)continue;let o=i.map(m=>m.caseResult),c=o.some(m=>m.status==="failed")?"failed":"passed",l=o[0]?.startedAt??new Date().toISOString(),u=o[o.length-1]?.finishedAt??new Date().toISOString(),d=new Date(u).getTime()-new Date(l).getTime();s.push({suiteId:n,suiteName:e?`${a.suiteName} [${e}]`:a.suiteName,suiteType:a.suiteType,status:c,cases:o,duration:d,browser:o[0]?.browser??"chromium",startedAt:l,finishedAt:u})}return s}function _b(r,e,t,s,n){let i=s.flatMap(d=>d.cases),a=i.filter(d=>d.status==="passed").length,o=i.filter(d=>d.status==="failed").length,c=i.filter(d=>d.status==="skipped").length,l=i.length,u=new Date(t).getTime()-new Date(e).getTime();return{runId:r,status:o>0?"failed":"passed",environment:n,suites:s,duration:u,startedAt:e,finishedAt:t,totalTests:l,passed:a,failed:o,skipped:c}}var nt,qm,Ur,pe,Zi,ec,sc=b(()=>{"use strict";g();nt=x(require("path")),qm=require("crypto"),Ur=x(require("fs-extra"));et();Rt();Tr();dn();fn();$r();cn();ep();sp();np();ap();cp();pp();yp();Rp();kp();$p();Kr();No();qp();Vt();j();pe=I("runner");Zi=null,ec=null});async function Xm(r){let e=r.rootDir;r.post("/run",async(t,s)=>{let n=Nb.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid run config",details:n.error.issues});try{let i=await J(e),a=n.data,o={...i,headless:a.headed!==void 0?!a.headed:!1,browsers:a.browsers?.length?a.browsers:i.browsers,parallel:a.parallel??i.parallel,screenshot:a.screenshot??i.screenshot,video:a.video??i.video,trace:a.trace??i.trace,device:a.device??i.device,...a.ci?{headless:!0}:{},healing:a.ci?{...i.healing,healAssertionLocators:!1}:i.healing},c=r.wsManager,l=new Date().toISOString();return nc.info({runConfig:a},"Run requested via API"),rc(e,a,o,c).then(u=>{c.broadcast("run:complete",{runId:u.runId,passed:u.passed,failed:u.failed,skipped:u.skipped,duration:u.duration,healed:u.healed??0}),nc.info({runId:u.runId,status:u.status},"API-triggered run complete")}).catch(u=>{nc.error({err:u},"API-triggered run failed"),c.broadcast("run:error",{error:u instanceof Error?u.message:String(u)})}),s.status(202).send({status:"accepted",startedAt:l,message:"Run started \u2014 subscribe to WebSocket for progress"})}catch(i){return w(s,500,"Failed to start run",i)}}),r.post("/run/stop",async(t,s)=>(tc(),s.status(202).send({status:"stopping"}))),r.get("/run",async(t,s)=>{let n=Fb.safeParse(t.query);if(!n.success)return s.status(400).send({error:"Invalid query",details:n.error.issues});try{let i=await fi(e,n.data.limit);return s.send(i)}catch(i){return w(s,500,"Failed to list run results",i)}}),r.get("/run/:runId",async(t,s)=>{try{let n=await rs(e,t.params.runId);return s.send(n)}catch{return s.status(404).send({error:`Run "${t.params.runId}" not found`})}})}var xe,nc,Ob,Lb,Db,Nb,Fb,Jm=b(()=>{"use strict";g();xe=require("zod");sc();Me();fn();de();j();nc=I("routes/run"),Ob=xe.z.enum(["off","on","only-on-failure"]),Lb=xe.z.enum(["off","on","on-first-retry","retain-on-failure"]),Db=xe.z.enum(["off","on","on-first-retry","retain-on-failure"]),Nb=xe.z.object({all:xe.z.boolean().optional(),suite:xe.z.string().optional(),tag:xe.z.string().optional(),test:xe.z.string().optional(),browsers:xe.z.array(xe.z.string()).optional(),env:xe.z.string().optional(),parallel:xe.z.number().int().positive().optional(),headed:xe.z.boolean().optional(),ci:xe.z.boolean().optional(),reporter:xe.z.array(xe.z.string()).optional(),noHealing:xe.z.boolean().optional(),screenshot:Ob.optional(),video:Lb.optional(),trace:Db.optional(),device:xe.z.string().optional()}),Fb=xe.z.object({limit:xe.z.coerce.number().int().positive().max(100).default(20)})});async function ic(r){let e;if(r.startsWith("http://")||r.startsWith("https://")){let t=r;t.includes("#")&&(t=t.split("#")[0]);let s=t.replace(/\/$/,""),n=[t,`${t}.json`,`${s}/swagger.json`,`${s}/openapi.json`,`${s}/openapi.yaml`,`${s}/openapi.yml`,`${s}/swagger.yaml`,`${s}/api-docs`,`${s}/api-docs.json`,`${s}/v2/swagger.json`,`${s}/v2/openapi.yaml`,`${s}/v2/openapi.json`,`${s}/v3/api-docs`,`${s}/v3/openapi.yaml`,s.replace(/\/[^/]+$/,"")+"/swagger.json",s.replace(/\/[^/]+$/,"")+"/openapi.json",s.replace(/\/[^/]+$/,"")+"/openapi.yaml",s.replace(/\/[^/]+$/,"")+"/openapi.yml",s.replace(/\/[^/]+\/[^/]+$/,"")+"/openapi.yaml",s.replace(/\/[^/]+\/[^/]+$/,"")+"/swagger.json"];try{let c=await fetch(t,{headers:{Accept:"text/html, */*"},signal:AbortSignal.timeout(1e4)});if(c.ok){let l=await c.text();if(l.includes("<!")||l.includes("<html")){let u=[/Redoc\.init\(\s*['"]([^'"]+)['"]/,/spec-url=['"]([^'"]+)['"]/i,/url:\s*['"]([^'"]+\.(?:json|yaml|yml))['"]/,/specUrl:\s*['"]([^'"]+)['"]/,/["']([^"']*(?:openapi|swagger)[^"']*\.(?:json|yaml|yml))["']/i,/href=['"]([^'"]*\.(?:json|yaml|yml))['"][^>]*>.*?(?:openapi|swagger|spec|api-doc)/i];for(let d of u){let m=l.match(d);if(m?.[1]){let p=m[1];p.startsWith("/")?p=`${new URL(t).origin}${p}`:p.startsWith("http")||(p=new URL(p,t).href),n.unshift(p),Cn.info({specUrl:p,pattern:d.source},"Extracted spec URL from HTML page")}}}}}catch{}let i=[...new Set(n)],a=!1,o=[];for(let c of i)try{Cn.debug({tryUrl:c},"Trying to fetch OpenAPI spec");let l=await fetch(c,{headers:{Accept:"application/json, application/yaml, */*"},signal:AbortSignal.timeout(15e3)});if(l.ok){let u=l.headers.get("content-type")??"",d=await l.text(),m=u.includes("json")||d.trimStart().startsWith("{")||d.trimStart().startsWith("["),p=u.includes("yaml")||u.includes("yml")||c.endsWith(".yaml")||c.endsWith(".yml");if(m||p)try{if(e=m?JSON.parse(d):ea.default.load(d),e&&(e.openapi||e.swagger||e.paths)){a=!0,Cn.info({tryUrl:c,format:p?"YAML":"JSON"},"Successfully fetched OpenAPI spec");break}o.push(`${c}: Parsed but not a valid OpenAPI spec`)}catch{o.push(`${c}: Response is not valid ${p?"YAML":"JSON"}`)}else{if(!u.includes("html")&&!d.trimStart().startsWith("<!"))try{let f=ea.default.load(d);if(f&&typeof f=="object"&&(f.openapi||f.swagger||f.paths)){e=f,a=!0,Cn.info({tryUrl:c,format:"YAML (text/plain)"},"Successfully fetched OpenAPI spec");break}}catch{}o.push(`${c}: Response is HTML/text, not JSON or YAML (content-type: ${u})`)}}else o.push(`${c}: HTTP ${l.status}`)}catch(l){o.push(`${c}: ${l instanceof Error?l.message:String(l)}`)}if(!a)throw new Error(`Could not auto-discover the OpenAPI spec from this URL.
|
|
948
948
|
This may be a developer portal that loads the spec dynamically.
|
|
949
949
|
|
|
950
950
|
Try one of these instead:
|
|
@@ -954,8 +954,8 @@ Try one of these instead:
|
|
|
954
954
|
|
|
955
955
|
Details:
|
|
956
956
|
${o.map(c=>` \u2022 ${c}`).join(`
|
|
957
|
-
`)}`)}else try{e=JSON.parse(r)}catch{e=
|
|
958
|
-
`:t.join("")}return""}async function
|
|
957
|
+
`)}`)}else try{e=JSON.parse(r)}catch{e=ea.default.load(r)}return Mb(e)}function Mb(r){let t=(r.info??{}).title??"API",s="";r.servers&&Array.isArray(r.servers)&&r.servers.length>0?s=r.servers[0].url??"":r.host&&(s=`${r.schemes?.[0]??"https"}://${r.host}${r.basePath??""}`);let n=r.paths??{},i=[];for(let[a,o]of Object.entries(n))for(let[c,l]of Object.entries(o)){if(["get","post","put","delete","patch"].indexOf(c)===-1)continue;let u=l,d=u.tags??["Default"],m=u.summary??u.operationId??`${c.toUpperCase()} ${a}`,p=(u.parameters??[]).map(C=>({name:C.name,in:C.in,required:C.required??!1,type:C.schema?.type??C.type??"string"})),f;if(u.requestBody){let v=u.requestBody.content;if(v){let k=Object.keys(v)[0],T=v[k]?.schema??{},P=v[k]?.example??T.example;f={contentType:k,schema:T,example:P}}}let h=u.responses??{},S=Object.entries(h).map(([C,v])=>({status:parseInt(C,10)||0,description:v.description??""}));i.push({method:c.toUpperCase(),path:a,summary:m,tag:d[0],parameters:p,requestBody:f,responses:S})}return Cn.info({title:t,endpointCount:i.length},"Parsed OpenAPI spec"),{title:t,baseUrl:s,endpoints:i}}var ea,Cn,Ym=b(()=>{"use strict";g();ea=x(require("js-yaml"));j();Cn=I("swagger-parser")});function ac(r){let e=r.trim();if(/^[A-Z][A-Z0-9_]+-\d+$/i.test(e))return e.toUpperCase();try{let t=new URL(e),s=t.pathname.match(/\/browse\/([A-Z][A-Z0-9_]+-\d+)/i);if(s)return s[1].toUpperCase();let n=t.searchParams.get("selectedIssue");if(n&&/^[A-Z][A-Z0-9_]+-\d+$/i.test(n))return n.toUpperCase();let i=t.pathname.match(/\/issue\/([A-Z][A-Z0-9_]+-\d+)/i);if(i)return i[1].toUpperCase()}catch{let t=e.match(/([A-Z][A-Z0-9_]+-\d+)/i);if(t)return t[1].toUpperCase()}return null}function jb(){let r=process.env.JIRA_BASE_URL,e=process.env.JIRA_EMAIL,t=process.env.JIRA_API_TOKEN;return!r||!e||!t?null:{baseUrl:r.replace(/\/+$/,""),email:e,apiToken:t}}function ra(r){if(!r||typeof r!="object")return"";let e=r;if(e.type==="text"&&typeof e.text=="string")return e.text;if(Array.isArray(e.content)){let t=e.content.map(n=>ra(n)),s=new Set(["paragraph","heading","bulletList","orderedList","listItem","blockquote","codeBlock","rule"]);return typeof e.type=="string"&&s.has(e.type)?t.join("")+`
|
|
958
|
+
`:t.join("")}return""}async function oc(r){let e=jb();if(!e)throw new Error("Jira not configured \u2014 set JIRA_BASE_URL, JIRA_EMAIL, and JIRA_API_TOKEN in your .env file. [JIRA_NOT_CONFIGURED]");let t="Basic "+Buffer.from(`${e.email}:${e.apiToken}`).toString("base64"),s=`${e.baseUrl}/rest/api/3/issue/${encodeURIComponent(r)}?fields=summary,description,comment,issuetype,status,priority,labels,customfield_10020`;pr.info({issueKey:r,url:s.replace(/\/rest.*/,"/...")},"Fetching Jira issue");let n=await fetch(s,{method:"GET",headers:{Authorization:t,Accept:"application/json"}});if(n.status===401||n.status===403)throw new Error(`Jira authentication failed (${n.status}). Check JIRA_EMAIL and JIRA_API_TOKEN in your .env file.`);if(n.status===404)throw new Error(`Jira issue "${r}" not found. Verify the issue key and that your account has access.`);if(n.status===429)throw new Error("Jira API rate limit exceeded. Please try again in a few seconds.");if(!n.ok){let m=await n.text().catch(()=>"Unknown error");throw new Error(`Jira API returned ${n.status}: ${m}`)}let i=await n.json(),a=i.fields??{},o="";typeof a.description=="string"?o=a.description:a.description&&typeof a.description=="object"&&(o=ra(a.description).trim());let c="",l=a.customfield_10020;if(typeof l=="string"?c=l:l&&typeof l=="object"&&(c=ra(l).trim()),!c&&o){let m=o.match(/(?:acceptance\s+criteria|AC|given\s*\/?\s*when\s*\/?\s*then)[:\s]*\n?([\s\S]*?)(?:\n\n|\n(?=[A-Z])|\n(?=#{1,3}\s)|$)/i);m&&(c=m[1].trim())}let u=[],d=a.comment;if(d&&Array.isArray(d.comments))for(let m of d.comments.slice(-5)){let p=m.body,f=typeof p=="string"?p:p?ra(p).trim():"";f&&u.push(f)}return{issueKey:String(i.key??r),summary:String(a.summary??""),description:o,acceptanceCriteria:c,comments:u,issueType:a.issuetype?.name??"Unknown",status:a.status?.name??"Unknown",priority:a.priority?.name??"Medium",labels:Array.isArray(a.labels)?a.labels:[]}}function Qm(r,e){let t=[];return t.push(`## Jira Issue: ${r.issueKey}`),t.push(`**Summary:** ${r.summary}`),t.push(`**Type:** ${r.issueType} | **Status:** ${r.status} | **Priority:** ${r.priority}`),r.labels.length>0&&t.push(`**Labels:** ${r.labels.join(", ")}`),r.description&&t.push(`
|
|
959
959
|
### Description
|
|
960
960
|
${r.description}`),r.acceptanceCriteria&&t.push(`
|
|
961
961
|
### Acceptance Criteria
|
|
@@ -963,9 +963,9 @@ ${r.acceptanceCriteria}`),r.comments.length>0&&(t.push(`
|
|
|
963
963
|
### Relevant Comments`),r.comments.forEach((s,n)=>t.push(`${n+1}. ${s}`))),e&&e.trim()&&t.push(`
|
|
964
964
|
### Additional Context (User Input)
|
|
965
965
|
${e.trim()}`),t.join(`
|
|
966
|
-
`)}async function
|
|
967
|
-
`),f=o.data.suiteName??`${c.title} API Tests`,h=
|
|
968
|
-
`).map(t=>({type:"paragraph",content:t.trim()?[{type:"text",text:t}]:[]}))}}var
|
|
966
|
+
`)}async function Zm(r){let e=r.rootDir;r.post("/story/fetch-jira",async(i,a)=>{let o=Ub.safeParse(i.body);if(!o.success)return a.status(400).send({error:"Invalid request",details:o.error.issues});let c=ac(o.data.jiraUrl);if(!c)return a.status(400).send({error:"Invalid Jira URL",details:"Could not extract an issue key. Expected format: https://domain.atlassian.net/browse/PROJ-123 or PROJ-123"});try{let l=await oc(c);return a.status(200).send(l)}catch(l){let u=l instanceof Error&&l.message.includes("not configured")?400:l instanceof Error&&(l.message.includes("401")||l.message.includes("403"))?401:l instanceof Error&&l.message.includes("not found")?404:l instanceof Error&&l.message.includes("rate limit")?429:500;return w(a,u,"Failed to fetch Jira issue",l)}}),r.post("/story/generate",async(i,a)=>{let o=Bb.safeParse(i.body);if(!o.success)return a.status(400).send({error:"Invalid request",details:o.error.issues});try{let c=ta.default.join(e,"tests"),l=o.data.story;if(o.data.jiraUrl){let m=ac(o.data.jiraUrl);if(m)try{let p=await oc(m);l=Qm(p,o.data.story||void 0),pr.info({issueKey:m},"Enriched story with Jira content")}catch(p){if(!l||l.length<10)return w(a,400,"Jira fetch failed and no user story provided",p);pr.warn({err:p},"Failed to fetch Jira issue \u2014 proceeding with user story only")}}if(!l||l.length<10)return a.status(400).send({error:"Not enough content to generate tests. Provide a story or a valid Jira link."});pr.info({suiteName:o.data.suiteName},"Story generation requested");let{suiteId:u,suiteName:d}=await un(l,{rootDir:e,outputDir:c,suiteName:o.data.suiteName});return a.status(201).send({message:"Suite generated successfully",suiteId:u,suiteName:d})}catch(c){return w(a,500,"Failed to generate suite from story",c)}});let t=Ee.z.object({story:Ee.z.string().min(10),suiteName:Ee.z.string().min(1).optional(),jiraUrl:Ee.z.string().optional()}),s=Ee.z.object({stories:Ee.z.array(t).min(1).max(500),concurrency:Ee.z.number().int().min(1).max(5).default(3)});r.post("/story/generate-bulk",async(i,a)=>{let o=s.safeParse(i.body);if(!o.success)return a.status(400).send({error:"Invalid request",details:o.error.issues});let{stories:c,concurrency:l}=o.data,u=ta.default.join(e,"tests"),d=r.wsManager;pr.info({total:c.length,concurrency:l},"Bulk story generation requested");let m=await Promise.all(c.map(async p=>{if(!p.jiraUrl)return{story:p.story,suiteName:p.suiteName};let f=ac(p.jiraUrl);if(!f)return{story:p.story,suiteName:p.suiteName};try{let h=await oc(f);return{story:Qm(h,p.story),suiteName:p.suiteName??h.summary}}catch{return pr.warn({issueKey:f},"Failed to fetch Jira issue for bulk item \u2014 using raw story"),{story:p.story,suiteName:p.suiteName}}}));return lo(m,{rootDir:e,outputDir:u},d,l).catch(p=>{pr.error({err:p},"Bulk story generation error")}),a.status(202).send({message:`Processing ${c.length} stories in the background`,total:c.length})}),r.post("/story/swagger-preview",async(i,a)=>{let o=i.body;if(!o.specUrl)return a.status(400).send({error:"specUrl is required"});try{let c=await ic(o.specUrl),l=[...new Set(c.endpoints.map(u=>u.tag))];return a.send({title:c.title,baseUrl:c.baseUrl,totalEndpoints:c.endpoints.length,tags:l,endpoints:c.endpoints.map(u=>({method:u.method,path:u.path,summary:u.summary,tag:u.tag}))})}catch(c){return w(a,500,"Could not fetch OpenAPI spec",c)}});let n=Ee.z.object({specUrl:Ee.z.string().min(1,"Swagger/OpenAPI spec URL or JSON is required"),suiteName:Ee.z.string().min(1).optional(),tags:Ee.z.array(Ee.z.string()).optional(),requirements:Ee.z.string().optional()});r.post("/story/generate-from-swagger",{config:{requestTimeout:12e4}},async(i,a)=>{let o=n.safeParse(i.body);if(!o.success)return a.status(400).send({error:"Invalid request",details:o.error.issues});try{let c=await ic(o.data.specUrl);pr.info({title:c.title,endpoints:c.endpoints.length},"Parsed OpenAPI spec");let l=c.endpoints;if(o.data.tags&&o.data.tags.length>0){let C=new Set(o.data.tags.map(v=>v.toLowerCase()));l=l.filter(v=>C.has(v.tag.toLowerCase()))}if(l.length===0)return a.status(400).send({error:"No endpoints found matching the specified tags"});let u=20,d=l.length;l.length>u&&(pr.warn({total:l.length,max:u},"Capping endpoints to max"),l=l.slice(0,u));let m=[`## API Test Suite: ${c.title}`,`Base URL: ${c.baseUrl}`,"","Generate API test cases for the following endpoints. Each test step should use Playwright request API (page.request.get/post/put/delete/patch). Include proper assertions for status codes and response bodies.",""];o.data.requirements?.trim()&&(m.push("### Additional Requirements"),m.push(o.data.requirements.trim()),m.push(""));for(let C of l)m.push(`### ${C.method} ${C.path}`),m.push(` Summary: ${C.summary}`),C.parameters.length>0&&m.push(` Parameters: ${C.parameters.map(v=>`${v.name} (${v.in}, ${v.type}${v.required?", required":""})`).join(", ")}`),C.requestBody&&(m.push(` Request Body: ${C.requestBody.contentType}`),C.requestBody.example&&m.push(` Example: ${JSON.stringify(C.requestBody.example)}`)),m.push(` Responses: ${C.responses.map(v=>`${v.status} (${v.description})`).join(", ")}`),m.push("");let p=m.join(`
|
|
967
|
+
`),f=o.data.suiteName??`${c.title} API Tests`,h=ta.default.join(e,"tests"),{suiteId:S}=await un(p,{rootDir:e,outputDir:h,suiteName:f,type:"api"});return a.status(201).send({message:"API test suite generated from OpenAPI spec",suiteId:S,suiteName:f,endpointsFound:l.length,totalEndpoints:d,specTitle:c.title})}catch(c){return w(a,500,"Failed to generate suite from OpenAPI spec",c)}})}var ta,Ee,pr,Bb,Ub,eg=b(()=>{"use strict";g();ta=x(require("path")),Ee=require("zod");$r();Ym();de();j();pr=I("routes/story");Bb=Ee.z.object({story:Ee.z.string().default(""),suiteName:Ee.z.string().min(1).optional(),jiraUrl:Ee.z.string().optional()}).refine(r=>r.story.length>=10||!!r.jiraUrl&&r.jiraUrl.trim().length>0,{message:"Provide a user story (\u2265 10 chars) or a Jira link",path:["story"]}),Ub=Ee.z.object({jiraUrl:Ee.z.string().min(1,"Jira URL is required")})});function tg(r,e){let t=Us.default.basename(e);return t!==e?null:Us.default.join(r,"results","downloads",t)}async function rg(r){let e=r.rootDir;r.get("/report",async(t,s)=>{let n=Vb.safeParse(t.query);if(!n.success)return s.status(400).send({error:"Invalid query",details:n.error.issues});try{let i=await fi(e,n.data.limit);return s.send(i)}catch(i){return w(s,500,"Failed to list reports",i)}}),r.get("/report/:runId",async(t,s)=>{try{let n=await rs(e,t.params.runId);return s.send(n)}catch{return s.status(404).send({error:`Report for run "${t.params.runId}" not found`})}}),r.get("/report/:runId/downloads",async(t,s)=>{try{let n=tg(e,t.params.runId);if(!n||!await Bs.default.pathExists(n))return s.send([]);let i=await Bs.default.readdir(n,{withFileTypes:!0}),a=await Promise.all(i.filter(o=>o.isFile()).map(async o=>{let c=await Bs.default.stat(Us.default.join(n,o.name));return{name:o.name,size:c.size}}));return a.sort((o,c)=>o.name.localeCompare(c.name)),s.send(a)}catch(n){return w(s,500,"Failed to list downloads",n)}}),r.get("/report/:runId/downloads/:file",async(t,s)=>{try{let n=tg(e,t.params.runId),i=Us.default.basename(t.params.file);if(!n||i!==t.params.file)return s.status(400).send({error:"Invalid path"});let a=Us.default.join(n,i);return await Bs.default.pathExists(a)?(s.header("Content-Disposition",`attachment; filename="${i}"`),s.type("application/octet-stream"),s.send(Bs.default.createReadStream(a))):s.status(404).send({error:"File not found"})}catch(n){return w(s,500,"Failed to download file",n)}}),r.delete("/report/:runId",async(t,s)=>{try{return await jd(e,t.params.runId),Hb.info({runId:t.params.runId},"Run result deleted"),s.status(204).send()}catch{return s.status(404).send({error:`Report for run "${t.params.runId}" not found`})}})}var Us,Bs,cc,Hb,Vb,sg=b(()=>{"use strict";g();Us=x(require("path")),Bs=x(require("fs-extra")),cc=require("zod");fn();de();j();Hb=I("routes/report");Vb=cc.z.object({limit:cc.z.coerce.number().int().positive().max(100).default(20)})});function ng(r){return lc.default.join(r,"results","baselines")}function ig(r,e){return lc.default.join(ng(r),`${e}.png`)}async function ag(r,e,t){let s=ng(r);await sa.default.ensureDir(s),await sa.default.copy(t,ig(r,e),{overwrite:!0})}async function og(r,e){return sa.default.pathExists(ig(r,e))}var lc,sa,cg=b(()=>{"use strict";g();lc=x(require("path")),sa=x(require("fs-extra"))});async function lg(r){r.get("/results/:category/:filename",async(e,t)=>{let{category:s,filename:n}=e.params;if(!["screenshots","videos","traces","logs","baselines"].includes(s))return t.status(404).send({error:"Unknown media category"});let i=is.default.basename(n),a=is.default.join(r.rootDir,"results",s,i);if(!await Hs.default.pathExists(a))return t.status(404).send({error:`File not found: ${i}`});let o=is.default.extname(i).toLowerCase(),c=Gb[o]??"application/octet-stream";o===".zip"&&t.header("Content-Disposition",`attachment; filename="${i}"`);let l=await Hs.default.stat(a);return t.header("Content-Length",l.size),t.type(c).send(Hs.default.createReadStream(a))}),r.post("/baselines/:stepId/capture",async(e,t)=>{try{let s=e.body;if(!s?.screenshotPath)return t.status(400).send({error:"screenshotPath required"});let n=is.default.isAbsolute(s.screenshotPath)?s.screenshotPath:is.default.join(r.rootDir,s.screenshotPath);return await Hs.default.pathExists(n)?(await ag(r.rootDir,e.params.stepId,n),t.send({saved:!0})):t.status(404).send({error:"Screenshot not found"})}catch(s){return w(t,500,"Failed to save baseline",s)}}),r.get("/baselines/:stepId",async(e,t)=>{try{if(!await og(r.rootDir,e.params.stepId))return t.send({hasBaseline:!1});let n=is.default.join(r.rootDir,"results","baselines",`${e.params.stepId}.png`),i=await Hs.default.stat(n).catch(()=>null);return t.send({hasBaseline:!0,capturedAt:i?.mtimeMs??Date.now()})}catch(s){return w(t,500,"Failed to check baseline",s)}})}var is,Hs,Gb,ug=b(()=>{"use strict";g();is=x(require("path")),Hs=x(require("fs-extra"));cg();de();Gb={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webm":"video/webm",".mp4":"video/mp4",".zip":"application/zip",".log":"text/plain",".txt":"text/plain"}});function na(r){return{type:"doc",version:1,content:r.split(`
|
|
968
|
+
`).map(t=>({type:"paragraph",content:t.trim()?[{type:"text",text:t}]:[]}))}}var F,ia,dg=b(()=>{"use strict";g();j();F=I("xray-client");ia=class{config;authHeader;xrayToken=null;constructor(e){this.config={...e,jiraBaseUrl:e.jiraBaseUrl.replace(/\/+$/,""),projectKey:e.projectKey.trim().toUpperCase()},this.authHeader="Basic "+Buffer.from(`${e.jiraEmail}:${e.jiraApiToken}`).toString("base64")}async jiraRequest(e,t,s){let n=`${this.config.jiraBaseUrl}/rest/api/3${t}`;F.debug({method:e,url:n},"Jira API request");let i={Authorization:this.authHeader,Accept:"application/json"};s!==void 0&&(i["Content-Type"]="application/json");let a=await fetch(n,{method:e,headers:i,body:s!==void 0?JSON.stringify(s):void 0});if(!a.ok){let o=await a.text();throw F.error({status:a.status,url:n,body:o},"Jira API error"),new Error(`Jira API ${e} ${t} \u2192 ${a.status}: ${o}`)}if(a.status!==204)return a.json()}async getXrayToken(){if(this.xrayToken)return this.xrayToken;if(!this.config.xrayClientId||!this.config.xrayClientSecret)return"";let e=await fetch("https://xray.cloud.getxray.app/api/v2/authenticate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_id:this.config.xrayClientId,client_secret:this.config.xrayClientSecret})});if(!e.ok){let t=await e.text();throw new Error(`Xray auth failed: ${e.status} \u2014 ${t}`)}return this.xrayToken=await e.json(),this.xrayToken}async xrayRequest(e,t,s){let n=await this.getXrayToken(),a=`${n?"https://xray.cloud.getxray.app/api/v2":`${this.config.jiraBaseUrl}/rest/raven/2.0/api`}${t}`;F.debug({method:e,url:a},"Xray API request");let o={Accept:"application/json"};n?o.Authorization=`Bearer ${n}`:o.Authorization=this.authHeader,s!==void 0&&(o["Content-Type"]="application/json");let c=await fetch(a,{method:e,headers:o,body:s!==void 0?JSON.stringify(s):void 0});if(!c.ok){let l=await c.text();throw F.error({status:c.status,url:a,body:l},"Xray API error"),new Error(`Xray API ${e} ${t} \u2192 ${c.status}: ${l}`)}if(c.status!==204)return c.json()}async findIssueByKey(e){try{return await this.jiraRequest("GET",`/issue/${e}`)}catch{return null}}async searchIssues(e,t=50){return(await this.jiraRequest("POST","/search",{jql:e,maxResults:t,fields:["summary","status","issuetype","labels","updated"]})).issues}async createTest(e,t,s){return this.isXrayCloud?this.createTestCloud(e,t,s):this.createTestServerDC(e,t)}async createTestCloud(e,t,s){let n=`[${t}] ${e.name}`,i=e.description||`Automated test case: ${e.name}`,a=e.steps.length>0?`steps: [${e.steps.map(p=>`{ action: ${JSON.stringify(p.instruction)}, data: ${JSON.stringify(p.generatedCode?`Strategy: ${p.strategy}`:"")}, result: "Step completes successfully" }`).join(`,
|
|
969
969
|
`)}],`:"",c=`mutation {
|
|
970
970
|
createTest(
|
|
971
971
|
testType: { name: "Manual" },
|
|
@@ -985,7 +985,7 @@ ${e.trim()}`),t.join(`
|
|
|
985
985
|
}
|
|
986
986
|
warnings
|
|
987
987
|
}
|
|
988
|
-
}`;
|
|
988
|
+
}`;F.info({caseName:e.name,projectKey:this.config.projectKey,folderPath:s},"Creating Test via Xray Cloud GraphQL");let l=await this.xrayGraphQL(c);if(!l?.createTest?.test)throw new Error("Xray createTest returned no test data");let u=l.createTest.test,d=u.jira?.key??"",m=l.createTest.warnings;return m&&m.length>0&&F.warn({warnings:m,jiraKey:d},"Xray createTest warnings"),F.info({issueId:u.issueId,jiraKey:d,stepCount:e.steps.length,folderPath:s},"Xray Test created via GraphQL"),{id:u.issueId,key:d,self:""}}async createTestServerDC(e,t){let s=this.config.testIssueType??"Test",n={project:{key:this.config.projectKey},summary:`[${t}] ${e.name}`,description:na(e.description||`Automated test case: ${e.name}`),issuetype:{name:s},labels:["assuremind",...e.tags]};F.info({caseName:e.name,projectKey:this.config.projectKey},"Creating Xray Test via Jira REST");let i=await this.jiraRequest("POST","/issue",{fields:n});return e.steps.length>0&&await this.addTestStepsServerDC(i.key,e.steps),{id:i.id,key:i.key,self:i.self}}async updateTest(e,t,s){let n={summary:`[${s}] ${t.name}`,description:na(t.description||`Automated test case: ${t.name}`),labels:["assuremind",...t.tags]};F.info({issueKey:e.key,caseName:t.name},"Updating Xray Test"),await this.jiraRequest("PUT",`/issue/${e.key}`,{fields:n}),t.steps.length>0&&await this.replaceTestSteps(e,t.steps)}async createTestSet(e,t){return this.isXrayCloud?this.createTestSetCloud(e,t):this.createTestSetServerDC(e)}async createTestSetCloud(e,t){let n=`mutation {
|
|
989
989
|
createTestSet(
|
|
990
990
|
${t&&t.length>0?`testIssueIds: [${t.map(l=>`"${l}"`).join(", ")}],`:""}
|
|
991
991
|
jira: {
|
|
@@ -1002,7 +1002,7 @@ ${e.trim()}`),t.join(`
|
|
|
1002
1002
|
}
|
|
1003
1003
|
warnings
|
|
1004
1004
|
}
|
|
1005
|
-
}`;
|
|
1005
|
+
}`;F.info({suiteName:e.name,projectKey:this.config.projectKey,testCount:t?.length},"Creating Test Set via Xray Cloud GraphQL");let i=await this.xrayGraphQL(n);if(!i?.createTestSet?.testSet)throw new Error("Xray createTestSet returned no data");let a=i.createTestSet.testSet,o=a.jira?.key??"",c=i.createTestSet.warnings;return c&&c.length>0&&F.warn({warnings:c,jiraKey:o},"Xray createTestSet warnings"),F.info({issueId:a.issueId,jiraKey:o},"Xray Test Set created via GraphQL"),{id:a.issueId,key:o,self:""}}async createTestSetServerDC(e){let t=this.config.testSetIssueType??"Test Set",s={project:{key:this.config.projectKey},summary:e.name,description:na(e.description||`Automated test suite: ${e.name}`),issuetype:{name:t},labels:["assuremind",...e.tags]};F.info({suiteName:e.name,projectKey:this.config.projectKey},"Creating Xray Test Set via Jira REST");let n=await this.jiraRequest("POST","/issue",{fields:s});return{id:n.id,key:n.key,self:n.self}}async updateTestSet(e,t){let s={summary:t.name,description:na(t.description||`Automated test suite: ${t.name}`),labels:["assuremind",...t.tags]};F.info({issueKey:e,suiteName:t.name},"Updating Xray Test Set"),await this.jiraRequest("PUT",`/issue/${e}`,{fields:s})}get isXrayCloud(){return!!(this.config.xrayClientId&&this.config.xrayClientSecret)}async addTestSteps(e,t){if(this.isXrayCloud){let s=await this.resolveXrayIssueId(e.key,"Test");if(!s){F.warn({testKey:e.key},"Cannot add steps \u2014 Test not found in Xray yet");return}await this.addTestStepsCloud(s,t)}else await this.addTestStepsServerDC(e.key,t)}async addTestStepsCloud(e,t){let s=0;for(let n of t){let i=n.instruction,a=n.generatedCode?`Generated code (strategy: ${n.strategy})`:"",o="Step completes successfully";try{await this.xrayGraphQL(`mutation {
|
|
1006
1006
|
addTestStep(
|
|
1007
1007
|
issueId: "${e}",
|
|
1008
1008
|
step: {
|
|
@@ -1014,7 +1014,7 @@ ${e.trim()}`),t.join(`
|
|
|
1014
1014
|
id
|
|
1015
1015
|
action
|
|
1016
1016
|
}
|
|
1017
|
-
}`),s++}catch(c){let l=c instanceof Error?c.message:String(c);if(l.includes("not found")){
|
|
1017
|
+
}`),s++}catch(c){let l=c instanceof Error?c.message:String(c);if(l.includes("not found")){F.warn({xrayIssueId:e},"Xray issue not found during step add \u2014 aborting");break}F.warn({xrayIssueId:e,step:i,err:l},"Failed to add test step")}}s>0?F.info({xrayIssueId:e,added:s,total:t.length},"Test steps added via Xray Cloud GraphQL"):t.length>0&&F.warn({xrayIssueId:e},"Failed to add any steps")}async addTestStepsServerDC(e,t){let s=t.map((n,i)=>({index:i+1,action:n.instruction,data:n.generatedCode?`Generated code (strategy: ${n.strategy})`:"",result:"Step completes successfully"}));try{await this.xrayRequest("PUT",`/test/${e}/step`,s),F.debug({testIssueKey:e,stepCount:t.length},"Test steps added")}catch(n){F.warn({testIssueKey:e,err:n},"Bulk step add failed, trying one-by-one");for(let i of s)try{await this.xrayRequest("POST",`/test/${e}/step`,i)}catch(a){F.warn({testIssueKey:e,step:i.index,err:a},"Individual step add failed")}}}async replaceTestSteps(e,t){if(this.isXrayCloud){let s=await this.resolveXrayIssueId(e.key,"Test");if(!s){F.warn({testKey:e.key},"Cannot replace steps \u2014 Test not found in Xray");return}await this.addTestStepsCloud(s,t);return}try{let s=await this.xrayRequest("GET",`/test/${e.key}/step`);for(let n of s)await this.xrayRequest("DELETE",`/test/${e.key}/step/${n.id}`)}catch{F.debug({testIssueKey:e.key},"No existing steps to delete (or endpoint unavailable)")}await this.addTestStepsServerDC(e.key,t)}async linkTestsToTestSet(e,t){if(t.length!==0)if(this.isXrayCloud)await this.linkTestsCloud(e,t);else try{await this.xrayRequest("POST",`/testset/${e.key}/test`,{add:t.map(s=>s.key)}),F.info({testSetKey:e.key,count:t.length},"Tests linked to Test Set")}catch(s){F.warn({testSetKey:e.key,err:s},"Xray link API failed, trying Jira issue links"),await this.linkViaJiraIssueLinks(e.key,t.map(n=>n.key))}}async linkTestsCloud(e,t){F.info({testSetKey:e.key,testKeys:t.map(i=>i.key)},"Resolving Xray IDs for linking");let s=await this.resolveXrayIssueId(e.key,"Test Set");if(!s){F.error({testSetKey:e.key},"Cannot link \u2014 Test Set not found in Xray");return}let n=[];for(let i of t){let a=await this.resolveXrayIssueId(i.key,"Test");a?n.push(a):F.warn({testKey:i.key},"Could not resolve Xray ID for test \u2014 skipping")}if(n.length===0){F.warn({testSetKey:e.key},"No test IDs resolved \u2014 skipping link");return}try{let i=await this.xrayGraphQL(`mutation {
|
|
1018
1018
|
addTestsToTestSet(
|
|
1019
1019
|
issueId: "${s}",
|
|
1020
1020
|
testIssueIds: [${n.map(a=>`"${a}"`).join(", ")}]
|
|
@@ -1022,7 +1022,7 @@ ${e.trim()}`),t.join(`
|
|
|
1022
1022
|
addedTests
|
|
1023
1023
|
warning
|
|
1024
1024
|
}
|
|
1025
|
-
}`);i&&
|
|
1025
|
+
}`);i&&F.info({testSetKey:e.key,xrayTestSetId:s,count:n.length,added:i.addTestsToTestSet?.addedTests?.length,warning:i.addTestsToTestSet?.warning},"Tests linked to Test Set via Xray Cloud")}catch(i){F.error({testSetKey:e.key,xrayTestSetId:s,err:i instanceof Error?i.message:String(i)},"linkTestsCloud failed")}}sleep(e){return new Promise(t=>setTimeout(t,e))}async linkViaJiraIssueLinks(e,t){for(let s of t)try{await this.jiraRequest("POST","/issueLink",{type:{name:"Test Set"},inwardIssue:{key:e},outwardIssue:{key:s}})}catch(n){F.error({testSetKey:e,testKey:s,err:n},"Failed to link test to test set")}}async createFolderAndAddTests(e,t){if(!this.isXrayCloud){F.debug("Folder creation skipped \u2014 only supported on Xray Cloud");return}if(t.length===0)return;let s=`/${e}`;try{let i=(await this.jiraRequest("GET",`/project/${this.config.projectKey}`)).id;try{await this.xrayGraphQL(`mutation {
|
|
1026
1026
|
createFolder(
|
|
1027
1027
|
projectId: "${i}",
|
|
1028
1028
|
path: ${JSON.stringify(s)}
|
|
@@ -1032,7 +1032,7 @@ ${e.trim()}`),t.join(`
|
|
|
1032
1032
|
path
|
|
1033
1033
|
}
|
|
1034
1034
|
}
|
|
1035
|
-
}`),
|
|
1035
|
+
}`),F.info({folderPath:s,projectId:i},"Test Repository folder created")}catch(o){if((o instanceof Error?o.message:String(o)).includes("already exists"))F.info({folderPath:s},"Test Repository folder already exists \u2014 reusing");else throw o}let a=[];for(let o of t){let c=await this.resolveXrayIssueId(o.key,"Test");c?a.push(c):F.warn({testKey:o.key},"Could not resolve Xray ID for folder \u2014 skipping")}if(a.length===0){F.warn({folderPath:s},"No Xray IDs resolved \u2014 cannot add tests to folder");return}F.info({folderPath:s,xrayTestIds:a},"Adding tests to folder with resolved Xray IDs");try{await this.xrayGraphQL(`mutation {
|
|
1036
1036
|
addTestsToFolder(
|
|
1037
1037
|
projectId: "${i}",
|
|
1038
1038
|
path: ${JSON.stringify(s)},
|
|
@@ -1041,7 +1041,7 @@ ${e.trim()}`),t.join(`
|
|
|
1041
1041
|
folder { name }
|
|
1042
1042
|
warnings
|
|
1043
1043
|
}
|
|
1044
|
-
}`),
|
|
1044
|
+
}`),F.info({folderPath:s,count:a.length},"Tests added to folder")}catch(o){F.warn({folderPath:s,err:o instanceof Error?o.message:String(o)},"Failed to add tests to folder")}}catch(n){F.error({folderName:e,err:n instanceof Error?n.message:String(n)},"FAILED to create folder or add tests \u2014 tests exist but are unorganized")}}async xrayGraphQL(e){let t=await this.getXrayToken(),n=await fetch("https://xray.cloud.getxray.app/api/v2/graphql",{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify({query:e})});if(!n.ok){let a=await n.text();throw F.warn({status:n.status,body:a},"Xray Cloud GraphQL request failed"),new Error(`Xray GraphQL \u2192 ${n.status}: ${a}`)}let i=await n.json();if(i.errors&&i.errors.length>0)throw F.warn({errors:i.errors},"Xray Cloud GraphQL returned errors"),new Error(`Xray GraphQL errors: ${i.errors.map(a=>a.message).join("; ")}`);return i.data??null}async resolveXrayIssueId(e,t="Test"){if(!this.isXrayCloud)return null;let s=t==="Test Set"?"getTestSets":"getTests",n=t==="Test Set"?"getTestSets":"getTests",i=[0,5e3,1e4,2e4];for(let a=0;a<i.length;a++){i[a]>0&&(F.info({jiraKey:e,attempt:a,delay:i[a]},`Waiting ${i[a]/1e3}s before retry`),await this.sleep(i[a]));try{let o=`{
|
|
1045
1045
|
${s}(jql: "key = ${e}", limit: 1) {
|
|
1046
1046
|
total
|
|
1047
1047
|
results {
|
|
@@ -1049,23 +1049,23 @@ ${e.trim()}`),t.join(`
|
|
|
1049
1049
|
jira(fields: ["key", "summary"])
|
|
1050
1050
|
}
|
|
1051
1051
|
}
|
|
1052
|
-
}`;N.info({jiraKey:e,attempt:a,query:s},`Querying Xray for ${t} issueId`);let l=(await this.xrayGraphQL(o))?.[n];N.info({jiraKey:e,attempt:a,total:l?.total,resultCount:l?.results?.length,raw:JSON.stringify(l)},`Xray ${s} response`);let u=l?.results;if(u&&u.length>0&&u[0].issueId)return N.info({jiraKey:e,xrayIssueId:u[0].issueId,attempt:a},`Resolved Xray issueId for ${t}`),u[0].issueId;N.warn({jiraKey:e,attempt:a,total:l?.total},`Xray has not indexed ${t} yet \u2014 no results`)}catch(o){N.warn({jiraKey:e,attempt:a,err:o instanceof Error?o.message:String(o)},`resolveXrayIssueId attempt ${a} failed`)}}return N.error({jiraKey:e},`Could not resolve Xray issueId for ${t} after ${i.length} attempts`),null}async validate(){try{return{valid:!0,issueTypes:((await this.jiraRequest("GET",`/project/${this.config.projectKey}`)).issueTypes??[]).map(s=>s.name)}}catch(e){return{valid:!1,error:`Cannot connect to Jira project "${this.config.projectKey}": ${e instanceof Error?e.message:String(e)}`}}}}});var he,UR,$b,_b,tg,rg=b(()=>{"use strict";m();he=require("zod"),UR=he.z.enum(["not-created","created","synced","update-pending"]),$b=he.z.object({caseId:he.z.string().min(1),xrayTestId:he.z.string().min(1),xrayTestIssueId:he.z.string().min(1),lastSyncedAt:he.z.string().datetime(),lastSyncHash:he.z.string()}),_b=he.z.object({suiteId:he.z.string().min(1),xrayTestSetId:he.z.string().min(1),xrayTestSetIssueId:he.z.string().min(1),lastSyncedAt:he.z.string().datetime(),lastSyncHash:he.z.string(),cases:he.z.array($b).default([])}),tg=he.z.object({projectKey:he.z.string().min(1),suites:he.z.array(_b).default([]),updatedAt:he.z.string().datetime()})});function ng(r){return Jo.default.join(r,"assuremind-data",Ob)}async function ig(r){let e=ng(r);if(!await Yo.default.pathExists(e))return{projectKey:"",suites:[],updatedAt:new Date().toISOString()};let t=await ue(e),s=tg.safeParse(t);return s.success?s.data:(sg.warn({path:e,errors:s.error.issues},"Invalid xray-mapping.json \u2014 returning empty"),{projectKey:"",suites:[],updatedAt:new Date().toISOString()})}async function Qt(r,e){let t=ng(r);await Yo.default.ensureDir(Jo.default.dirname(t));let s={...e,updatedAt:new Date().toISOString()};await W(t,s),sg.debug({path:t},"Xray mapping written")}function Zt(r,e){return r.suites.find(t=>t.suiteId===e)}function Gi(r,e){for(let t of r.suites){let s=t.cases.find(n=>n.caseId===e);if(s)return{suiteMapping:t,caseMapping:s}}}function er(r,e){let t=r.suites.findIndex(n=>n.suiteId===e.suiteId),s=[...r.suites];return t>=0?s[t]=e:s.push(e),{...r,suites:s,updatedAt:new Date().toISOString()}}function mn(r,e){let t=r.cases.findIndex(n=>n.caseId===e.caseId),s=[...r.cases];return t>=0?s[t]=e:s.push(e),{...r,cases:s}}var Jo,Yo,sg,Ob,ag=b(()=>{"use strict";m();Jo=x(require("path")),Yo=x(require("fs-extra"));rg();ze();F();sg=C("xray-store"),Ob="xray-mapping.json"});function cg(r){return og.default.createHash("sha256").update(JSON.stringify(r)).digest("hex").slice(0,16)}function ks(r){return cg({name:r.name,description:r.description,tags:r.tags})}function gn(r){return cg({name:r.name,description:r.description,tags:r.tags,priority:r.priority,steps:r.steps.map(e=>({instruction:e.instruction,code:e.generatedCode}))})}var og,St,zi,lg=b(()=>{"use strict";m();og=x(require("crypto"));eg();ag();Ke();gt();F();St=C("xray-service");zi=class{client;rootDir;testsDir;projectKey;constructor(e,t){this.rootDir=e,this.testsDir=`${e}/tests`,this.projectKey=t.projectKey.trim().toUpperCase(),this.client=new Vi(t)}async readMapping(){let e=await ig(this.rootDir);return e.projectKey||(e.projectKey=this.projectKey),e}async getAllStatuses(){let e=await this.readMapping(),t=await Ar(this.testsDir),s=[];for(let n of t)s.push(await this.getSuiteStatus(n,e));return s}async getSuiteStatusById(e){let t=await de(this.testsDir,e);if(!t)return null;let s=await ve(t),n=await this.readMapping();return this.getSuiteStatus(s,n)}async getSuiteStatus(e,t){let s=Zt(t,e.id),n=await de(this.testsDir,e.id),i=n?await We(n):[],a="not-created";s&&(a=ks(e)===s.lastSyncHash?"synced":"update-pending");let o=i.map(c=>{let l=s?.cases.find(d=>d.caseId===c.id),u="not-created";return l&&(u=gn(c)===l.lastSyncHash?"synced":"update-pending"),{caseId:c.id,status:u,xrayTestId:l?.xrayTestId??null,lastSyncedAt:l?.lastSyncedAt??null}});return{suiteId:e.id,status:a,xrayTestSetId:s?.xrayTestSetId??null,lastSyncedAt:s?.lastSyncedAt??null,cases:o}}async createAll(){let e=await Ar(this.testsDir),t=[];for(let s of e){let n=await this.createSuite(s.id);t.push(...n.results)}return this.summarizeResults(t)}async createSuite(e){let t=await this.readMapping(),s=await de(this.testsDir,e);if(!s)return this.summarizeResults([{success:!1,suiteId:e,action:"failed",error:"Suite not found"}]);let n=await ve(s),i=await We(s),a=[],o=Zt(t,e);if(o)return St.info({suiteId:e},"Suite already mapped in Xray \u2014 delegating to sync"),this.syncSuite(e);let c=`/${n.name}`,l=[];for(let p of i){let g=Gi(t,p.id);if(g){a.push({success:!0,caseId:p.id,xrayKey:g.caseMapping.xrayTestId,action:"skipped"});continue}try{let f=await this.client.createTest(p,n.name,c),h=new Date().toISOString(),S={caseId:p.id,xrayTestId:f.key,xrayTestIssueId:f.id,lastSyncedAt:h,lastSyncHash:gn(p)};o||(o={suiteId:e,xrayTestSetId:"",xrayTestSetIssueId:"",lastSyncedAt:h,lastSyncHash:ks(n),cases:[]}),o=mn(o,S),t=er(t,o),await Qt(this.rootDir,t),l.push(f.id),a.push({success:!0,caseId:p.id,xrayKey:f.key,xrayId:f.id,action:"created"}),St.info({caseId:p.id,xrayKey:f.key,xrayId:f.id},"Test created")}catch(f){a.push({success:!1,caseId:p.id,action:"failed",error:f instanceof Error?f.message:String(f)})}}let u=[],d=Zt(t,e);if(d)for(let p of d.cases)u.push({key:p.xrayTestId,id:p.xrayTestIssueId});if(u.length>0)try{await this.client.createFolderAndAddTests(n.name,u),St.info({folderPath:c,testCount:u.length},"Tests organized into folder")}catch(p){St.warn({folderPath:c,err:p instanceof Error?p.message:String(p)},"Folder organization failed (non-fatal)")}if(!Zt(t,e)?.xrayTestSetId)try{let p=await this.client.createTestSet(n,l.length>0?l:void 0),g=new Date().toISOString();o={...o,xrayTestSetId:p.key,xrayTestSetIssueId:p.id,lastSyncedAt:g,lastSyncHash:ks(n)},t=er(t,o),await Qt(this.rootDir,t),a.push({success:!0,suiteId:e,xrayKey:p.key,action:"created"}),St.info({suiteId:e,xrayKey:p.key,testCount:l.length},"Test Set created with linked tests")}catch(p){a.push({success:!1,suiteId:e,action:"failed",error:p instanceof Error?p.message:String(p)})}return this.summarizeResults(a)}async createSingleCase(e,t){let s=await this.readMapping(),n=await de(this.testsDir,e);if(!n)return this.summarizeResults([{success:!1,caseId:t,action:"failed",error:"Suite not found"}]);let i=await ve(n),o=(await We(n)).find(d=>d.id===t);if(!o)return this.summarizeResults([{success:!1,caseId:t,action:"failed",error:"Test case not found"}]);let c=Zt(s,e);if(!c)try{let d=await this.client.createTestSet(i),p=new Date().toISOString();c={suiteId:e,xrayTestSetId:d.key,xrayTestSetIssueId:d.id,lastSyncedAt:p,lastSyncHash:ks(i),cases:[]},s=er(s,c),await Qt(this.rootDir,s)}catch(d){return this.summarizeResults([{success:!1,suiteId:e,action:"failed",error:`Failed to create parent Test Set: ${d instanceof Error?d.message:String(d)}`}])}let l=`/${i.name}`,u=await this.createCase(e,o,i.name,s,c,l);if(u.result.xrayKey&&u.result.xrayId)try{await this.client.createFolderAndAddTests(i.name,[{key:u.result.xrayKey,id:u.result.xrayId}])}catch(d){St.warn({err:d instanceof Error?d.message:String(d)},"Folder organization failed for single case (non-fatal)")}if(u.result.xrayId&&c.xrayTestSetIssueId)try{await this.client.linkTestsToTestSet({key:c.xrayTestSetId,id:c.xrayTestSetIssueId},[{key:u.result.xrayKey??"",id:u.result.xrayId}])}catch(d){St.warn({err:d instanceof Error?d.message:String(d)},"Failed to link test to test set")}return this.summarizeResults([u.result])}async createCase(e,t,s,n,i,a){let o=Gi(n,t.id);if(o)return{result:{success:!0,caseId:t.id,xrayKey:o.caseMapping.xrayTestId,action:"skipped"},mapping:n};try{let c=await this.client.createTest(t,s,a),l=new Date().toISOString(),u={caseId:t.id,xrayTestId:c.key,xrayTestIssueId:c.id,lastSyncedAt:l,lastSyncHash:gn(t)},d=mn(i,u),p=er(n,d);return await Qt(this.rootDir,p),St.info({caseId:t.id,xrayKey:c.key,xrayId:c.id},"Test created"),{result:{success:!0,caseId:t.id,xrayKey:c.key,xrayId:c.id,action:"created"},mapping:p}}catch(c){return{result:{success:!1,caseId:t.id,action:"failed",error:c instanceof Error?c.message:String(c)},mapping:n}}}async syncAll(){let e=await Ar(this.testsDir),t=[];for(let s of e){let n=await this.syncSuite(s.id);t.push(...n.results)}return this.summarizeResults(t)}async syncSuite(e){let t=await this.readMapping(),s=await de(this.testsDir,e);if(!s)return this.summarizeResults([{success:!1,suiteId:e,action:"failed",error:"Suite not found"}]);let n=await ve(s),i=await We(s),a=[],o=Zt(t,e);if(!o)return this.createSuite(e);if(!o.xrayTestSetId)try{let u=o.cases.map(g=>g.xrayTestIssueId).filter(Boolean),d=await this.client.createTestSet(n,u.length>0?u:void 0),p=new Date().toISOString();o={...o,xrayTestSetId:d.key,xrayTestSetIssueId:d.id,lastSyncedAt:p,lastSyncHash:ks(n)},t=er(t,o),await Qt(this.rootDir,t),a.push({success:!0,suiteId:e,xrayKey:d.key,action:"created"}),St.info({suiteId:e,xrayKey:d.key},"Test Set created during sync (was missing)")}catch(u){a.push({success:!1,suiteId:e,action:"failed",error:`Failed to create Test Set: ${u instanceof Error?u.message:String(u)}`})}let c=ks(n);if(o.xrayTestSetId&&c!==o.lastSyncHash)try{await this.client.updateTestSet(o.xrayTestSetId,n),o={...o,lastSyncedAt:new Date().toISOString(),lastSyncHash:c},t=er(t,o),await Qt(this.rootDir,t),a.push({success:!0,suiteId:e,xrayKey:o.xrayTestSetId,action:"synced"})}catch(u){a.push({success:!1,suiteId:e,action:"failed",error:u instanceof Error?u.message:String(u)})}else c===o.lastSyncHash&&a.push({success:!0,suiteId:e,xrayKey:o.xrayTestSetId,action:"skipped"});let l=[];for(let u of i){let d=o.cases.find(g=>g.caseId===u.id);if(!d){let g=await this.createCase(e,u,n.name,t,o);a.push(g.result),t=g.mapping,o=Zt(t,e)??o,g.result.xrayKey&&l.push(g.result.xrayKey);continue}let p=gn(u);if(p===d.lastSyncHash){a.push({success:!0,caseId:u.id,xrayKey:d.xrayTestId,action:"skipped"});continue}try{await this.client.updateTest({key:d.xrayTestId,id:d.xrayTestIssueId},u,n.name);let g={...d,lastSyncedAt:new Date().toISOString(),lastSyncHash:p},f=mn(o,g);t=er(t,f),o=f,await Qt(this.rootDir,t),a.push({success:!0,caseId:u.id,xrayKey:d.xrayTestId,action:"synced"})}catch(g){a.push({success:!1,caseId:u.id,action:"failed",error:g instanceof Error?g.message:String(g)})}}if(l.length>0&&o.xrayTestSetId&&o.xrayTestSetIssueId)try{let u=Zt(t,e),d=l.map(p=>{let g=u?.cases.find(f=>f.xrayTestId===p);return g?{key:g.xrayTestId,id:g.xrayTestIssueId}:null}).filter(p=>p!==null);if(d.length>0){await this.client.linkTestsToTestSet({key:o.xrayTestSetId,id:o.xrayTestSetIssueId},d);try{await this.client.createFolderAndAddTests(n.name,d)}catch(p){St.warn({err:p instanceof Error?p.message:String(p)},"Folder organization failed for synced tests (non-fatal)")}}}catch(u){St.warn({err:u},"Failed to link new tests to test set")}return this.summarizeResults(a)}async syncSingleCase(e,t){let s=await this.readMapping(),n=await de(this.testsDir,e);if(!n)return this.summarizeResults([{success:!1,caseId:t,action:"failed",error:"Suite not found"}]);let i=await ve(n),o=(await We(n)).find(u=>u.id===t);if(!o)return this.summarizeResults([{success:!1,caseId:t,action:"failed",error:"Test case not found"}]);let c=Gi(s,t);if(!c)return this.createSingleCase(e,t);let l=gn(o);if(l===c.caseMapping.lastSyncHash)return this.summarizeResults([{success:!0,caseId:t,xrayKey:c.caseMapping.xrayTestId,action:"skipped"}]);try{await this.client.updateTest({key:c.caseMapping.xrayTestId,id:c.caseMapping.xrayTestIssueId},o,i.name);let u={...c.caseMapping,lastSyncedAt:new Date().toISOString(),lastSyncHash:l},d=mn(c.suiteMapping,u),p=er(s,d);return await Qt(this.rootDir,p),this.summarizeResults([{success:!0,caseId:t,xrayKey:c.caseMapping.xrayTestId,action:"synced"}])}catch(u){return this.summarizeResults([{success:!1,caseId:t,action:"failed",error:u instanceof Error?u.message:String(u)}])}}async validate(){return this.client.validate()}summarizeResults(e){return{total:e.length,created:e.filter(t=>t.action==="created").length,synced:e.filter(t=>t.action==="synced").length,skipped:e.filter(t=>t.action==="skipped").length,failed:e.filter(t=>t.action==="failed").length,results:e}}}});function ug(){let r=process.env.JIRA_BASE_URL,e=process.env.JIRA_EMAIL,t=process.env.JIRA_API_TOKEN,s=process.env.XRAY_PROJECT_KEY;return!r||!e||!t||!s?null:{jiraBaseUrl:r.replace(/\/+$/,""),jiraEmail:e,jiraApiToken:t,projectKey:s.trim().toUpperCase(),xrayClientId:process.env.XRAY_CLIENT_ID??void 0,xrayClientSecret:process.env.XRAY_CLIENT_SECRET??void 0,testIssueType:process.env.XRAY_TEST_ISSUE_TYPE??"Test",testSetIssueType:process.env.XRAY_TEST_SET_ISSUE_TYPE??"Test Set"}}async function dg(r){let e=r.rootDir;function t(){let n=ug();return n?new zi(e,n):null}function s(n){let i=t();return i||(n.status(422).send({error:"Xray integration not configured",detail:"Set JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN, and XRAY_PROJECT_KEY in your .env file.",code:"CONFIG_ERROR",featureHint:"XRAY_NOT_CONFIGURED"}),null)}r.get("/xray/enabled",async(n,i)=>{let a=ug();return i.send({enabled:a!==null})}),r.get("/xray/status",async(n,i)=>{let a=s(i);if(a)try{let o=await a.getAllStatuses();return i.send(o)}catch(o){return w(i,500,"Failed to get Xray status",o)}}),r.get("/xray/status/:suiteId",async(n,i)=>{let a=s(i);if(a)try{let o=await a.getSuiteStatusById(n.params.suiteId);return o?i.send(o):i.status(404).send({error:"Suite not found"})}catch(o){return w(i,500,"Failed to get suite Xray status",o)}}),r.post("/xray/create-all",async(n,i)=>{let a=s(i);if(a)try{qi.info("Creating all suites and cases in Xray");let o=await a.createAll();return i.send(o)}catch(o){return w(i,500,"Failed to create in Xray",o)}}),r.post("/xray/create/suite/:suiteId",async(n,i)=>{let a=s(i);if(a)try{qi.info({suiteId:n.params.suiteId},"Creating suite in Xray");let o=await a.createSuite(n.params.suiteId);return i.send(o)}catch(o){return w(i,500,"Failed to create suite in Xray",o)}}),r.post("/xray/create/case/:suiteId/:caseId",async(n,i)=>{let a=s(i);if(a)try{let o=await a.createSingleCase(n.params.suiteId,n.params.caseId);return i.send(o)}catch(o){return w(i,500,"Failed to create case in Xray",o)}}),r.post("/xray/sync-all",async(n,i)=>{let a=s(i);if(a)try{qi.info("Syncing all suites and cases with Xray");let o=await a.syncAll();return i.send(o)}catch(o){return w(i,500,"Failed to sync with Xray",o)}}),r.post("/xray/sync/suite/:suiteId",async(n,i)=>{let a=s(i);if(a)try{qi.info({suiteId:n.params.suiteId},"Syncing suite with Xray");let o=await a.syncSuite(n.params.suiteId);return i.send(o)}catch(o){return w(i,500,"Failed to sync suite with Xray",o)}}),r.post("/xray/sync/case/:suiteId/:caseId",async(n,i)=>{let a=s(i);if(a)try{let o=await a.syncSingleCase(n.params.suiteId,n.params.caseId);return i.send(o)}catch(o){return w(i,500,"Failed to sync case with Xray",o)}}),r.get("/xray/validate",async(n,i)=>{let a=s(i);if(a)try{let o=await a.validate();return i.send(o)}catch(o){return w(i,500,"Failed to validate Xray connection",o)}})}var qi,pg=b(()=>{"use strict";m();lg();te();F();qi=C("xray-routes")});var mg,Qo,Zo,As,$s,ec=b(()=>{"use strict";m();mg=x(require("simple-git")),Qo=x(require("fs-extra")),Zo=x(require("path"));F();As=C("git-service"),$s=class{git;rootDir;wsManager;constructor(e,t){this.rootDir=e,this.wsManager=t,this.git=(0,mg.default)({baseDir:e,binary:"git",maxConcurrentProcesses:6,trimmed:!1})}broadcastLog(e,t,s){this.wsManager?.broadcast("git:log",{step:e,status:t,message:s,timestamp:Date.now()})}broadcastConflict(e){this.wsManager?.broadcast("git:conflict",{files:e})}broadcastDone(e){this.wsManager?.broadcast("git:operation-done",{operation:e})}async getStatus(){let e=await this.git.status();return{branch:e.current??"HEAD (detached)",tracking:e.tracking??null,isClean:e.isClean(),modified:e.modified,added:e.created,deleted:e.deleted,untracked:e.not_added,staged:e.staged,ahead:e.ahead,behind:e.behind,conflicted:e.conflicted}}async getBranches(){let e=await this.git.branchLocal(),t=[];try{t=(await this.git.branch(["-r"])).all}catch{}return{current:e.current,all:e.all,remote:t}}async checkout(e){this.broadcastLog("Checkout branch","running");try{await this.git.checkout(e),this.broadcastLog("Checkout branch","done",e),As.info({branch:e},"Switched branch")}catch(t){let s=t instanceof Error?t.message:String(t);throw this.broadcastLog("Checkout branch","error",s),t}}async commit(e){this.broadcastLog("Stage files","running"),await this.git.add("."),this.broadcastLog("Stage files","done"),this.broadcastLog("Create commit","running");try{let s=(await this.git.commit(e)).commit||"unknown";return this.broadcastLog("Create commit","done",s),this.broadcastDone("commit"),As.info({hash:s,message:e},"Commit created"),s}catch(t){let s=t instanceof Error?t.message:String(t);throw this.broadcastLog("Create commit","error",s),t}}async pull(e=!1){let t=e?"Pull with rebase":"Pull latest changes";this.broadcastLog(t,"running");try{let s=e?{"--rebase":null}:{};await this.git.pull(void 0,void 0,s),this.broadcastLog(t,"done",e?"Rebase successful":"Pull successful"),this.broadcastDone("pull"),As.info({rebase:e},"Pull completed")}catch(s){let n=s instanceof Error?s.message:String(s);if(n.includes("CONFLICT")||n.includes("conflict")||n.includes("could not apply")){this.broadcastLog(t,"error","Merge conflict detected");let i=await this.git.status();i.conflicted.length>0&&this.broadcastConflict(i.conflicted)}else this.broadcastLog(t,"error",n);throw s}}async push(){this.broadcastLog("Push to origin","running");try{await this.git.push(),this.broadcastLog("Push to origin","done"),this.broadcastDone("push"),As.info("Push completed")}catch(e){let t=e instanceof Error?e.message:String(e);throw this.broadcastLog("Push to origin","error",t),e}}async fullSync(e){try{this.broadcastLog("Pull with rebase","running");try{await this.git.pull(void 0,void 0,{"--rebase":null}),this.broadcastLog("Pull with rebase","done","Rebase successful")}catch(s){let n=s instanceof Error?s.message:String(s);if(n.includes("CONFLICT")||n.includes("conflict")||n.includes("could not apply")){this.broadcastLog("Pull with rebase","error","Merge conflict detected");let i=await this.git.status();throw i.conflicted.length>0&&this.broadcastConflict(i.conflicted),s}this.broadcastLog("Pull with rebase","done","Skipped (no remote or up to date)")}if(this.broadcastLog("Stage files","running"),await this.git.add("."),this.broadcastLog("Stage files","done"),this.broadcastLog("Create commit","running"),(await this.git.status()).isClean())this.broadcastLog("Create commit","done","Nothing to commit (clean)");else{let s=await this.git.commit(e);this.broadcastLog("Create commit","done",s.commit||"done")}this.broadcastLog("Push to origin","running");try{await this.git.push(),this.broadcastLog("Push to origin","done")}catch(s){let n=s instanceof Error?s.message:String(s);throw this.broadcastLog("Push to origin","error",n),s}this.broadcastDone("full-sync"),As.info("Full sync completed")}catch(t){throw this.broadcastDone("full-sync"),t}}async getLog(e=30){return(await this.git.log({maxCount:e})).all.map(s=>({hash:s.hash,shortHash:s.hash.slice(0,7),date:s.date,message:s.message,author:s.author_name}))}async getDiff(e=!1){if(e)return this.git.diff(["--cached"]);let t=await this.git.diff();return[await this.git.diff(["--cached"]),t].filter(Boolean).join(`
|
|
1053
|
-
`)}async getConflictedFiles(){return(await this.git.status()).conflicted}async getFileConflictContent(e){let t=
|
|
1052
|
+
}`;F.info({jiraKey:e,attempt:a,query:s},`Querying Xray for ${t} issueId`);let l=(await this.xrayGraphQL(o))?.[n];F.info({jiraKey:e,attempt:a,total:l?.total,resultCount:l?.results?.length,raw:JSON.stringify(l)},`Xray ${s} response`);let u=l?.results;if(u&&u.length>0&&u[0].issueId)return F.info({jiraKey:e,xrayIssueId:u[0].issueId,attempt:a},`Resolved Xray issueId for ${t}`),u[0].issueId;F.warn({jiraKey:e,attempt:a,total:l?.total},`Xray has not indexed ${t} yet \u2014 no results`)}catch(o){F.warn({jiraKey:e,attempt:a,err:o instanceof Error?o.message:String(o)},`resolveXrayIssueId attempt ${a} failed`)}}return F.error({jiraKey:e},`Could not resolve Xray issueId for ${t} after ${i.length} attempts`),null}async validate(){try{return{valid:!0,issueTypes:((await this.jiraRequest("GET",`/project/${this.config.projectKey}`)).issueTypes??[]).map(s=>s.name)}}catch(e){return{valid:!1,error:`Cannot connect to Jira project "${this.config.projectKey}": ${e instanceof Error?e.message:String(e)}`}}}}});var ke,sk,zb,qb,pg,mg=b(()=>{"use strict";g();ke=require("zod"),sk=ke.z.enum(["not-created","created","synced","update-pending"]),zb=ke.z.object({caseId:ke.z.string().min(1),xrayTestId:ke.z.string().min(1),xrayTestIssueId:ke.z.string().min(1),lastSyncedAt:ke.z.string().datetime(),lastSyncHash:ke.z.string()}),qb=ke.z.object({suiteId:ke.z.string().min(1),xrayTestSetId:ke.z.string().min(1),xrayTestSetIssueId:ke.z.string().min(1),lastSyncedAt:ke.z.string().datetime(),lastSyncHash:ke.z.string(),cases:ke.z.array(zb).default([])}),pg=ke.z.object({projectKey:ke.z.string().min(1),suites:ke.z.array(qb).default([]),updatedAt:ke.z.string().datetime()})});function fg(r){return uc.default.join(r,"assuremind-data",Kb)}async function hg(r){let e=fg(r);if(!await dc.default.pathExists(e))return{projectKey:"",suites:[],updatedAt:new Date().toISOString()};let t=await ve(e),s=pg.safeParse(t);return s.success?s.data:(gg.warn({path:e,errors:s.error.issues},"Invalid xray-mapping.json \u2014 returning empty"),{projectKey:"",suites:[],updatedAt:new Date().toISOString()})}async function mr(r,e){let t=fg(r);await dc.default.ensureDir(uc.default.dirname(t));let s={...e,updatedAt:new Date().toISOString()};await re(t,s),gg.debug({path:t},"Xray mapping written")}function gr(r,e){return r.suites.find(t=>t.suiteId===e)}function aa(r,e){for(let t of r.suites){let s=t.cases.find(n=>n.caseId===e);if(s)return{suiteMapping:t,caseMapping:s}}}function fr(r,e){let t=r.suites.findIndex(n=>n.suiteId===e.suiteId),s=[...r.suites];return t>=0?s[t]=e:s.push(e),{...r,suites:s,updatedAt:new Date().toISOString()}}function Pn(r,e){let t=r.cases.findIndex(n=>n.caseId===e.caseId),s=[...r.cases];return t>=0?s[t]=e:s.push(e),{...r,cases:s}}var uc,dc,gg,Kb,yg=b(()=>{"use strict";g();uc=x(require("path")),dc=x(require("fs-extra"));mg();ot();j();gg=I("xray-store"),Kb="xray-mapping.json"});function bg(r){return wg.default.createHash("sha256").update(JSON.stringify(r)).digest("hex").slice(0,16)}function Vs(r){return bg({name:r.name,description:r.description,tags:r.tags})}function Rn(r){return bg({name:r.name,description:r.description,tags:r.tags,priority:r.priority,steps:r.steps.map(e=>({instruction:e.instruction,code:e.generatedCode}))})}var wg,Dt,oa,vg=b(()=>{"use strict";g();wg=x(require("crypto"));dg();yg();et();Rt();j();Dt=I("xray-service");oa=class{client;rootDir;testsDir;projectKey;constructor(e,t){this.rootDir=e,this.testsDir=`${e}/tests`,this.projectKey=t.projectKey.trim().toUpperCase(),this.client=new ia(t)}async readMapping(){let e=await hg(this.rootDir);return e.projectKey||(e.projectKey=this.projectKey),e}async getAllStatuses(){let e=await this.readMapping(),t=await qr(this.testsDir),s=[];for(let n of t)s.push(await this.getSuiteStatus(n,e));return s}async getSuiteStatusById(e){let t=await we(this.testsDir,e);if(!t)return null;let s=await Ae(t),n=await this.readMapping();return this.getSuiteStatus(s,n)}async getSuiteStatus(e,t){let s=gr(t,e.id),n=await we(this.testsDir,e.id),i=n?await lt(n):[],a="not-created";s&&(a=Vs(e)===s.lastSyncHash?"synced":"update-pending");let o=i.map(c=>{let l=s?.cases.find(d=>d.caseId===c.id),u="not-created";return l&&(u=Rn(c)===l.lastSyncHash?"synced":"update-pending"),{caseId:c.id,status:u,xrayTestId:l?.xrayTestId??null,lastSyncedAt:l?.lastSyncedAt??null}});return{suiteId:e.id,status:a,xrayTestSetId:s?.xrayTestSetId??null,lastSyncedAt:s?.lastSyncedAt??null,cases:o}}async createAll(){let e=await qr(this.testsDir),t=[];for(let s of e){let n=await this.createSuite(s.id);t.push(...n.results)}return this.summarizeResults(t)}async createSuite(e){let t=await this.readMapping(),s=await we(this.testsDir,e);if(!s)return this.summarizeResults([{success:!1,suiteId:e,action:"failed",error:"Suite not found"}]);let n=await Ae(s),i=await lt(s),a=[],o=gr(t,e);if(o)return Dt.info({suiteId:e},"Suite already mapped in Xray \u2014 delegating to sync"),this.syncSuite(e);let c=`/${n.name}`,l=[];for(let m of i){let p=aa(t,m.id);if(p){a.push({success:!0,caseId:m.id,xrayKey:p.caseMapping.xrayTestId,action:"skipped"});continue}try{let f=await this.client.createTest(m,n.name,c),h=new Date().toISOString(),S={caseId:m.id,xrayTestId:f.key,xrayTestIssueId:f.id,lastSyncedAt:h,lastSyncHash:Rn(m)};o||(o={suiteId:e,xrayTestSetId:"",xrayTestSetIssueId:"",lastSyncedAt:h,lastSyncHash:Vs(n),cases:[]}),o=Pn(o,S),t=fr(t,o),await mr(this.rootDir,t),l.push(f.id),a.push({success:!0,caseId:m.id,xrayKey:f.key,xrayId:f.id,action:"created"}),Dt.info({caseId:m.id,xrayKey:f.key,xrayId:f.id},"Test created")}catch(f){a.push({success:!1,caseId:m.id,action:"failed",error:f instanceof Error?f.message:String(f)})}}let u=[],d=gr(t,e);if(d)for(let m of d.cases)u.push({key:m.xrayTestId,id:m.xrayTestIssueId});if(u.length>0)try{await this.client.createFolderAndAddTests(n.name,u),Dt.info({folderPath:c,testCount:u.length},"Tests organized into folder")}catch(m){Dt.warn({folderPath:c,err:m instanceof Error?m.message:String(m)},"Folder organization failed (non-fatal)")}if(!gr(t,e)?.xrayTestSetId)try{let m=await this.client.createTestSet(n,l.length>0?l:void 0),p=new Date().toISOString();o={...o,xrayTestSetId:m.key,xrayTestSetIssueId:m.id,lastSyncedAt:p,lastSyncHash:Vs(n)},t=fr(t,o),await mr(this.rootDir,t),a.push({success:!0,suiteId:e,xrayKey:m.key,action:"created"}),Dt.info({suiteId:e,xrayKey:m.key,testCount:l.length},"Test Set created with linked tests")}catch(m){a.push({success:!1,suiteId:e,action:"failed",error:m instanceof Error?m.message:String(m)})}return this.summarizeResults(a)}async createSingleCase(e,t){let s=await this.readMapping(),n=await we(this.testsDir,e);if(!n)return this.summarizeResults([{success:!1,caseId:t,action:"failed",error:"Suite not found"}]);let i=await Ae(n),o=(await lt(n)).find(d=>d.id===t);if(!o)return this.summarizeResults([{success:!1,caseId:t,action:"failed",error:"Test case not found"}]);let c=gr(s,e);if(!c)try{let d=await this.client.createTestSet(i),m=new Date().toISOString();c={suiteId:e,xrayTestSetId:d.key,xrayTestSetIssueId:d.id,lastSyncedAt:m,lastSyncHash:Vs(i),cases:[]},s=fr(s,c),await mr(this.rootDir,s)}catch(d){return this.summarizeResults([{success:!1,suiteId:e,action:"failed",error:`Failed to create parent Test Set: ${d instanceof Error?d.message:String(d)}`}])}let l=`/${i.name}`,u=await this.createCase(e,o,i.name,s,c,l);if(u.result.xrayKey&&u.result.xrayId)try{await this.client.createFolderAndAddTests(i.name,[{key:u.result.xrayKey,id:u.result.xrayId}])}catch(d){Dt.warn({err:d instanceof Error?d.message:String(d)},"Folder organization failed for single case (non-fatal)")}if(u.result.xrayId&&c.xrayTestSetIssueId)try{await this.client.linkTestsToTestSet({key:c.xrayTestSetId,id:c.xrayTestSetIssueId},[{key:u.result.xrayKey??"",id:u.result.xrayId}])}catch(d){Dt.warn({err:d instanceof Error?d.message:String(d)},"Failed to link test to test set")}return this.summarizeResults([u.result])}async createCase(e,t,s,n,i,a){let o=aa(n,t.id);if(o)return{result:{success:!0,caseId:t.id,xrayKey:o.caseMapping.xrayTestId,action:"skipped"},mapping:n};try{let c=await this.client.createTest(t,s,a),l=new Date().toISOString(),u={caseId:t.id,xrayTestId:c.key,xrayTestIssueId:c.id,lastSyncedAt:l,lastSyncHash:Rn(t)},d=Pn(i,u),m=fr(n,d);return await mr(this.rootDir,m),Dt.info({caseId:t.id,xrayKey:c.key,xrayId:c.id},"Test created"),{result:{success:!0,caseId:t.id,xrayKey:c.key,xrayId:c.id,action:"created"},mapping:m}}catch(c){return{result:{success:!1,caseId:t.id,action:"failed",error:c instanceof Error?c.message:String(c)},mapping:n}}}async syncAll(){let e=await qr(this.testsDir),t=[];for(let s of e){let n=await this.syncSuite(s.id);t.push(...n.results)}return this.summarizeResults(t)}async syncSuite(e){let t=await this.readMapping(),s=await we(this.testsDir,e);if(!s)return this.summarizeResults([{success:!1,suiteId:e,action:"failed",error:"Suite not found"}]);let n=await Ae(s),i=await lt(s),a=[],o=gr(t,e);if(!o)return this.createSuite(e);if(!o.xrayTestSetId)try{let u=o.cases.map(p=>p.xrayTestIssueId).filter(Boolean),d=await this.client.createTestSet(n,u.length>0?u:void 0),m=new Date().toISOString();o={...o,xrayTestSetId:d.key,xrayTestSetIssueId:d.id,lastSyncedAt:m,lastSyncHash:Vs(n)},t=fr(t,o),await mr(this.rootDir,t),a.push({success:!0,suiteId:e,xrayKey:d.key,action:"created"}),Dt.info({suiteId:e,xrayKey:d.key},"Test Set created during sync (was missing)")}catch(u){a.push({success:!1,suiteId:e,action:"failed",error:`Failed to create Test Set: ${u instanceof Error?u.message:String(u)}`})}let c=Vs(n);if(o.xrayTestSetId&&c!==o.lastSyncHash)try{await this.client.updateTestSet(o.xrayTestSetId,n),o={...o,lastSyncedAt:new Date().toISOString(),lastSyncHash:c},t=fr(t,o),await mr(this.rootDir,t),a.push({success:!0,suiteId:e,xrayKey:o.xrayTestSetId,action:"synced"})}catch(u){a.push({success:!1,suiteId:e,action:"failed",error:u instanceof Error?u.message:String(u)})}else c===o.lastSyncHash&&a.push({success:!0,suiteId:e,xrayKey:o.xrayTestSetId,action:"skipped"});let l=[];for(let u of i){let d=o.cases.find(p=>p.caseId===u.id);if(!d){let p=await this.createCase(e,u,n.name,t,o);a.push(p.result),t=p.mapping,o=gr(t,e)??o,p.result.xrayKey&&l.push(p.result.xrayKey);continue}let m=Rn(u);if(m===d.lastSyncHash){a.push({success:!0,caseId:u.id,xrayKey:d.xrayTestId,action:"skipped"});continue}try{await this.client.updateTest({key:d.xrayTestId,id:d.xrayTestIssueId},u,n.name);let p={...d,lastSyncedAt:new Date().toISOString(),lastSyncHash:m},f=Pn(o,p);t=fr(t,f),o=f,await mr(this.rootDir,t),a.push({success:!0,caseId:u.id,xrayKey:d.xrayTestId,action:"synced"})}catch(p){a.push({success:!1,caseId:u.id,action:"failed",error:p instanceof Error?p.message:String(p)})}}if(l.length>0&&o.xrayTestSetId&&o.xrayTestSetIssueId)try{let u=gr(t,e),d=l.map(m=>{let p=u?.cases.find(f=>f.xrayTestId===m);return p?{key:p.xrayTestId,id:p.xrayTestIssueId}:null}).filter(m=>m!==null);if(d.length>0){await this.client.linkTestsToTestSet({key:o.xrayTestSetId,id:o.xrayTestSetIssueId},d);try{await this.client.createFolderAndAddTests(n.name,d)}catch(m){Dt.warn({err:m instanceof Error?m.message:String(m)},"Folder organization failed for synced tests (non-fatal)")}}}catch(u){Dt.warn({err:u},"Failed to link new tests to test set")}return this.summarizeResults(a)}async syncSingleCase(e,t){let s=await this.readMapping(),n=await we(this.testsDir,e);if(!n)return this.summarizeResults([{success:!1,caseId:t,action:"failed",error:"Suite not found"}]);let i=await Ae(n),o=(await lt(n)).find(u=>u.id===t);if(!o)return this.summarizeResults([{success:!1,caseId:t,action:"failed",error:"Test case not found"}]);let c=aa(s,t);if(!c)return this.createSingleCase(e,t);let l=Rn(o);if(l===c.caseMapping.lastSyncHash)return this.summarizeResults([{success:!0,caseId:t,xrayKey:c.caseMapping.xrayTestId,action:"skipped"}]);try{await this.client.updateTest({key:c.caseMapping.xrayTestId,id:c.caseMapping.xrayTestIssueId},o,i.name);let u={...c.caseMapping,lastSyncedAt:new Date().toISOString(),lastSyncHash:l},d=Pn(c.suiteMapping,u),m=fr(s,d);return await mr(this.rootDir,m),this.summarizeResults([{success:!0,caseId:t,xrayKey:c.caseMapping.xrayTestId,action:"synced"}])}catch(u){return this.summarizeResults([{success:!1,caseId:t,action:"failed",error:u instanceof Error?u.message:String(u)}])}}async validate(){return this.client.validate()}summarizeResults(e){return{total:e.length,created:e.filter(t=>t.action==="created").length,synced:e.filter(t=>t.action==="synced").length,skipped:e.filter(t=>t.action==="skipped").length,failed:e.filter(t=>t.action==="failed").length,results:e}}}});function xg(){let r=process.env.JIRA_BASE_URL,e=process.env.JIRA_EMAIL,t=process.env.JIRA_API_TOKEN,s=process.env.XRAY_PROJECT_KEY;return!r||!e||!t||!s?null:{jiraBaseUrl:r.replace(/\/+$/,""),jiraEmail:e,jiraApiToken:t,projectKey:s.trim().toUpperCase(),xrayClientId:process.env.XRAY_CLIENT_ID??void 0,xrayClientSecret:process.env.XRAY_CLIENT_SECRET??void 0,testIssueType:process.env.XRAY_TEST_ISSUE_TYPE??"Test",testSetIssueType:process.env.XRAY_TEST_SET_ISSUE_TYPE??"Test Set"}}async function Sg(r){let e=r.rootDir;function t(){let n=xg();return n?new oa(e,n):null}function s(n){let i=t();return i||(n.status(422).send({error:"Xray integration not configured",detail:"Set JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN, and XRAY_PROJECT_KEY in your .env file.",code:"CONFIG_ERROR",featureHint:"XRAY_NOT_CONFIGURED"}),null)}r.get("/xray/enabled",async(n,i)=>{let a=xg();return i.send({enabled:a!==null})}),r.get("/xray/status",async(n,i)=>{let a=s(i);if(a)try{let o=await a.getAllStatuses();return i.send(o)}catch(o){return w(i,500,"Failed to get Xray status",o)}}),r.get("/xray/status/:suiteId",async(n,i)=>{let a=s(i);if(a)try{let o=await a.getSuiteStatusById(n.params.suiteId);return o?i.send(o):i.status(404).send({error:"Suite not found"})}catch(o){return w(i,500,"Failed to get suite Xray status",o)}}),r.post("/xray/create-all",async(n,i)=>{let a=s(i);if(a)try{ca.info("Creating all suites and cases in Xray");let o=await a.createAll();return i.send(o)}catch(o){return w(i,500,"Failed to create in Xray",o)}}),r.post("/xray/create/suite/:suiteId",async(n,i)=>{let a=s(i);if(a)try{ca.info({suiteId:n.params.suiteId},"Creating suite in Xray");let o=await a.createSuite(n.params.suiteId);return i.send(o)}catch(o){return w(i,500,"Failed to create suite in Xray",o)}}),r.post("/xray/create/case/:suiteId/:caseId",async(n,i)=>{let a=s(i);if(a)try{let o=await a.createSingleCase(n.params.suiteId,n.params.caseId);return i.send(o)}catch(o){return w(i,500,"Failed to create case in Xray",o)}}),r.post("/xray/sync-all",async(n,i)=>{let a=s(i);if(a)try{ca.info("Syncing all suites and cases with Xray");let o=await a.syncAll();return i.send(o)}catch(o){return w(i,500,"Failed to sync with Xray",o)}}),r.post("/xray/sync/suite/:suiteId",async(n,i)=>{let a=s(i);if(a)try{ca.info({suiteId:n.params.suiteId},"Syncing suite with Xray");let o=await a.syncSuite(n.params.suiteId);return i.send(o)}catch(o){return w(i,500,"Failed to sync suite with Xray",o)}}),r.post("/xray/sync/case/:suiteId/:caseId",async(n,i)=>{let a=s(i);if(a)try{let o=await a.syncSingleCase(n.params.suiteId,n.params.caseId);return i.send(o)}catch(o){return w(i,500,"Failed to sync case with Xray",o)}}),r.get("/xray/validate",async(n,i)=>{let a=s(i);if(a)try{let o=await a.validate();return i.send(o)}catch(o){return w(i,500,"Failed to validate Xray connection",o)}})}var ca,Eg=b(()=>{"use strict";g();vg();de();j();ca=I("xray-routes")});var Ig,pc,mc,Gs,zs,gc=b(()=>{"use strict";g();Ig=x(require("simple-git")),pc=x(require("fs-extra")),mc=x(require("path"));j();Gs=I("git-service"),zs=class{git;rootDir;wsManager;constructor(e,t){this.rootDir=e,this.wsManager=t,this.git=(0,Ig.default)({baseDir:e,binary:"git",maxConcurrentProcesses:6,trimmed:!1})}broadcastLog(e,t,s){this.wsManager?.broadcast("git:log",{step:e,status:t,message:s,timestamp:Date.now()})}broadcastConflict(e){this.wsManager?.broadcast("git:conflict",{files:e})}broadcastDone(e){this.wsManager?.broadcast("git:operation-done",{operation:e})}async getStatus(){let e=await this.git.status();return{branch:e.current??"HEAD (detached)",tracking:e.tracking??null,isClean:e.isClean(),modified:e.modified,added:e.created,deleted:e.deleted,untracked:e.not_added,staged:e.staged,ahead:e.ahead,behind:e.behind,conflicted:e.conflicted}}async getBranches(){let e=await this.git.branchLocal(),t=[];try{t=(await this.git.branch(["-r"])).all}catch{}return{current:e.current,all:e.all,remote:t}}async checkout(e){this.broadcastLog("Checkout branch","running");try{await this.git.checkout(e),this.broadcastLog("Checkout branch","done",e),Gs.info({branch:e},"Switched branch")}catch(t){let s=t instanceof Error?t.message:String(t);throw this.broadcastLog("Checkout branch","error",s),t}}async commit(e){this.broadcastLog("Stage files","running"),await this.git.add("."),this.broadcastLog("Stage files","done"),this.broadcastLog("Create commit","running");try{let s=(await this.git.commit(e)).commit||"unknown";return this.broadcastLog("Create commit","done",s),this.broadcastDone("commit"),Gs.info({hash:s,message:e},"Commit created"),s}catch(t){let s=t instanceof Error?t.message:String(t);throw this.broadcastLog("Create commit","error",s),t}}async pull(e=!1){let t=e?"Pull with rebase":"Pull latest changes";this.broadcastLog(t,"running");try{let s=e?{"--rebase":null}:{};await this.git.pull(void 0,void 0,s),this.broadcastLog(t,"done",e?"Rebase successful":"Pull successful"),this.broadcastDone("pull"),Gs.info({rebase:e},"Pull completed")}catch(s){let n=s instanceof Error?s.message:String(s);if(n.includes("CONFLICT")||n.includes("conflict")||n.includes("could not apply")){this.broadcastLog(t,"error","Merge conflict detected");let i=await this.git.status();i.conflicted.length>0&&this.broadcastConflict(i.conflicted)}else this.broadcastLog(t,"error",n);throw s}}async push(){this.broadcastLog("Push to origin","running");try{await this.git.push(),this.broadcastLog("Push to origin","done"),this.broadcastDone("push"),Gs.info("Push completed")}catch(e){let t=e instanceof Error?e.message:String(e);throw this.broadcastLog("Push to origin","error",t),e}}async fullSync(e){try{this.broadcastLog("Pull with rebase","running");try{await this.git.pull(void 0,void 0,{"--rebase":null}),this.broadcastLog("Pull with rebase","done","Rebase successful")}catch(s){let n=s instanceof Error?s.message:String(s);if(n.includes("CONFLICT")||n.includes("conflict")||n.includes("could not apply")){this.broadcastLog("Pull with rebase","error","Merge conflict detected");let i=await this.git.status();throw i.conflicted.length>0&&this.broadcastConflict(i.conflicted),s}this.broadcastLog("Pull with rebase","done","Skipped (no remote or up to date)")}if(this.broadcastLog("Stage files","running"),await this.git.add("."),this.broadcastLog("Stage files","done"),this.broadcastLog("Create commit","running"),(await this.git.status()).isClean())this.broadcastLog("Create commit","done","Nothing to commit (clean)");else{let s=await this.git.commit(e);this.broadcastLog("Create commit","done",s.commit||"done")}this.broadcastLog("Push to origin","running");try{await this.git.push(),this.broadcastLog("Push to origin","done")}catch(s){let n=s instanceof Error?s.message:String(s);throw this.broadcastLog("Push to origin","error",n),s}this.broadcastDone("full-sync"),Gs.info("Full sync completed")}catch(t){throw this.broadcastDone("full-sync"),t}}async getLog(e=30){return(await this.git.log({maxCount:e})).all.map(s=>({hash:s.hash,shortHash:s.hash.slice(0,7),date:s.date,message:s.message,author:s.author_name}))}async getDiff(e=!1){if(e)return this.git.diff(["--cached"]);let t=await this.git.diff();return[await this.git.diff(["--cached"]),t].filter(Boolean).join(`
|
|
1053
|
+
`)}async getConflictedFiles(){return(await this.git.status()).conflicted}async getFileConflictContent(e){let t=mc.default.join(this.rootDir,e),s=await pc.default.readFile(t,"utf-8"),n="",i="",a="none";for(let o of s.split(`
|
|
1054
1054
|
`)){if(o.startsWith("<<<<<<<")){a="ours";continue}if(o.startsWith("=======")){a="theirs";continue}if(o.startsWith(">>>>>>>")){a="none";continue}a==="ours"?n+=o+`
|
|
1055
1055
|
`:a==="theirs"&&(i+=o+`
|
|
1056
|
-
`)}return{ours:n.trimEnd(),theirs:i.trimEnd()}}async resolveConflict(e,t){let s=
|
|
1056
|
+
`)}return{ours:n.trimEnd(),theirs:i.trimEnd()}}async resolveConflict(e,t){let s=mc.default.join(this.rootDir,e);await pc.default.writeFile(s,t,"utf-8"),await this.git.add(e),Gs.info({filePath:e},"Conflict resolved")}async continueRebase(){this.broadcastLog("Continuing rebase...","running");try{await this.git.rebase(["--continue"]),this.broadcastLog("Rebase continued","done")}catch(e){let t=e instanceof Error?e.message:String(e);throw this.broadcastLog(`Rebase continue failed: ${t}`,"error"),e}}async abortRebase(){this.broadcastLog("Aborting rebase...","running");try{await this.git.rebase(["--abort"]),this.broadcastLog("Rebase aborted","done")}catch(e){let t=e instanceof Error?e.message:String(e);throw this.broadcastLog(`Rebase abort failed: ${t}`,"error"),e}}}});function Cg(r){return{url:"git://local",title:"Git Operations",interactiveElements:"",htmlSnapshot:r,previousSteps:[],variables:{}}}async function Pg(r){if(!r.trim())return"chore: update files";let e=8e3,t=r.length>e?r.slice(0,e)+`
|
|
1057
1057
|
... (diff truncated)`:r,s=["You are a developer writing a git commit message. Write it the way a human would \u2014 natural, specific, and varied.","Use Conventional Commits format: type(scope): description","Types: feat, fix, chore, refactor, docs, test, style, perf, ci, build","","Rules:","- Be specific about WHAT changed, not generic",`- Vary your word choices \u2014 don't always start with "add", "update", or "fix"`,'- Use active voice: "streamline login flow" not "login flow was streamlined"',"- Keep it under 72 characters","- Sound like a real developer, not a robot","- Each message should feel unique even for similar changes","",'Bad examples (too generic): "update files", "fix bug", "make changes"','Good examples: "feat(cart): wire up quantity stepper with debounced save", "fix(auth): guard against expired refresh tokens on silent renew", "refactor: collapse duplicated validation into shared helper"',"","Return ONLY the commit message. No quotes, no explanation, no markdown.","","Diff:","```",t,"```"].join(`
|
|
1058
|
-
`);try{let a=(await
|
|
1059
|
-
`)[0].trim(),
|
|
1060
|
-
`),n=
|
|
1058
|
+
`);try{let a=(await Qr(void 0,{temperature:.4}).generateCode(s,Cg(t))).trim();return a=a.replace(/^["'`]+|["'`]+$/g,""),a=a.replace(/^(commit message:?\s*)/i,""),a=a.split(`
|
|
1059
|
+
`)[0].trim(),la.info({messageLength:a.length},"AI commit message generated"),a||"chore: update files"}catch(n){return la.warn({err:n instanceof Error?n.message:String(n)},"AI commit message generation failed"),"chore: update files"}}async function Rg(r,e,t){let s=[`Merge these two conflicting versions of the file "${t}" into a single resolved version.`,"Combine changes from both sides intelligently. Preserve the intent of both changes.","Return ONLY the merged file content, no explanation, no markdown code blocks.","","=== LOCAL VERSION (ours) ===",r,"","=== INCOMING VERSION (theirs) ===",e].join(`
|
|
1060
|
+
`),n=Cg(`Conflict in: ${t}
|
|
1061
1061
|
|
|
1062
1062
|
Ours:
|
|
1063
1063
|
${r}
|
|
1064
1064
|
|
|
1065
1065
|
Theirs:
|
|
1066
|
-
${e}`);try{let a=await
|
|
1067
|
-
`).filter(Boolean),{listSuiteDirs:
|
|
1068
|
-
`).map(s=>s.trim()).filter(Boolean);if(e.length<2)return[];let t=e[0].split(",").map(s=>s.trim().replace(/^"|"$/g,""));return e.slice(1).map(s=>{let n=[],i="",a=!1;for(let c of s){if(c==='"'){a=!a;continue}if(c===","&&!a){n.push(i.trim()),i="";continue}i+=c}n.push(i.trim());let o={};return t.forEach((c,l)=>{o[c]=n[l]??""}),o})}async function
|
|
1066
|
+
${e}`);try{let a=await Qr(void 0,{temperature:.4}).generateCode(s,n);return a=a.replace(/^```[\w]*\n?/,"").replace(/\n?```$/,""),la.info({filePath:t,resultLength:a.length},"AI conflict resolution generated"),a}catch(i){throw la.warn({filePath:t,err:i instanceof Error?i.message:String(i)},"AI conflict resolution failed"),i}}var la,kg=b(()=>{"use strict";g();$r();j();la=I("git-ai")});async function Tg(r){let e=r.rootDir,t=r.wsManager,s=new zs(e,t),n=null,i=async()=>{if(n!==null)return n;try{await s.getStatus(),n=!0}catch{n=!1,Wb.info("Project directory is not a git repository \u2014 git features disabled")}return n},a={error:"not-a-git-repo",message:'Project directory is not a git repository. Run "git init" to enable git features.'};r.get("/git/status",async(o,c)=>{try{if(!await i())return c.code(200).send({branch:"",tracking:null,isClean:!0,modified:[],added:[],deleted:[],untracked:[],staged:[],ahead:0,behind:0,conflicted:[],notARepo:!0});let l=await s.getStatus();return c.send(l)}catch(l){return w(c,500,"Failed to get git status",l)}}),r.get("/git/branches",async(o,c)=>{try{if(!await i())return c.code(200).send({current:"",all:[],remote:[]});let l=await s.getBranches();return c.send(l)}catch(l){return w(c,500,"Failed to get branches",l)}}),r.get("/git/log",async(o,c)=>{try{if(!await i())return c.code(200).send([]);let l=o.query.count?parseInt(o.query.count,10):void 0,u=await s.getLog(l);return c.send(u)}catch(l){return w(c,500,"Failed to get git log",l)}}),r.get("/git/diff",async(o,c)=>{try{let l=o.query.staged==="true",u=await s.getDiff(l);return c.send({diff:u})}catch(l){return w(c,500,"Failed to get diff",l)}}),r.post("/git/checkout",async(o,c)=>{let l=Xb.safeParse(o.body);if(!l.success)return c.status(400).send({error:"Invalid body",details:l.error.issues});try{return await s.checkout(l.data.branch),c.send({success:!0,branch:l.data.branch})}catch(u){return w(c,500,"Failed to checkout branch",u)}}),r.post("/git/commit",async(o,c)=>{let l=Jb.safeParse(o.body);if(!l.success)return c.status(400).send({error:"Invalid body",details:l.error.issues});try{let u=await s.commit(l.data.message);return c.send({success:!0,hash:u})}catch(u){return w(c,500,"Failed to commit",u)}}),r.post("/git/pull",async(o,c)=>{let l=Yb.safeParse(o.body);if(!l.success)return c.status(400).send({error:"Invalid body",details:l.error.issues});try{return await s.pull(l.data.rebase),c.send({success:!0})}catch(u){let d=await s.getStatus().catch(()=>null);return d&&d.conflicted.length>0?c.status(409).send({error:"Merge conflict",conflicted:d.conflicted}):w(c,500,"Failed to pull",u)}}),r.post("/git/push",async(o,c)=>{try{return await s.push(),c.send({success:!0})}catch(l){return w(c,500,"Failed to push",l)}}),r.post("/git/full-sync",async(o,c)=>{let l=Qb.safeParse(o.body);if(!l.success)return c.status(400).send({error:"Invalid body",details:l.error.issues});try{return await s.fullSync(l.data.message),c.send({success:!0})}catch(u){let d=await s.getStatus().catch(()=>null);return d&&d.conflicted.length>0?c.status(409).send({error:"Merge conflict",conflicted:d.conflicted}):w(c,500,"Full sync failed",u)}}),r.post("/git/resolve-conflict",async(o,c)=>{let l=Zb.safeParse(o.body);if(!l.success)return c.status(400).send({error:"Invalid body",details:l.error.issues});try{return await s.resolveConflict(l.data.filePath,l.data.content),c.send({success:!0})}catch(u){return w(c,500,"Failed to resolve conflict",u)}}),r.post("/git/continue-rebase",async(o,c)=>{try{return await s.continueRebase(),c.send({success:!0})}catch(l){return w(c,500,"Failed to continue rebase",l)}}),r.post("/git/abort-rebase",async(o,c)=>{try{return await s.abortRebase(),c.send({success:!0})}catch(l){return w(c,500,"Failed to abort rebase",l)}}),r.get("/git/conflict-content/*",async(o,c)=>{try{let l=o.params["*"];if(!l)return c.status(400).send({error:"File path required"});let u=await s.getFileConflictContent(l);return c.send(u)}catch(l){return w(c,500,"Failed to get conflict content",l)}}),r.post("/git/generate-commit-message",async(o,c)=>{try{let l=await s.getDiff(),u=await Pg(l);return c.send({message:u})}catch(l){return w(c,500,"Failed to generate commit message",l)}}),r.post("/git/resolve-conflict-ai",async(o,c)=>{let l=ev.safeParse(o.body);if(!l.success)return c.status(400).send({error:"Invalid body",details:l.error.issues});try{let u=await Rg(l.data.ours,l.data.theirs,l.data.filePath);return c.send({resolved:u})}catch(u){return w(c,500,"Failed to AI-resolve conflict",u)}}),r.get("/git/impact",async(o,c)=>{try{let{execa:l}=await import("execa"),{stdout:u}=await l("git",["diff","--name-only","HEAD"],{cwd:e}),d=u.split(`
|
|
1067
|
+
`).filter(Boolean),{listSuiteDirs:m,readSuite:p}=await Promise.resolve().then(()=>(et(),Vn)),{listCases:f}=await Promise.resolve().then(()=>(Rt(),Il)),h=ua.default.join(e,"tests"),S=await m(h),C=[];for(let v of S)try{let k=await p(v),T=await f(v);for(let P of T){let G=[P.name,P.description??"",...P.steps.map(ne=>ne.instruction)].join(" ").toLowerCase();for(let ne of d){let ae=ua.default.basename(ne).toLowerCase(),me=ua.default.dirname(ne).toLowerCase();if(G.includes(ae)||G.includes(me)){C.push({caseId:P.id,caseName:P.name,suiteId:k.id,suiteName:k.name,reason:`Mentions "${ne}"`});break}}}}catch{}return c.send({changedFiles:d,impactedCases:C})}catch(l){return w(c,500,"Failed to analyze impact",l)}})}var ua,He,Wb,Xb,Jb,Yb,Qb,Zb,ev,Ag=b(()=>{"use strict";g();ua=x(require("path")),He=require("zod");gc();kg();de();j();Wb=I("git-routes"),Xb=He.z.object({branch:He.z.string().min(1)}),Jb=He.z.object({message:He.z.string().min(1)}),Yb=He.z.object({rebase:He.z.boolean().optional()}),Qb=He.z.object({message:He.z.string().min(1)}),Zb=He.z.object({filePath:He.z.string().min(1),content:He.z.string()}),ev=He.z.object({filePath:He.z.string().min(1),ours:He.z.string(),theirs:He.z.string()})});function $g(r){let e=r.trim().split(`
|
|
1068
|
+
`).map(s=>s.trim()).filter(Boolean);if(e.length<2)return[];let t=e[0].split(",").map(s=>s.trim().replace(/^"|"$/g,""));return e.slice(1).map(s=>{let n=[],i="",a=!1;for(let c of s){if(c==='"'){a=!a;continue}if(c===","&&!a){n.push(i.trim()),i="";continue}i+=c}n.push(i.trim());let o={};return t.forEach((c,l)=>{o[c]=n[l]??""}),o})}async function _g(r){let e=r.rootDir,t=Nt.default.join(e,"tests","data");await xt.default.ensureDir(t),r.get("/data-files",async(s,n)=>{try{await xt.default.ensureDir(t);let a=(await xt.default.readdir(t,{withFileTypes:!0})).filter(o=>o.isFile()&&hc.some(c=>o.name.endsWith(c))).map(o=>({name:o.name,type:o.name.endsWith(".csv")?"csv":"json",path:`data/${o.name}`}));return n.send(a)}catch(i){return w(n,500,"Failed to list data files",i)}}),r.get("/data-files/:filename",async(s,n)=>{try{let i=Nt.default.basename(s.params.filename),a=Nt.default.join(t,i);if(!await xt.default.pathExists(a))return n.status(404).send({error:`Data file "${i}" not found`});let o=await xt.default.readFile(a,"utf8"),c;if(i.endsWith(".csv"))c=$g(o);else{let l=JSON.parse(o);c=Array.isArray(l)?l.map(u=>Object.fromEntries(Object.entries(u).map(([d,m])=>[d,String(m)]))):[]}return n.send({filename:i,raw:o,rows:c,rowCount:c.length})}catch(i){return w(n,500,"Failed to read data file",i)}}),r.post("/data-files/upload",async(s,n)=>{try{let i=s.body,a=String(i.filename??"").trim(),o=String(i.content??"");if(!a)return n.status(400).send({error:"filename is required"});let c=Nt.default.extname(a).toLowerCase();if(!hc.includes(c))return n.status(400).send({error:`Only ${hc.join(", ")} files are allowed`});let l=Nt.default.basename(a),u=Nt.default.join(t,l);await xt.default.ensureDir(t),await xt.default.writeFile(u,o,"utf8");let d=0;try{if(c===".csv")d=$g(o).length;else{let m=JSON.parse(o);d=Array.isArray(m)?m.length:0}}catch{}return fc.info({filename:l,rowCount:d},"Data file uploaded"),n.status(201).send({filename:l,path:`data/${l}`,rowCount:d})}catch(i){return w(n,500,"Failed to upload data file",i)}}),r.put("/data-files/:filename",async(s,n)=>{try{let i=Nt.default.basename(s.params.filename),a=Nt.default.join(t,i),o=s.body,c=String(o.content??"");return await xt.default.ensureDir(t),await xt.default.writeFile(a,c,"utf8"),fc.info({filename:i},"Data file updated"),n.send({filename:i,path:`data/${i}`})}catch(i){return w(n,500,"Failed to update data file",i)}}),r.delete("/data-files/:filename",async(s,n)=>{try{let i=Nt.default.basename(s.params.filename),a=Nt.default.join(t,i);return await xt.default.pathExists(a)?(await xt.default.remove(a),fc.info({filename:i},"Data file deleted"),n.status(204).send()):n.status(404).send({error:`Data file "${i}" not found`})}catch(i){return w(n,500,"Failed to delete data file",i)}})}var Nt,xt,fc,hc,Og=b(()=>{"use strict";g();Nt=x(require("path")),xt=x(require("fs-extra"));de();j();fc=I("routes/data-files"),hc=[".json",".csv"]});function Lg(r){return as.default.basename(r).replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^\.+/,"")||"file"}async function Dg(r){let e=r.rootDir,t=as.default.join(e,yc);r.post("/files/upload",{bodyLimit:wc+8*1024*1024},async(s,n)=>{let i=rv.safeParse(s.body);if(!i.success)return n.status(400).send({error:"Invalid upload",details:i.error.issues});try{let a=Lg(i.data.name),o=Buffer.from(i.data.dataBase64,"base64");if(o.length>wc)return n.status(413).send({error:`File exceeds ${wc/(1024*1024)} MB limit`});await Hr.default.ensureDir(t);let c=as.default.join(t,a);await Hr.default.writeFile(c,o),tv.info({name:a,size:o.length},"Fixture file uploaded");let l=`${yc.replace(/\\/g,"/")}/${a}`;return n.send({name:a,path:l,size:o.length})}catch(a){return w(n,500,"Failed to save uploaded file",a)}}),r.get("/files",async(s,n)=>{try{if(!await Hr.default.pathExists(t))return n.send([]);let i=await Hr.default.readdir(t,{withFileTypes:!0}),a=await Promise.all(i.filter(o=>o.isFile()).map(async o=>{let c=await Hr.default.stat(as.default.join(t,o.name));return{name:o.name,path:`${yc.replace(/\\/g,"/")}/${o.name}`,size:c.size,token:`{{FILE:${o.name}}}`}}));return a.sort((o,c)=>o.name.localeCompare(c.name)),n.send(a)}catch(i){return w(n,500,"Failed to list files",i)}}),r.delete("/files/:name",async(s,n)=>{try{let i=Lg(s.params.name),a=as.default.join(t,i);return await Hr.default.pathExists(a)?(await Hr.default.remove(a),n.status(204).send()):n.status(404).send({error:`File "${i}" not found`})}catch(i){return w(n,500,"Failed to delete file",i)}})}var as,Hr,da,tv,yc,wc,rv,Ng=b(()=>{"use strict";g();as=x(require("path")),Hr=x(require("fs-extra")),da=require("zod");de();j();tv=I("routes/files"),yc=as.default.join("fixtures","uploads"),wc=25*1024*1024,rv=da.z.object({name:da.z.string().min(1).max(255),dataBase64:da.z.string().min(1)})});function Fg(r){switch(r){case"name":return 0;case"description":return 1;case"tag":return 2;case"instruction":return 3}}async function jg(r){let e=Mg.default.join(r.rootDir,"tests");r.get("/search",async(t,s)=>{let n=(t.query.q??"").trim();if(n.length<2)return s.send([]);try{let i=new RegExp(n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"i"),a=await Gt(e),o=[];for(let c of a){let l;try{l=await Ae(c)}catch{continue}i.test(l.name)?o.push({type:"suite",suiteId:l.id,suiteName:l.name,matchedOn:"name"}):l.description&&i.test(l.description)?o.push({type:"suite",suiteId:l.id,suiteName:l.name,matchedOn:"description"}):l.tags?.some(d=>i.test(d))&&o.push({type:"suite",suiteId:l.id,suiteName:l.name,matchedOn:"tag"});let u;try{u=await lt(c)}catch{u=[]}for(let d of u){i.test(d.name)?o.push({type:"case",suiteId:l.id,suiteName:l.name,caseId:d.id,caseName:d.name,matchedOn:"name"}):d.description&&i.test(d.description)?o.push({type:"case",suiteId:l.id,suiteName:l.name,caseId:d.id,caseName:d.name,matchedOn:"description"}):d.tags?.some(m=>i.test(m))&&o.push({type:"case",suiteId:l.id,suiteName:l.name,caseId:d.id,caseName:d.name,matchedOn:"tag"});for(let m of d.steps??[])m.instruction&&i.test(m.instruction)&&o.push({type:"step",suiteId:l.id,suiteName:l.name,caseId:d.id,caseName:d.name,stepId:m.id,instruction:m.instruction,matchedOn:"instruction"})}if(o.length>=20)break}return o.sort((c,l)=>Fg(c.matchedOn)-Fg(l.matchedOn)),s.send(o.slice(0,20))}catch(i){return w(s,500,"Search failed",i)}})}var Mg,Bg=b(()=>{"use strict";g();Mg=x(require("path"));et();Rt();de()});function xc(r,e,t){let s=["--all",`--browser ${r.join(" ")}`,`--parallel ${e}`,"--ci"];return t&&s.push(`--device "${t}"`),s.join(" ")}function sv(r,e,t){let s=xc(r,e,t);return`name: Assuremind Tests
|
|
1069
1069
|
on:
|
|
1070
1070
|
push:
|
|
1071
1071
|
branches: [main]
|
|
@@ -1088,7 +1088,7 @@ jobs:
|
|
|
1088
1088
|
with:
|
|
1089
1089
|
name: assuremind-results
|
|
1090
1090
|
path: results/
|
|
1091
|
-
`}function
|
|
1091
|
+
`}function nv(r,e,t){let s=xc(r,e,t);return`stages: [test]
|
|
1092
1092
|
test:
|
|
1093
1093
|
stage: test
|
|
1094
1094
|
image: mcr.microsoft.com/playwright:v1.40.0-jammy${t?`
|
|
@@ -1100,7 +1100,7 @@ test:
|
|
|
1100
1100
|
when: always
|
|
1101
1101
|
paths: [results/]
|
|
1102
1102
|
expire_in: 7 days
|
|
1103
|
-
`}function
|
|
1103
|
+
`}function iv(r,e,t){let s=xc(r,e,t);return`pipeline {
|
|
1104
1104
|
agent any
|
|
1105
1105
|
stages {
|
|
1106
1106
|
stage('Install') { steps { sh 'npm ci' } }
|
|
@@ -1111,7 +1111,7 @@ test:
|
|
|
1111
1111
|
}
|
|
1112
1112
|
post { always { archiveArtifacts artifacts: 'results/**', allowEmptyArchive: true } }
|
|
1113
1113
|
}
|
|
1114
|
-
`}function
|
|
1114
|
+
`}function Vg(r,e,t,s){return r==="github"?sv(e,t,s):r==="gitlab"?nv(e,t,s):iv(e,t,s)}async function Gg(r){let e=r.rootDir;r.post("/ci-config",async(t,s)=>{let n=Ug.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid CI config request",details:n.error.issues});try{let{platform:i,browsers:a,parallel:o,device:c}=n.data,l=Vg(i,a,o,c);return s.send({yaml:l,filename:Hg[i]})}catch(i){return w(s,500,"Failed to generate CI config",i)}}),r.post("/ci-config/write",async(t,s)=>{let n=Ug.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid CI config request",details:n.error.issues});try{if(!(await J(e)).ci?.writeToRepo)return s.status(403).send({error:"Writing CI files to the repo is disabled. Enable it in Settings \u2192 CI/CD Integration."});let{platform:a,browsers:o,parallel:c,device:l}=n.data,u=n.data.yaml?.trim()?n.data.yaml:Vg(a,o,c,l),d=Hg[a],m=bc.default.join(e,d);return await vc.default.ensureDir(bc.default.dirname(m)),await vc.default.writeFile(m,u,"utf-8"),s.send({written:!0,path:d})}catch(i){return w(s,500,"Failed to write CI config to repo",i)}})}var bc,vc,Jt,Ug,Hg,zg=b(()=>{"use strict";g();bc=x(require("path")),vc=x(require("fs-extra")),Jt=require("zod");de();Me();Ug=Jt.z.object({platform:Jt.z.enum(["github","gitlab","jenkins"]),browsers:Jt.z.array(Jt.z.string()).default(["chromium"]),parallel:Jt.z.number().int().positive().default(2),headed:Jt.z.boolean().default(!1),env:Jt.z.string().optional(),device:Jt.z.string().optional(),yaml:Jt.z.string().optional()});Hg={github:".github/workflows/assuremind.yml",gitlab:".gitlab-ci.yml",jenkins:"Jenkinsfile"}});async function qg(r){r.get("/templates",async(e,t)=>{try{return t.send(av)}catch(s){return w(t,500,"Failed to list templates",s)}})}var av,Kg=b(()=>{"use strict";g();de();av=[{id:"user-login",name:"User Login",description:"Standard login flow with credential validation",category:"auth",steps:[{instruction:"Navigate to the login page"},{instruction:"Enter the username in the username field"},{instruction:"Enter the password in the password field"},{instruction:"Click the login button"},{instruction:"Verify the dashboard page is displayed"}]},{id:"crud-record",name:"CRUD Operations",description:"Create, read, update, and delete a record",category:"crud",steps:[{instruction:"Navigate to the records list page"},{instruction:"Click the Create New button"},{instruction:"Fill in the required fields"},{instruction:"Click Save to create the record"},{instruction:"Verify the record appears in the list"},{instruction:"Click on the record to open it"},{instruction:"Edit one of the fields"},{instruction:"Save the changes"},{instruction:"Delete the record"},{instruction:"Verify the record is no longer in the list"}]},{id:"page-navigation",name:"Page Navigation",description:"Verify navigation between key pages",category:"navigation",steps:[{instruction:"Navigate to the home page"},{instruction:"Click the main navigation menu item"},{instruction:"Verify the correct page title is shown"},{instruction:"Use the browser back button"},{instruction:"Verify returning to the home page"}]},{id:"form-submission",name:"Form Submission",description:"Fill and submit a form with validation",category:"form",steps:[{instruction:"Navigate to the form page"},{instruction:"Fill in all required fields"},{instruction:"Attempt to submit the form with empty fields first"},{instruction:"Verify validation error messages are shown"},{instruction:"Fill in all required fields correctly"},{instruction:"Click the submit button"},{instruction:"Verify the success message is displayed"}]},{id:"api-health-check",name:"REST API Health Check",description:"Verify API endpoints return expected responses",category:"api",steps:[{instruction:"Send a GET request to the health check endpoint"},{instruction:"Verify the response status is 200"},{instruction:"Verify the response body contains status ok"},{instruction:"Send a GET request to the main API endpoint"},{instruction:"Verify the response contains the expected data structure"}]}]});async function Wg(r){let e=r.rootDir;r.get("/flakiness",async(t,s)=>{try{let{suiteId:n,caseId:i}=t.query,o=(await gi(e)).slice(0,20),c=new Map,l=new Map;for(let m of o)try{let p=await rs(e,m);for(let f of p.suites)if(!(n&&f.suiteId!==n))for(let h of f.cases){if(i&&h.caseId!==i)continue;let S=c.get(h.caseId)??{caseName:h.caseName,passes:0,failures:0};h.status==="passed"?S.passes++:h.status==="failed"&&S.failures++,c.set(h.caseId,S),l.has(h.caseId)||l.set(h.caseId,new Map);let C=l.get(h.caseId);for(let v of h.steps){let k=C.get(v.stepId)??{instruction:v.instruction,passes:0,failures:0};v.status==="passed"?k.passes++:v.status==="failed"&&k.failures++,C.set(v.stepId,k)}}}catch{}let u=[];for(let[m,p]of c){let f=p.passes+p.failures,h=f>0?p.passes/f:1,S=f>=3&&h>0&&h<1,C=l.get(m),v=[];if(C)for(let[k,T]of C){let P=T.passes+T.failures,G=P>0?T.passes/P:1,ne=P>=3&&G>0&&G<1;v.push({stepId:k,instruction:T.instruction,passRate:G,runs:P,flaky:ne})}u.push({caseId:m,caseName:p.caseName,passRate:h,runs:f,flaky:S,steps:v})}let d={cases:u,generatedAt:new Date().toISOString()};return s.send(d)}catch(n){return w(s,500,"Failed to compute flakiness",n)}})}var Xg=b(()=>{"use strict";g();fn();de()});function Yg(r){return Sc.default.join(r,"tests",".step-library.json")}async function qs(r){let e=Yg(r);if(!await pa.default.pathExists(e))return[];try{let t=await pa.default.readJson(e);return Array.isArray(t)?t.map(s=>ov.parse(s)):[]}catch{return[]}}async function Ec(r,e){let t=Yg(r);await pa.default.ensureDir(Sc.default.dirname(t)),await re(t,e)}async function Qg(r,e){let t=await qs(r),s=new Date().toISOString(),n={id:(0,Jg.v4)(),name:e.name,instruction:e.instruction,generatedCode:e.generatedCode??"",tags:e.tags??[],createdAt:s,updatedAt:s};return t.push(n),await Ec(r,t),n}async function Ic(r,e,t){let s=await qs(r),n=s.findIndex(i=>i.id===e);if(n<0)throw new Error(`Library step "${e}" not found`);return s[n]={...s[n],...t,updatedAt:new Date().toISOString()},await Ec(r,s),s[n]}async function Zg(r,e){let s=(await qs(r)).filter(n=>n.id!==e);await Ec(r,s)}var Sc,pa,Jg,Yt,ov,ef=b(()=>{"use strict";g();Sc=x(require("path")),pa=x(require("fs-extra")),Jg=require("uuid"),Yt=require("zod");ot();ov=Yt.z.object({id:Yt.z.string(),name:Yt.z.string().min(1),instruction:Yt.z.string().min(1),generatedCode:Yt.z.string().optional().default(""),tags:Yt.z.array(Yt.z.string()).default([]),createdAt:Yt.z.string(),updatedAt:Yt.z.string()})});async function tf(r){let e=r.rootDir;r.get("/step-library",async(t,s)=>{try{let n=await qs(e);return s.send(n)}catch(n){return w(s,500,"Failed to list step library",n)}}),r.post("/step-library",async(t,s)=>{let n=cv.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid request",details:n.error.issues});try{let i=await Qg(e,n.data);return s.status(201).send(i)}catch(i){return w(s,500,"Failed to create library step",i)}}),r.patch("/step-library/:id",async(t,s)=>{let n=lv.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid request",details:n.error.issues});try{let i=await Ic(e,t.params.id,n.data);return s.send(i)}catch(i){return w(s,500,"Failed to update library step",i)}}),r.delete("/step-library/:id",async(t,s)=>{try{return await Zg(e,t.params.id),s.status(204).send()}catch(n){return w(s,500,"Failed to delete library step",n)}}),r.post("/step-library/:id/generate",async(t,s)=>{let n;try{let a=(await qs(e)).find(p=>p.id===t.params.id);if(!a)return s.status(404).send({error:`Library step "${t.params.id}" not found`});let o=await J(e),c=await ft(e).catch(()=>({})),l=o.mcp?.enabled;l&&(n=new Tt({headless:o.mcp?.headless??!0,actionTimeout:o.mcp?.actionTimeout??15e3,idleTimeout:o.mcp?.idleTimeout??3e4}));let u=ht(e,n,l?o.mcp?.actThenScript:!1),d=await u.generate(a.instruction,{url:o.baseUrl,title:"",interactiveElements:"",htmlSnapshot:"",previousSteps:[],variables:c});await u.getCache().persist();let m=await Ic(e,a.id,{generatedCode:d.code});return s.send({step:m,strategy:d.strategy,cost:d.cost})}catch(i){return w(s,500,"Failed to generate code for library step",i)}finally{n?.release()}})}var pt,cv,lv,rf=b(()=>{"use strict";g();pt=require("zod");ef();Me();Tr();$r();cn();de();cv=pt.z.object({name:pt.z.string().min(1),instruction:pt.z.string().min(1),generatedCode:pt.z.string().optional(),tags:pt.z.array(pt.z.string()).optional()}),lv=pt.z.object({name:pt.z.string().min(1).optional(),instruction:pt.z.string().min(1).optional(),generatedCode:pt.z.string().optional(),tags:pt.z.array(pt.z.string()).optional()})});function uv(){return`
|
|
1115
1115
|
(function() {
|
|
1116
1116
|
if (window.__amListenersAttached) return;
|
|
1117
1117
|
window.__amListenersAttached = true;
|
|
@@ -1500,67 +1500,67 @@ test:
|
|
|
1500
1500
|
}
|
|
1501
1501
|
}, true);
|
|
1502
1502
|
})();
|
|
1503
|
-
`}async function Yb(r,e){let t=[];if(e.shadowPath){let a=e.frameSelector?`frameLocator('${ye(e.frameSelector)}').`:"",o=e.shadowPath.split(" >> ").map(c=>`locator('${ye(c.trim())}')`).join(".");return`${a}${o}`}let s=!!e.frameSelector,n=s?`frameLocator('${ye(e.frameSelector)}').`:"",i=s?r.frameLocator(e.frameSelector):r;try{let a=e.ariaRole||Qb(e.tag,e.type),o=e.ariaLabel||e.labelText||e.innerText||e.title||"",c=Zb(e.tag);e.testId&&t.push({name:"testid",locator:i.getByTestId(e.testId),pw:`${n}getByTestId('${ye(e.testId)}')`}),a&&o&&o.length<=60&&(c&&t.push({name:"role+name+level",locator:i.getByRole(a,{name:o,exact:!0,level:c}),pw:`${n}getByRole('${a}', { name: '${ye(o)}', level: ${c} })`}),t.push({name:"role+name-exact",locator:i.getByRole(a,{name:o,exact:!0}),pw:`${n}getByRole('${a}', { name: '${ye(o)}' })`}),t.push({name:"role+name-fuzzy",locator:i.getByRole(a,{name:o,exact:!1}),pw:`${n}getByRole('${a}', { name: '${ye(o)}' })`})),e.labelText&&(t.push({name:"label",locator:i.getByLabel(e.labelText,{exact:!0}),pw:`${n}getByLabel('${ye(e.labelText)}')`}),t.push({name:"label-fuzzy",locator:i.getByLabel(e.labelText,{exact:!1}),pw:`${n}getByLabel('${ye(e.labelText)}')`})),e.placeholder&&t.push({name:"placeholder",locator:i.getByPlaceholder(e.placeholder,{exact:!0}),pw:`${n}getByPlaceholder('${ye(e.placeholder)}')`}),e.innerText&&e.innerText.length<=60&&(t.push({name:"text-exact",locator:i.getByText(e.innerText,{exact:!0}),pw:`${n}getByText('${ye(e.innerText)}')`}),t.push({name:"text-fuzzy",locator:i.getByText(e.innerText,{exact:!1}),pw:`${n}getByText('${ye(e.innerText)}')`})),e.cssPath&&t.push({name:"css",locator:i.locator(e.cssPath),pw:`${n}locator('${ye(e.cssPath)}')`});for(let l of t)try{if(await l.locator.count()===1)return Oe.debug({strategy:l.name,pw:l.pw,inIframe:s},"Locator resolved"),l.pw}catch{}return e.cssPath?`${n}locator('${ye(e.cssPath)}')`:e.id?`${n}locator('#${ye(e.id)}')`:`${n}locator('${ye(e.tag)}')`}catch(a){return Oe.warn({err:a,meta:e.cssPath},"Locator resolution failed \u2014 using CSS fallback"),e.cssPath?`${n}locator('${ye(e.cssPath)}')`:`${n}locator('${ye(e.tag)}')`}}function Qb(r,e){if(r==="button"||r==="input"&&e==="submit")return"button";if(r==="a")return"link";if(r==="input"){if(e==="checkbox")return"checkbox";if(e==="radio")return"radio";if(["text","email","password","search","tel","url","number",""].includes(e))return"textbox"}return r==="textarea"?"textbox":r==="select"?"combobox":r==="img"?"img":["h1","h2","h3","h4","h5","h6"].includes(r)?"heading":null}function Zb(r){let e=r.match(/^h([1-6])$/);return e?parseInt(e[1],10):null}function ye(r){return r.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}var qg,Oe,Yi,Kg=b(()=>{"use strict";m();qg=require("playwright");F();Oe=C("recorder");Yi=class{browser=null;context=null;page=null;actions=[];actionCounter=0;running=!1;options;_setLastUserActionTime=null;constructor(e){this.options=e}get isRunning(){return this.running}get recordedActions(){return[...this.actions]}async start(){if(this.running)throw new Error("Recorder is already running");Oe.info({url:this.options.startUrl},"Starting recorder"),this.actions=[],this.actionCounter=0,this.running=!0;try{this.browser=await qg.chromium.launch({headless:!1,args:["--start-maximized"]}),this.context=await this.browser.newContext({viewport:this.options.viewport||{width:1280,height:720},ignoreHTTPSErrors:!0}),await this.context.exposeBinding("__amSend",(o,c)=>{this.handleRawAction(c)});let e=Jb();await this.context.addInitScript(e),this.page=await this.context.newPage(),this.page.on("pageerror",o=>{Oe.warn({error:o.message},"Page error during recording")});let t=async o=>{try{await o.evaluate(e),Oe.debug({url:o.url(),name:o.name()},"Capture script injected into frame")}catch(c){Oe.debug({err:c,url:o.url()},"Could not inject into frame (likely cross-origin)")}},s=async o=>{await t(o.mainFrame());for(let c of o.frames())c!==o.mainFrame()&&await t(c)};this.page.on("load",()=>{!this.running||!this.page||setTimeout(()=>s(this.page),150)}),this.page.on("frameattached",o=>{this.running&&o.waitForLoadState("load").then(()=>{t(o)}).catch(()=>{})}),this.page.on("framenavigated",o=>{!this.running||!this.page||o!==this.page.mainFrame()&&setTimeout(()=>t(o),150)});let n="",i=0,a=3e3;this.page.on("framenavigated",o=>{if(!this.running||!this.page||o!==this.page.mainFrame())return;let c=o.url();if(c===n||c==="about:blank")return;n=c;let l=this.actionCounter===0;if(!l&&i>0){let d=Date.now()-i;if(d<a){Oe.debug({url:c,elapsed:d},"Skipping redirect navigation after user action");return}}let u={id:`rec-${++this.actionCounter}`,order:this.actionCounter,action:"navigate",instruction:l?`Go to ${c}`:`Navigate to ${c}`,locator:"",url:c,timestamp:Date.now()};this.actions.push(u),this.options.onAction(u),Oe.debug({action:u.instruction},"Recorded navigation")}),this._setLastUserActionTime=o=>{i=o},this.page.on("dialog",async o=>{if(!this.running)return;let c=o.type(),l=o.message();await o.accept().catch(()=>{});for(let u=this.actions.length-1;u>=0;u--){let d=this.actions[u];if(["click","fill","check","uncheck","select","press"].includes(d.action)){d.dialogHandler={type:"accept",dialogType:c,message:l};break}}Oe.debug({dialogType:c,message:l},"Dialog auto-accepted and tagged on triggering action")}),this.page.on("close",()=>{this.running&&(Oe.info("Browser closed by user \u2014 stopping recorder"),this.running=!1,this.options.onStop?.(this.actions))}),await this.page.goto(this.options.startUrl,{waitUntil:"load",timeout:3e4}),await s(this.page),Oe.info("Recorder started \u2014 browser is open for interaction")}catch(e){throw Oe.error({err:e},"Failed to start recorder"),await this.cleanup(),e}}async stop(){if(!this.running)return this.actions;Oe.info({actionCount:this.actions.length},"Stopping recorder"),this.running=!1;let e=[...this.actions];return await this.cleanup(),this.options.onStop?.(e),e}async handleRawAction(e){if(!(!this.running||!this.page))try{let t=JSON.parse(e),s=t.meta,n="";if(s&&this.page)try{n=await Yb(this.page,s)}catch(o){Oe.warn({err:o},"Locator resolution failed \u2014 using CSS fallback"),n=s.cssPath?`locator('${ye(s.cssPath)}')`:""}["click","fill","check","uncheck","select","press"].includes(t.action)&&this._setLastUserActionTime&&this._setLastUserActionTime(Date.now());let a={id:`rec-${++this.actionCounter}`,order:this.actionCounter,action:t.action,instruction:t.instruction,locator:n,value:t.value,url:t.url,timestamp:Date.now(),...t.soft?{soft:!0}:{},...s?.frameSelector?{frameSelector:s.frameSelector}:{},...s?.shadowPath?{shadowPath:s.shadowPath}:{}};this.actions.push(a),this.options.onAction(a),Oe.debug({action:a.instruction,locator:n,strategy:"resolved"},"Recorded action")}catch(t){Oe.warn({err:t,raw:e},"Failed to parse recorded action")}}async cleanup(){try{this.context&&await this.context.close().catch(()=>{}),this.browser&&await this.browser.close().catch(()=>{})}catch{}this.browser=null,this.context=null,this.page=null}}});async function Wg(r){let e=r.rootDir,t=r.wsManager;r.post("/recorder/start",async(s,n)=>{try{if(Ct?.isRunning)return n.status(409).send({error:"A recording session is already active. Stop it first."});let i=tv.parse(s.body||{}),a=await G(e),o=i.url||a.baseUrl||"https://www.google.com";return Ct=new Yi({startUrl:o,viewport:i.viewport,onAction:c=>{t.broadcast("recorder:action",c)},onStop:c=>{t.broadcast("recorder:stopped",{actionCount:c.length}),ev.info({actionCount:c.length},"Recording stopped (browser closed)")}}),await Ct.start(),n.send({status:"recording",url:o,message:"Browser launched \u2014 interact with your app. Actions are streamed in real time."})}catch(i){return Ct=null,w(n,500,"Failed to start recorder",i)}}),r.post("/recorder/stop",async(s,n)=>{try{if(!Ct?.isRunning)return n.status(404).send({error:"No active recording session."});let i=await Ct.stop();return Ct=null,t.broadcast("recorder:stopped",{actionCount:i.length}),n.send({status:"stopped",actions:i,actionCount:i.length})}catch(i){return Ct=null,w(n,500,"Failed to stop recorder",i)}}),r.get("/recorder/status",async(s,n)=>{let i=Ct?.isRunning??!1,a=Ct?.recordedActions.length??0;return n.send({recording:i,actionCount:a,actions:i?Ct.recordedActions:[]})})}var Os,ev,tv,Ct,Xg=b(()=>{"use strict";m();Os=require("zod");Kg();Te();te();F();ev=C("recorder-routes"),tv=Os.z.object({url:Os.z.string().url().optional(),viewport:Os.z.object({width:Os.z.number().int().positive(),height:Os.z.number().int().positive()}).optional()}),Ct=null});var Jg,Yg=b(()=>{"use strict";m();mo();Jg=async r=>{r.get("/faker/generators",async(e,t)=>{let s=gi();return t.send(s)})}});async function Qg(r){let e=r.rootDir;r.get("/visual-regression/config",async(t,s)=>{try{let n=await on(e);return s.send(n)}catch(n){return w(s,500,"Failed to read visual-regression config",n)}}),r.put("/visual-regression/config",async(t,s)=>{try{let n=t.body,i=await on(e),a={enabled:typeof n.enabled=="boolean"?n.enabled:i.enabled,defaultThreshold:typeof n.defaultThreshold=="number"?n.defaultThreshold:i.defaultThreshold,fullPage:typeof n.fullPage=="boolean"?n.fullPage:i.fullPage,maskSelectors:Array.isArray(n.maskSelectors)?n.maskSelectors:i.maskSelectors};return await Ip(e,a),s.send(a)}catch(n){return w(s,500,"Failed to save visual-regression config",n)}}),r.get("/visual-regression/baselines",async(t,s)=>{try{let n=await kp(e);return s.send(n)}catch(n){return w(s,500,"Failed to list baselines",n)}}),r.delete("/visual-regression/baselines",async(t,s)=>{try{let{path:n}=t.body;return n?(await Ap(e,n),s.send({ok:!0})):s.status(400).send({error:'Missing "path" in request body'})}catch(n){return w(s,500,"Failed to delete baseline",n)}}),r.post("/visual-regression/baselines/update",async(t,s)=>{try{let{actualPath:n,baselinePath:i}=t.body;return!n||!i?s.status(400).send({error:"Missing actualPath or baselinePath"}):await Ls.default.pathExists(n)?(await Ls.default.ensureDir(fn.default.dirname(i)),await Ls.default.copy(n,i,{overwrite:!0}),s.send({ok:!0})):s.status(404).send({error:"Actual screenshot not found"})}catch(n){return w(s,500,"Failed to update baseline",n)}}),r.get("/visual-regression/diffs",async(t,s)=>{try{let n=await Pp(e);return s.send(n)}catch(n){return w(s,500,"Failed to list diff runs",n)}}),r.get("/visual-regression/diffs/:runId",async(t,s)=>{try{let{runId:n}=t.params,i=await wi(e,n);return s.send(i)}catch(n){return w(s,500,"Failed to read diff summary",n)}}),r.patch("/visual-regression/diffs/:runId/:stepId",async(t,s)=>{try{let{runId:n,stepId:i}=t.params,{status:a}=t.body;if(!["approved","rejected"].includes(a))return s.status(400).send({error:'Status must be "approved" or "rejected"'});let o=await Rp(e,n,i,a);return s.send(o)}catch(n){return w(s,500,"Failed to update diff status",n)}}),r.post("/visual-regression/diffs/:runId/approve-all",async(t,s)=>{try{let{runId:n}=t.params,i=await Tp(e,n);return s.send(i)}catch(n){return w(s,500,"Failed to approve all diffs",n)}}),r.get("/visual-regression/image/*",async(t,s)=>{try{let n=t.params["*"];if(!n)return s.status(400).send({error:"Missing image path"});let i=fn.default.resolve(e,n);if(!i.startsWith(fn.default.resolve(e)))return s.status(403).send({error:"Forbidden"});if(!await Ls.default.pathExists(i))return s.status(404).send({error:"Image not found"});let a=fn.default.extname(i).toLowerCase(),c={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp"}[a]??"application/octet-stream",l=await Ls.default.readFile(i);return s.type(c).send(l)}catch(n){return w(s,500,"Failed to serve image",n)}})}var Ls,fn,Zg=b(()=>{"use strict";m();Ls=x(require("fs-extra")),fn=x(require("path"));te();Eo()});function tf(r){return Ds.default.join(r,".assuremind","ci-integration.json")}function rf(r){return Ds.default.join(r,"results","quality-gates")}function ov(r,e){return Ds.default.join(rf(r),`${e}.json`)}async function ea(r){let e=tf(r);if(!await tr.default.pathExists(e))return Qi.parse({});try{let t=await tr.default.readJson(e);return Qi.parse(t)}catch{return Qi.parse({})}}async function sf(r,e){let t=tf(r),s=Qi.parse(e);await tr.default.ensureDir(Ds.default.dirname(t)),await W(t,s)}async function nf(r){let e=rf(r);if(!await tr.default.pathExists(e))return[];try{let s=(await tr.default.readdir(e)).filter(i=>i.endsWith(".json")).sort().reverse(),n=[];for(let i of s)try{let a=await tr.default.readJson(Ds.default.join(e,i));n.push(ef.parse(a))}catch{}return n}catch{return[]}}async function af(r,e){let t=ov(r,e);if(!await tr.default.pathExists(t))return null;try{let s=await tr.default.readJson(t);return ef.parse(s)}catch{return null}}var Ds,tr,L,Zi,rv,sv,nv,iv,Qi,av,ef,of=b(()=>{"use strict";m();Ds=x(require("path")),tr=x(require("fs-extra")),L=require("zod");ze();Zi=L.z.enum(["every-run","failure","healing"]),rv=L.z.object({enabled:L.z.boolean().default(!1),webhookUrl:L.z.string().default(""),on:L.z.array(Zi).default(["failure"])}),sv=L.z.object({enabled:L.z.boolean().default(!1),recipients:L.z.array(L.z.string()).default([]),on:L.z.array(Zi).default(["failure"])}),nv=L.z.object({enabled:L.z.boolean().default(!1),webhookUrl:L.z.string().default(""),on:L.z.array(Zi).default(["failure"])}),iv=L.z.object({enabled:L.z.boolean().default(!1),url:L.z.string().default(""),headers:L.z.record(L.z.string()).default({}),on:L.z.array(Zi).default(["failure"])}),Qi=L.z.object({prComments:L.z.object({enabled:L.z.boolean().default(!1),provider:L.z.enum(["github","gitlab","azure-devops"]).default("github"),includeHealingSummary:L.z.boolean().default(!0),includeVisualDiffs:L.z.boolean().default(!1)}).default({}),qualityGate:L.z.object({enabled:L.z.boolean().default(!0),minPassRate:L.z.number().min(0).max(100).default(95),maxFlakyRate:L.z.number().min(0).max(100).default(10),maxDuration:L.z.number().min(0).default(0),requireTags:L.z.array(L.z.string()).default([])}).default({}),notifications:L.z.object({slack:rv.default({}),email:sv.default({}),teams:nv.default({}),customWebhook:iv.default({})}).default({})}),av=L.z.object({name:L.z.string(),passed:L.z.boolean(),expected:L.z.string(),actual:L.z.string()}),ef=L.z.object({passed:L.z.boolean(),runId:L.z.string(),timestamp:L.z.string(),metrics:L.z.object({passRate:L.z.number(),totalTests:L.z.number(),passed:L.z.number(),failed:L.z.number(),skipped:L.z.number(),duration:L.z.number(),healingCount:L.z.number()}),gates:L.z.array(av),reason:L.z.string().optional()})});function cf(r){let e=Math.floor(r/60),t=r%60;return e>0?`${e}m ${t}s`:`${t}s`}function cv(r){let e=r.passed+r.failed+r.skipped,t=e>0?(r.passed/e*100).toFixed(1):"0",s=r.failed>0?":x: Failed":":white_check_mark: Passed",n=[];if(r.suiteResults)for(let a of r.suiteResults)for(let o of a.cases)o.status==="failed"&&n.push(o.caseName);let i=[{type:"header",text:{type:"plain_text",text:"Assuremind Test Results",emoji:!0}},{type:"section",fields:[{type:"mrkdwn",text:`*Status:*
|
|
1503
|
+
`}async function dv(r,e){let t=[];if(e.shadowPath){let a=e.frameSelector?`frameLocator('${Te(e.frameSelector)}').`:"",o=e.shadowPath.split(" >> ").map(c=>`locator('${Te(c.trim())}')`).join(".");return`${a}${o}`}let s=!!e.frameSelector,n=s?`frameLocator('${Te(e.frameSelector)}').`:"",i=s?r.frameLocator(e.frameSelector):r;try{let a=e.ariaRole||pv(e.tag,e.type),o=e.ariaLabel||e.labelText||e.innerText||e.title||"",c=mv(e.tag);e.testId&&t.push({name:"testid",locator:i.getByTestId(e.testId),pw:`${n}getByTestId('${Te(e.testId)}')`}),a&&o&&o.length<=60&&(c&&t.push({name:"role+name+level",locator:i.getByRole(a,{name:o,exact:!0,level:c}),pw:`${n}getByRole('${a}', { name: '${Te(o)}', level: ${c} })`}),t.push({name:"role+name-exact",locator:i.getByRole(a,{name:o,exact:!0}),pw:`${n}getByRole('${a}', { name: '${Te(o)}' })`}),t.push({name:"role+name-fuzzy",locator:i.getByRole(a,{name:o,exact:!1}),pw:`${n}getByRole('${a}', { name: '${Te(o)}' })`})),e.labelText&&(t.push({name:"label",locator:i.getByLabel(e.labelText,{exact:!0}),pw:`${n}getByLabel('${Te(e.labelText)}')`}),t.push({name:"label-fuzzy",locator:i.getByLabel(e.labelText,{exact:!1}),pw:`${n}getByLabel('${Te(e.labelText)}')`})),e.placeholder&&t.push({name:"placeholder",locator:i.getByPlaceholder(e.placeholder,{exact:!0}),pw:`${n}getByPlaceholder('${Te(e.placeholder)}')`}),e.innerText&&e.innerText.length<=60&&(t.push({name:"text-exact",locator:i.getByText(e.innerText,{exact:!0}),pw:`${n}getByText('${Te(e.innerText)}')`}),t.push({name:"text-fuzzy",locator:i.getByText(e.innerText,{exact:!1}),pw:`${n}getByText('${Te(e.innerText)}')`})),e.cssPath&&t.push({name:"css",locator:i.locator(e.cssPath),pw:`${n}locator('${Te(e.cssPath)}')`});for(let l of t)try{if(await l.locator.count()===1)return Ve.debug({strategy:l.name,pw:l.pw,inIframe:s},"Locator resolved"),l.pw}catch{}return e.cssPath?`${n}locator('${Te(e.cssPath)}')`:e.id?`${n}locator('#${Te(e.id)}')`:`${n}locator('${Te(e.tag)}')`}catch(a){return Ve.warn({err:a,meta:e.cssPath},"Locator resolution failed \u2014 using CSS fallback"),e.cssPath?`${n}locator('${Te(e.cssPath)}')`:`${n}locator('${Te(e.tag)}')`}}function pv(r,e){if(r==="button"||r==="input"&&e==="submit")return"button";if(r==="a")return"link";if(r==="input"){if(e==="checkbox")return"checkbox";if(e==="radio")return"radio";if(["text","email","password","search","tel","url","number",""].includes(e))return"textbox"}return r==="textarea"?"textbox":r==="select"?"combobox":r==="img"?"img":["h1","h2","h3","h4","h5","h6"].includes(r)?"heading":null}function mv(r){let e=r.match(/^h([1-6])$/);return e?parseInt(e[1],10):null}function Te(r){return r.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}var sf,Ve,ma,nf=b(()=>{"use strict";g();sf=require("playwright");j();Ve=I("recorder");ma=class{browser=null;context=null;page=null;actions=[];actionCounter=0;running=!1;options;_setLastUserActionTime=null;constructor(e){this.options=e}get isRunning(){return this.running}get recordedActions(){return[...this.actions]}async start(){if(this.running)throw new Error("Recorder is already running");Ve.info({url:this.options.startUrl},"Starting recorder"),this.actions=[],this.actionCounter=0,this.running=!0;try{this.browser=await sf.chromium.launch({headless:!1,args:["--start-maximized"]}),this.context=await this.browser.newContext({viewport:this.options.viewport||{width:1280,height:720},ignoreHTTPSErrors:!0}),await this.context.exposeBinding("__amSend",(o,c)=>{this.handleRawAction(c)});let e=uv();await this.context.addInitScript(e),this.page=await this.context.newPage(),this.page.on("pageerror",o=>{Ve.warn({error:o.message},"Page error during recording")});let t=async o=>{try{await o.evaluate(e),Ve.debug({url:o.url(),name:o.name()},"Capture script injected into frame")}catch(c){Ve.debug({err:c,url:o.url()},"Could not inject into frame (likely cross-origin)")}},s=async o=>{await t(o.mainFrame());for(let c of o.frames())c!==o.mainFrame()&&await t(c)};this.page.on("load",()=>{!this.running||!this.page||setTimeout(()=>s(this.page),150)}),this.page.on("frameattached",o=>{this.running&&o.waitForLoadState("load").then(()=>{t(o)}).catch(()=>{})}),this.page.on("framenavigated",o=>{!this.running||!this.page||o!==this.page.mainFrame()&&setTimeout(()=>t(o),150)});let n="",i=0,a=3e3;this.page.on("framenavigated",o=>{if(!this.running||!this.page||o!==this.page.mainFrame())return;let c=o.url();if(c===n||c==="about:blank")return;n=c;let l=this.actionCounter===0;if(!l&&i>0){let d=Date.now()-i;if(d<a){Ve.debug({url:c,elapsed:d},"Skipping redirect navigation after user action");return}}let u={id:`rec-${++this.actionCounter}`,order:this.actionCounter,action:"navigate",instruction:l?`Go to ${c}`:`Navigate to ${c}`,locator:"",url:c,timestamp:Date.now()};this.actions.push(u),this.options.onAction(u),Ve.debug({action:u.instruction},"Recorded navigation")}),this._setLastUserActionTime=o=>{i=o},this.page.on("dialog",async o=>{if(!this.running)return;let c=o.type(),l=o.message();await o.accept().catch(()=>{});for(let u=this.actions.length-1;u>=0;u--){let d=this.actions[u];if(["click","fill","check","uncheck","select","press"].includes(d.action)){d.dialogHandler={type:"accept",dialogType:c,message:l};break}}Ve.debug({dialogType:c,message:l},"Dialog auto-accepted and tagged on triggering action")}),this.page.on("close",()=>{this.running&&(Ve.info("Browser closed by user \u2014 stopping recorder"),this.running=!1,this.options.onStop?.(this.actions))}),await this.page.goto(this.options.startUrl,{waitUntil:"load",timeout:3e4}),await s(this.page),Ve.info("Recorder started \u2014 browser is open for interaction")}catch(e){throw Ve.error({err:e},"Failed to start recorder"),await this.cleanup(),e}}async stop(){if(!this.running)return this.actions;Ve.info({actionCount:this.actions.length},"Stopping recorder"),this.running=!1;let e=[...this.actions];return await this.cleanup(),this.options.onStop?.(e),e}async handleRawAction(e){if(!(!this.running||!this.page))try{let t=JSON.parse(e),s=t.meta,n="";if(s&&this.page)try{n=await dv(this.page,s)}catch(o){Ve.warn({err:o},"Locator resolution failed \u2014 using CSS fallback"),n=s.cssPath?`locator('${Te(s.cssPath)}')`:""}["click","fill","check","uncheck","select","press"].includes(t.action)&&this._setLastUserActionTime&&this._setLastUserActionTime(Date.now());let a={id:`rec-${++this.actionCounter}`,order:this.actionCounter,action:t.action,instruction:t.instruction,locator:n,value:t.value,url:t.url,timestamp:Date.now(),...t.soft?{soft:!0}:{},...s?.frameSelector?{frameSelector:s.frameSelector}:{},...s?.shadowPath?{shadowPath:s.shadowPath}:{}};this.actions.push(a),this.options.onAction(a),Ve.debug({action:a.instruction,locator:n,strategy:"resolved"},"Recorded action")}catch(t){Ve.warn({err:t,raw:e},"Failed to parse recorded action")}}async cleanup(){try{this.context&&await this.context.close().catch(()=>{}),this.browser&&await this.browser.close().catch(()=>{})}catch{}this.browser=null,this.context=null,this.page=null}}});async function af(r){let e=r.rootDir,t=r.wsManager;r.post("/recorder/start",async(s,n)=>{try{if(Ft?.isRunning)return n.status(409).send({error:"A recording session is already active. Stop it first."});let i=fv.parse(s.body||{}),a=await J(e),o=i.url||a.baseUrl||"https://www.google.com";return Ft=new ma({startUrl:o,viewport:i.viewport,onAction:c=>{t.broadcast("recorder:action",c)},onStop:c=>{t.broadcast("recorder:stopped",{actionCount:c.length}),gv.info({actionCount:c.length},"Recording stopped (browser closed)")}}),await Ft.start(),n.send({status:"recording",url:o,message:"Browser launched \u2014 interact with your app. Actions are streamed in real time."})}catch(i){return Ft=null,w(n,500,"Failed to start recorder",i)}}),r.post("/recorder/stop",async(s,n)=>{try{if(!Ft?.isRunning)return n.status(404).send({error:"No active recording session."});let i=await Ft.stop();return Ft=null,t.broadcast("recorder:stopped",{actionCount:i.length}),n.send({status:"stopped",actions:i,actionCount:i.length})}catch(i){return Ft=null,w(n,500,"Failed to stop recorder",i)}}),r.get("/recorder/status",async(s,n)=>{let i=Ft?.isRunning??!1,a=Ft?.recordedActions.length??0;return n.send({recording:i,actionCount:a,actions:i?Ft.recordedActions:[]})})}var Ks,gv,fv,Ft,of=b(()=>{"use strict";g();Ks=require("zod");nf();Me();de();j();gv=I("recorder-routes"),fv=Ks.z.object({url:Ks.z.string().url().optional(),viewport:Ks.z.object({width:Ks.z.number().int().positive(),height:Ks.z.number().int().positive()}).optional()}),Ft=null});var cf,lf=b(()=>{"use strict";g();Ro();cf=async r=>{r.get("/faker/generators",async(e,t)=>{let s=Ti();return t.send(s)})}});async function uf(r){let e=r.rootDir;r.get("/visual-regression/config",async(t,s)=>{try{let n=await bn(e);return s.send(n)}catch(n){return w(s,500,"Failed to read visual-regression config",n)}}),r.put("/visual-regression/config",async(t,s)=>{try{let n=t.body,i=await bn(e),a={enabled:typeof n.enabled=="boolean"?n.enabled:i.enabled,defaultThreshold:typeof n.defaultThreshold=="number"?n.defaultThreshold:i.defaultThreshold,fullPage:typeof n.fullPage=="boolean"?n.fullPage:i.fullPage,maskSelectors:Array.isArray(n.maskSelectors)?n.maskSelectors:i.maskSelectors};return await Dp(e,a),s.send(a)}catch(n){return w(s,500,"Failed to save visual-regression config",n)}}),r.get("/visual-regression/baselines",async(t,s)=>{try{let n=await jp(e);return s.send(n)}catch(n){return w(s,500,"Failed to list baselines",n)}}),r.delete("/visual-regression/baselines",async(t,s)=>{try{let{path:n}=t.body;return n?(await Bp(e,n),s.send({ok:!0})):s.status(400).send({error:'Missing "path" in request body'})}catch(n){return w(s,500,"Failed to delete baseline",n)}}),r.post("/visual-regression/baselines/update",async(t,s)=>{try{let{actualPath:n,baselinePath:i}=t.body;return!n||!i?s.status(400).send({error:"Missing actualPath or baselinePath"}):await Ws.default.pathExists(n)?(await Ws.default.ensureDir(kn.default.dirname(i)),await Ws.default.copy(n,i,{overwrite:!0}),s.send({ok:!0})):s.status(404).send({error:"Actual screenshot not found"})}catch(n){return w(s,500,"Failed to update baseline",n)}}),r.get("/visual-regression/diffs",async(t,s)=>{try{let n=await Np(e);return s.send(n)}catch(n){return w(s,500,"Failed to list diff runs",n)}}),r.get("/visual-regression/diffs/:runId",async(t,s)=>{try{let{runId:n}=t.params,i=await Di(e,n);return s.send(i)}catch(n){return w(s,500,"Failed to read diff summary",n)}}),r.patch("/visual-regression/diffs/:runId/:stepId",async(t,s)=>{try{let{runId:n,stepId:i}=t.params,{status:a}=t.body;if(!["approved","rejected"].includes(a))return s.status(400).send({error:'Status must be "approved" or "rejected"'});let o=await Fp(e,n,i,a);return s.send(o)}catch(n){return w(s,500,"Failed to update diff status",n)}}),r.post("/visual-regression/diffs/:runId/approve-all",async(t,s)=>{try{let{runId:n}=t.params,i=await Mp(e,n);return s.send(i)}catch(n){return w(s,500,"Failed to approve all diffs",n)}}),r.get("/visual-regression/image/*",async(t,s)=>{try{let n=t.params["*"];if(!n)return s.status(400).send({error:"Missing image path"});let i=kn.default.resolve(e,n);if(!i.startsWith(kn.default.resolve(e)))return s.status(403).send({error:"Forbidden"});if(!await Ws.default.pathExists(i))return s.status(404).send({error:"Image not found"});let a=kn.default.extname(i).toLowerCase(),c={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp"}[a]??"application/octet-stream",l=await Ws.default.readFile(i);return s.type(c).send(l)}catch(n){return w(s,500,"Failed to serve image",n)}})}var Ws,kn,df=b(()=>{"use strict";g();Ws=x(require("fs-extra")),kn=x(require("path"));de();No()});function mf(r){return Xs.default.join(r,".assuremind","ci-integration.json")}function gf(r){return Xs.default.join(r,"results","quality-gates")}function xv(r,e){return Xs.default.join(gf(r),`${e}.json`)}async function ha(r){let e=mf(r);if(!await hr.default.pathExists(e))return ga.parse({});try{let t=await hr.default.readJson(e);return ga.parse(t)}catch{return ga.parse({})}}async function ff(r,e){let t=mf(r),s=ga.parse(e);await hr.default.ensureDir(Xs.default.dirname(t)),await re(t,s)}async function hf(r){let e=gf(r);if(!await hr.default.pathExists(e))return[];try{let s=(await hr.default.readdir(e)).filter(i=>i.endsWith(".json")).sort().reverse(),n=[];for(let i of s)try{let a=await hr.default.readJson(Xs.default.join(e,i));n.push(pf.parse(a))}catch{}return n}catch{return[]}}async function yf(r,e){let t=xv(r,e);if(!await hr.default.pathExists(t))return null;try{let s=await hr.default.readJson(t);return pf.parse(s)}catch{return null}}var Xs,hr,D,fa,hv,yv,wv,bv,ga,vv,pf,wf=b(()=>{"use strict";g();Xs=x(require("path")),hr=x(require("fs-extra")),D=require("zod");ot();fa=D.z.enum(["every-run","failure","healing"]),hv=D.z.object({enabled:D.z.boolean().default(!1),webhookUrl:D.z.string().default(""),on:D.z.array(fa).default(["failure"])}),yv=D.z.object({enabled:D.z.boolean().default(!1),recipients:D.z.array(D.z.string()).default([]),on:D.z.array(fa).default(["failure"])}),wv=D.z.object({enabled:D.z.boolean().default(!1),webhookUrl:D.z.string().default(""),on:D.z.array(fa).default(["failure"])}),bv=D.z.object({enabled:D.z.boolean().default(!1),url:D.z.string().default(""),headers:D.z.record(D.z.string()).default({}),on:D.z.array(fa).default(["failure"])}),ga=D.z.object({prComments:D.z.object({enabled:D.z.boolean().default(!1),provider:D.z.enum(["github","gitlab","azure-devops"]).default("github"),includeHealingSummary:D.z.boolean().default(!0),includeVisualDiffs:D.z.boolean().default(!1)}).default({}),qualityGate:D.z.object({enabled:D.z.boolean().default(!0),minPassRate:D.z.number().min(0).max(100).default(95),maxFlakyRate:D.z.number().min(0).max(100).default(10),maxDuration:D.z.number().min(0).default(0),requireTags:D.z.array(D.z.string()).default([])}).default({}),notifications:D.z.object({slack:hv.default({}),email:yv.default({}),teams:wv.default({}),customWebhook:bv.default({})}).default({})}),vv=D.z.object({name:D.z.string(),passed:D.z.boolean(),expected:D.z.string(),actual:D.z.string()}),pf=D.z.object({passed:D.z.boolean(),runId:D.z.string(),timestamp:D.z.string(),metrics:D.z.object({passRate:D.z.number(),totalTests:D.z.number(),passed:D.z.number(),failed:D.z.number(),skipped:D.z.number(),duration:D.z.number(),healingCount:D.z.number()}),gates:D.z.array(vv),reason:D.z.string().optional()})});function bf(r){let e=Math.floor(r/60),t=r%60;return e>0?`${e}m ${t}s`:`${t}s`}function Sv(r){let e=r.passed+r.failed+r.skipped,t=e>0?(r.passed/e*100).toFixed(1):"0",s=r.failed>0?":x: Failed":":white_check_mark: Passed",n=[];if(r.suiteResults)for(let a of r.suiteResults)for(let o of a.cases)o.status==="failed"&&n.push(o.caseName);let i=[{type:"header",text:{type:"plain_text",text:"Assuremind Test Results",emoji:!0}},{type:"section",fields:[{type:"mrkdwn",text:`*Status:*
|
|
1504
1504
|
${s}`},{type:"mrkdwn",text:`*Pass Rate:*
|
|
1505
1505
|
${t}% (${r.passed}/${e})`},{type:"mrkdwn",text:`*Failed:*
|
|
1506
1506
|
${r.failed}`},{type:"mrkdwn",text:`*Duration:*
|
|
1507
|
-
${
|
|
1507
|
+
${bf(r.duration)}`}]}];return n.length>0&&i.push({type:"section",text:{type:"mrkdwn",text:`*Failed Tests:*
|
|
1508
1508
|
${n.map(a=>`\u2022 ${a}`).join(`
|
|
1509
|
-
`)}`}}),i.push({type:"context",elements:[{type:"mrkdwn",text:":robot_face: Generated by Assuremind"}]}),{blocks:i}}function
|
|
1510
|
-
`),
|
|
1511
|
-
`),
|
|
1509
|
+
`)}`}}),i.push({type:"context",elements:[{type:"mrkdwn",text:":robot_face: Generated by Assuremind"}]}),{blocks:i}}function Ev(r){let e=r.passed+r.failed+r.skipped,t=e>0?(r.passed/e*100).toFixed(1):"0",s=r.failed>0?"attention":"good",n=[{title:"Pass Rate",value:`${t}% (${r.passed}/${e})`},{title:"Failed",value:`${r.failed}`},{title:"Skipped",value:`${r.skipped}`},{title:"Duration",value:bf(r.duration)}];return{type:"message",attachments:[{contentType:"application/vnd.microsoft.card.adaptive",content:{$schema:"http://adaptivecards.io/schemas/adaptive-card.json",type:"AdaptiveCard",version:"1.4",body:[{type:"TextBlock",text:"Assuremind Test Results",weight:"bolder",size:"medium",color:s},{type:"FactSet",facts:n}]}}]}}async function Iv(r,e){let t=Sv(e);yr.info("Sending Slack notification");let s=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(s.ok)yr.info("Slack notification sent");else{let n=await s.text();yr.error({status:s.status,text:n},"Failed to send Slack notification")}}async function Cv(r,e){let t=Ev(e);yr.info("Sending Teams notification");let s=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(s.ok)yr.info("Teams notification sent");else{let n=await s.text();yr.error({status:s.status,text:n},"Failed to send Teams notification")}}async function Pv(r,e,t){yr.info({url:r},"Sending custom webhook notification");let s=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",...e},body:JSON.stringify({source:"assuremind",runId:t.runId,passed:t.passed,failed:t.failed,skipped:t.skipped,duration:t.duration,timestamp:new Date().toISOString()})});if(s.ok)yr.info("Custom webhook notification sent");else{let n=await s.text();yr.error({status:s.status,text:n},"Failed to send custom webhook notification")}}async function vf(r,e){let t={runId:"test-notification",passed:10,failed:1,skipped:2,duration:45,suiteResults:[{suiteId:"demo",suiteName:"Demo Suite",cases:[{caseId:"c1",caseName:"Test notification",status:"passed"},{caseId:"c2",caseName:"Example failed test",status:"failed",error:"This is a test notification"}]}]};try{switch(r){case"slack":{let{webhookUrl:s}=e.notifications.slack;return s?(await Iv(s,t),{ok:!0}):{ok:!1,error:"Slack webhook URL not configured"}}case"teams":{let{webhookUrl:s}=e.notifications.teams;return s?(await Cv(s,t),{ok:!0}):{ok:!1,error:"Teams webhook URL not configured"}}case"custom":{let{url:s,headers:n}=e.notifications.customWebhook;return s?(await Pv(s,n,t),{ok:!0}):{ok:!1,error:"Custom webhook URL not configured"}}default:return{ok:!1,error:`Unsupported channel: ${r}`}}}catch(s){return{ok:!1,error:s instanceof Error?s.message:String(s)}}}var yr,xf=b(()=>{"use strict";g();j();yr=I("notifier")});async function Sf(r){let e=r.rootDir;r.get("/ci-integration/config",async(t,s)=>{try{let n=await ha(e);return s.send(n)}catch(n){return w(s,500,"Failed to read CI integration config",n)}}),r.put("/ci-integration/config",async(t,s)=>{try{let n=t.body;await ff(e,n);let i=await ha(e);return s.send(i)}catch(n){return w(s,500,"Failed to save CI integration config",n)}}),r.get("/ci-integration/quality-gates",async(t,s)=>{try{let n=await hf(e);return s.send(n)}catch(n){return w(s,500,"Failed to list quality gate results",n)}}),r.get("/ci-integration/quality-gates/:runId",async(t,s)=>{try{let n=await yf(e,t.params.runId);return n?s.send(n):s.status(404).send({error:"Quality gate result not found"})}catch(n){return w(s,500,"Failed to read quality gate result",n)}}),r.post("/ci-integration/test-notification",async(t,s)=>{let n=Rv.safeParse(t.body);if(!n.success)return s.status(400).send({error:"Invalid request",details:n.error.issues});try{let i=await ha(e),a=await vf(n.data.channel,i);return s.send(a)}catch(i){return w(s,500,"Failed to send test notification",i)}})}var Cc,Rv,Ef=b(()=>{"use strict";g();Cc=require("zod");wf();xf();de();Rv=Cc.z.object({channel:Cc.z.enum(["slack","teams","custom"])})});var kf={};ie(kf,{startServer:()=>kv});async function kv(r){let{port:e,rootDir:t}=r,s=(0,Cf.default)({logger:!1}),n=new jn;return s.decorate("rootDir",t),s.decorate("wsManager",n),await s.register(Pf.default,{origin:!0,methods:["GET","POST","PUT","PATCH","DELETE","OPTIONS"]}),await s.register(Rf.default),s.get("/ws",{websocket:!0},i=>{n.add(i)}),await s.register(cl,{prefix:"/api"}),await s.register(gl,{prefix:"/api"}),await s.register(xl,{prefix:"/api"}),await s.register(Pl,{prefix:"/api"}),await s.register(ad,{prefix:"/api"}),await s.register(gd,{prefix:"/api"}),await s.register(yd,{prefix:"/api"}),await s.register(_d,{prefix:"/api"}),await s.register(Xm,{prefix:"/api"}),await s.register(Zm,{prefix:"/api"}),await s.register(rg,{prefix:"/api"}),await s.register(lg,{prefix:"/api"}),await s.register(Sg,{prefix:"/api"}),await s.register(Tg,{prefix:"/api"}),await s.register(_g,{prefix:"/api"}),await s.register(Dg,{prefix:"/api"}),await s.register(jg,{prefix:"/api"}),await s.register(Gg,{prefix:"/api"}),await s.register(qg,{prefix:"/api"}),await s.register(Wg,{prefix:"/api"}),await s.register(tf,{prefix:"/api"}),await s.register(af,{prefix:"/api"}),await s.register(cf,{prefix:"/api"}),await s.register(uf,{prefix:"/api"}),await s.register(Sf,{prefix:"/api"}),await il(s,t),await s.listen({port:e,host:"127.0.0.1"}),If.info({port:e,rootDir:t},`Assuremind Studio listening on http://127.0.0.1:${e}`),{async close(){await s.close(),If.info("Server closed")}}}var Cf,Pf,Rf,If,Tf=b(()=>{"use strict";g();Cf=x(require("fastify")),Pf=x(require("@fastify/cors")),Rf=x(require("@fastify/websocket"));sl();nl();al();ll();fl();Sl();Rl();od();fd();wd();Od();Jm();eg();sg();ug();Eg();Ag();Og();Ng();Bg();zg();Kg();Xg();rf();of();lf();df();Ef();j();If=I("server")});var $f={};ie($f,{runStudio:()=>Tv});async function Tv(r={}){process.on("unhandledRejection",n=>{let i=n instanceof Error?n.message:String(n);se(`Unhandled rejection (non-fatal): ${i}`)}),process.stdout.write(`
|
|
1510
|
+
`),X(`Starting Assuremind Studio\u2026
|
|
1511
|
+
`),ar()||at(`AI provider not configured. The Studio will start, but AI features
|
|
1512
1512
|
(test generation, code generation, self-healing) require a valid
|
|
1513
1513
|
AI_PROVIDER and its API key in your .env file.
|
|
1514
1514
|
See .env.example for details.
|
|
1515
|
-
`);let t;try{let n=await
|
|
1515
|
+
`);let t;try{let n=await J(process.cwd());t=r.port??n.studioPort}catch(n){se(`Config error: ${n instanceof Error?n.message:String(n)}`),process.exit(1)}let s=jt("Starting server\u2026");s.start();try{let{startServer:n}=await Promise.resolve().then(()=>(Tf(),kf)).catch(()=>{throw new Re(`Server module is not available yet.
|
|
1516
1516
|
This is expected during development \u2014 Phase 5 implements the server.
|
|
1517
|
-
Build the full package with "npm run build" first.`,"SERVER_NOT_AVAILABLE")}),i=await n({port:t,rootDir:process.cwd()});s.succeed(`Server running at http://localhost:${t}`);let a=`http://localhost:${t}`;
|
|
1518
|
-
Studio stopped.`),process.exit(0)}).catch(c)}),process.on("SIGTERM",()=>{i.close().then(()=>process.exit(0)).catch(c)})})}catch(n){s.fail("Failed to start server");let i=n instanceof Error?n.message:String(n);i.includes("EADDRINUSE")?(
|
|
1517
|
+
Build the full package with "npm run build" first.`,"SERVER_NOT_AVAILABLE")}),i=await n({port:t,rootDir:process.cwd()});s.succeed(`Server running at http://localhost:${t}`);let a=`http://localhost:${t}`;nr(["\u{1F9EA} Assuremind Studio","",` URL: ${a}`,` Port: ${t}`,"","Press Ctrl+C to stop"]),r.noOpen||await(0,Af.default)(a),await new Promise((o,c)=>{process.on("SIGINT",()=>{i.close().then(()=>{X(`
|
|
1518
|
+
Studio stopped.`),process.exit(0)}).catch(c)}),process.on("SIGTERM",()=>{i.close().then(()=>process.exit(0)).catch(c)})})}catch(n){s.fail("Failed to start server");let i=n instanceof Error?n.message:String(n);i.includes("EADDRINUSE")?(se(`Port ${t} is already in use.`),X(`Fix: kill the process using the port, then try again:
|
|
1519
1519
|
npx kill-port ${t}
|
|
1520
1520
|
npx assuremind studio --port ${t}
|
|
1521
1521
|
|
|
1522
1522
|
Or use a different port:
|
|
1523
1523
|
npx assuremind studio --port ${t+1}
|
|
1524
|
-
`)):
|
|
1525
|
-
Filters can be combined: e.g. --type audit --tag regression`,"RUN_NO_SELECTOR");if(r.all&&e.length>1)throw new
|
|
1526
|
-
`),!e||r.status==="failed"){for(let n of r.steps)if(n.status==="failed"){if(process.stdout.write(` ${
|
|
1524
|
+
`)):se(i),process.exit(1)}}var Af,_f=b(()=>{"use strict";g();Af=x(require("open"));ir();Pr();Me();ge()});var Lf={};ie(Lf,{runRun:()=>Dv});function Av(r){let e={};return r.all&&(e.all=!0),r.type&&(e.type=r.type),r.suite&&(e.suite=r.suite),r.tag&&(e.tag=r.tag),r.test&&(e.test=r.test),r.browser&&(e.browsers=r.browser),r.device&&(e.device=r.device),r.env&&(e.env=r.env),r.parallel!==void 0&&(e.parallel=r.parallel),r.headed&&(e.headed=!0),r.ci&&(e.ci=!0),r.reporter&&(e.reporter=r.reporter),r.noHealing&&(e.noHealing=!0),e}function $v(r){let e=[r.all,r.type,r.suite,r.tag,r.test].filter(Boolean);if(e.length===0)throw new Re(`Specify what to run: --all, --type <type>, --suite <name>, --tag <tag>, or --test <name>
|
|
1525
|
+
Filters can be combined: e.g. --type audit --tag regression`,"RUN_NO_SELECTOR");if(r.all&&e.length>1)throw new Re("--all cannot be combined with other filters (--type, --suite, --tag, --test)","RUN_MULTIPLE_SELECTORS")}function Pc(r){if(r<1e3)return`${r}ms`;if(r<6e4)return`${(r/1e3).toFixed(1)}s`;let e=Math.floor(r/6e4),t=Math.round(r%6e4/1e3);return`${e}m ${t}s`}function Of(r){switch(r){case"passed":return L.success("\u2713");case"failed":return L.error("\u2717");case"skipped":return L.warn("\u2013");default:return L.dim("?")}}function _v(r,e){let t=Of(r.status),s=L.dim(`(${Pc(r.duration)})`);if(process.stdout.write(` ${t} ${r.caseName} ${s}
|
|
1526
|
+
`),!e||r.status==="failed"){for(let n of r.steps)if(n.status==="failed"){if(process.stdout.write(` ${L.error("\u2717")} ${L.dim(n.instruction)}
|
|
1527
1527
|
`),n.error){let i=n.error.split(`
|
|
1528
|
-
`)[0]?.slice(0,120)??"";process.stdout.write(` ${
|
|
1529
|
-
`)}n.healed&&process.stdout.write(` ${
|
|
1530
|
-
`)}}}function
|
|
1531
|
-
${t} ${
|
|
1532
|
-
`);for(let i of r.cases)
|
|
1533
|
-
`);for(let o of r.suites)
|
|
1534
|
-
`);try{
|
|
1535
|
-
`);let o=
|
|
1536
|
-
Make sure you have run "npm run build" or are running from source.`),process.exit(1)}let l=
|
|
1537
|
-
`);try{
|
|
1538
|
-
`);let n=
|
|
1539
|
-
Make sure you have run "npm run build" or are running from source.`),process.exit(1)}let a=
|
|
1540
|
-
|
|
1541
|
-
`)}catch(o){a.fail("Generation failed"),
|
|
1542
|
-
`);let e={passed:0,warnings:0,errors:0};await
|
|
1543
|
-
`),await
|
|
1544
|
-
`),await
|
|
1545
|
-
`),await
|
|
1546
|
-
`);let t=e.passed+e.warnings+e.errors,s=[e.passed>0?
|
|
1547
|
-
|
|
1548
|
-
`),e.errors>0&&process.exit(1)}var
|
|
1549
|
-
`),
|
|
1550
|
-
`);let r=await Promise.all([
|
|
1551
|
-
`),e.length===0?
|
|
1552
|
-
${
|
|
1553
|
-
${
|
|
1554
|
-
${
|
|
1555
|
-
|
|
1556
|
-
${
|
|
1557
|
-
${
|
|
1558
|
-
`)}async function
|
|
1559
|
-
${
|
|
1560
|
-
`),
|
|
1561
|
-
`)):o=(await
|
|
1562
|
-
|
|
1563
|
-
Auto-generated by Assuremind self-healing engine.`;await a.commit(u),
|
|
1528
|
+
`)[0]?.slice(0,120)??"";process.stdout.write(` ${L.dim(i)}
|
|
1529
|
+
`)}n.healed&&process.stdout.write(` ${L.warn("\u2695")} ${L.dim("Self-healed \u2014 review in Studio")}
|
|
1530
|
+
`)}}}function Ov(r,e){let t=Of(r.status),s=L.dim(`(${Pc(r.duration)})`),n=L.dim(`[${r.browser}]`);process.stdout.write(`
|
|
1531
|
+
${t} ${L.bold(r.suiteName)} ${n} ${s}
|
|
1532
|
+
`);for(let i of r.cases)_v(i,e)}function Lv(r,e){let t=Pc(r.duration),s=L.success(`${r.passed} passed`),n=r.failed>0?L.error(`${r.failed} failed`):L.dim("0 failed"),i=r.skipped>0?L.warn(`${r.skipped} skipped`):L.dim("0 skipped");if(!e){process.stdout.write(`
|
|
1533
|
+
`);for(let o of r.suites)Ov(o,e)}let a=[r.status==="passed"?"\u2705 All tests passed!":"\u274C Some tests failed","",` ${s} ${n} ${i}`,` Duration: ${t}`,` Run ID: ${r.runId}`,"",` Results saved to: results/runs/${r.runId}.json`];nr(a)}async function Dv(r){process.stdout.write(`
|
|
1534
|
+
`);try{$v(r)}catch(d){se(d instanceof Error?d.message:String(d)),process.exit(1)}try{Cr()}catch(d){se(d instanceof Error?d.message:String(d)),process.exit(1)}let e=process.cwd(),t;try{t=await J(e)}catch(d){se(d instanceof Error?d.message:String(d)),process.exit(1)}let s=Av(r);s.browsers&&(t.browsers=s.browsers),s.parallel&&(t.parallel=s.parallel),s.headed&&(t.headless=!1),s.noHealing&&(t.healing.enabled=!1),s.device&&(t.device=s.device),r.ci&&(t.healing.healAssertionLocators=!1);let n=[];r.all&&n.push("all suites"),r.type&&n.push(`type "${r.type}"`),r.suite&&n.push(`suite "${r.suite}"`),r.tag&&n.push(`tag "${r.tag}"`),r.test&&n.push(`test "${r.test}"`);let i=n.join(" + ")||"all suites",a=t.device?` | device: ${t.device}`:"";X(`Running ${i} on [${t.browsers.join(", ")}]${a}`),r.ci||X(`Healing: ${t.healing.enabled?"enabled":"disabled"}`),process.stdout.write(`
|
|
1535
|
+
`);let o=jt("Loading test runner\u2026");o.start();let c;try{c=(await Promise.resolve().then(()=>(sc(),Wm))).runTests,o.succeed("Runner loaded")}catch{o.fail("Failed to load test runner"),se(`Test runner could not be loaded.
|
|
1536
|
+
Make sure you have run "npm run build" or are running from source.`),process.exit(1)}let l=jt("Running tests\u2026");r.ci||l.start();let u;try{u=await c(e,s,t)}catch(d){r.ci||l.fail("Run failed"),se(d instanceof Error?d.message:String(d)),process.exit(1)}r.ci||(u.status==="passed"?l.succeed("Run complete"):l.fail("Run complete with failures")),Lv(u,r.ci??!1),u.failed>0&&(at(`${u.failed} test(s) failed. Check the report above for details.`),t.healing.enabled&&X('Self-healing events (if any) saved \u2014 run "npx assuremind apply-healing" to review.')),process.exit(u.status==="passed"?0:1)}var Df=b(()=>{"use strict";g();ir();Pr();Me();ge()});var Nf={};ie(Nf,{runGenerate:()=>Fv});async function Nv(r){if(r.story)return r.story;if(r.storyFile){let e=Js.default.resolve(r.storyFile);if(!await Rc.default.pathExists(e))throw new Re(`Story file not found: "${e}"`,"STORY_FILE_NOT_FOUND");return Rc.default.readFile(e,"utf8")}throw new Re('Provide a story with --story "<text>" or --story-file <path>',"GENERATE_NO_STORY")}async function Fv(r){process.stdout.write(`
|
|
1537
|
+
`);try{Cr()}catch(o){se(o instanceof Error?o.message:String(o)),process.exit(1)}let e;try{e=await Nv(r)}catch(o){se(o instanceof Error?o.message:String(o)),process.exit(1)}let t=process.cwd(),s=r.output?Js.default.resolve(r.output):Js.default.join(t,"tests");X(`Story: ${L.dim(`${e.slice(0,80)}${e.length>80?"\u2026":""}`)}`),r.suite&&X(`Suite name: ${r.suite}`),X(`Output: ${s}`),process.stdout.write(`
|
|
1538
|
+
`);let n=jt("Loading AI layer\u2026");n.start();let i;try{i=(await Promise.resolve().then(()=>($r(),sd))).generateSuiteFromStory,n.succeed("AI layer ready")}catch{n.fail("Failed to load AI layer"),se(`AI layer could not be loaded.
|
|
1539
|
+
Make sure you have run "npm run build" or are running from source.`),process.exit(1)}let a=jt(`Generating test suite${r.suite?` "${r.suite}"`:""}\u2026`);a.start();try{await i(e,{rootDir:t,outputDir:s,suiteName:r.suite}),a.succeed("Suite generated");let o=r.output??Js.default.join(t,"tests");Pe(`Test suite created in: ${Js.default.relative(t,o)}`),X("Open Studio or use the Test Editor to review and generate Playwright code:"),process.stdout.write(` ${L.accent("npx assuremind studio")}
|
|
1540
|
+
|
|
1541
|
+
`)}catch(o){a.fail("Generation failed"),se(o instanceof Error?o.message:String(o)),process.exit(1)}}var Js,Rc,Ff=b(()=>{"use strict";g();Js=x(require("path")),Rc=x(require("fs-extra"));ir();Pr();ge()});var Mf={};ie(Mf,{runValidate:()=>Hv});async function Mv(r,e){if(X("Checking configuration\u2026"),!await Tn.default.pathExists(Ys.default.join(r,"autotest.config.json"))&&!await Tn.default.pathExists(Ys.default.join(r,"autotest.config.ts"))){se(' autotest.config.ts not found \u2014 run "npx assuremind init" to create it'),e.errors++;return}try{let t=await J(r);Pe(` Config valid \u2014 baseUrl: ${L.accent(t.baseUrl)}, browsers: ${t.browsers.join(", ")}`),e.passed++}catch(t){se(` Config invalid: ${t instanceof Error?t.message:String(t)}`),e.errors++}}async function jv(r){if(X("Checking environment\u2026"),!await Tn.default.pathExists(".env")){at(" .env file not found \u2014 copy .env.example to .env and fill in your API key"),r.warnings++;return}let e=ar();if(!e){se(" .env validation failed \u2014 check required variables for your AI provider"),r.errors++;return}Pe(` Provider: ${L.accent(String(e.AI_PROVIDER))}, tiered: ${e.AI_TIERED_ENABLED}`),r.passed++}async function Bv(r,e){X("Scanning test files\u2026");let t=Ys.default.join(r,"tests");if(!await Tn.default.pathExists(t)){at(" tests/ directory not found \u2014 no tests to validate"),e.warnings++;return}let s=await Gt(t);if(s.length===0){at(" No test suites found \u2014 create one in Studio"),e.warnings++;return}let n=0,i=0;for(let o of s){let c=await or(o);n+=c.length;for(let l of c)try{await fe(l)}catch{se(` Invalid: ${Ys.default.relative(r,l)}`),i++,e.errors++}}let a=n-i;i===0?(Pe(` ${s.length} suite(s), ${n} test case(s) \u2014 all valid`),e.passed++):(at(` ${s.length} suite(s), ${a}/${n} cases valid`),e.warnings++)}async function Uv(r,e){X("Checking variable files\u2026");let t=await jl(r);if(t.length===0){at(" No variable files found"),e.warnings++;return}let s=0;for(let n of t)try{await kr(n)}catch{se(` Invalid: ${Ys.default.relative(r,n)}`),s++,e.errors++}s===0?(Pe(` ${t.length} variable file(s) \u2014 all valid`),e.passed++):e.warnings++}async function Hv(){let r=process.cwd();process.stdout.write(`
|
|
1542
|
+
`);let e={passed:0,warnings:0,errors:0};await Mv(r,e),process.stdout.write(`
|
|
1543
|
+
`),await jv(e),process.stdout.write(`
|
|
1544
|
+
`),await Bv(r,e),process.stdout.write(`
|
|
1545
|
+
`),await Uv(r,e),process.stdout.write(`
|
|
1546
|
+
`);let t=e.passed+e.warnings+e.errors,s=[e.passed>0?L.success(`${e.passed} passed`):null,e.warnings>0?L.warn(`${e.warnings} warnings`):null,e.errors>0?L.error(`${e.errors} errors`):null].filter(Boolean).join(L.dim(" | "));process.stdout.write(`Validation complete (${t} checks): ${s}
|
|
1547
|
+
|
|
1548
|
+
`),e.errors>0&&process.exit(1)}var Ys,Tn,jf=b(()=>{"use strict";g();Ys=x(require("path")),Tn=x(require("fs-extra"));ir();Me();et();Rt();Tr();Pr()});var Bf={};ie(Bf,{runDoctor:()=>Yv});function Ie(r,e,t){return{label:r,ok:e,detail:t}}function Tc(r){try{return(0,kc.execSync)(r,{stdio:"pipe"}).toString().trim()}catch{return""}}async function Vv(){let r=process.version;return parseInt(r.slice(1).split(".")[0],10)>=18?Ie("Node.js",!0,`${r} (>= 18 required)`):Ie("Node.js",!1,`${r} is too old \u2014 upgrade to Node.js >= 18 from https://nodejs.org`)}async function Gv(){let r=Tc("npm --version");return r?Ie("npm",!0,`v${r}`):Ie("npm",!1,"npm not found \u2014 ensure Node.js is installed correctly")}async function zv(){let r=Tc("npx playwright --version");if(!r)return Ie("Playwright",!1,'Not found \u2014 run "npx assuremind init" to install');try{return(0,kc.execSync)("npx playwright install --dry-run 2>&1",{stdio:"pipe"}),Ie("Playwright",!0,`${r} \u2014 browsers installed`)}catch{return Ie("Playwright",!1,`${r} installed but browsers may be missing \u2014 run "npx playwright install --with-deps"`)}}async function qv(){if(!await ya.default.pathExists(".env"))return Ie(".env file",!1,"Missing \u2014 copy .env.example to .env and fill in your API key");let r=ar();return r?Ie(".env file",!0,`Provider: ${String(r.AI_PROVIDER)}, tiered: ${r.AI_TIERED_ENABLED}`):Ie(".env file",!1,"Present but invalid \u2014 check required variables for your AI provider")}async function Kv(){try{let r=await J(process.cwd());return Ie("Config",!0,`baseUrl: ${r.baseUrl}, port: ${r.studioPort}`)}catch(r){return Ie("Config",!1,r instanceof Error?r.message:"Config validation failed")}}async function Wv(){if(!await ya.default.pathExists("package.json"))return Ie("package.json",!1,'Not found \u2014 run "npm init -y" first');try{let r=await ya.default.readJson("package.json");return Ie("package.json",!0,`${r.name??"unnamed"} v${r.version??"?"}`)}catch{return Ie("package.json",!1,"Exists but is not valid JSON")}}async function Xv(){let r=ar();if(!r)return Ie("AI connectivity",!1,"Cannot check \u2014 .env not configured");let e=String(r.AI_PROVIDER),s={anthropic:"https://api.anthropic.com",openai:"https://api.openai.com",google:"https://generativelanguage.googleapis.com",groq:"https://api.groq.com",deepseek:"https://api.deepseek.com",together:"https://api.together.xyz",perplexity:"https://api.perplexity.ai"}[e];if(!s)return Ie("AI connectivity",!0,`${e} \u2014 skipped (local/custom provider)`);try{let n=Tc(`curl -s -o /dev/null -w "%{http_code}" --max-time 5 "${s}"`),i=parseInt(n,10);return i>0?Ie("AI connectivity",!0,`${e} endpoint reachable (HTTP ${i})`):Ie("AI connectivity",!1,`${e} endpoint unreachable \u2014 check your network or VPN`)}catch{return Ie("AI connectivity",!1,`Could not reach ${e} endpoint`)}}function Jv(r){r.ok?Pe(` ${L.bold(r.label)}: ${L.dim(r.detail)}`):se(` ${L.bold(r.label)}: ${r.detail}`)}async function Yv(){process.stdout.write(`
|
|
1549
|
+
`),X(`Running Assuremind system diagnostics\u2026
|
|
1550
|
+
`);let r=await Promise.all([Vv(),Gv(),Wv(),zv(),qv(),Kv(),Xv()]);for(let s of r)Jv(s);let e=r.filter(s=>!s.ok),t=r.filter(s=>s.ok);process.stdout.write(`
|
|
1551
|
+
`),e.length===0?nr(["\u2705 All checks passed \u2014 Assuremind is ready!","","Run: npx assuremind studio"]):(nr([`\u26A0 ${t.length}/${r.length} checks passed`,"",'Fix the issues above, then run "npx assuremind doctor" again.']),process.exit(1))}var kc,ya,Uf=b(()=>{"use strict";g();kc=require("child_process"),ya=x(require("fs-extra"));ir();Pr();Me()});var Gf={};ie(Gf,{runApplyHealing:()=>ax});async function Qv(r,e){let t=await Gt(r);for(let s of t){let n=await or(s);for(let i of n)try{if((await fe(i)).id===e)return i}catch{}}return null}function ex(r){if(!r.startsWith("hook_"))return null;let e=r.slice(5);for(let t of Zv)if(e.startsWith(`${t}_`))return{hookType:t,suiteId:e.slice(t.length+1)};return null}async function tx(r,e){let t=os.default.join(r,"tests"),s=ex(e.caseId);if(s){let l=await we(t,s.suiteId);if(!l)throw new Error(`Suite "${s.suiteId}" not found for ${s.hookType} hook heal.`);let u=await ut(l);if(!u[s.hookType].some(d=>d.id===e.stepId))throw new Error(`Hook step "${e.stepId}" not found in ${s.hookType} of suite "${s.suiteId}".`);u[s.hookType]=u[s.hookType].map(d=>d.id===e.stepId?{...d,generatedCode:e.healedCode,lastHealed:new Date().toISOString()}:d),await zt(l,u),Pe(` Updated: ${s.hookType} hook in suite "${s.suiteId}"`);return}let n=await Qv(t,e.caseId);if(!n)throw new Error(`Test case "${e.caseId}" not found in ${t}. The file may have been deleted or renamed since the heal was recorded.`);let i=await fe(n),a=i.steps.findIndex(l=>l.id===e.stepId);if(a!==-1){i.steps[a]={...i.steps[a],generatedCode:e.healedCode,lastHealed:new Date().toISOString()},i.updatedAt=new Date().toISOString(),await ws(n,i),Pe(` Updated: ${os.default.relative(r,n)} \u203A step "${i.steps[a].instruction}"`);return}let o=i.caseHooks?.before?.some(l=>l.id===e.stepId)??!1,c=i.caseHooks?.after?.some(l=>l.id===e.stepId)??!1;if(o||c){i.caseHooks={before:(i.caseHooks?.before??[]).map(l=>l.id===e.stepId?{...l,generatedCode:e.healedCode}:l),after:(i.caseHooks?.after??[]).map(l=>l.id===e.stepId?{...l,generatedCode:e.healedCode}:l)},i.updatedAt=new Date().toISOString(),await ws(n,i),Pe(` Updated: ${os.default.relative(r,n)} \u203A ${o?"before":"after"} hook`);return}throw new Error(`Step "${e.stepId}" not found in "${i.name}". The step may have been deleted since the heal was recorded.`)}function rx(r,e){return new Promise(t=>r.question(e,t))}function sx(r){process.stdout.write(`
|
|
1552
|
+
${L.bold("Step:")} ${r.stepInstruction}
|
|
1553
|
+
${L.bold("Case:")} ${r.caseId}
|
|
1554
|
+
${L.dim("Error:")} ${r.error}
|
|
1555
|
+
|
|
1556
|
+
${L.error("- "+r.failedCode)}
|
|
1557
|
+
${L.success("+ "+r.healedCode)}
|
|
1558
|
+
`)}async function nx(r,e,t){let s={accepted:0,rejected:0,skipped:0},n=Hf.default.createInterface({input:process.stdin,output:process.stdout});try{for(let i=0;i<e.length;i++){let a=e[i];process.stdout.write(`
|
|
1559
|
+
${L.accent(`[${i+1}/${e.length}]`)} Healing event ${L.dim(a.id)}
|
|
1560
|
+
`),sx(a);let o;if(t?(o="y",process.stdout.write(` Accept? ${L.dim("(auto-accepting all)")} y
|
|
1561
|
+
`)):o=(await rx(n,` Accept? ${L.dim("(y)es / (n)o / (a)ll / (q)uit")} > `)).trim().toLowerCase(),o==="q"||o==="quit"){at(` Stopping \u2014 ${e.length-i} heals remaining`),s.skipped+=e.length-i;break}if((o==="a"||o==="all")&&(t=!0,o="y"),o==="y"||o==="yes")try{await tx(r,a),await pn(r,a.id),s.accepted++}catch(c){se(` Failed to apply: ${c instanceof Error?c.message:String(c)}`),s.skipped++}else await ui(r,a.id),X(" Rejected \u2014 step will keep original code"),s.rejected++}}finally{n.close()}return s}async function ix(r,e){let t=process.env.GITHUB_TOKEN,s=process.env.GITHUB_REPO_OWNER,n=process.env.GITHUB_REPO_NAME,i=process.env.GITHUB_BASE_BRANCH||"main";if(!t||!s||!n){at("Auto PR skipped \u2014 missing GITHUB_TOKEN, GITHUB_REPO_OWNER, or GITHUB_REPO_NAME in .env");return}let a=new zs(r),c=`fix/self-heal-${Date.now()}`;try{X("Creating healing branch...");let l=a.git;await l.checkoutLocalBranch(c),X("Committing healed code...");let u=`fix: apply ${e} self-healed selector(s)
|
|
1562
|
+
|
|
1563
|
+
Auto-generated by Assuremind self-healing engine.`;await a.commit(u),X("Pushing branch to remote..."),await l.push("origin",c,["--set-upstream"]),X("Creating pull request...");let d=await fetch(`https://api.github.com/repos/${s}/${n}/pulls`,{method:"POST",headers:{Authorization:`Bearer ${t}`,Accept:"application/vnd.github+json","Content-Type":"application/json","X-GitHub-Api-Version":"2022-11-28"},body:JSON.stringify({title:`fix: self-healed ${e} broken selector(s)`,body:`## Self-Healing Auto PR
|
|
1564
1564
|
|
|
1565
1565
|
Assuremind detected and fixed **${e}** broken test selector(s) during CI execution.
|
|
1566
1566
|
|
|
@@ -1572,12 +1572,12 @@ Assuremind detected and fixed **${e}** broken test selector(s) during CI executi
|
|
|
1572
1572
|
Review the changes and merge if the fixes look correct.
|
|
1573
1573
|
|
|
1574
1574
|
---
|
|
1575
|
-
\u{1F916} *Auto-generated by Assuremind*`,head:c,base:i})});if(d.ok){let
|
|
1576
|
-
`);let t;if(r.from){let n=
|
|
1577
|
-
`);let s=await
|
|
1578
|
-
`),
|
|
1579
|
-
`),s.accepted>0)try{(await
|
|
1580
|
-
`),await
|
|
1575
|
+
\u{1F916} *Auto-generated by Assuremind*`,head:c,base:i})});if(d.ok){let m=await d.json();Pe(`Pull request created: ${m.html_url}`)}else{let m=await d.json();se(`Failed to create PR: ${m.message||d.statusText}`)}await a.checkout(i)}catch(l){let u=l instanceof Error?l.message:String(l);se(`Auto PR failed: ${u}`);try{await a.checkout(i)}catch{}}}async function ax(r){let e=process.cwd();(0,Vf.config)({path:os.default.join(e,".env")}),process.stdout.write(`
|
|
1576
|
+
`);let t;if(r.from){let n=os.default.resolve(r.from);await Ac.default.pathExists(n)||(se(`Healing report not found: ${n}`),process.exit(1));try{let i=os.default.basename(n,".json").replace("healing-report-","");t=(await li(e,i)).events.filter(o=>o.status==="pending")}catch{let i=await Ac.default.readJson(n);t=Array.isArray(i)?i.filter(a=>a.status==="pending"):(i.events??[]).filter(a=>a.status==="pending")}}else t=(await yt(e)).filter(n=>n.status==="pending");if(t.length===0){X("No pending healing events found \u2014 nothing to apply.");return}X(`Found ${L.bold(String(t.length))} pending healing event(s).
|
|
1577
|
+
`);let s=await nx(e,t,r.yes??!1);if(process.stdout.write(`
|
|
1578
|
+
`),X(`Done: ${L.success(String(s.accepted)+" accepted")}, ${L.error(String(s.rejected)+" rejected")}, ${L.dim(String(s.skipped)+" skipped")}`),process.stdout.write(`
|
|
1579
|
+
`),s.accepted>0)try{(await J(e)).healing.autoPR&&(X(`Auto PR enabled \u2014 creating pull request with healed code...
|
|
1580
|
+
`),await ix(e,s.accepted))}catch{}}var os,Ac,Hf,Vf,Zv,zf=b(()=>{"use strict";g();os=x(require("path")),Ac=x(require("fs-extra")),Hf=x(require("readline")),Vf=require("dotenv");di();et();Rt();dn();Me();gc();ir();Zv=["before_all","before_each","after_each","after_all"]});g();var Kf=require("commander"),Wf=require("module");ir();var ox=(0,Wf.createRequire)(__filename),qf=ox("../../package.json"),wr=new Kf.Command;wr.name("assuremind").description(qf.description).version(qf.version,"-v, --version","Print version number");wr.command("init").description("Initialise Assuremind in the current project").option("--skip-playwright","Skip Playwright browser installation").action(async r=>{let{runInit:e}=await Promise.resolve().then(()=>(Jc(),Xc));await e({skipPlaywright:r.skipPlaywright}).catch(Bt)});wr.command("studio").description("Open the Assuremind web UI").option("-p, --port <number>","Override studio port",parseInt).option("--no-open","Do not open browser automatically").action(async r=>{let{runStudio:e}=await Promise.resolve().then(()=>(_f(),$f));await e({port:r.port,noOpen:r.open===!1}).catch(Bt)});wr.command("run").description("Run tests from the command line").option("--all","Run all test suites").option("--type <type>","Filter suites by type: ui, api, audit, performance").option("--suite <name>","Run suite(s) matching name (case-insensitive partial match) or ID").option("--tag <tag>","Run all tests matching a tag").option("--test <name>","Run test(s) matching name (case-insensitive partial match) or ID").option("--browser <list...>","Browser(s) to use: chromium, firefox, webkit").option("--device <name>",'Emulate a device (e.g. "iPhone 15 Pro", "Pixel 7", "iPad Pro 11")').option("--env <name>","Environment to use (dev, staging, prod)").option("--parallel <n>","Number of parallel workers",parseInt).option("--headed","Run in headed (visible) mode").option("--ci","CI mode \u2014 no interactive prompts, exit code reflects pass/fail").option("--reporter <list...>","Reporter(s): allure, html, json").option("--no-healing","Disable self-healing for this run").addHelpText("after",`
|
|
1581
1581
|
Examples:
|
|
1582
1582
|
assuremind run --all
|
|
1583
1583
|
assuremind run --type ui
|
|
@@ -1595,7 +1595,7 @@ Examples:
|
|
|
1595
1595
|
|
|
1596
1596
|
Filters are combinable (AND logic \u2014 all specified filters must match).
|
|
1597
1597
|
Device names must match Playwright device descriptors exactly (case-sensitive).
|
|
1598
|
-
`).action(async r=>{let{runRun:e}=await Promise.resolve().then(()=>(
|
|
1598
|
+
`).action(async r=>{let{runRun:e}=await Promise.resolve().then(()=>(Df(),Lf));await e({all:r.all,type:r.type,suite:r.suite,tag:r.tag,test:r.test,browser:r.browser,device:r.device,env:r.env,parallel:r.parallel,headed:r.headed,ci:r.ci,reporter:r.reporter,noHealing:r.noHealing}).catch(Bt)});wr.command("generate").description("Generate test cases from a user story").option("--story <text>","User story text (plain English)").option("--story-file <path>","Path to a file containing the user story").option("--suite <name>","Name of the suite to create").option("--output <path>","Output directory (default: tests/)").action(async r=>{let{runGenerate:e}=await Promise.resolve().then(()=>(Ff(),Nf));await e(r).catch(Bt)});wr.command("validate").description("Validate config, environment, and test files").action(async()=>{let{runValidate:r}=await Promise.resolve().then(()=>(jf(),Mf));await r().catch(Bt)});wr.command("doctor").description("Check system requirements and configuration health").action(async()=>{let{runDoctor:r}=await Promise.resolve().then(()=>(Uf(),Bf));await r().catch(Bt)});wr.command("apply-healing").description("Review and apply self-healing suggestions to test files").option("--from <path>","Path to a specific healing report JSON file").option("-y, --yes","Accept all pending heals without prompting").action(async r=>{let{runApplyHealing:e}=await Promise.resolve().then(()=>(zf(),Gf));await e(r).catch(Bt)});wr.parse(process.argv);
|
|
1599
1599
|
/*! Bundled license information:
|
|
1600
1600
|
|
|
1601
1601
|
lighthouse-logger/index.js:
|