@zibby/core 0.1.33 → 0.1.36
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/dist/agents/base.js +286 -14
- package/dist/backend-client.js +1 -1
- package/dist/constants/tool-names.js +1 -1
- package/dist/constants/zibby-scratch.js +1 -1
- package/dist/constants.js +1 -1
- package/dist/enrichment/base.js +1 -1
- package/dist/enrichment/enrichers/accessibility-enricher.js +1 -1
- package/dist/enrichment/enrichers/dom-enricher.js +1 -1
- package/dist/enrichment/enrichers/page-state-enricher.js +1 -1
- package/dist/enrichment/enrichers/position-enricher.js +1 -1
- package/dist/enrichment/index.js +4 -1
- package/dist/enrichment/mcp-integration.js +4 -1
- package/dist/enrichment/mcp-ref-enricher.js +1 -1
- package/dist/enrichment/pipeline.js +2 -2
- package/dist/enrichment/trace-text-enricher.js +2 -1
- package/dist/framework/agents/assistant-strategy.js +69 -5
- package/dist/framework/agents/base.js +1 -1
- package/dist/framework/agents/claude-strategy.js +106 -4
- package/dist/framework/agents/codex-strategy.js +23 -4
- package/dist/framework/agents/cursor-strategy.js +106 -20
- package/dist/framework/agents/gemini-strategy.js +34 -7
- package/dist/framework/agents/index.js +252 -6
- package/dist/framework/agents/middleware/assistant-round-pipeline.js +2 -2
- package/dist/framework/agents/providers/base.js +1 -1
- package/dist/framework/agents/providers/index.js +4 -1
- package/dist/framework/agents/providers/openai-transport.js +4 -2
- package/dist/framework/agents/providers/openai.js +1 -1
- package/dist/framework/agents/providers/transport-base.js +1 -1
- package/dist/framework/agents/utils/auth-resolver.js +1 -1
- package/dist/framework/agents/utils/cursor-output-formatter.js +25 -1
- package/dist/framework/agents/utils/openai-proxy-formatter.js +43 -5
- package/dist/framework/agents/utils/payload-budget.js +2 -2
- package/dist/framework/agents/utils/structured-output-formatter.js +8 -4
- package/dist/framework/code-generator.js +276 -10
- package/dist/framework/constants.js +1 -1
- package/dist/framework/context-loader.js +2 -2
- package/dist/framework/function-bridge.js +60 -1
- package/dist/framework/function-skill-registry.js +1 -1
- package/dist/framework/graph-compiler.js +281 -1
- package/dist/framework/graph.js +272 -4
- package/dist/framework/index.js +298 -1
- package/dist/framework/mcp-client.js +56 -2
- package/dist/framework/node-registry.js +262 -4
- package/dist/framework/node.js +264 -4
- package/dist/framework/output-parser.js +2 -2
- package/dist/framework/skill-registry.js +1 -1
- package/dist/framework/state-utils.js +5 -1
- package/dist/framework/state.js +1 -1
- package/dist/framework/tool-resolver.js +1 -1
- package/dist/index.js +479 -5
- package/dist/package.json +1 -1
- package/dist/runtime/generation/base.js +1 -1
- package/dist/runtime/generation/index.js +58 -3
- package/dist/runtime/generation/mcp-ref-strategy.js +17 -17
- package/dist/runtime/generation/stable-id-strategy.js +14 -14
- package/dist/runtime/stable-id-runtime.js +1 -1
- package/dist/runtime/verification/base.js +1 -1
- package/dist/runtime/verification/index.js +3 -3
- package/dist/runtime/verification/playwright-json-strategy.js +1 -1
- package/dist/runtime/zibby-runtime.js +1 -1
- package/dist/sync/index.js +1 -1
- package/dist/sync/uploader.js +1 -1
- package/dist/tools/run-playwright-test.js +3 -3
- package/dist/utils/adf-converter.js +1 -1
- package/dist/utils/ast-utils.js +9 -1
- package/dist/utils/ci-setup.js +4 -4
- package/dist/utils/cursor-mcp-isolated-home.js +1 -1
- package/dist/utils/cursor-utils.js +1 -1
- package/dist/utils/live-frame-discovery.js +1 -1
- package/dist/utils/logger.js +1 -1
- package/dist/utils/mcp-config-writer.js +4 -4
- package/dist/utils/mission-control-from-run-states.js +1 -1
- package/dist/utils/node-schema-parser.js +9 -1
- package/dist/utils/parallel-config.js +1 -1
- package/dist/utils/post-process-events.js +2 -1
- package/dist/utils/repo-clone.js +1 -0
- package/dist/utils/result-handler.js +1 -1
- package/dist/utils/ripple-effect.js +1 -1
- package/dist/utils/run-capacity-coordinator.js +3 -1
- package/dist/utils/run-capacity-queue.js +2 -2
- package/dist/utils/run-index-merge.js +1 -1
- package/dist/utils/run-index-post-cli.js +4 -1
- package/dist/utils/run-registry.js +3 -3
- package/dist/utils/run-state-session.js +2 -2
- package/dist/utils/selector-generator.js +4 -4
- package/dist/utils/session-state-constants.js +1 -1
- package/dist/utils/session-state-live-runs.js +1 -1
- package/dist/utils/streaming-parser.js +3 -3
- package/dist/utils/test-post-processor.js +14 -11
- package/dist/utils/timeline.js +6 -6
- package/dist/utils/trace-parser.js +2 -2
- package/dist/utils/video-organizer.js +3 -3
- package/package.json +1 -1
- package/templates/browser-test-automation/result-handler.mjs +4 -39
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
2
|
-
${r.slice(1).map(
|
|
3
|
-
`)};`}static generateRoleSelector(e){
|
|
4
|
-
${
|
|
1
|
+
var a=class{static generate(e,n="element"){let{selectors:t}=e;if(!t||typeof t!="object")return this.generateFallbackSelector(e,n);let r=[];return t.role&&r.push(this.generateRoleSelector(t.role)),t.attributes&&r.push(this.generateAttributeSelector(t.attributes)),t.partialMatch&&r.push(this.generatePartialMatchSelector(t.partialMatch)),t.structure&&r.push(this.generateStructuralSelector(t.structure)),r.length===0?this.generateFallbackSelector(e,n):r.length===1?`const ${n} = ${r[0]};`:`const ${n} = ${r[0]}
|
|
2
|
+
${r.slice(1).map(i=>` .or(${i})`).join(`
|
|
3
|
+
`)};`}static generateRoleSelector(e){let{role:n,name:t}=e;if(!n)return null;if(t){let r=this.escapeRegex(t);return`page.getByRole('${n}', { name: /${r}/i })`}return`page.getByRole('${n}')`}static generateAttributeSelector(e){if(!e||typeof e!="object")return null;let n=Object.entries(e).filter(([r,c])=>c!=null).map(([r,c])=>r==="placeholder"||r==="aria-label"?r==="placeholder"?`page.getByPlaceholder('${this.escapeString(c)}')`:`page.locator('[${r}="${this.escapeString(c)}"]')`:`[${r}="${this.escapeString(c)}"]`);if(e.placeholder)return`page.getByPlaceholder('${this.escapeString(e.placeholder)}')`;let t=n.filter(r=>!r.startsWith("page.")).join("");return t?`page.locator('${t}')`:null}static generatePartialMatchSelector(e){if(!e||typeof e!="object")return null;let n=Object.entries(e).filter(([t,r])=>r!==void 0).map(([t,r])=>{let c=r.replace(/^\^/,"");return`[${t}^="${this.escapeString(c)}"]`});return n.length>0?`page.locator('${n.join("")}')`:null}static generateStructuralSelector(e){return!e||typeof e!="string"?null:`page.locator('${this.escapeString(e)}')`}static generateFallbackSelector(e,n){let{description:t,type:r}=e;if(r==="fill"||r==="type")return`const ${n} = page.locator('input').first();`;if(r==="click"){if(t.toLowerCase().includes("button"))return`const ${n} = page.locator('button').first();`;if(t.toLowerCase().includes("link"))return`const ${n} = page.locator('a').first();`}return`const ${n} = page.locator('body');`}static escapeString(e){return typeof e!="string"?String(e):e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/"/g,'\\"')}static escapeRegex(e){return typeof e!="string"?String(e):e.replace(/[.*+?^${}()[\]\\]/g,"\\$&")}static generateActionCode(e,n){let{type:t,value:r,description:c,selectors:i}=e,o=`element${n}`,s=this.generate(e,o),l=this.generateActionMethod(t,o,r);return`${s}
|
|
4
|
+
${l}`}static generateActionMethod(e,n,t){switch(e){case"fill":case"type":return`await ${n}.fill('${this.escapeString(t||"")}');`;case"click":return`await ${n}.click();`;case"navigate":return`await page.goto('${this.escapeString(t||"")}');`;case"wait":return`await page.waitForTimeout(${parseInt(t)||2e3});`;default:return`// Unknown action type: ${e}`}}static generateAssertionCode(e,n){let{description:t,expected:r,actual:c,passed:i}=e;return t.toLowerCase().includes("url")?`await expect(page).toHaveURL(/${this.escapeRegex(c)}/);`:t.toLowerCase().includes("visible")?"await expect(page.locator('body')).toBeVisible();":`// ${t}`}},g=a;export{a as SelectorGenerator,g as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
var _=12e4,S=72e4,T=72e4,E=72e4;export{E as SESSION_STATE_DEFAULT_LIST_MAX_STALE_MS,S as SESSION_STATE_DEFAULT_STALE_NO_UPDATED_AT_MS,T as SESSION_STATE_DEFAULT_STALE_RUNNING_NO_PID_MS,_ as SESSION_STATE_DEFAULT_STUDIO_NO_PID_MAX_AGE_MS};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function d(n){
|
|
1
|
+
function d(n){let i=[],s={};for(let e of n||[]){let t=String(e.studioTestCaseId!=null&&String(e.studioTestCaseId).trim()!==""?e.studioTestCaseId:e.sessionId||"").trim();if(!t)continue;i.push(t);let o=Number(e.updatedAt)||0;s[t]={ts:o,activeStageIndex:typeof e.activeStageIndex=="number"&&Number.isFinite(e.activeStageIndex)?e.activeStageIndex:null,activeNode:e.activeNode!=null?String(e.activeNode):null}}return{liveIdList:i,progressByKey:s}}export{d as liveRunsFromSessionStateRows};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
2
|
-
`);this.buffer=s.pop()||"";let r="";for(
|
|
1
|
+
var p=class d{constructor(){this.buffer="",this.extractedResult=null,this.rawText="",this.zodSchema=null,this.lastOutputLength=0,this.onToolCall=null,this._lastToolEmit=null}processChunk(t){if(!t)return null;this.buffer+=t;let s=this.buffer.split(`
|
|
2
|
+
`);this.buffer=s.pop()||"";let r="";for(let l of s)if(l.trim())try{let n=JSON.parse(l);this._emitToolCalls(n);let e=this.extractText(n);if(e){if(this.rawText&&e.startsWith(this.rawText)){let i=e.substring(this.rawText.length);this.rawText=e,r+=i}else(!this.rawText.includes(e)||e.length<20)&&(this.rawText+=e,r+=e);this.tryExtractResult(this.rawText)}else this.isValidResult(n)&&(this.rawText+=`${l}
|
|
3
3
|
`,r+=`${l}
|
|
4
|
-
`,this.extractedResult=n)}catch{if(l.includes('"text"')||l.includes('"content"')){
|
|
4
|
+
`,this.extractedResult=n)}catch{if(l.includes('"text"')||l.includes('"content"')){let e=l.match(/"text"\s*:\s*"([^"]*)/),i=l.match(/"content"\s*:\s*"([^"]*)/),a=e?e[1]:i?i[1]:null;a&&!this.rawText.includes(a)&&(r+=a,this.rawText+=a)}}return r||null}flush(){if(!this.buffer.trim())return null;let t="";try{let s=JSON.parse(this.buffer);this._emitToolCalls(s);let r=this.extractText(s);r&&(this.rawText+=r,t+=r,this.tryExtractResult(r))}catch{this.rawText+=this.buffer,t+=this.buffer,this.tryExtractResult(this.buffer)}return this.buffer="",t||null}_emitToolCalls(t){if(!this.onToolCall)return;let s=(e,i)=>{if(!e)return;let a=`${e}:${JSON.stringify(i??{})}`;this._lastToolEmit!==a&&(this._lastToolEmit=a,this.onToolCall(e,i??void 0))},r=e=>{if(e!=null){if(typeof e=="object"&&!Array.isArray(e))return e;if(typeof e=="string")try{return JSON.parse(e)}catch{return}}};if(t.type==="tool_use"||t.type==="tool_call"){if(t.name){s(t.name,r(t.input??t.arguments));return}let e=t.tool_call;if(e&&typeof e=="object"&&!Array.isArray(e)){let i=Object.keys(e);if(i.length===1){let a=i[0],o=e[a],h=o&&typeof o=="object"?o.args??o.input??o:void 0;s(a,r(h))}return}return}if(Array.isArray(t.tool_calls)){for(let e of t.tool_calls)s(e.name,r(e.input??e.arguments));return}let l=t.message??t;if(Array.isArray(l?.tool_calls)){for(let e of l.tool_calls)s(e.name,r(e.input??e.arguments));return}let n=l?.content??t.content;if(Array.isArray(n))for(let e of n)(e.type==="tool_use"||e.type==="tool_call")&&e.name&&s(e.name,r(e.input??e.arguments))}extractText(t){if(t.type==="assistant"&&t.message?.content){let s=t.message.content;if(Array.isArray(s))return s.filter(r=>r.type==="text"&&r.text).map(r=>r.text).join("")}return t.type==="thinking"&&t.text||t.text?t.text:t.content&&typeof t.content=="string"?t.content:t.delta?t.delta:null}tryExtractResult(t){if(!t||typeof t!="string")return;let s=[],r=/```json\s*\n?([\s\S]*?)\n?```/g,l;for(;(l=r.exec(t))!==null;){let c=l[1].trim();try{JSON.parse(c),s.push({text:c,source:"markdown"})}catch{}}let n=0,e=0;for(;n<t.length&&(n=t.indexOf("{",n),n!==-1);){let c=0,f=n;for(let u=n;u<t.length;u++)if(t[u]==="{")c++;else if(t[u]==="}"&&(c--,c===0)){f=u,s.push({text:t.substring(n,f+1),source:"brace"}),e++;break}n=f+1}let i=this.extractedResult,a=i?JSON.stringify(i).length:0,o=0,h=-1;for(let c=0;c<s.length;c++){let f=s[c];try{let u=f.text.replace(/,(\s*[}\]])/g,"$1"),y=JSON.parse(u);this.isValidResult(y)&&(o++,a=JSON.stringify(y).length,i=y,h=c)}catch{}}i&&(this.extractedResult=i)}isValidResult(t){if(!t||typeof t!="object"||Array.isArray(t)||t.session_id||t.timestamp_ms||t.type||t.call_id||t.tool_call||t.result&&typeof t.result=="object"&&(t.result.success&&typeof t.result.success=="object"||t.result.error&&typeof t.result.error=="object")||t.args&&typeof t.args=="object")return!1;if(this.zodSchema)try{return this.zodSchema.parse(t),!0}catch{return!1}return!0}getResult(){return this.extractedResult}getRawText(){return this.rawText}static extractResult(t,s=null){let r=new d;r.zodSchema=s,r.processChunk(t),r.flush();let l=r.getResult();return!l&&process.env.LOG_LEVEL==="debug"&&console.error("[StreamingParser] No result extracted from",t?.length||0,"chars"),l}};export{p as StreamingParser};
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import d from"fs/promises";var h=class{static generate(t,n="element"){let{selectors:r}=t;if(!r||typeof r!="object")return this.generateFallbackSelector(t,n);let e=[];return r.role&&e.push(this.generateRoleSelector(r.role)),r.attributes&&e.push(this.generateAttributeSelector(r.attributes)),r.partialMatch&&e.push(this.generatePartialMatchSelector(r.partialMatch)),r.structure&&e.push(this.generateStructuralSelector(r.structure)),e.length===0?this.generateFallbackSelector(t,n):e.length===1?`const ${n} = ${e[0]};`:`const ${n} = ${e[0]}
|
|
2
|
+
${e.slice(1).map(o=>` .or(${o})`).join(`
|
|
3
|
+
`)};`}static generateRoleSelector(t){let{role:n,name:r}=t;if(!n)return null;if(r){let e=this.escapeRegex(r);return`page.getByRole('${n}', { name: /${e}/i })`}return`page.getByRole('${n}')`}static generateAttributeSelector(t){if(!t||typeof t!="object")return null;let n=Object.entries(t).filter(([e,s])=>s!=null).map(([e,s])=>e==="placeholder"||e==="aria-label"?e==="placeholder"?`page.getByPlaceholder('${this.escapeString(s)}')`:`page.locator('[${e}="${this.escapeString(s)}"]')`:`[${e}="${this.escapeString(s)}"]`);if(t.placeholder)return`page.getByPlaceholder('${this.escapeString(t.placeholder)}')`;let r=n.filter(e=>!e.startsWith("page.")).join("");return r?`page.locator('${r}')`:null}static generatePartialMatchSelector(t){if(!t||typeof t!="object")return null;let n=Object.entries(t).filter(([r,e])=>e!==void 0).map(([r,e])=>{let s=e.replace(/^\^/,"");return`[${r}^="${this.escapeString(s)}"]`});return n.length>0?`page.locator('${n.join("")}')`:null}static generateStructuralSelector(t){return!t||typeof t!="string"?null:`page.locator('${this.escapeString(t)}')`}static generateFallbackSelector(t,n){let{description:r,type:e}=t;if(e==="fill"||e==="type")return`const ${n} = page.locator('input').first();`;if(e==="click"){if(r.toLowerCase().includes("button"))return`const ${n} = page.locator('button').first();`;if(r.toLowerCase().includes("link"))return`const ${n} = page.locator('a').first();`}return`const ${n} = page.locator('body');`}static escapeString(t){return typeof t!="string"?String(t):t.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/"/g,'\\"')}static escapeRegex(t){return typeof t!="string"?String(t):t.replace(/[.*+?^${}()[\]\\]/g,"\\$&")}static generateActionCode(t,n){let{type:r,value:e,description:s,selectors:o}=t,i=`element${n}`,l=this.generate(t,i),a=this.generateActionMethod(r,i,e);return`${l}
|
|
4
|
+
${a}`}static generateActionMethod(t,n,r){switch(t){case"fill":case"type":return`await ${n}.fill('${this.escapeString(r||"")}');`;case"click":return`await ${n}.click();`;case"navigate":return`await page.goto('${this.escapeString(r||"")}');`;case"wait":return`await page.waitForTimeout(${parseInt(r)||2e3});`;default:return`// Unknown action type: ${t}`}}static generateAssertionCode(t,n){let{description:r,expected:e,actual:s,passed:o}=t;return r.toLowerCase().includes("url")?`await expect(page).toHaveURL(/${this.escapeRegex(s)}/);`:r.toLowerCase().includes("visible")?"await expect(page.locator('body')).toBeVisible();":`// ${r}`}};var $=class{static async generateFromEvents(t,n,r,e){try{console.log("[TestPostProcessor] \u{1F3AF} Generating test from events.json (100% accurate)");let{readFileSync:s}=await import("fs"),o=JSON.parse(s(n,"utf-8")),i=o.filter(c=>["navigate","type","fill","click","select_option"].includes(c.type)),l=i.filter(c=>c.type!=="navigate");console.log(`[TestPostProcessor] Found ${i.length} action events, ${r.length} trace actions`);let a=`import { ZibbyRuntime } from '@zibby/core';
|
|
2
5
|
import { test, expect } from '@playwright/test';
|
|
3
6
|
|
|
4
|
-
`;
|
|
5
|
-
`,
|
|
7
|
+
`;a+=`test('${e}', async ({ page }) => {
|
|
8
|
+
`,a+=` const timestamp = Date.now();
|
|
6
9
|
|
|
7
|
-
`;let
|
|
10
|
+
`;let p=0;for(let c of i)if(c.type==="navigate")a+=` await page.goto('${c.data.params.url}');
|
|
8
11
|
|
|
9
|
-
`;else if(
|
|
12
|
+
`;else if(c.type==="type"||c.type==="fill"){let{element:f,text:g}=c.data.params,m=r[p]?.strategies||[];a+=` await ZibbyRuntime.step(page, ${JSON.stringify({name:f,action:"fill",value:g,strategies:m},null,2)});
|
|
10
13
|
|
|
11
|
-
`,
|
|
14
|
+
`,p++}else if(c.type==="click"){let{element:f}=c.data.params,g=r[p]?.strategies||[];a+=` await ZibbyRuntime.step(page, ${JSON.stringify({name:f,action:"click",value:"",strategies:g},null,2)});
|
|
12
15
|
|
|
13
|
-
`,
|
|
16
|
+
`,p++}else if(c.type==="select_option"){let{element:f,values:g}=c.data.params,m=r[p]?.strategies||[],w={name:f,action:"selectOption",value:Array.isArray(g)?g[0]:g,strategies:m};a+=` await ZibbyRuntime.step(page, ${JSON.stringify(w,null,2)});
|
|
14
17
|
|
|
15
|
-
`,
|
|
16
|
-
`;
|
|
17
|
-
${e}`);for(let
|
|
18
|
-
await ${y}.${
|
|
18
|
+
`,p++}a+=`});
|
|
19
|
+
`;let{dirname:u}=await import("path"),{mkdirSync:y}=await import("fs");return y(u(t),{recursive:!0}),await d.writeFile(t,a,"utf-8"),console.log(`[TestPostProcessor] \u2705 Generated test with ${o.filter(c=>["type","fill","click","select_option"].includes(c.type)).length} actions`),!0}catch(s){return console.warn("[TestPostProcessor] Failed to generate from events:",s.message),!1}}static async enhanceSelectorsWithTrace(t,n,r){try{console.log("[TestPostProcessor] \u{1F6E1}\uFE0F Applying Zibby Safe Action Wrappers...");let e=await d.readFile(t,"utf-8");e.includes("ZibbyRuntime")||(e=`import { ZibbyRuntime } from '@zibby/core';
|
|
20
|
+
${e}`);for(let s=0;s<n.length;s++){let o=n[s],i=`element${s}`,l=new RegExp(`const ${i}\\b\\s*=\\s*page\\.[^;]+;(\\s*await ${i}\\.waitFor\\([^)]*\\);)?\\s*await ${i}\\.(click|fill|type|selectOption|pressSequentially)\\(([^)]*)\\);`,"s"),a=e.match(l);if(!a)continue;let p={name:o.name||`Action ${s}`,action:o.method==="type"?"fill":o.method,value:a[3].trim().replace(/^['"]|['"]$/g,""),strategies:o.strategies||[]},u=`await ZibbyRuntime.step(page, ${JSON.stringify(p,null,2)});`;e=e.replace(a[0],u)}return await d.writeFile(t,e,"utf-8"),console.log("[TestPostProcessor] \u2705 Successfully converted test to Resilient Zibby format"),!0}catch(e){return console.warn("[TestPostProcessor] Failed to apply safe wrappers:",e.message),!1}}static async enhanceSelectors(t,n){try{let{actions:r=[]}=n;if(!r.length)return!1;let e=await d.readFile(t,"utf-8"),s=this.buildSelectorMap(r);return e=this.replaceSimpleSelectors(e,s),await d.writeFile(t,e,"utf-8"),!0}catch(r){return console.warn("Failed to enhance selectors:",r),!1}}static buildSelectorMap(t){let n=new Map;for(let r=0;r<t.length;r++){let e=t[r];if(!e.selectors||e.type==="navigate")continue;let s=`element${r}`,i=h.generate(e,s).match(/= (.+);/s);if(i){let l=i[1].trim(),a=`${e.type}:${this.normalizeDescription(e.description)}`;n.set(a,l),e.selectors.role&&n.set(`role:${e.selectors.role.name}`,l),e.selectors.attributes?.placeholder&&n.set(`placeholder:${e.selectors.attributes.placeholder}`,l)}}return n}static replaceSimpleSelectors(t,n){let r=t,e=[/await page\.(getByRole|getByPlaceholder|getByText|getByLabel|locator)\([^)]+\)\.(fill|click|type)\([^)]*\)/g];for(let s of e)r=r.replace(s,o=>{for(let[i,l]of n.entries())if(o.includes(i.split(":")[1])){let a=o.match(/\.(fill|click|type)\(([^)]*)\)/);if(a){let[,p,u]=a,y="element";return`const ${y} = ${l};
|
|
21
|
+
await ${y}.${p}(${u})`}}return o});return r}static normalizeDescription(t){return t?t.toLowerCase().replace(/[^a-z0-9]+/g," ").trim():""}},R=$;export{$ as TestPostProcessor,R as default};
|
package/dist/utils/timeline.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import e from"chalk";
|
|
1
|
+
import e from"chalk";var E="__WORKFLOW_GRAPH_LOG__",c=e.gray("\u2502"),I=e.gray("\u250C"),S=e.gray("\u2514"),h=e.green("\u25C6"),p=e.hex("#c084fc")("\u25C6"),W=e.hex("#2dd4bf")("\u25C6"),l=e.red("\u25C6"),w=`${c} `,f=2;function g(d){return d<1e3?`${d}ms`:`${(d/1e3).toFixed(1)}s`}function $(d,t){return(r,o,n)=>{if(typeof r!="string")return d(r,o,n);let _=process.stdout.columns||120,s="";for(let u=0;u<r.length;u++){let i=r[u];t.lineStart&&(s+=w,t.col=f,t.lineStart=!1),i===`
|
|
2
2
|
`?(s+=i,t.lineStart=!0,t.col=0,t.inEsc=!1):i==="\x1B"?(t.inEsc=!0,s+=i):t.inEsc?(s+=i,(i>="A"&&i<="Z"||i>="a"&&i<="z")&&(t.inEsc=!1)):(t.col++,s+=i,t.col>=_&&(s+=`
|
|
3
|
-
${
|
|
3
|
+
${w}`,t.col=f))}return d(s,o,n)}}var a=class{constructor(){this._currentNode=null,this._origStdoutWrite=null,this._origStderrWrite=null;let t=String(process.env.ZIBBY_RUN_SOURCE||"").trim().toLowerCase(),r=String(process.env.ZIBBY_WORKFLOW_GRAPH_LOG_MARKERS||"").trim()==="1";this._emitWorkflowGraphMarkers=r||t==="studio"}get isInsideNode(){return this._currentNode!==null}_startIntercepting(){this._origStdoutWrite=process.stdout.write.bind(process.stdout),this._origStderrWrite=process.stderr.write.bind(process.stderr);let t={lineStart:!0,col:0,inEsc:!1},r={lineStart:!0,col:0,inEsc:!1};this._outState=t,this._errState=r,process.stdout.write=$(this._origStdoutWrite,t),process.stderr.write=$(this._origStderrWrite,r)}_stopIntercepting(){this._origStdoutWrite&&(this._outState&&!this._outState.lineStart&&this._origStdoutWrite(`
|
|
4
4
|
`),process.stdout.write=this._origStdoutWrite),this._origStderrWrite&&(this._errState&&!this._errState.lineStart&&this._origStderrWrite(`
|
|
5
5
|
`),process.stderr.write=this._origStderrWrite),this._origStdoutWrite=null,this._origStderrWrite=null}_rawWrite(t){(this._origStdoutWrite||process.stdout.write.bind(process.stdout))(`${t}
|
|
6
|
-
`)}_emitGraphLogMarker(t){if(!this._emitWorkflowGraphMarkers)return;
|
|
6
|
+
`)}_emitGraphLogMarker(t){if(!this._emitWorkflowGraphMarkers)return;let r=`${E}${JSON.stringify(t)}
|
|
7
7
|
`;this._origStdoutWrite?this._origStdoutWrite(r):process.stdout.write(r)}_writeDot(t,r){this._origStdoutWrite?(this._outState&&!this._outState.lineStart&&(this._origStdoutWrite(`
|
|
8
8
|
`),this._outState.lineStart=!0,this._outState.col=0),this._origStdoutWrite(`${t} ${r}
|
|
9
9
|
`)):process.stdout.write.bind(process.stdout)(`${t} ${r}
|
|
10
10
|
`)}step(t){this._origStdoutWrite?this._writeDot(h,t):process.stdout.write.bind(process.stdout)(`${c} ${h} ${t}
|
|
11
|
-
`)}stepTool(t){this._origStdoutWrite?this._writeDot(
|
|
12
|
-
`)}stepMemory(t){
|
|
11
|
+
`)}stepTool(t){this._origStdoutWrite?this._writeDot(p,t):process.stdout.write.bind(process.stdout)(`${c} ${p} ${t}
|
|
12
|
+
`)}stepMemory(t){let r=e.hex("#2dd4bf")(t);this._origStdoutWrite?this._writeDot(W,r):process.stdout.write.bind(process.stdout)(`${c} ${W} ${r}
|
|
13
13
|
`)}stepFail(t){this._origStdoutWrite?this._writeDot(l,e.red(t)):process.stdout.write.bind(process.stdout)(`${c} ${l} ${e.red(t)}
|
|
14
|
-
`)}nodeStart(t){this._currentNode=t,this._emitGraphLogMarker({phase:"node_begin",node:t}),this._rawWrite(`${
|
|
14
|
+
`)}nodeStart(t){this._currentNode=t,this._emitGraphLogMarker({phase:"node_begin",node:t}),this._rawWrite(`${I} ${t}`),this._startIntercepting()}nodeComplete(t,r={}){this._stopIntercepting();let{duration:o,details:n}=r;if(n)for(let s of n)this._rawWrite(`${h} ${s}`);let _=o?e.dim(` ${g(o)}`):"";this._rawWrite(`${S} ${e.green("done")}${_}`),this._emitGraphLogMarker({phase:"node_end",node:t}),this._rawWrite("")}nodeFailed(t,r,o={}){this._stopIntercepting();let{duration:n}=o,_=n?e.dim(` ${g(n)}`):"";this._rawWrite(`${l} ${e.red(r)}`),this._rawWrite(`${S} ${e.red("failed")}${_}`),this._emitGraphLogMarker({phase:"node_end",node:t}),this._rawWrite("")}route(t,r){this._rawWrite(e.dim(` ${t} \u2192 ${r}`)),this._rawWrite("")}graphComplete(){this._rawWrite(e.green.bold("\u2713 Workflow completed"))}},O=new a,P=O;export{E as WORKFLOW_GRAPH_LOG_MARKER_PREFIX,P as default,O as timeline};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{readFileSync as
|
|
2
|
-
`),a=[],c=new Map,
|
|
1
|
+
import{readFileSync as W,existsSync as F,readdirSync as O}from"node:fs";import{join as N}from"node:path";import{execSync as L}from"node:child_process";import{tmpdir as B}from"node:os";var v=class{static async parseTraceZip(h){let o;if(h.endsWith(".zip")&&F(h)){let n=N(B(),`trace-${Date.now()}`);L(`unzip -q "${h}" -d "${n}"`,{stdio:"pipe"});let a=O(n).find(c=>c.endsWith(".trace"));if(!a)throw new Error("No .trace file found in zip");o=N(n,a)}else if(F(h)){let u=O(h).find(a=>a.endsWith(".trace"));if(!u)throw new Error("No .trace file found");o=N(h,u)}else throw new Error(`Trace not found at ${h}`);try{let u=W(o,"utf-8").trim().split(`
|
|
2
|
+
`),a=[],c=new Map,r=new Map;for(let w of u)try{let t=JSON.parse(w);if(t.type==="snapshot"&&t.snapshot&&t.snapshot.accessibility){let l=new Map;for(let i of t.snapshot.accessibility)i.ref&&l.set(i.ref,i);c.set(t.snapshotName,l)}if(t.type==="frame-snapshot"&&t.snapshot){let l=Buffer.from(t.snapshot.html||"","base64").toString("utf-8");l&&l.length>100&&r.set(t.pageId||"default",l)}}catch{}for(let w of u)try{let t=JSON.parse(w);if(t.type==="before"&&t.params&&t.params.selector){let l=t.method;if(["click","fill","type","selectOption"].includes(l)){let i=t.params.selector,z=t.params.text||t.params.value||"",e=[],p=null,f=null,m=null,R=i.match(/aria-ref=([a-z0-9]+)/i);if(R&&t.snapshotName){let s=R[1],x=c.get(t.snapshotName);if(x&&x.has(s)){let y=x.get(s);p=y.name||null,f=y.role||null,m=y.label||null,console.log(`[TraceParser] \u2705 Found ACTUAL element data: text="${p}", role="${f}"`)}}let d=i.match(/internal:text="([^"]+)"/i),E=i.match(/internal:label="([^"]+)"/i),C=i.match(/internal:placeholder="([^"]+)"/i),T=i.match(/internal:role=([^ ]+)/i),M=i.match(/internal:describe="([^"]+)"/i),k=i.match(/name="([^"]+)"/i);if(d){e.push({type:"text",text:d[1]});let s=d[1].split(" ");s.length>1&&(e.push({type:"text",text:s[0],fuzzy:!0}),e.push({type:"text",text:s[s.length-1],fuzzy:!0}))}if(E&&e.push({type:"label",label:E[1]}),C&&e.push({type:"placeholder",placeholder:C[1]}),p){e.unshift({type:"text",text:p,source:"accessibility-tree"});let s=p.split(" ");s.length>1&&e.push({type:"text",text:s[0],fuzzy:!0,source:"accessibility-tree"})}if(f||T){let s=f||T[1],x=p||(k?k[1]:d?d[1]:null);e.unshift({type:"role",role:s,name:x,source:p?"accessibility-tree":"selector"})}if(m&&e.unshift({type:"label",label:m,source:"accessibility-tree"}),M){let s=M[1],x=["link","button","textbox","menuitem","submenu","combobox","checkbox","radio","tab","treeitem","menu item"],y=null,$=s;for(let g of x)if(s.toLowerCase().endsWith(` ${g}`)){y=g.replace(" ",""),$=s.substring(0,s.length-g.length-1);break}if(y){e.push({type:"role",role:y,name:$});let g=$.replace(/\s*\([^)]+\)\s*$/,"");e.push({type:"text",text:g}),e.push({type:"text",text:$});let S=$.split(" ");S.length>1&&(e.push({type:"text",text:S[0],fuzzy:!0}),e.push({type:"text",text:S.slice(0,2).join(" "),fuzzy:!0}))}else{let g=s.replace(/\s*\([^)]+\)\s*$/,"");e.push({type:"text",text:g}),e.push({type:"text",text:s})}}let A=this.extractDOMStrategies(i,r,d?.[1]||M?.[1],t.pageId);e.push(...A);let b=this.extractStructuralContext(i);(b.parent||b.sibling)&&e.forEach(s=>{["role","text","label","testid"].includes(s.type)&&(b.parent&&(s.parent=b.parent),b.sibling&&(s.sibling=b.sibling))}),e.push({type:"css",value:i});let j=p||d?d[1]:M?M[1].replace(/\s*\([^)]+\)\s*$/,""):`Action ${a.length}`;a.push({method:l,name:j,action:l==="type"?"fill":l,value:z,strategies:e,timestamp:t.startTime,actualText:p,actualRole:f,actualAriaLabel:m})}}}catch{}return a}catch(n){throw new Error(`Failed to parse trace: ${n.message}`,{cause:n})}}static extractDOMStrategies(h,o,n,u){let a=[];if(!o||o.size===0||!n)return a;try{let c=o.get(u);if(!c)return a;let r=n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),w=new RegExp(`data-testid=["']([^"']+)["'][^>]*>[^<]*${r}`,"i"),t=c.match(w);t&&a.push({type:"testid",value:t[1],priority:"high"});let l=new RegExp(`class=["']([^"']+)["'][^>]*>[^<]*${r}`,"gi"),i=c.matchAll(l);for(let p of i){let f=p[1].split(/\s+/).filter(m=>m&&!m.match(/^(css|jss|makeStyles|MuiBox|MuiStack)-\w+/)&&m.length>2);f.length>0&&(a.push({type:"class",value:f.join("."),priority:"medium"}),f.length===1&&a.push({type:"class",value:f[0],priority:"medium"}))}let z=new RegExp(`id=["']([^"']+)["'][^>]*>[^<]*${r}`,"i"),e=c.match(z);e&&!e[1].match(/^(root|app|\d+|[a-f0-9-]{20,})$/i)&&a.push({type:"id",value:e[1],priority:"high"})}catch(c){console.warn(`[TraceParser] DOM extraction failed: ${c.message}`)}return a}static extractStructuralContext(h){let o={parent:null,sibling:null},n=h.split(">>").map(c=>c.trim());if(n.length>1){let r=n[n.length-2].replace(/internal:text="[^"]+"\s*/gi,"").replace(/internal:role=\S+\s*/gi,"").replace(/internal:label="[^"]+"\s*/gi,"").trim();r&&(r.match(/^(form|section|nav|header|aside|main|article)\b/)||r.includes("[")||r.match(/[#.]\w/))&&(o.parent=r)}let a=n[n.length-1].match(/([^+~]+)\s*[+~]\s*(.+)/);return a&&(o.sibling=a[1].trim()),o}},U=v;export{v as TraceParser,U as default};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{readdir as v,access as h,copyFile as w,constants as g}from"fs/promises";import{join as i,relative as $}from"path";async function F(c={}){
|
|
2
|
-
`);
|
|
3
|
-
\u{1F3AC} Organized ${f} video(s)`),console.log(`\u{1F4C2} Videos are now next to their test files in ${o}/`)),{movedCount:f,success:!0}}catch(e){return s&&console.error("\u274C Error organizing videos:",e.message),{movedCount:0,success:!1,error:e.message}}}async function P(c,a){
|
|
1
|
+
import{readdir as v,access as h,copyFile as w,constants as g}from"fs/promises";import{join as i,relative as $}from"node:path";async function F(c={}){let{testResultsDir:a="test-results",testsDir:o="tests",projectRoot:t=process.cwd(),verbose:s=!0}=c;s&&console.log(`\u{1F3A5} Organizing test videos...
|
|
2
|
+
`);let r=i(t,a),l=i(t,o);try{let e=await v(r),f=0;for(let n of e){if(n.startsWith("."))continue;let u=i(r,n,"video.webm");try{await h(u,g.F_OK)}catch{continue}let m=n.replace(/-chromium$/,"").replace(/-firefox$/,"").replace(/-webkit$/,""),d=await P(l,m);if(d){let p=d.replace(/\.spec\.(js|ts)$/,".spec.webm");await w(u,p),s&&console.log(`\u2705 ${$(t,p)}`),f++}else s&&console.log(`\u26A0\uFE0F Could not find test file for: ${n}`)}return s&&(console.log(`
|
|
3
|
+
\u{1F3AC} Organized ${f} video(s)`),console.log(`\u{1F4C2} Videos are now next to their test files in ${o}/`)),{movedCount:f,success:!0}}catch(e){return s&&console.error("\u274C Error organizing videos:",e.message),{movedCount:0,success:!1,error:e.message}}}async function P(c,a){let o=a.split("-");for(let t=o.length;t>0;t--){let r=o.slice(0,t).join("/");for(let l of["js","ts"]){let e=i(c,`${r}.spec.${l}`);try{return await h(e,g.F_OK),e}catch{}}}return null}export{F as organizeVideos};
|
package/package.json
CHANGED
|
@@ -8,8 +8,6 @@ import {
|
|
|
8
8
|
writeFileSync,
|
|
9
9
|
existsSync,
|
|
10
10
|
readFileSync,
|
|
11
|
-
readdirSync,
|
|
12
|
-
renameSync,
|
|
13
11
|
statSync,
|
|
14
12
|
copyFileSync,
|
|
15
13
|
mkdirSync,
|
|
@@ -22,7 +20,10 @@ export class BrowserTestResultHandler extends ResultHandler {
|
|
|
22
20
|
|
|
23
21
|
static async onNodeSaved(nodeFolder, executionData) {
|
|
24
22
|
this.enrichEvents(nodeFolder, executionData);
|
|
25
|
-
|
|
23
|
+
const videoPath = join(nodeFolder, 'recording.webm');
|
|
24
|
+
if (existsSync(videoPath)) {
|
|
25
|
+
this.recalculateEventTimestamps(nodeFolder, videoPath);
|
|
26
|
+
}
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
// ── Video ──────────────────────────────────────────────────
|
|
@@ -124,42 +125,6 @@ export class BrowserTestResultHandler extends ResultHandler {
|
|
|
124
125
|
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(3, '0')}`;
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
static async renameVideoFile(folder) {
|
|
128
|
-
logger.debug(`renameVideoFile called for: ${folder}`);
|
|
129
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
const files = readdirSync(folder);
|
|
133
|
-
logger.debug(`Files in directory: ${files.join(', ')}`);
|
|
134
|
-
let videoFile = files.find(f => f.endsWith('.webm') && f !== 'recording.webm');
|
|
135
|
-
|
|
136
|
-
if (!videoFile) {
|
|
137
|
-
const parentDir = join(folder, '..');
|
|
138
|
-
const parentFiles = readdirSync(parentDir);
|
|
139
|
-
const parentVideo = parentFiles.find(f => f.endsWith('.webm') && f !== 'recording.webm');
|
|
140
|
-
if (parentVideo) {
|
|
141
|
-
const srcPath = join(parentDir, parentVideo);
|
|
142
|
-
const destPath = join(folder, parentVideo);
|
|
143
|
-
renameSync(srcPath, destPath);
|
|
144
|
-
logger.debug(`Moved video from session root: ${parentVideo}`);
|
|
145
|
-
videoFile = parentVideo;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (videoFile) {
|
|
150
|
-
const oldPath = join(folder, videoFile);
|
|
151
|
-
const newPath = join(folder, 'recording.webm');
|
|
152
|
-
renameSync(oldPath, newPath);
|
|
153
|
-
logger.debug(`Renamed video: ${videoFile} → recording.webm`);
|
|
154
|
-
this.recalculateEventTimestamps(folder, newPath);
|
|
155
|
-
} else {
|
|
156
|
-
logger.debug(`No video file to rename (files: ${files.length})`);
|
|
157
|
-
}
|
|
158
|
-
} catch (err) {
|
|
159
|
-
console.warn(`⚠️ Error in renameVideoFile: ${err.message}`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
128
|
// ── Event enrichment + assertion resolution ────────────────
|
|
164
129
|
|
|
165
130
|
static enrichEvents(executeLiveFolder, executionData) {
|