shiplightai 0.1.49 → 0.1.51

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/reporter.js CHANGED
@@ -879,6 +879,6 @@ Shiplight cloud report: ${f.data.reportUrl}`)}var We=Je(()=>{"use strict"});impo
879
879
  </html>`}var Gt={before:0,main:1,teardown:2,after:3};function Ge(e){let t=e.split(".")[0];return Gt[t]??1}function He(e){return e.split(".").map(t=>{let r=Number(t);return Number.isNaN(r)?0:r})}function Ht(e){return[...e].sort(([t],[r])=>{let i=Ge(t),o=Ge(r);if(i!==o)return i-o;let n=He(t),s=He(r);for(let c=0;c<Math.max(n.length,s.length);c++){let d=n[c]??-1,p=s[c]??-1;if(d!==p)return d-p}return 0})}function Kt(e){let t=new Set;for(let r of e)if(t.add(r.category),r.category==="hook")for(let i of r.steps)t.add(i.category);return t}function jt(e,t){let r=e.toLowerCase();return r.includes("before")?"before":r.includes("after")?"after":t}function se(e,t="main",r,i,o){r===void 0&&(r=!Kt(e).has("test.step")),o||(o=new Map);let n=[];for(let s of e){if(s.category==="fixture"||s.category==="test.attach")continue;if(s.category==="hook"){let d=jt(s.title,t);n.push(...se(s.steps,d,r,i,o));continue}if(s.category==="test.step"||r&&(s.category==="expect"||s.category==="pw:api")){let d=o.get(t)??0;o.set(t,d+1);let p=`${t}.${d}`,m={stepId:p,description:s.title,status:s.error?"failure":s.duration===-1?"skipped":"success",duration:s.duration>=0?s.duration:void 0};s.error&&(m.error=s.error.message??s.error.stack),i&&s.location&&i.set(p,s.location),n.push(m),s.steps.length>0&&n.push(...se(s.steps,p,r,i,o))}}return n}var ae=class{outputFolder;openMode;reportToCloud;apiKey;collected=[];config;runStartTime;constructor(t={}){this.outputFolder=t.outputFolder||"shiplight-report",this.openMode=t.open||"on-failure",this.reportToCloud=t.reportToCloud??!1,this.apiKey=t.apiKey||process.env.__SHIPLIGHT_API_KEY}onBegin(t,r){this.config=t,this.runStartTime=new Date().toISOString()}onTestEnd(t,r){this.collected.push({test:t,result:r})}async onEnd(t){if(this.collected.length===0)return;let r=new Map;for(let p of this.collected){let m=p.test.titlePath().join(" > "),b=r.get(m);b||(b=[],r.set(m,b)),b.push(p)}let i=[];for(let[,p]of r.entries()){let m=p[0].test.location.file,b=[],u,h,f;for(let v=0;v<p.length;v++){let{test:T,result:_}=p[v],k=await this.buildReportTest(T,_,m);u=k,h||(h=k.startTime),f=k.endTime,b.push({attemptNumber:v+1,status:_.status,duration:_.duration,steps:k.steps,error:k.error,videoPath:k.videoPath,tracePath:k.tracePath})}let a=b[b.length-1],{test:S}=p[p.length-1],y={title:S.title,baseTitle:u?.baseTitle,file:x.relative(process.cwd(),m),status:a.status,duration:a.duration,steps:a.steps,error:a.error,videoPath:a.videoPath,tracePath:a.tracePath,actionStepsMap:u?.actionStepsMap,tags:u?.tags,baseUrl:u?.baseUrl,skip:u?.skip,slow:u?.slow,timeout:u?.timeout,parameterSetName:u?.parameterSetName,startTime:h,endTime:f,suiteName:u?.suiteName},w=b.some(v=>v.status==="failed"||v.status==="timedOut");b.length>1&&w&&a.status==="passed"&&(y.flaky=!0,y.retries=b.length-1,y.attempts=b),i.push(y)}let o={tests:i,totalDuration:t.duration,timestamp:new Date().toISOString()},n=x.isAbsolute(this.outputFolder)?this.outputFolder:x.join(process.cwd(),this.outputFolder);$.mkdirSync(n,{recursive:!0});let s=x.join(n,"screenshots");for(let p=0;p<o.tests.length;p++){let m=o.tests[p],b=m.attempts&&m.attempts.length>0,u=[{obj:m,prefix:b?`test-${p}-attempt-0`:`test-${p}`,screenshotSubDir:`test-${p}`}];if(m.attempts)for(let h=0;h<m.attempts.length;h++)u.push({obj:m.attempts[h],prefix:`test-${p}-attempt-${h+1}`,screenshotSubDir:`test-${p}/attempt-${h}`});for(let{obj:h,prefix:f,screenshotSubDir:a}of u){let S=x.join(s,a),y=!1;for(let w of h.steps)if(w.screenshot&&x.isAbsolute(w.screenshot))try{y||($.mkdirSync(S,{recursive:!0}),y=!0);let v=`${w.stepId.replace(/\./g,"-")}.png`;$.copyFileSync(w.screenshot,x.join(S,v)),w.screenshot=`screenshots/${a}/${v}`}catch(v){console.warn(`[reporter] Failed to copy screenshot for ${w.stepId}:`,v)}if(h.videoPath&&x.isAbsolute(h.videoPath)){let w=x.extname(h.videoPath)||".webm",v=`${f}-video${w}`;try{$.copyFileSync(h.videoPath,x.join(n,v)),h.videoPath=v}catch{h.videoPath=void 0}}if(h.tracePath&&x.isAbsolute(h.tracePath)){let w=x.extname(h.tracePath)||".zip",v=`${f}-trace${w}`;try{$.copyFileSync(h.tracePath,x.join(n,v)),h.tracePath=v}catch{h.tracePath=void 0}}}}let c=x.join(n,"report-data.json");$.writeFileSync(c,JSON.stringify(o,null,2),"utf-8");let d=x.join(n,"index.html");if($.writeFileSync(d,De(o),"utf-8"),console.log(`
880
880
  Shiplight report written to: ${d}`),this.openMode==="always"||this.openMode==="on-failure"&&t.status!=="passed")try{let p=(await import("open")).default;await p(d)}catch{}if(this.reportToCloud){if(!this.apiKey){console.warn("[reporter] reportToCloud is enabled but no apiKey found, skipping cloud upload.");return}try{let{uploadToCloud:p}=await Promise.resolve().then(()=>(We(),Fe));await p(o,n,this.runStartTime,this.apiKey)}catch(p){console.warn("[reporter] Cloud upload failed:",p)}}}printsToStdio(){return!1}async buildReportTest(t,r,i){let o={title:t.title,file:x.relative(process.cwd(),i),status:r.status,duration:r.duration,steps:[],startTime:new Date(r.startTime).toISOString(),endTime:new Date(r.startTime.getTime()+r.duration).toISOString()};r.errors.length>0&&(o.error=r.errors.map(u=>u.message||u.stack||String(u)).join(`
881
881
 
882
- `)),r.stdout.length>0&&(o.stdout=r.stdout.map(u=>typeof u=="string"?u:u.toString()).join("")),r.stderr.length>0&&(o.stderr=r.stderr.map(u=>typeof u=="string"?u:u.toString()).join(""));for(let u of r.attachments)u.name==="video"&&u.path&&(o.videoPath=u.path),u.name==="trace"&&u.path&&(o.tracePath=u.path);let n=r.attachments.find(u=>u.name==="shiplight-results"),s=null;if(n)try{if(n.body)s=JSON.parse(n.body.toString("utf-8"));else if(n.path){let u=$.readFileSync(n.path,"utf-8");s=JSON.parse(u)}}catch{}let c=i.replace(/\.yaml\.spec\.ts$/,".test.yaml"),d={},p=t.title.match(/^(.*)\s+\[([^\]]+)\]$/),m=p?p[1]:t.title,b=p?p[2]:void 0;if(b&&(o.parameterSetName=b),$.existsSync(c))try{let u=$.readFileSync(c,"utf-8"),h=Le(u,c);if(h.suite){let f=h.suite.tests.find(a=>a.name===m);f&&(d=X(f.testFlow),f.tags?.length&&(o.tags=f.tags),f.skip!==void 0&&(o.skip=f.skip),f.slow&&(o.slow=f.slow),f.timeout!==void 0&&(o.timeout=f.timeout),o.baseTitle=f.name||f.testFlow?.goal),o.suiteName=h.name,h.tags?.length&&(o.suiteTags=h.tags),h.use?.baseURL&&(o.baseUrl=h.use.baseURL)}else h.testFlow&&(d=X(h.testFlow),o.baseTitle=h.name||h.testFlow?.goal,h.tags?.length&&(o.tags=h.tags),h.use?.baseURL&&(o.baseUrl=h.use.baseURL))}catch{}if(s||Object.keys(d).length>0){let u=new Set([...Object.keys(d),...Object.keys(s||{})]),h=Array.from(u).map(a=>[a,null]),f=Ht(h);for(let[a]of f){let S=d[a],y=s?.[a],w=S?.description;if(!w||w==="Action"||w==="Draft"){let T=S?.action_entity;w=T?.action_description||T?.action_data?.kwargs?.description||y?.description||a}let v={stepId:a,description:w,status:y?.status||"pending",duration:y?.duration};if(y?.message){let T=typeof y.message=="string"?y.message:JSON.stringify(y.message,null,2);y.status==="failure"?v.error=T:v.message=T}if(y?.screenshot){let T=y.screenshot,_=n?.path?x.dirname(n.path):"",k=x.isAbsolute(T)?T:x.join(_,T);$.existsSync(k)&&(v.screenshot=k)}v.code=y.code,o.steps.push(v)}}if(s===null&&Object.keys(d).length===0&&!i.endsWith(".yaml.spec.ts")){let u=new Map;if(o.steps=se(r.steps,"main",void 0,u),o.steps.length>0){let h=new Map;o.actionStepsMap=Object.fromEntries(o.steps.map(f=>{let a=u.get(f.stepId),S;if(a?.file){if(!h.has(a.file))try{h.set(a.file,$.readFileSync(a.file,"utf-8").split(`
882
+ `)),r.stdout.length>0&&(o.stdout=r.stdout.map(u=>typeof u=="string"?u:u.toString()).join("")),r.stderr.length>0&&(o.stderr=r.stderr.map(u=>typeof u=="string"?u:u.toString()).join(""));for(let u of r.attachments)u.name==="video"&&u.path&&(o.videoPath=u.path),u.name==="trace"&&u.path&&(o.tracePath=u.path);let n=r.attachments.find(u=>u.name==="shiplight-results"),s=null;if(n)try{if(n.body)s=JSON.parse(n.body.toString("utf-8"));else if(n.path){let u=$.readFileSync(n.path,"utf-8");s=JSON.parse(u)}}catch{}let c=i.replace(/\.yaml\.spec\.ts$/,".test.yaml"),d={},p=t.title.match(/^(.*)\s+\[([^\]]+)\]$/),m=p?p[1]:t.title,b=p?p[2]:void 0;if(b&&(o.parameterSetName=b),$.existsSync(c))try{let u=$.readFileSync(c,"utf-8"),h=Le(u,c);if(h.suite){let f=h.suite.tests.find(a=>a.name===m);f&&(d=X(f.testFlow),f.tags?.length&&(o.tags=f.tags),f.skip!==void 0&&(o.skip=f.skip),f.slow&&(o.slow=f.slow),f.timeout!==void 0&&(o.timeout=f.timeout),o.baseTitle=f.name||f.testFlow?.goal),o.suiteName=h.name,h.tags?.length&&(o.suiteTags=h.tags),h.use?.baseURL&&(o.baseUrl=h.use.baseURL)}else h.testFlow&&(d=X(h.testFlow),o.baseTitle=h.name||h.testFlow?.goal,h.tags?.length&&(o.tags=h.tags),h.use?.baseURL&&(o.baseUrl=h.use.baseURL))}catch{}if(s||Object.keys(d).length>0){let u=new Set([...Object.keys(d),...Object.keys(s||{})]),h=Array.from(u).map(a=>[a,null]),f=Ht(h);for(let[a]of f){let S=d[a],y=s?.[a],w=S?.description;if(!w||w==="Action"||w==="Draft"){let T=S?.action_entity;w=T?.action_description||T?.action_data?.kwargs?.description||y?.description||a}let v={stepId:a,description:w,status:y?.status||"pending",duration:y?.duration};if(y?.message){let T=typeof y.message=="string"?y.message:JSON.stringify(y.message,null,2);y.status==="failure"?v.error=T:v.message=T}if(y?.screenshot){let T=y.screenshot,_=n?.path?x.dirname(n.path):"",k=x.isAbsolute(T)?T:x.join(_,T);$.existsSync(k)&&(v.screenshot=k)}y?.code&&(v.code=y.code),o.steps.push(v)}}if(s===null&&Object.keys(d).length===0&&!i.endsWith(".yaml.spec.ts")){let u=new Map;if(o.steps=se(r.steps,"main",void 0,u),o.steps.length>0){let h=new Map;o.actionStepsMap=Object.fromEntries(o.steps.map(f=>{let a=u.get(f.stepId),S;if(a?.file){if(!h.has(a.file))try{h.set(a.file,$.readFileSync(a.file,"utf-8").split(`
883
883
  `))}catch{h.set(a.file,[])}let w=h.get(a.file);S=w[a.line-1]?.trim();let v=a.line-2,T=a.line,_=[],k=a.line;v>=0&&(_.push(w[v]??""),k=a.line-1),_.push(w[a.line-1]??""),T<w.length&&_.push(w[T]??""),f.code=_.join(`
884
884
  `),f.codeStartLine=k,f.codeLine=a.line}let y={description:f.description,...S&&{action_entity:{action_description:f.description,action_data:{action_name:"js_code",args:[],kwargs:{code:S}}}}};return[f.stepId,y]}))}}return Object.keys(d).length>0?o.actionStepsMap=d:s&&o.steps.length>0&&(o.actionStepsMap=Object.fromEntries(o.steps.map(u=>{let h=s[u.stepId],f=h?.type,a={description:u.description,...f&&{action_entity:{action_description:u.description,action_data:{action_name:f==="step"?"js_code":f,args:[],kwargs:f==="step"?{code:h?.code??u.description}:{statement:u.description}}}}};return[u.stepId,a]}))),o}},zt=ae;export{zt as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shiplightai",
3
- "version": "0.1.49",
3
+ "version": "0.1.51",
4
4
  "type": "module",
5
5
  "description": "Shiplight CLI for running and debugging .test.yaml files",
6
6
  "main": "dist/index.js",
@@ -84,10 +84,10 @@
84
84
  "tsup": "^8.3.5",
85
85
  "typescript": "5.5.4",
86
86
  "mcp-tools": "1.0.0",
87
- "sdk-core": "0.1.0",
88
87
  "sdk-internal": "0.1.1",
89
- "shiplight-types": "0.1.0",
88
+ "sdk-core": "0.1.0",
90
89
  "shiplight-tools": "1.0.0",
90
+ "shiplight-types": "0.1.0",
91
91
  "@loggia/common": "1.0.0"
92
92
  },
93
93
  "peerDependencies": {