@testrelic/playwright-analytics 2.7.0-next.60 → 2.7.0-next.61
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/index.cjs +20 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +20 -20
- package/dist/index.js.map +1 -1
- package/dist/reporter-entry.cjs +22 -22
- package/dist/reporter-entry.cjs.map +1 -1
- package/dist/reporter-entry.js +22 -22
- package/dist/reporter-entry.js.map +1 -1
- package/package.json +2 -2
package/dist/reporter-entry.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {randomUUID,createHash}from'crypto';import {writeFileSync,renameSync,mkdirSync,unlinkSync,existsSync,readFileSync,appendFileSync,readdirSync,statSync,rmSync,createReadStream}from'fs';import {join,dirname,relative,extname,resolve,parse,basename}from'path';import {tmpdir,platform}from'os';import {isValidConfig,createError,ErrorCode,ATTACHMENT_NAME,isTestRelicFilePayload,isTestRelicDataPayload,isValidEndpointUrl,isValidCloudConfig,isValidQueueEntry}from'@testrelic/core';import {spawn,exec,execSync}from'child_process';import {createServer}from'http';import {createInterface}from'readline';import {copyFile}from'fs/promises';import {gzipSync}from'zlib';import {Readable}from'stream';var Mr=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,r)=>(typeof require<"u"?require:t)[r]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var Fr=".testrelic-report";function ye(e,t,r,a){let s=`${e}|${t.join("|")}|${r}|${a}`;return createHash("sha256").update(s).digest("hex").substring(0,12)}var G=class{constructor(t){this.writeErrors=[];this.totalBytesWritten=0;this.reportDir=join(t,Fr),this.testsDir=join(this.reportDir,"tests"),mkdirSync(this.testsDir,{recursive:true});}getReportDir(){return this.reportDir}getWriteErrors(){return this.writeErrors}writeTestDetail(t,r,a){try{let s=join(this.testsDir,t);mkdirSync(s,{recursive:!0});let n=join(s,"meta.json"),o=n+".tmp",i=JSON.stringify(r);return writeFileSync(o,i,"utf-8"),renameSync(o,n),this.totalBytesWritten+=Buffer.byteLength(i,"utf-8"),a?.networkRequestsFile&&this.moveFile(a.networkRequestsFile,join(s,"network.jsonl")),a?.consoleLogsFile&&this.moveFile(a.consoleLogsFile,join(s,"console.jsonl")),a?.apiCallsFile&&this.moveFile(a.apiCallsFile,join(s,"api-calls.jsonl")),!0}catch(s){let n=s instanceof Error?s.message:String(s);return this.writeErrors.push({testId:t,error:n,timestamp:new Date().toISOString()}),process.stderr.write(`[testrelic] Failed to write test detail for ${t}: ${n}
|
|
2
2
|
`),false}}moveFile(t,r){try{renameSync(t,r);}catch{try{let{copyFileSync:a}=Mr("fs");a(t,r);try{unlinkSync(t);}catch{}}catch(a){process.stderr.write(`[testrelic] Failed to move file ${t} \u2192 ${r}: ${a instanceof Error?a.message:String(a)}
|
|
3
|
-
`);}}}writeIndex(t){let r=join(this.reportDir,"index.json"),a=r+".tmp",s=JSON.stringify(t);writeFileSync(a,s,"utf-8"),renameSync(a,r),this.totalBytesWritten+=Buffer.byteLength(s,"utf-8");}writeCompactIndex(t){let r=join(this.reportDir,"index-compact.json"),a=r+".tmp",s=t.map(({titlePath:o,project:i,...l})=>l),n=JSON.stringify(s);writeFileSync(a,n,"utf-8"),renameSync(a,r),this.totalBytesWritten+=Buffer.byteLength(n,"utf-8");}writeSummary(t){let r=join(this.reportDir,"summary.json"),a=r+".tmp",s=JSON.stringify(t);writeFileSync(a,s,"utf-8"),renameSync(a,r),this.totalBytesWritten+=Buffer.byteLength(s,"utf-8");}writeManifest(t){let r=join(this.reportDir,"manifest.json"),a=r+".tmp",s=JSON.stringify(t);writeFileSync(a,s,"utf-8"),renameSync(a,r),this.totalBytesWritten+=Buffer.byteLength(s,"utf-8");}computeTotalSize(){return this.totalBytesWritten}dispose(){}};var gt=".testrelic",Hr="testrelic-config.json",Ur=".testrelic",zr=5,$r=new Set(["__proto__","constructor","prototype"]),
|
|
4
|
-
`),s}catch{}let n=dirname(t);if(n===t)break;t=n;}return null}function mt(e){try{let t=readFileSync(e,"utf-8"),r=JSON.parse(t);return typeof r!="object"||r===null||Array.isArray(r)||!vt(r)?null:r}catch{return null}}function vt(e){for(let t of Object.keys(e)){if($r.has(t))return false;let r=e[t];if(typeof r=="object"&&r!==null&&!Array.isArray(r)&&!vt(r))return false}return true}function yt(e){let t=/^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/.exec(e);if(t)return process.env[t[1]]??null;let r=/^\$([A-Za-z_][A-Za-z0-9_]*)$/.exec(e);return r?process.env[r[1]]??null:e}function be(e){let t=Object.create(null);for(let r of Object.keys(e)){let a=e[r];typeof a=="string"&&a.startsWith("$")?t[r]=yt(a):typeof a=="object"&&a!==null&&!Array.isArray(a)?t[r]=be(a):t[r]=a;}return t}function ft(e){let t=/^(\d+)\s*([smhd])$/.exec(e.trim());if(!t)return null;let r=parseInt(t[1],10),a=t[2],s=jr[a];return !s||r<=0?null:r*s}function bt(e,t){let r=Object.create(null);if(Object.assign(r,
|
|
5
|
-
`);let
|
|
6
|
-
`);if(t<1||t>s.length)return null;let n=Math.max(1,t-r),o=Math.min(s.length,t+r),i=[];for(let l=n;l<=o;l++){let d=l===t?">":" ",
|
|
3
|
+
`);}}}writeIndex(t){let r=join(this.reportDir,"index.json"),a=r+".tmp",s=JSON.stringify(t);writeFileSync(a,s,"utf-8"),renameSync(a,r),this.totalBytesWritten+=Buffer.byteLength(s,"utf-8");}writeCompactIndex(t){let r=join(this.reportDir,"index-compact.json"),a=r+".tmp",s=t.map(({titlePath:o,project:i,...l})=>l),n=JSON.stringify(s);writeFileSync(a,n,"utf-8"),renameSync(a,r),this.totalBytesWritten+=Buffer.byteLength(n,"utf-8");}writeSummary(t){let r=join(this.reportDir,"summary.json"),a=r+".tmp",s=JSON.stringify(t);writeFileSync(a,s,"utf-8"),renameSync(a,r),this.totalBytesWritten+=Buffer.byteLength(s,"utf-8");}writeManifest(t){let r=join(this.reportDir,"manifest.json"),a=r+".tmp",s=JSON.stringify(t);writeFileSync(a,s,"utf-8"),renameSync(a,r),this.totalBytesWritten+=Buffer.byteLength(s,"utf-8");}computeTotalSize(){return this.totalBytesWritten}dispose(){}};var gt=".testrelic",Hr="testrelic-config.json",Ur=".testrelic",zr=5,$r=new Set(["__proto__","constructor","prototype"]),ut=Object.freeze({apiKey:null,endpoint:"https://platform.testrelic.ai/api/v1",uploadStrategy:"batch",timeout:3e4,projectName:null,queueMaxAge:6048e5,queueDirectory:`${gt}/queue`,uploadArtifacts:true,artifactMaxSizeMb:50}),jr={s:1e3,m:60*1e3,h:3600*1e3,d:1440*60*1e3};function ht(e){let t=resolve(e);for(let r=0;r<=zr;r++){let a=join(t,gt,Hr);if(existsSync(a))try{if(statSync(a).isFile())return a}catch{}let s=join(t,Ur);if(existsSync(s))try{if(statSync(s).isFile())return process.stderr.write(`[testrelic] Deprecation: config file ".testrelic" has moved to ".testrelic/testrelic-config.json". Please migrate your config file to the new location.
|
|
4
|
+
`),s}catch{}let n=dirname(t);if(n===t)break;t=n;}return null}function mt(e){try{let t=readFileSync(e,"utf-8"),r=JSON.parse(t);return typeof r!="object"||r===null||Array.isArray(r)||!vt(r)?null:r}catch{return null}}function vt(e){for(let t of Object.keys(e)){if($r.has(t))return false;let r=e[t];if(typeof r=="object"&&r!==null&&!Array.isArray(r)&&!vt(r))return false}return true}function yt(e){let t=/^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/.exec(e);if(t)return process.env[t[1]]??null;let r=/^\$([A-Za-z_][A-Za-z0-9_]*)$/.exec(e);return r?process.env[r[1]]??null:e}function be(e){let t=Object.create(null);for(let r of Object.keys(e)){let a=e[r];typeof a=="string"&&a.startsWith("$")?t[r]=yt(a):typeof a=="object"&&a!==null&&!Array.isArray(a)?t[r]=be(a):t[r]=a;}return t}function ft(e){let t=/^(\d+)\s*([smhd])$/.exec(e.trim());if(!t)return null;let r=parseInt(t[1],10),a=t[2],s=jr[a];return !s||r<=0?null:r*s}function bt(e,t){let r=Object.create(null);if(Object.assign(r,ut),e){let l=e.cloud;l&&typeof l=="object"&&(typeof l.endpoint=="string"&&(r.endpoint=l.endpoint),typeof l.upload=="string"&&(r.uploadStrategy=l.upload),typeof l.timeout=="number"&&(r.timeout=l.timeout),typeof l.apiKey=="string"&&l.apiKey.length>0&&(r.apiKey=l.apiKey));let d=e["testrelic-repo"]??e.project;d&&typeof d=="object"&&typeof d.name=="string"&&(r.projectName=d.name),e.project&&!e["testrelic-repo"]&&process.stderr.write(`[testrelic] Deprecation: "project" key in config is deprecated. Rename it to "testrelic-repo".
|
|
5
|
+
`);let u=e.queue;if(u&&typeof u=="object"){if(typeof u.maxAge=="string"){let f=ft(u.maxAge);f!==null&&(r.queueMaxAge=f);}typeof u.directory=="string"&&(r.queueDirectory=u.directory);}}if(t){if(typeof t.apiKey=="string"&&t.apiKey.length>0){let l=t.apiKey.startsWith("$")?yt(t.apiKey):t.apiKey;l&&(r.apiKey=l);}if(typeof t.endpoint=="string"&&(r.endpoint=t.endpoint),typeof t.upload=="string"&&(r.uploadStrategy=t.upload),typeof t.timeout=="number"&&(r.timeout=t.timeout),typeof t.projectName=="string"&&(r.projectName=t.projectName),typeof t.queueMaxAge=="string"){let l=ft(t.queueMaxAge);l!==null&&(r.queueMaxAge=l);}typeof t.queueDirectory=="string"&&(r.queueDirectory=t.queueDirectory),typeof t.uploadArtifacts=="boolean"&&(r.uploadArtifacts=t.uploadArtifacts),typeof t.artifactMaxSizeMb=="number"&&(r.artifactMaxSizeMb=t.artifactMaxSizeMb);}let a=process.env.TESTRELIC_API_KEY;a&&a.length>0&&(r.apiKey=a);let s=process.env.TESTRELIC_CLOUD_ENDPOINT;s&&isValidEndpointUrl(s)&&(r.endpoint=s);let n=process.env.TESTRELIC_UPLOAD_STRATEGY;n&&["batch","realtime","both"].includes(n)&&(r.uploadStrategy=n);let o=process.env.TESTRELIC_CLOUD_TIMEOUT;if(o){let l=parseInt(o,10);!isNaN(l)&&l>=1e3&&l<=12e4&&(r.timeout=l);}let i=Object.freeze(r);return isValidCloudConfig(i)?i:ut}var Kr=[/AKIA[A-Z0-9]{16}/g,/Bearer\s+[A-Za-z0-9\-._~+/]+=*/g,/-----BEGIN\s+(RSA\s+)?PRIVATE\sKEY-----[\s\S]*?-----END/g,/\/\/[^:]+:[^@]+@/g],Jr=["authorization","cookie","set-cookie","x-api-key"],Yr=["password","secret","token","apiKey","api_key"];function xt(e){let t=Object.create(null);return t.trackApiCalls=e?.trackApiCalls??true,t.captureRequestHeaders=e?.captureRequestHeaders??true,t.captureResponseHeaders=e?.captureResponseHeaders??true,t.captureRequestBody=e?.captureRequestBody??true,t.captureResponseBody=e?.captureResponseBody??true,t.captureAssertions=e?.captureAssertions??true,t.redactHeaders=e?.redactHeaders??[...Jr],t.redactBodyFields=e?.redactBodyFields??[...Yr],t.apiIncludeUrls=e?.apiIncludeUrls??[],t.apiExcludeUrls=e?.apiExcludeUrls??[],Object.freeze(t)}function wt(e){if(e!==void 0&&!isValidConfig(e))throw createError(ErrorCode.CONFIG_INVALID,"Invalid reporter configuration");let t=Object.create(null);t.outputPath=e?.outputPath??"./test-results/analytics-timeline.json",t.includeStackTrace=e?.includeStackTrace??false,t.includeCodeSnippets=e?.includeCodeSnippets??true,t.codeContextLines=e?.codeContextLines??3,t.includeNetworkStats=e?.includeNetworkStats??true,t.navigationTypes=e?.navigationTypes??null,t.redactPatterns=[...Kr,...e?.redactPatterns??[]],t.testRunId=e?.testRunId??null,t.metadata=e?.metadata??null;let r=t.outputPath;t.openReport=e?.openReport??true,t.htmlReportPath=e?.htmlReportPath??r.replace(/\.json$/,".html"),t.includeArtifacts=e?.includeArtifacts??true,t.trackApiCalls=e?.trackApiCalls??true,t.quiet=e?.quiet??false,t.includeActionSteps=e?.includeActionSteps??true,t.captureConsoleLogs=e?.captureConsoleLogs??true;let a=ht(process.cwd()),s=a?mt(a):null,n=s?be(s):null,o=bt(n,e?.cloud);return t.cloud=o.apiKey?o:null,t.reportMode=e?.reportMode??"streaming",t.streamingThreshold=e?.streamingThreshold??0,Object.freeze(t)}var xe="1.3.0";function kt(e,t,r){try{let s=readFileSync(e,"utf-8").split(`
|
|
6
|
+
`);if(t<1||t>s.length)return null;let n=Math.max(1,t-r),o=Math.min(s.length,t+r),i=[];for(let l=n;l<=o;l++){let d=l===t?">":" ",u=String(l).padStart(String(o).length," ");i.push(`${d} ${u} | ${s[l-1]}`);}return i.join(`
|
|
7
7
|
`)}catch{return null}}function Tt(e){return t=>{let r=t;for(let a of e)if(typeof a=="string"){let s=a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");r=r.replace(new RegExp(s,"g"),"[REDACTED]");}else {let s=a.flags.includes("g")?a.flags:a.flags+"g",n=new RegExp(a.source,s);r=r.replace(n,"[REDACTED]");}return r}}function Qr(e){return e.GITHUB_ACTIONS!=="true"?null:{provider:"github-actions",buildId:e.GITHUB_RUN_ID??null,commitSha:e.GITHUB_SHA??null,branch:e.GITHUB_REF_NAME??null,runUrl:e.GITHUB_SERVER_URL&&e.GITHUB_REPOSITORY&&e.GITHUB_RUN_ID?`${e.GITHUB_SERVER_URL}/${e.GITHUB_REPOSITORY}/actions/runs/${e.GITHUB_RUN_ID}`:null}}function Xr(e){return e.GITLAB_CI!=="true"?null:{provider:"gitlab-ci",buildId:e.CI_PIPELINE_ID??null,commitSha:e.CI_COMMIT_SHA??null,branch:e.CI_COMMIT_BRANCH??e.CI_COMMIT_REF_NAME??null,runUrl:e.CI_PIPELINE_URL??null}}function ea(e){if(!e.JENKINS_URL)return null;let t=e.GIT_BRANCH??null;return t?.startsWith("origin/")&&(t=t.slice(7)),{provider:"jenkins",buildId:e.BUILD_ID??null,commitSha:e.GIT_COMMIT??null,branch:t,runUrl:e.BUILD_URL??null}}function ta(e){return e.CIRCLECI!=="true"?null:{provider:"circleci",buildId:e.CIRCLE_BUILD_NUM??null,commitSha:e.CIRCLE_SHA1??null,branch:e.CIRCLE_BRANCH??null,runUrl:e.CIRCLE_BUILD_URL??null}}function ra(e){if(!e.BITBUCKET_PIPELINE_UUID)return null;let t=e.BITBUCKET_WORKSPACE&&e.BITBUCKET_REPO_SLUG&&e.BITBUCKET_PIPELINE_UUID?`https://bitbucket.org/${e.BITBUCKET_WORKSPACE}/${e.BITBUCKET_REPO_SLUG}/pipelines/results/${e.BITBUCKET_PIPELINE_UUID}`:null;return {provider:"bitbucket-pipelines",buildId:e.BITBUCKET_BUILD_NUMBER??null,commitSha:e.BITBUCKET_COMMIT??null,branch:e.BITBUCKET_BRANCH??null,runUrl:t}}var aa=[Qr,Xr,ea,ta,ra];function P(e){let t=process.env;for(let r of aa){let a=r(t);if(a)return a}return null}var we=`
|
|
8
8
|
/* Theme Variables */
|
|
9
9
|
:root,[data-theme="dark"]{
|
|
@@ -2544,23 +2544,23 @@ ${o?`<script id="artifact-manifest-data" type="application/json">${o}</script>`:
|
|
|
2544
2544
|
<script>${d}</script>
|
|
2545
2545
|
</body>
|
|
2546
2546
|
</html>`}function Ee(e){try{let t=process.platform,r;t==="darwin"?r=`open "${e}"`:t==="win32"?r=`start "" "${e}"`:r=`xdg-open "${e}"`,exec(r,a=>{a&&process.stderr.write(`[testrelic] Failed to open browser: ${a.message}
|
|
2547
|
-
`);});}catch{}}join(tmpdir(),"testrelic-data");async function Ct(e,t,r,a){let s=(t-1)*r,n=[],o=0,i=createInterface({input:createReadStream(e,{encoding:"utf-8"}),crlfDelay:1/0});for await(let
|
|
2547
|
+
`);});}catch{}}join(tmpdir(),"testrelic-data");async function Ct(e,t,r,a){let s=(t-1)*r,n=[],o=0,i=createInterface({input:createReadStream(e,{encoding:"utf-8"}),crlfDelay:1/0});for await(let u of i)if(u.length!==0){if(o>=s&&n.length<r)try{n.push(JSON.parse(u));}catch{}if(o++,n.length>=r&&a!==void 0)break}let l=a??o,d=Math.max(1,Math.ceil(l/r));return {items:n,total:l,page:t,pageSize:r,totalPages:d}}var _t=/^[a-f0-9]{12}$/,Pe=/^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}(-\d+)?$/,It=500;function x(e,t,r){e.writeHead(t,{"Content-Type":"application/json"}),e.end(JSON.stringify(r));}function Lt(e){e.setHeader("Access-Control-Allow-Origin","*"),e.setHeader("Access-Control-Allow-Methods","GET, DELETE, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type");}function re(e){try{return existsSync(e)?JSON.parse(readFileSync(e,"utf-8")):null}catch{return null}}function ae(e){let t=0;try{let r=readdirSync(e,{withFileTypes:!0});for(let a of r){let s=join(e,a.name);a.isFile()?t+=statSync(s).size:a.isDirectory()&&(t+=ae(s));}}catch{}return t}function Mt(e,t,r,a){let s=re(join(r,"index.json"));x(t,200,{status:"ok",reportMode:"streaming",testCount:s?.length??0,uptime:Math.floor((Date.now()-a)/1e3)});}function Et(e,t,r){let a=re(join(r,"summary.json"));if(!a){x(t,404,{error:"Summary not found"});return}x(t,200,a);}function Pt(e,t,r){let a=re(join(r,"index.json"));if(!a){x(t,404,{error:"Test index not found"});return}let n=new URL(e.url??"/",`http://${e.headers.host}`).searchParams,o=Math.max(1,parseInt(n.get("page")??"1",10)||1),i=Math.min(It,Math.max(1,parseInt(n.get("pageSize")??"100",10)||100)),l=n.get("status")?.split(",").filter(Boolean)??null,d=n.get("file")??null,u=n.get("search")?.toLowerCase()??null,f=n.get("tag")?.split(",").filter(Boolean)??null,m=n.get("sort")??"file",c=n.get("order")==="desc"?-1:1,p=a;l&&l.length>0&&(p=p.filter(y=>l.includes(y.status))),d&&(p=p.filter(y=>y.filePath===d||y.filePath.startsWith(d+"/"))),u&&(p=p.filter(y=>y.title.toLowerCase().includes(u)||y.filePath.toLowerCase().includes(u))),f&&f.length>0&&(p=p.filter(y=>f.some(b=>y.tags.includes(b)))),p=[...p].sort((y,b)=>{let T=0;switch(m){case "duration":T=y.duration-b.duration;break;case "status":T=y.status.localeCompare(b.status);break;case "title":T=y.title.localeCompare(b.title);break;default:T=y.filePath.localeCompare(b.filePath);break}return T*c});let g=p.length,h=Math.max(1,Math.ceil(g/i)),w=(o-1)*i,S=p.slice(w,w+i);x(t,200,{tests:S,pagination:{page:o,pageSize:i,totalItems:g,totalPages:h},filters:{status:l,file:d,search:u,tag:f}});}function Ft(e,t,r,a){if(!_t.test(a)){x(t,400,{error:"Invalid test ID format"});return}let s=join(r,"tests",a,"meta.json"),n=join(r,"tests",`${a}.json`),o=existsSync(s)?s:existsSync(n)?n:null;if(!o){x(t,404,{error:`Test not found: ${a}`});return}try{let i=readFileSync(o,"utf-8");t.writeHead(200,{"Content-Type":"application/json"}),t.end(i);}catch(i){x(t,500,{error:i instanceof Error?i.message:String(i)});}}async function Dt(e,t,r,a,s){if(!_t.test(a)){x(t,400,{error:"Invalid test ID format"});return}let o=join(r,"tests",a,{network:"network.jsonl",console:"console.jsonl","api-calls":"api-calls.jsonl"}[s]);if(!existsSync(o)){x(t,200,{items:[],total:0,page:1,pageSize:50,totalPages:0});return}try{let l=new URL(e.url??"/",`http://${e.headers.host}`).searchParams,d=Math.max(1,parseInt(l.get("page")??"1",10)||1),u=Math.min(It,Math.max(1,parseInt(l.get("pageSize")??"50",10)||50)),f,m=join(r,"tests",a,"meta.json");if(existsSync(m))try{let p=JSON.parse(readFileSync(m,"utf-8"));switch(s){case "network":f=p.networkRequestsCount;break;case "console":f=p.consoleLogsCount;break;case "api-calls":f=p.apiCallsCount;break}}catch{}let c=await Ct(o,d,u,f);x(t,200,c);}catch(i){x(t,500,{error:i instanceof Error?i.message:String(i)});}}function Nt(e,t,r){let a=re(join(r,"index.json"));if(!a){x(t,404,{error:"Test index not found"});return}let s=new Map;for(let o of a){if(o.isRetry)continue;let i=s.get(o.filePath);switch(i||(i={total:0,passed:0,failed:0,flaky:0,skipped:0,timedOut:0},s.set(o.filePath,i)),i.total++,o.status){case "passed":i.passed++;break;case "failed":i.failed++;break;case "flaky":i.flaky++;break;case "skipped":i.skipped++;break;case "timedout":i.timedOut++;break}}let n=Array.from(s.entries()).map(([o,i])=>({filePath:o,...i})).sort((o,i)=>o.filePath.localeCompare(i.filePath));x(t,200,{files:n});}function Bt(e,t,r){if(!existsSync(r)){x(t,200,{runs:[],totalSizeBytes:0});return}try{let a=[],s=0,n=readdirSync(r,{withFileTypes:!0});for(let o of n){if(!o.isDirectory()||!Pe.test(o.name))continue;let i=join(r,o.name),l=ae(i),d=readdirSync(i,{withFileTypes:!0}).filter(u=>u.isDirectory());a.push({folderName:o.name,totalSizeBytes:l,testCount:d.length}),s+=l;}a.sort((o,i)=>i.folderName.localeCompare(o.folderName)),x(t,200,{runs:a,totalSizeBytes:s});}catch(a){x(t,500,{error:a instanceof Error?a.message:String(a)});}}function Ot(e,t,r){try{let a=0,s=0;if(existsSync(r)){let n=readdirSync(r,{withFileTypes:!0});for(let o of n){if(!o.isDirectory()||!Pe.test(o.name))continue;let i=join(r,o.name),l=ae(i);rmSync(i,{recursive:!0,force:!0}),s+=l,a++;}}x(t,200,{deletedCount:a,freedBytes:s});}catch(a){x(t,500,{error:a instanceof Error?a.message:String(a)});}}function qt(e,t,r,a){if(!Pe.test(a)){x(t,400,{error:"Invalid folder name"});return}let s=join(r,a);try{if(!statSync(s).isDirectory()){x(t,404,{error:"Not found"});return}}catch{x(t,404,{error:"Not found"});return}try{let n=ae(s);rmSync(s,{recursive:!0,force:!0}),x(t,200,{deleted:a,freedBytes:n});}catch(n){x(t,500,{error:n instanceof Error?n.message:String(n)});}}function Ht(e,t,r){x(t,200,{status:"shutting_down"}),r.close();}function Fe(e,t,r){if(!existsSync(r)){x(t,404,{error:"File not found"});return}try{let a=readFileSync(r),s=extname(r).toLowerCase(),n=pa[s]??"application/octet-stream";t.writeHead(200,{"Content-Type":n}),t.end(a);}catch(a){x(t,500,{error:a instanceof Error?a.message:String(a)});}}var pa={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".webm":"video/webm",".mp4":"video/mp4",".json":"application/json",".html":"text/html",".css":"text/css",".js":"text/javascript",".svg":"image/svg+xml"};var ha=9323,ma=10,va=1800*1e3;function W(e,t){return new Promise((r,a)=>{let s=t?.port??ha,n=Date.now(),o,i=0,l=existsSync(join(e,"artifacts"))?join(e,"artifacts"):existsSync(join(e,"..","artifacts"))?join(e,"..","artifacts"):join(e,"artifacts");function d(){clearTimeout(o),o=setTimeout(()=>{f.close();},va);}let u=t?.htmlPath??null;if(!u){let c=dirname(e);try{let p=readdirSync(c).find(g=>g.endsWith(".html"));p&&(u=join(c,p));}catch{}}let f=createServer((c,p)=>{if(d(),Lt(p),c.method==="OPTIONS"){p.writeHead(204),p.end();return}let g;try{g=new URL(c.url??"/",`http://${c.headers.host??"localhost"}`).pathname;}catch{x(p,400,{error:"Invalid URL"});return}if(c.method==="GET"&&(g==="/"||g==="/index.html")){if(u&&existsSync(u)){Fe(c,p,u);return}x(p,404,{error:"HTML report not found"});return}if(c.method==="GET"&&g==="/api/health"){Mt(c,p,e,n);return}if(c.method==="GET"&&g==="/api/summary"){Et(c,p,e);return}if(c.method==="GET"&&g==="/api/tests"){Pt(c,p,e);return}if(c.method==="GET"&&g==="/api/files"){Nt(c,p,e);return}let h=g.match(/^\/api\/tests\/([a-f0-9]+)\/(network|console|api-calls)$/);if(c.method==="GET"&&h){Dt(c,p,e,h[1],h[2]);return}let w=g.match(/^\/api\/tests\/([a-f0-9]+)$/);if(c.method==="GET"&&w){Ft(c,p,e,w[1]);return}if(c.method==="GET"&&g==="/api/artifacts"){Bt(c,p,l);return}if(c.method==="DELETE"&&g==="/api/artifacts"){Ot(c,p,l);return}let S=g.match(/^\/api\/artifacts\/(.+)$/);if(c.method==="DELETE"&&S){qt(c,p,l,decodeURIComponent(S[1]));return}if(c.method==="GET"&&g.startsWith("/artifacts/")){let y=decodeURIComponent(g.slice(11));if(y.includes("..")||y.includes("\0")){x(p,400,{error:"Invalid path"});return}Fe(c,p,join(l,y));return}if(c.method==="POST"&&g==="/api/shutdown"){Ht(c,p,f);return}x(p,404,{error:"Not found"});});function m(c){let p=g=>{g.code==="EADDRINUSE"&&i<ma?(i++,m(c+1)):a(g);};f.once("error",p),f.listen(c,"127.0.0.1",()=>{f.removeListener("error",p);let g=f.address();if(!g||typeof g=="string"){a(new Error("Failed to get server address"));return}d(),r({port:g.port,dispose:()=>new Promise(h=>{clearTimeout(o),f.close(()=>h());})});});}m(s);})}async function Ut(e){let t=await W(e);return {port:t.port,dispose:t.dispose}}function xa(e,t,r){let a=JSON.stringify(e),s=r?JSON.stringify(r):null;return St(a,t,s)}async function Ne(e,t,r){try{let a=null,s=null,n=t.reportMode==="streaming"||t.reportMode==="auto"&&e.timeline.length===0&&e.summary.total>=t.streamingThreshold,o,i=t.htmlReportPath,l=dirname(i);mkdirSync(l,{recursive:!0});let d="",u="[]",f=null;if(n){d=JSON.stringify(e.summary);let c=dirname(t.outputPath),p=join(c,".testrelic-report");try{let g=join(p,"index-compact.json"),h=join(p,"index.json");existsSync(g)?u=readFileSync(g,"utf-8"):existsSync(h)&&(u=readFileSync(h,"utf-8"));}catch{}f=r?JSON.stringify(r):null,o=Me(d,u,null,f);}else o=xa(e,null,r);let m=i+".tmp";if(writeFileSync(m,o,"utf-8"),renameSync(m,i),n){if(e.ci===null){let c=dirname(t.outputPath),p=join(c,".testrelic-report"),g=resolve(i);try{if(await ka(),a=await Ta(p),!a){s=await W(p,{htmlPath:g}),a=s.port;let h=setInterval(()=>{},6e4),w=()=>{clearInterval(h),s?.dispose();};process.on("SIGINT",w),process.on("SIGTERM",w),setTimeout(w,1800*1e3).unref();}if(a){process.stderr.write(`
|
|
2548
2548
|
Report server: http://127.0.0.1:${a}
|
|
2549
|
-
`);try{let h=Me(d,
|
|
2550
|
-
`);}}var ie=9323,Gt=10;async function wa(){for(let e=ie;e<ie+Gt;e++)try{let t=new AbortController,r=setTimeout(()=>t.abort(),500),a=await fetch(`http://127.0.0.1:${e}/api/health`,{signal:t.signal});if(clearTimeout(r),a.ok)return e}catch{}return null}async function ka(){for(let e=ie;e<ie+Gt;e++)try{let t=new AbortController,r=setTimeout(()=>t.abort(),1e3);await fetch(`http://127.0.0.1:${e}/api/shutdown`,{method:"POST",signal:t.signal}),clearTimeout(r);}catch{}await new Promise(e=>setTimeout(e,300));}async function Ta(e){let r=[join(__dirname,"cli.cjs"),join(__dirname,"cli.js"),join(__dirname,"..","dist","cli.cjs"),join(__dirname,"..","dist","cli.js")].find(a=>existsSync(a));if(!r){let a=await W(e);return process.on("exit",()=>{a?.dispose();}),a.port}return new Promise(a=>{let s=spawn(process.execPath,[r,"serve",e],{detached:true,stdio:["ignore","ignore","pipe"],env:{...process.env}}),n="",o=false,i=setTimeout(()=>{o||(o=true,s.stderr?.removeAllListeners(),wa().then(a));},5e3);s.stderr?.on("data",l=>{n+=l.toString();let d=n.match(/127\.0\.0\.1:(\d+)/);d&&!o&&(o=true,clearTimeout(i),s.stderr?.removeAllListeners(),s.unref(),a(Number(d[1])));}),s.on("error",()=>{o||(o=true,clearTimeout(i),a(null));}),s.on("exit",()=>{o||(o=true,clearTimeout(i),a(null));}),s.unref();})}var Ra=20,Be=0,Oe=[];async function Kt(e,t){Be>=Ra&&await new Promise(r=>Oe.push(r)),Be++;try{return await copyFile(e,t),!0}catch{return false}finally{Be--,Oe.length>0&&Oe.shift()();}}var J=[];async function Jt(){J.length!==0&&(await Promise.allSettled(J),J.length=0);}function Yt(e){let t=new Date,r=n=>String(n).padStart(2,"0"),a=`${t.getFullYear()}-${r(t.getMonth()+1)}-${r(t.getDate())}T${r(t.getHours())}-${r(t.getMinutes())}-${r(t.getSeconds())}`;if(!existsSync(join(e,a)))return a;let s=1;for(;existsSync(join(e,`${a}-${s}`));)s++;return `${a}-${s}`}function Aa(e){let t=e.replace(/[^a-zA-Z0-9\-_ ]/g,"-").replace(/\s+/g,"-").replace(/-{2,}/g,"-").replace(/^-+|-+$/g,"");return t.length>100&&(t=t.substring(0,100).replace(/-+$/,"")),t||"unnamed-test"}function Zt(e,t,r,a,s){let n=e.find(m=>m.name==="screenshot"&&m.path),o=e.find(m=>m.name==="video"&&m.path);if(!n&&!o)return null;let i=Aa(t);r>0&&(i+=`--retry-${r}`);let l=s?["artifacts",s,i]:["artifacts",i],d=join(a,...l),
|
|
2549
|
+
`);try{let h=Me(d,u,a,f),w=i+".tmp";writeFileSync(w,h,"utf-8"),renameSync(w,i);}catch{}}}catch{}}}else if(t.openReport&&e.ci===null&&t.includeArtifacts){let c=dirname(t.outputPath),p=join(c,"artifacts");if(existsSync(p))try{let g=await Ut(p);a=g.port,process.on("exit",()=>{g.dispose();});}catch{}}if(t.openReport&&e.ci===null)if(n&&a)Ee(`http://127.0.0.1:${a}`);else {let c=resolve(i);Ee(c);}}catch(a){process.stderr.write(`[testrelic] Failed to write HTML report: ${a instanceof Error?a.message:String(a)}
|
|
2550
|
+
`);}}var ie=9323,Gt=10;async function wa(){for(let e=ie;e<ie+Gt;e++)try{let t=new AbortController,r=setTimeout(()=>t.abort(),500),a=await fetch(`http://127.0.0.1:${e}/api/health`,{signal:t.signal});if(clearTimeout(r),a.ok)return e}catch{}return null}async function ka(){for(let e=ie;e<ie+Gt;e++)try{let t=new AbortController,r=setTimeout(()=>t.abort(),1e3);await fetch(`http://127.0.0.1:${e}/api/shutdown`,{method:"POST",signal:t.signal}),clearTimeout(r);}catch{}await new Promise(e=>setTimeout(e,300));}async function Ta(e){let r=[join(__dirname,"cli.cjs"),join(__dirname,"cli.js"),join(__dirname,"..","dist","cli.cjs"),join(__dirname,"..","dist","cli.js")].find(a=>existsSync(a));if(!r){let a=await W(e);return process.on("exit",()=>{a?.dispose();}),a.port}return new Promise(a=>{let s=spawn(process.execPath,[r,"serve",e],{detached:true,stdio:["ignore","ignore","pipe"],env:{...process.env}}),n="",o=false,i=setTimeout(()=>{o||(o=true,s.stderr?.removeAllListeners(),wa().then(a));},5e3);s.stderr?.on("data",l=>{n+=l.toString();let d=n.match(/127\.0\.0\.1:(\d+)/);d&&!o&&(o=true,clearTimeout(i),s.stderr?.removeAllListeners(),s.unref(),a(Number(d[1])));}),s.on("error",()=>{o||(o=true,clearTimeout(i),a(null));}),s.on("exit",()=>{o||(o=true,clearTimeout(i),a(null));}),s.unref();})}var Ra=20,Be=0,Oe=[];async function Kt(e,t){Be>=Ra&&await new Promise(r=>Oe.push(r)),Be++;try{return await copyFile(e,t),!0}catch{return false}finally{Be--,Oe.length>0&&Oe.shift()();}}var J=[];async function Jt(){J.length!==0&&(await Promise.allSettled(J),J.length=0);}function Yt(e){let t=new Date,r=n=>String(n).padStart(2,"0"),a=`${t.getFullYear()}-${r(t.getMonth()+1)}-${r(t.getDate())}T${r(t.getHours())}-${r(t.getMinutes())}-${r(t.getSeconds())}`;if(!existsSync(join(e,a)))return a;let s=1;for(;existsSync(join(e,`${a}-${s}`));)s++;return `${a}-${s}`}function Aa(e){let t=e.replace(/[^a-zA-Z0-9\-_ ]/g,"-").replace(/\s+/g,"-").replace(/-{2,}/g,"-").replace(/^-+|-+$/g,"");return t.length>100&&(t=t.substring(0,100).replace(/-+$/,"")),t||"unnamed-test"}function Zt(e,t,r,a,s){let n=e.find(m=>m.name==="screenshot"&&m.path),o=e.find(m=>m.name==="video"&&m.path);if(!n&&!o)return null;let i=Aa(t);r>0&&(i+=`--retry-${r}`);let l=s?["artifacts",s,i]:["artifacts",i],d=join(a,...l),u=l,f={};try{mkdirSync(d,{recursive:!0});}catch{return null}if(n?.path&&existsSync(n.path)){let c=`screenshot${extname(n.path)||".png"}`,p=join(d,c);J.push(Kt(n.path,p).then(()=>{})),f.screenshot=`${u.join("/")}/${c}`;}if(o?.path&&existsSync(o.path)){let c=`video${extname(o.path)||".webm"}`,p=join(d,c);J.push(Kt(o.path,p).then(()=>{})),f.video=`${u.join("/")}/${c}`;}return !f.screenshot&&!f.video?null:f}var La=/^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}(-\d+)?$/,Ma="1.0";function Ea(e){let t=extname(e).toLowerCase();return [".png",".jpg",".jpeg",".gif",".bmp",".webp"].includes(t)?"screenshot":[".webm",".mp4",".avi",".mov"].includes(t)?"video":"other"}function Qt(e,t,r){let a=[];try{let s=readdirSync(e,{withFileTypes:!0});for(let n of s){if(!n.isFile())continue;let o=join(e,n.name),i=statSync(o);a.push({name:n.name,type:Ea(n.name),relativePath:`artifacts/${t}/${r}/${n.name}`,sizeBytes:i.size});}}catch{}return {testName:r,files:a}}function Pa(e,t){let r=join(e,t),a=[],s=0;try{let o=readdirSync(r,{withFileTypes:!0});for(let i of o){if(!i.isDirectory())continue;let l=Qt(join(r,i.name),t,i.name);a.push(l);for(let d of l.files)s+=d.sizeBytes;}}catch{}let n=t.replace(/^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})/,"$1T$2:$3:$4").replace(/-\d+$/,"");return a.sort((o,i)=>o.testName.localeCompare(i.testName)),{folderName:t,timestamp:n,totalSizeBytes:s,testCount:a.length,tests:a,isCurrentRun:false}}function Xt(e,t){let r=join(e,"artifacts"),a=[],s=[],n=0;try{let i=readdirSync(r,{withFileTypes:!0});for(let l of i)if(l.isDirectory())if(La.test(l.name)){let d=Pa(r,l.name);a.push({...d,isCurrentRun:l.name===t});}else {let d=Qt(join(r,l.name),l.name,l.name);s.push(d);for(let u of d.files)n+=u.sizeBytes;}}catch{}a.sort((i,l)=>l.timestamp.localeCompare(i.timestamp)),s.length>0&&(s.sort((i,l)=>i.testName.localeCompare(l.testName)),a.push({folderName:"__legacy__",timestamp:"1970-01-01T00:00:00",totalSizeBytes:n,testCount:s.length,tests:s,isCurrentRun:false}));let o=a.reduce((i,l)=>i+l.totalSizeBytes,0);return {schemaVersion:Ma,generatedAt:new Date().toISOString(),artifactBaseDir:"artifacts",totalSizeBytes:o,runs:a,serverPort:null}}function Oa(e){let t=e,{root:r}=parse(t);for(;t!==r;){if(existsSync(join(t,".git")))return t;t=join(t,"..");}return null}function tr(e){try{let t=Oa(e);if(!t)return;let r=join(t,".gitignore"),a=relative(t,e).replace(/\\/g,"/"),s=a.endsWith("/")?a:`${a}/`;if(existsSync(r)&&readFileSync(r,"utf-8").split(`
|
|
2551
2551
|
`).map(l=>l.trim()).some(l=>l===s||l===a))return;let n=`
|
|
2552
2552
|
# TestRelic test artifacts
|
|
2553
2553
|
${s}
|
|
2554
|
-
`;appendFileSync(r,n,"utf-8");}catch{}}function sr(e,t){let r=[];for(let a of e){let s=qa(a),n=Ha(a),o=Ua(a,s,n,t),i=za(a,s,n);if(o.length===0&&i.length===0){r.push({type:"navigation",url:"about:blank",timestamp:a.startedAt,durationOnUrl:a.duration,navigationType:"dummy",domContentLoadedAt:null,networkIdleAt:null,networkStats:null,specFile:a.specFile,test:s,tests:[n],_testTitle:a.title});continue}r.push(...o,...i);}return r.sort($a),ja(r,e),r.map((a,s)=>a.type==="navigation"?{...{index:s,type:"navigation",url:a.url,timestamp:a.timestamp,durationOnUrl:a.durationOnUrl??0,navigationType:a.navigationType,domContentLoadedAt:a.domContentLoadedAt??null,networkIdleAt:a.networkIdleAt??null,networkStats:a.networkStats??null,specFile:a.specFile,test:a.test},tests:a.tests}:{...{index:s,type:"api_call",callId:a.callId,method:a.method,url:a.url,timestamp:a.timestamp,responseTime:a.responseTime??null,request:a.request,response:a.response??null,...a.error?{error:a.error}:{},assertions:a.assertions??[],specFile:a.specFile,test:a.test},tests:a.tests})}function qa(e){return {title:e.title,fullTitle:e.titlePath,status:e.status,duration:e.duration,retries:e.retryCount,retry:e.retry,tags:e.tags,failure:e.failure}}function Ha(e){return {title:e.title,status:e.status,duration:e.duration,startedAt:e.startedAt,completedAt:e.completedAt,retryCount:e.retryCount,tags:e.tags,failure:e.failure,testId:e.testId,filePath:e.filePath,suiteName:e.suiteName,testType:e.testType,isFlaky:e.isFlaky,retryStatus:e.retryStatus,expectedStatus:e.expectedStatus,actualStatus:e.actualStatus,artifacts:e.artifacts,networkRequests:e.networkRequests,apiCalls:e.apiCalls,apiAssertions:e.apiAssertions,actions:e.actions,consoleLogs:e.consoleLogs}}function Ua(e,t,r,a){let s=[];for(let n of e.navigations)a.navigationTypes!==null&&!a.navigationTypes.includes(n.navigationType)||s.push({type:"navigation",url:n.url,timestamp:n.timestamp,durationOnUrl:0,navigationType:n.navigationType,domContentLoadedAt:n.domContentLoadedAt??null,networkIdleAt:n.networkIdleAt??null,networkStats:n.networkStats??null,specFile:e.specFile,test:t,tests:[r],_testTitle:e.title});return s}function za(e,t,r){if(!e.apiCalls||e.apiCalls.length===0)return [];let a=e.apiAssertions??[];return e.apiCalls.map(s=>{let n=a.filter(i=>i.callId===s.id).map(i=>({type:i.type,expected:i.expected,actual:i.actual,status:i.status,location:i.location,...i.expression!==void 0?{expression:i.expression}:{}})),o=null;return s.responseStatusCode!==null&&s.responseStatusText!==null&&(o={statusCode:s.responseStatusCode,statusText:s.responseStatusText,headers:s.responseHeaders,body:ar(s.responseBody)}),{type:"api_call",callId:s.id,method:s.method,url:s.url,timestamp:s.timestamp,responseTime:s.error?null:s.responseTimeMs,request:{headers:s.requestHeaders,body:ar(s.requestBody)},response:o,...s.error?{error:s.error}:{},assertions:n,specFile:e.specFile,test:t,tests:[r],_testTitle:e.title}})}function $a(e,t){let r=new Date(e.timestamp).getTime(),a=new Date(t.timestamp).getTime();if(r!==a)return r-a;let s={navigation:0,api_call:1},n=s[e.type],o=s[t.type];return n!==o?n-o:e.type==="api_call"&&t.type==="api_call"&&e.callId&&t.callId?rr(e.callId)-rr(t.callId):0}function rr(e){let t=e.match(/(\d+)$/);return t?parseInt(t[1],10):0}function ja(e,t){for(let r=0;r<e.length;r++){let a=e[r];if(a.type!=="navigation")continue;let s=new Date(a.timestamp).getTime(),n=null;for(let o=r+1;o<e.length;o++)if(e[o]._testTitle===a._testTitle){n=new Date(e[o].timestamp).getTime();break}if(n!==null)a.durationOnUrl=Math.max(0,n-s);else {let o=t.find(i=>i.title===a._testTitle);if(o){let i=new Date(o.completedAt).getTime();a.durationOnUrl=Math.max(0,i-s);}}}}function ar(e){if(e==null)return null;try{return JSON.parse(e)}catch{return e}}function Ue(e,t){let r=e.length,a=Math.min(Math.ceil(t/100*r)-1,r-1);return e[Math.max(0,a)]}function Va(e){try{let t=new URL(e);return t.origin+t.pathname}catch{return e}}function Ga(e){return e==null?"error":e>=200&&e<300?"2xx":e>=300&&e<400?"3xx":e>=400&&e<500?"4xx":e>=500&&e<600?"5xx":"error"}function nr(e,t){let r=0,a=0,s=0,n=0,o=0;for(let v of e)switch(v.status){case "passed":r++;break;case "failed":a++;break;case "flaky":s++;break;case "skipped":n++;break;case "timedout":o++;break}let i=[];for(let v of e)v.apiCalls&&i.push(...v.apiCalls);let l=i.length,d=new Set,
|
|
2554
|
+
`;appendFileSync(r,n,"utf-8");}catch{}}function sr(e,t){let r=[];for(let a of e){let s=qa(a),n=Ha(a),o=Ua(a,s,n,t),i=za(a,s,n);if(o.length===0&&i.length===0){r.push({type:"navigation",url:"about:blank",timestamp:a.startedAt,durationOnUrl:a.duration,navigationType:"dummy",domContentLoadedAt:null,networkIdleAt:null,networkStats:null,specFile:a.specFile,test:s,tests:[n],_testTitle:a.title});continue}r.push(...o,...i);}return r.sort($a),ja(r,e),r.map((a,s)=>a.type==="navigation"?{...{index:s,type:"navigation",url:a.url,timestamp:a.timestamp,durationOnUrl:a.durationOnUrl??0,navigationType:a.navigationType,domContentLoadedAt:a.domContentLoadedAt??null,networkIdleAt:a.networkIdleAt??null,networkStats:a.networkStats??null,specFile:a.specFile,test:a.test},tests:a.tests}:{...{index:s,type:"api_call",callId:a.callId,method:a.method,url:a.url,timestamp:a.timestamp,responseTime:a.responseTime??null,request:a.request,response:a.response??null,...a.error?{error:a.error}:{},assertions:a.assertions??[],specFile:a.specFile,test:a.test},tests:a.tests})}function qa(e){return {title:e.title,fullTitle:e.titlePath,status:e.status,duration:e.duration,retries:e.retryCount,retry:e.retry,tags:e.tags,failure:e.failure}}function Ha(e){return {title:e.title,status:e.status,duration:e.duration,startedAt:e.startedAt,completedAt:e.completedAt,retryCount:e.retryCount,tags:e.tags,failure:e.failure,testId:e.testId,filePath:e.filePath,suiteName:e.suiteName,testType:e.testType,isFlaky:e.isFlaky,retryStatus:e.retryStatus,expectedStatus:e.expectedStatus,actualStatus:e.actualStatus,artifacts:e.artifacts,networkRequests:e.networkRequests,apiCalls:e.apiCalls,apiAssertions:e.apiAssertions,actions:e.actions,consoleLogs:e.consoleLogs}}function Ua(e,t,r,a){let s=[];for(let n of e.navigations)a.navigationTypes!==null&&!a.navigationTypes.includes(n.navigationType)||s.push({type:"navigation",url:n.url,timestamp:n.timestamp,durationOnUrl:0,navigationType:n.navigationType,domContentLoadedAt:n.domContentLoadedAt??null,networkIdleAt:n.networkIdleAt??null,networkStats:n.networkStats??null,specFile:e.specFile,test:t,tests:[r],_testTitle:e.title});return s}function za(e,t,r){if(!e.apiCalls||e.apiCalls.length===0)return [];let a=e.apiAssertions??[];return e.apiCalls.map(s=>{let n=a.filter(i=>i.callId===s.id).map(i=>({type:i.type,expected:i.expected,actual:i.actual,status:i.status,location:i.location,...i.expression!==void 0?{expression:i.expression}:{}})),o=null;return s.responseStatusCode!==null&&s.responseStatusText!==null&&(o={statusCode:s.responseStatusCode,statusText:s.responseStatusText,headers:s.responseHeaders,body:ar(s.responseBody)}),{type:"api_call",callId:s.id,method:s.method,url:s.url,timestamp:s.timestamp,responseTime:s.error?null:s.responseTimeMs,request:{headers:s.requestHeaders,body:ar(s.requestBody)},response:o,...s.error?{error:s.error}:{},assertions:n,specFile:e.specFile,test:t,tests:[r],_testTitle:e.title}})}function $a(e,t){let r=new Date(e.timestamp).getTime(),a=new Date(t.timestamp).getTime();if(r!==a)return r-a;let s={navigation:0,api_call:1},n=s[e.type],o=s[t.type];return n!==o?n-o:e.type==="api_call"&&t.type==="api_call"&&e.callId&&t.callId?rr(e.callId)-rr(t.callId):0}function rr(e){let t=e.match(/(\d+)$/);return t?parseInt(t[1],10):0}function ja(e,t){for(let r=0;r<e.length;r++){let a=e[r];if(a.type!=="navigation")continue;let s=new Date(a.timestamp).getTime(),n=null;for(let o=r+1;o<e.length;o++)if(e[o]._testTitle===a._testTitle){n=new Date(e[o].timestamp).getTime();break}if(n!==null)a.durationOnUrl=Math.max(0,n-s);else {let o=t.find(i=>i.title===a._testTitle);if(o){let i=new Date(o.completedAt).getTime();a.durationOnUrl=Math.max(0,i-s);}}}}function ar(e){if(e==null)return null;try{return JSON.parse(e)}catch{return e}}function Ue(e,t){let r=e.length,a=Math.min(Math.ceil(t/100*r)-1,r-1);return e[Math.max(0,a)]}function Va(e){try{let t=new URL(e);return t.origin+t.pathname}catch{return e}}function Ga(e){return e==null?"error":e>=200&&e<300?"2xx":e>=300&&e<400?"3xx":e>=400&&e<500?"4xx":e>=500&&e<600?"5xx":"error"}function nr(e,t){let r=0,a=0,s=0,n=0,o=0;for(let v of e)switch(v.status){case "passed":r++;break;case "failed":a++;break;case "flaky":s++;break;case "skipped":n++;break;case "timedout":o++;break}let i=[];for(let v of e)v.apiCalls&&i.push(...v.apiCalls);let l=i.length,d=new Set,u={},f={"2xx":0,"3xx":0,"4xx":0,"5xx":0,error:0},m=[];for(let v of i)d.add(Va(v.url)),u[v.method]=(u[v.method]??0)+1,f[Ga(v.responseStatusCode)]+=1,m.push(v.responseTimeMs);let c=null;if(m.length>0){let v=[...m].sort((A,q)=>A-q),k=v.reduce((A,q)=>A+q,0);c={avg:Math.round(k/v.length),min:v[0],max:v[v.length-1],p50:Ue(v,50),p95:Ue(v,95),p99:Ue(v,99)};}let p=0,g=0,h=0;for(let v of e)if(v.apiAssertions)for(let k of v.apiAssertions)p++,k.status==="passed"?g++:h++;let w=0,S=new Set;for(let v of e){w+=v.navigations.length;for(let k of v.navigations)S.add(k.url);}let y=0,b={};function T(v){for(let k of v)y++,b[k.category]=(b[k.category]??0)+1,k.children.length>0&&T(k.children);}for(let v of e)v.actions&&T(v.actions);return {total:e.length,passed:r,failed:a,flaky:s,skipped:n,timedout:o,totalApiCalls:l,uniqueApiUrls:d.size,apiCallsByMethod:u,apiCallsByStatusRange:f,apiResponseTime:c,totalAssertions:p,passedAssertions:g,failedAssertions:h,totalNavigations:w,uniqueNavigationUrls:S.size,totalTimelineSteps:t,totalActionSteps:y,actionStepsByCategory:b}}function Wa(e){let t=56-e.length;return t>0?e+" ".repeat(t):e}function I(e){return `\u2502 ${Wa(e)} \u2502
|
|
2555
2555
|
`}function ze(e,t,r,a){if(a||e.total===0)return;let s=`\u250C${"\u2500".repeat(58)}\u2510
|
|
2556
2556
|
`,n=`\u2514${"\u2500".repeat(58)}\u2518
|
|
2557
|
-
`,o=I(""),i=s;i+=I("TestRelic AI - Playwright Test Report"),i+=o;let l=[];if(e.passed>0&&l.push(`${e.passed} \u2713`),e.failed>0&&l.push(`${e.failed} \u2717`),e.flaky>0&&l.push(`${e.flaky} \u26A0`),e.skipped>0&&l.push(`${e.skipped} skipped`),e.timedout>0&&l.push(`${e.timedout} timedout`),i+=I(`Tests: ${e.total} total (${l.join(" ")})`),e.totalNavigations>0&&(i+=I(`Navigations: ${e.totalNavigations} visits across ${e.uniqueNavigationUrls} unique URLs`)),e.totalApiCalls>0){i+=I(`API Calls: ${e.totalApiCalls} calls across ${e.uniqueApiUrls} unique endpoints`);let d=Object.entries(e.apiCallsByMethod).filter(([,f])=>f>0).map(([f,m])=>`${f}: ${m}`);d.length>0&&(i+=I(` ${d.join(" ")}`));let
|
|
2558
|
-
`,process.stderr.write(i);}var Ka=new Set(["pw:api","expect","test.step"]);function Ja(e){return e==="expect"?"assertion":e==="test.step"?"custom_step":"ui_action"}function $e(e,t,r){if(Ka.has(e.category)){let a=[];for(let n of e.steps)$e(n,t,a);let s=e.startTime.getTime()-t.getTime();r.push({title:e.title,category:Ja(e.category),timestamp:e.startTime.toISOString(),duration:e.duration,videoOffset:s>=0?s/1e3:null,status:e.error?"failed":"passed",error:e.error?.message??null,children:a});}else for(let a of e.steps)$e(a,t,r);}function ir(e,t){let r=[];for(let a of e)$e(a,t,r);return r}var Qa=new Set(["localhost","127.0.0.1","0.0.0.0"]);function lr(e){let t=new URL(e);if(t.protocol!=="https:"&&!(t.protocol==="http:"&&Qa.has(t.hostname)))throw createError(ErrorCode.CLOUD_CONFIG_INVALID,`HTTPS is required for cloud communication. Got: ${t.protocol}//${t.hostname}`)}var Xa=3e3;async function je(e){let t=new AbortController,r=setTimeout(()=>t.abort(),Xa);try{return (await fetch(`${e}/health`,{method:"GET",signal:t.signal})).ok}catch{return false}finally{clearTimeout(r);}}async function dr(e){return new Promise(t=>setTimeout(t,e))}async function or(e){try{let r=(await e.json()).error;if(r&&typeof r.message=="string"){let a=r.details;if(Array.isArray(a)&&a.length>0){let s=a.map(n=>`${n.field}: ${n.message}`).join(", ");return `${r.message} (${s})`}return r.message}}catch{}return `HTTP ${e.status}`}async function Ve(e,t,r){let a=new AbortController,s=setTimeout(()=>a.abort(),r);try{let n=await fetch(`${e}/sdk/auth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:t}),signal:a.signal});if(n.ok){let i=await n.json();return {accessToken:i.accessToken,refreshToken:i.refreshToken,expiresIn:i.expiresIn,orgId:i.orgId,orgName:i.orgName,userId:i.userId,userName:i.userName}}if(n.status===429){let i=n.headers.get("Retry-After"),l=i?parseInt(i,10)*1e3:5e3;if(!isNaN(l)&&l>0){await dr(l);let d=await fetch(`${e}/sdk/auth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:t}),signal:a.signal});if(d.ok){let
|
|
2559
|
-
`)[0]?.trim();return a?D(`git remote get-url ${a}`,e):null}function Z(e){let t=e.trim();return t=t.replace(/^[a-z+]+:\/\//,""),t=t.replace(/^[^@]+@/,""),t=t.replace(/:(?!\d)/,"/"),t=t.replace(/\.git$/,""),t=t.replace(/\/+$/,""),t=t.replace(/^[^@/]+@/,""),t.toLowerCase()}function fr(e){let t=e.split("/").filter(Boolean);return t[t.length-1]??e}function We(e){try{let t=readFileSync(join(e,"package.json"),"utf-8"),r=JSON.parse(t);return typeof r.name=="string"&&r.name.length>0?r.name:null}catch{return null}}function Q(e){let t=We(e);return t||`local/${basename(e)}`}var
|
|
2557
|
+
`,o=I(""),i=s;i+=I("TestRelic AI - Playwright Test Report"),i+=o;let l=[];if(e.passed>0&&l.push(`${e.passed} \u2713`),e.failed>0&&l.push(`${e.failed} \u2717`),e.flaky>0&&l.push(`${e.flaky} \u26A0`),e.skipped>0&&l.push(`${e.skipped} skipped`),e.timedout>0&&l.push(`${e.timedout} timedout`),i+=I(`Tests: ${e.total} total (${l.join(" ")})`),e.totalNavigations>0&&(i+=I(`Navigations: ${e.totalNavigations} visits across ${e.uniqueNavigationUrls} unique URLs`)),e.totalApiCalls>0){i+=I(`API Calls: ${e.totalApiCalls} calls across ${e.uniqueApiUrls} unique endpoints`);let d=Object.entries(e.apiCallsByMethod).filter(([,f])=>f>0).map(([f,m])=>`${f}: ${m}`);d.length>0&&(i+=I(` ${d.join(" ")}`));let u=Object.entries(e.apiCallsByStatusRange).filter(([,f])=>f>0).map(([f,m])=>`${f}: ${m}`);u.length>0&&(i+=I(` ${u.join(" ")}`)),e.apiResponseTime&&(i+=I(` Avg response: ${e.apiResponseTime.avg}ms P95: ${e.apiResponseTime.p95}ms`));}if(e.totalAssertions>0&&(i+=I(`Assertions: ${e.totalAssertions} total (${e.passedAssertions} \u2713 ${e.failedAssertions} \u2717)`)),e.totalActionSteps>0){let d=[],u=e.actionStepsByCategory;u.ui_action&&d.push(`${u.ui_action} UI`),u.assertion&&d.push(`${u.assertion} assertions`),u.custom_step&&d.push(`${u.custom_step} custom`);let f=d.length>0?` (${d.join(" ")})`:"";i+=I(`Actions: ${e.totalActionSteps} steps${f}`);}i+=I(`Report: ${r}`),i+=I(`Data: ${t}`),i+=n,i+=`For more information visit us at https://docs.testrelic.ai
|
|
2558
|
+
`,process.stderr.write(i);}var Ka=new Set(["pw:api","expect","test.step"]);function Ja(e){return e==="expect"?"assertion":e==="test.step"?"custom_step":"ui_action"}function $e(e,t,r){if(Ka.has(e.category)){let a=[];for(let n of e.steps)$e(n,t,a);let s=e.startTime.getTime()-t.getTime();r.push({title:e.title,category:Ja(e.category),timestamp:e.startTime.toISOString(),duration:e.duration,videoOffset:s>=0?s/1e3:null,status:e.error?"failed":"passed",error:e.error?.message??null,children:a});}else for(let a of e.steps)$e(a,t,r);}function ir(e,t){let r=[];for(let a of e)$e(a,t,r);return r}var Qa=new Set(["localhost","127.0.0.1","0.0.0.0"]);function lr(e){let t=new URL(e);if(t.protocol!=="https:"&&!(t.protocol==="http:"&&Qa.has(t.hostname)))throw createError(ErrorCode.CLOUD_CONFIG_INVALID,`HTTPS is required for cloud communication. Got: ${t.protocol}//${t.hostname}`)}var Xa=3e3;async function je(e){let t=new AbortController,r=setTimeout(()=>t.abort(),Xa);try{return (await fetch(`${e}/health`,{method:"GET",signal:t.signal})).ok}catch{return false}finally{clearTimeout(r);}}async function dr(e){return new Promise(t=>setTimeout(t,e))}async function or(e){try{let r=(await e.json()).error;if(r&&typeof r.message=="string"){let a=r.details;if(Array.isArray(a)&&a.length>0){let s=a.map(n=>`${n.field}: ${n.message}`).join(", ");return `${r.message} (${s})`}return r.message}}catch{}return `HTTP ${e.status}`}async function Ve(e,t,r){let a=new AbortController,s=setTimeout(()=>a.abort(),r);try{let n=await fetch(`${e}/sdk/auth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:t}),signal:a.signal});if(n.ok){let i=await n.json();return {accessToken:i.accessToken,refreshToken:i.refreshToken,expiresIn:i.expiresIn,orgId:i.orgId,orgName:i.orgName,userId:i.userId,userName:i.userName}}if(n.status===429){let i=n.headers.get("Retry-After"),l=i?parseInt(i,10)*1e3:5e3;if(!isNaN(l)&&l>0){await dr(l);let d=await fetch(`${e}/sdk/auth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:t}),signal:a.signal});if(d.ok){let u=await d.json();return {accessToken:u.accessToken,refreshToken:u.refreshToken,expiresIn:u.expiresIn,orgId:u.orgId,orgName:u.orgName,userId:u.userId,userName:u.userName}}}return {code:"rate_limited",message:"Rate limited during token exchange.",statusCode:429}}return n.status===400?{code:"validation_error",message:await or(n),statusCode:400}:n.status===401?{code:"invalid_key",message:"API key is invalid or has been revoked.",statusCode:401}:n.status===403?{code:"expired_key",message:"API key has expired.",statusCode:403}:{code:"server_error",message:await or(n),statusCode:n.status}}catch(n){return n instanceof DOMException&&n.name==="AbortError"?{code:"timeout",message:"Token exchange timed out.",statusCode:null}:{code:"network_error",message:"Failed to reach cloud for token exchange.",statusCode:null}}finally{clearTimeout(s);}}function Ge(e){return "code"in e&&typeof e.code=="string"&&["invalid_key","expired_key","validation_error","rate_limited","server_error","network_error","timeout"].includes(e.code)}async function cr(e,t,r){let a=new AbortController,s=setTimeout(()=>a.abort(),r);try{let n=await fetch(`${e}/sdk/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:t}),signal:a.signal});if(n.status===429){let i=n.headers.get("Retry-After"),l=i?parseInt(i,10)*1e3:5e3;!isNaN(l)&&l>0&&(await dr(l),n=await fetch(`${e}/sdk/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:t}),signal:a.signal}));}if(!n.ok)return null;let o=await n.json();return {accessToken:o.accessToken,refreshToken:o.refreshToken,expiresIn:o.expiresIn}}catch{return null}finally{clearTimeout(s);}}async function pr(e,t,r,a,s,n){let o=new AbortController,i=setTimeout(()=>o.abort(),s);try{let l={gitId:r,displayName:a};n&&(l.branch=n);let d=await fetch(`${e}/repos/resolve`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(l),signal:o.signal});if(!d.ok)return null;let u=await d.json();return {repoId:u.repoId,displayName:u.displayName}}catch{return null}finally{clearTimeout(i);}}var ss=5e3;function D(e,t){try{return execSync(e,{cwd:t,timeout:ss,encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()||null}catch{return null}}function ur(e){let t=D("git rev-parse --abbrev-ref HEAD",e),r=D("git rev-parse --short HEAD",e),a=D("git log -1 --pretty=%s",e),s=D("git log -1 --format=%an",e)??D("git config user.name",e),n=ns(e),o=n?Z(n):null;return {branch:t,commitSha:r,commitMessage:a,commitAuthor:s,remoteUrl:o}}function ns(e){let t=D("git remote get-url origin",e);if(t)return t;let r=D("git remote",e);if(!r)return null;let a=r.split(`
|
|
2559
|
+
`)[0]?.trim();return a?D(`git remote get-url ${a}`,e):null}function Z(e){let t=e.trim();return t=t.replace(/^[a-z+]+:\/\//,""),t=t.replace(/^[^@]+@/,""),t=t.replace(/:(?!\d)/,"/"),t=t.replace(/\.git$/,""),t=t.replace(/\/+$/,""),t=t.replace(/^[^@/]+@/,""),t.toLowerCase()}function fr(e){let t=e.split("/").filter(Boolean);return t[t.length-1]??e}function We(e){try{let t=readFileSync(join(e,"package.json"),"utf-8"),r=JSON.parse(t);return typeof r.name=="string"&&r.name.length>0?r.name:null}catch{return null}}function Q(e){let t=We(e);return t||`local/${basename(e)}`}var us=1048576,fs="1.0.0",gr=10;function X(e,t,r,a,s,n,o,i){try{mkdirSync(e,{recursive:!0});let l=Date.now(),d=`${l}-${t}-${r}.json`,u=join(e,d),f={version:fs,queuedAt:new Date(l).toISOString(),reason:a,retryCount:0,targetEndpoint:s,method:n,payload:o,headers:i},m=u+".tmp";writeFileSync(m,JSON.stringify(f,null,2),"utf-8"),renameSync(m,u);}catch(l){l.code==="ENOSPC"?process.stderr.write(`\u26A0 TestRelic: Disk full \u2014 unable to queue upload data.
|
|
2560
2560
|
`):process.stderr.write(`\u26A0 TestRelic: Unable to queue upload data.
|
|
2561
|
-
`);}}async function vr(e,t,r){let a;try{a=readdirSync(e).filter(n=>n.endsWith(".json")&&!n.endsWith(".tmp")).sort();}catch{return}if(a.length===0)return;let s=[];for(let n=0;n<a.length;n+=gr)s.push(a.slice(n,n+gr));for(let n of s)for(let o of n){let i=join(e,o);try{if(!statSync(i).isFile())continue;let d=readFileSync(i,"utf-8"),
|
|
2562
|
-
`),unlinkSync(i);continue}let f=
|
|
2563
|
-
`);return}try{if(lr(this.config.endpoint),this.gitMetadata=
|
|
2561
|
+
`);}}async function vr(e,t,r){let a;try{a=readdirSync(e).filter(n=>n.endsWith(".json")&&!n.endsWith(".tmp")).sort();}catch{return}if(a.length===0)return;let s=[];for(let n=0;n<a.length;n+=gr)s.push(a.slice(n,n+gr));for(let n of s)for(let o of n){let i=join(e,o);try{if(!statSync(i).isFile())continue;let d=readFileSync(i,"utf-8"),u=JSON.parse(d);if(!isValidQueueEntry(u)){process.stderr.write(`\u26A0 TestRelic: Corrupted queue file deleted: ${o}
|
|
2562
|
+
`),unlinkSync(i);continue}let f=u,m=JSON.stringify(f.payload),c={...f.headers,Authorization:`Bearer ${r}`},p=m;if(Buffer.byteLength(m,"utf-8")>us&&(p=gzipSync(Buffer.from(m,"utf-8")),c["Content-Encoding"]="gzip"),(await fetch(f.targetEndpoint,{method:f.method,headers:c,body:p})).ok)unlinkSync(i);else return}catch{return}}}function yr(e,t){try{let r=readdirSync(e).filter(s=>s.endsWith(".json")&&!s.endsWith(".tmp")),a=Date.now();for(let s of r){let n=join(e,s);try{let o=readFileSync(n,"utf-8"),l=JSON.parse(o).queuedAt;if(typeof l=="string"){let d=new Date(l).getTime();a-d>t&&unlinkSync(n);}}catch{try{unlinkSync(n);}catch{}}}}catch{}}var xs=3e5,ws=864e5,ks=6e4,br=3e4,Ye="https://platform.testrelic.ai/settings/api-keys",de=class{constructor(t){this.gitMetadata=null;this.repoId=null;this.failureReason=null;this.flushPromise=null;this.healthCheckTimer=null;this.config=t,this.authState={mode:"pending",accessToken:null,refreshToken:null,expiresAt:null,orgId:null,orgName:null,userId:null,userName:null};}async initialize(){if(!this.config||!this.config.apiKey){this.setLocalMode("no_api_key"),process.stderr.write(`\u2139 TestRelic: No API key configured. Running in local mode.
|
|
2563
|
+
`);return}try{if(lr(this.config.endpoint),this.gitMetadata=ur(),!await je(this.config.endpoint)){this.setLocalMode("cloud_unreachable"),process.stderr.write(`\u26A0 TestRelic: Cloud unreachable. Switching to local mode. Data will be queued for later upload.
|
|
2564
2564
|
`);return}let r=await Ve(this.config.endpoint,this.config.apiKey,this.config.timeout);if(Ge(r)){this.handleAuthError(r.code,r.statusCode);return}let a=r;this.authState={mode:"cloud",accessToken:a.accessToken,refreshToken:a.refreshToken,expiresAt:Date.now()+a.expiresIn*1e3,orgId:a.orgId,orgName:a.orgName,userId:a.userId,userName:a.userName},process.stderr.write(`\u2713 TestRelic: Connected to cloud (${a.orgName} / ${a.userName})
|
|
2565
2565
|
`),await this.resolveRepoId(),this.config.queueDirectory&&(yr(this.config.queueDirectory,this.config.queueMaxAge),this.startBackgroundFlush()),this.startHealthCheck();}catch(t){this.setLocalMode("unexpected_error"),process.stderr.write(`\u26A0 TestRelic: Unexpected error during cloud initialization. Running in local mode. ${t instanceof Error?t.message:String(t)}
|
|
2566
2566
|
`);}}getMode(){return this.authState.mode}isCloudMode(){return this.authState.mode==="cloud"}isLocalMode(){return this.authState.mode==="local"}getAccessToken(){return this.authState.accessToken}getRepoId(){return this.repoId}getGitMetadata(){return this.gitMetadata}getConfig(){return this.config}getFailureReason(){return this.failureReason}getEndpoint(){return this.config?.endpoint??"https://platform.testrelic.ai/api/v1"}async ensureValidToken(){if(!this.isCloudMode()||!this.authState.accessToken)return false;let t=this.authState.expiresAt??0;if(Date.now()+xs<t)return true;if(!this.config||!this.authState.refreshToken)return this.switchToLocalMode("token_expired_no_refresh"),false;try{let r=await cr(this.config.endpoint,this.authState.refreshToken,this.config.timeout);return r?(this.authState.accessToken=r.accessToken,this.authState.refreshToken=r.refreshToken,this.authState.expiresAt=Date.now()+r.expiresIn*1e3,!0):(this.switchToLocalMode("token_refresh_failed"),!1)}catch{return this.switchToLocalMode("token_refresh_error"),false}}switchToLocalMode(t){this.authState.mode!=="local"&&(this.authState.mode="local",this.failureReason=t,process.stderr.write(`\u26A0 TestRelic: Switched to local mode (${t}).
|
|
@@ -2573,14 +2573,14 @@ ${s}
|
|
|
2573
2573
|
`);break;case "rate_limited":this.setLocalMode("rate_limited"),process.stderr.write(`\u26A0 TestRelic: Rate limited during authentication. Running in local mode.
|
|
2574
2574
|
`);break;case "timeout":this.setLocalMode("auth_timeout"),process.stderr.write(`\u26A0 TestRelic: Authentication timed out. Running in local mode.
|
|
2575
2575
|
`);break;default:this.setLocalMode(`auth_error_${r??"unknown"}`),process.stderr.write(`\u26A0 TestRelic: Cloud authentication failed. Running in local mode.
|
|
2576
|
-
`);break}}async resolveRepoId(){if(!this.config||!this.authState.accessToken)return;let t=this.gitMetadata?.remoteUrl?Z(this.gitMetadata.remoteUrl):null,r=t?fr(t):this.config.projectName??Q(process.cwd()),a=t??this.config.projectName??Q(process.cwd()),s=this.readRepoCache(a);if(s){this.repoId=s.repoId;return}try{let n=await
|
|
2576
|
+
`);break}}async resolveRepoId(){if(!this.config||!this.authState.accessToken)return;let t=this.gitMetadata?.remoteUrl?Z(this.gitMetadata.remoteUrl):null,r=t?fr(t):this.config.projectName??Q(process.cwd()),a=t??this.config.projectName??Q(process.cwd()),s=this.readRepoCache(a);if(s){this.repoId=s.repoId;return}try{let n=await pr(this.config.endpoint,this.authState.accessToken,a,r,this.config.timeout,this.gitMetadata?.branch);n&&(this.repoId=n.repoId,this.writeRepoCache(a,n.repoId,n.displayName));}catch{}!t&&!this.config.projectName&&!We(process.cwd())&&process.stderr.write(`\u2139 TestRelic: No git remote or package.json detected. Set project.name in .testrelic for stable project identity.
|
|
2577
2577
|
`);}readRepoCache(t){if(!this.config)return null;let r=join(this.config.queueDirectory,"..","cache","repo.json");try{if(!existsSync(r))return null;let a=readFileSync(r,"utf-8"),s=JSON.parse(a);if(s.gitId!==t||Date.now()-s.resolvedAt>ws)return null;let n=this.hashApiKey();return n&&s.apiKeyHash!==n?null:s}catch{return null}}writeRepoCache(t,r,a){if(!this.config)return;let s=join(this.config.queueDirectory,"..","cache"),n=join(s,"repo.json");try{mkdirSync(s,{recursive:!0});let o={repoId:r,gitId:t,displayName:a,resolvedAt:Date.now(),apiKeyHash:this.hashApiKey()??""},i=n+".tmp";writeFileSync(i,JSON.stringify(o,null,2),"utf-8"),renameSync(i,n);}catch{}}hashApiKey(){return this.config?.apiKey?createHash("sha256").update(this.config.apiKey).digest("hex").substring(0,16):null}startBackgroundFlush(){if(!this.config||!this.authState.accessToken)return;let t=this.authState.accessToken,r=this.config.queueDirectory,a=this.config.endpoint;this.flushPromise=Promise.race([vr(r,a,t),new Promise(s=>setTimeout(s,br))]).catch(()=>{});}startHealthCheck(){if(!this.config)return;let t=this.config.endpoint;this.healthCheckTimer=setInterval(async()=>{if(!this.isCloudMode()&&!(!this.failureReason||!["cloud_unreachable","auth_timeout","network_error"].includes(this.failureReason)))try{if(!await je(t))return;if(this.config?.apiKey){let a=await Ve(this.config.endpoint,this.config.apiKey,this.config.timeout);if(!Ge(a)){let s=a;this.authState={mode:"cloud",accessToken:s.accessToken,refreshToken:s.refreshToken,expiresAt:Date.now()+s.expiresIn*1e3,orgId:s.orgId,orgName:s.orgName,userId:s.userId,userName:s.userName},this.failureReason=null,process.stderr.write(`\u2713 TestRelic: Cloud connectivity restored.
|
|
2578
|
-
`),this.startBackgroundFlush();}}}catch{}},ks);}};var Ts=new Set(["smoke","regression","nightly","ci"]);function ce(){let e=process.env.TESTRELIC_RUN_TYPE?.trim().toLowerCase();if(e&&Ts.has(e))return e}var Cs=1048576,
|
|
2578
|
+
`),this.startBackgroundFlush();}}}catch{}},ks);}};var Ts=new Set(["smoke","regression","nightly","ci"]);function ce(){let e=process.env.TESTRELIC_RUN_TYPE?.trim().toLowerCase();if(e&&Ts.has(e))return e}var Cs=1048576,pe=[1e3,3e3,9e3],ue=3,Rs=40*1048576,As=new Set(["e2e","mobile","unit"]);function Ze(e){if(e&&e.length>0&&e.every(s=>s.testType==="api")){let s=new Map;for(let i of e){let l=i.apiProtocol??"rest";s.set(l,(s.get(l)??0)+1);}let n="rest",o=0;for(let[i,l]of s)l>o&&(o=l,n=i);return {testType:"api",apiProtocol:n,metadata:{testType:"api",primaryProtocol:n}}}let t=new Map;for(let s of e??[])As.has(s.testType)&&t.set(s.testType,(t.get(s.testType)??0)+1);let r=null,a=0;for(let[s,n]of t)n>a&&(a=n,r=s);return {testType:r??"e2e"}}function Qe(e,t,r,a,s,n){let o=n?.cloudPlatform?n.cloudPlatform:a?.provider&&a.provider!=="unknown"?a.provider:"local",i=ce(),l=Ze(s),d=l.testType==="api"?l:null,u=d&&s?s.map(m=>m.apiProtocol?m:{...m,apiProtocol:d.apiProtocol}):s,f={runId:e.testRunId,repoGitId:t,startedAt:e.startedAt,summary:e.summary,timeline:e.timeline,environment:o,...i?{runType:i}:{},...d?{testType:d.testType,apiProtocol:d.apiProtocol,metadata:d.metadata}:{testType:l.testType},...u&&u.length>0?{tests:u}:{},...r?.branch?{branch:r.branch}:{},...r?.commitSha?{commit:r.commitSha}:{},...r?.commitMessage?{commitMessage:r.commitMessage}:{},...r?.commitAuthor?{commitAuthor:r.commitAuthor}:{},...e.completedAt?{finishedAt:e.completedAt}:{},...e.totalDuration?{duration:e.totalDuration}:{},...a?.provider?{ciProvider:a.provider}:{},...a?.runUrl?{ciRunUrl:a.runUrl}:{},...n?.cloudPlatform?{cloudPlatform:n.cloudPlatform}:{},...n?.cloudBuildId?{cloudBuildId:n.cloudBuildId}:{},...n?.cloudSessionId?{cloudSessionId:n.cloudSessionId}:{}};if(f.tests&&f.tests.length>0){let m=JSON.stringify(f);if(Buffer.byteLength(m,"utf-8")>Rs){let c=f.tests.map(({consoleLogs:p,networkRequests:g,...h})=>h);return {...f,tests:c}}}return f}async function fe(e){return new Promise(t=>setTimeout(t,e))}function _s(e){let t={};for(let[r,a]of Object.entries(e))a!=null&&(t[r]=a);return t}async function xr(e,t,r){for(let a=0;a<ue;a++)try{let s=await fetch(e,t);if(s.ok)return s;if(s.status===401&&r&&a===0){let n=await r();if(n){let o={...t,headers:{...t.headers,Authorization:`Bearer ${n}`}},i=await fetch(e,o);if(i.ok)return i}return null}if(s.status===429&&a<ue-1){let n=s.headers.get("Retry-After"),o=n?parseInt(n,10)*1e3:pe[a];!isNaN(o)&&o>0?await fe(o):await fe(pe[a]);continue}if(s.status>=500&&a<ue-1){await fe(pe[a]);continue}return s}catch{if(a<ue-1){await fe(pe[a]);continue}return null}return null}function Is(e){let t=JSON.stringify(e),r={"Content-Type":"application/json"};if(Buffer.byteLength(t,"utf-8")>Cs){let a=gzipSync(Buffer.from(t,"utf-8"));return r["Content-Encoding"]="gzip",{body:a,headers:r}}return {body:t,headers:r}}async function wr(e,t,r,a){let s=`${e}/runs`,{body:n,headers:o}=Is(r);o.Authorization=`Bearer ${t}`;let i=await xr(s,{method:"POST",headers:o,body:n},a);return i?.ok?{success:true,statusCode:i.status,error:null}:i?.status===409?{success:true,statusCode:409,error:null}:{success:false,reason:i?`Upload failed with status ${i.status}`:"Upload failed after retries",statusCode:i?.status??null,payload:r,targetEndpoint:s,method:"POST"}}async function kr(e,t,r){let a=`${e}/runs/init`;try{let s=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(_s(r))});return s.ok?{runId:(await s.json()).runId}:null}catch{return null}}function Tr(e,t,r,a){let s=`${e}/runs/${r}/tests`;fetch(s,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(a)}).catch(()=>{});}async function Sr(e,t,r,a){let s=`${e}/runs/${r}/finalize`;return (await xr(s,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(a)}))?.ok??false}function Xe(e,t,r,a){let s=`${e}/runs/${r}/finalize`,n=new Date,o={finishedAt:n.toISOString(),duration:n.getTime()-new Date(a).getTime(),summary:{}};fetch(s,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(o)}).catch(()=>{});}function Cr(e,t,r,a){let s=[],n=[],o=null,i=0,l=null,d=0,u=null,f=0,m=e.filter(g=>g.name===ATTACHMENT_NAME&&g.body),c=false;if(m.length>0)for(let g of m)try{let h=JSON.parse(g.body.toString());if(isTestRelicFilePayload(h)){s=s.concat(h.navigations),n=n.concat(h.apiAssertions),h.networkRequestsFile&&(o=h.networkRequestsFile,i+=h.networkRequestsCount),h.consoleLogsFile&&(l=h.consoleLogsFile,d+=h.consoleLogsCount),h.apiCallsFile&&(u=h.apiCallsFile,f+=h.apiCallsCount),c=!0;continue}if(isTestRelicDataPayload(h)){s=s.concat(h.navigations),Array.isArray(h.apiAssertions)&&(n=n.concat(h.apiAssertions)),i+=h.networkRequests?.length??0,f+=h.apiCalls?.length??0;let w=h.consoleLogs;Array.isArray(w)&&(d+=w.length),c=!0;}}catch{process.stderr.write(`[testrelic] Warning: Corrupt attachment for test "${r}"
|
|
2579
2579
|
`);}return c||(s=t.filter(g=>g.type==="lambdatest-navigation"&&g.description).map(g=>{try{return JSON.parse(g.description)}catch{return null}}).filter(g=>g!==null)),s.length===0&&i===0&&f===0&&!a&&!c&&process.stderr.write(`[testrelic] Warning: No data found for test "${r}". Make sure your tests import { test, expect } from '@testrelic/playwright-analytics/fixture' instead of '@playwright/test'. Without the TestRelic fixture, network logs, video sync, and timeline data cannot be captured.
|
|
2580
|
-
`),{navigations:s,apiAssertions:n,networkRequestsFile:o,networkRequestsCount:i,consoleLogsFile:l,consoleLogsCount:d,apiCallsFile:
|
|
2581
|
-
`),s}async function at(e,t,r,a,s,n,o,i,l,d,
|
|
2582
|
-
`).filter(r=>r.length>0).map(r=>{try{return JSON.parse(r)}catch{return null}}).filter(r=>r!==null)}catch{return []}}function nt(e){switch(e){case "passed":return "passed";case "failed":return "failed";case "timedOut":return "timedout";case "skipped":return "skipped";case "interrupted":return "failed";default:return "failed"}}function
|
|
2583
|
-
`),this.activeReportMode==="streaming"){let n=dirname(this.config.outputPath);this.streamingWriter=new G(n);let o=()=>{if(this.streamingWriter){try{this.streamingWriter.writeIndex(this.testIndex);let i={testRunId:this.testRunId??"",startedAt:this.startedAt,completedAt:new Date().toISOString(),totalDuration:Date.now()-new Date(this.startedAt).getTime(),...this.summaryCounters,metadata:null,reportMode:"streaming",files:[],enrichedSummary:null,validation:null,writeErrors:this.streamingWriter.getWriteErrors()};this.streamingWriter.writeSummary(i);}catch{}this.streamingWriter.dispose();}try{if(this.cloudClient?.isCloudMode()&&this.cloudRunId){let i=this.cloudClient.getAccessToken();i&&Xe(this.cloudClient.getEndpoint(),i,this.cloudRunId,this.startedAt);}}catch{}};process.on("SIGTERM",o),process.on("SIGINT",o);}if(this.config.includeArtifacts){let n=dirname(this.config.outputPath);this.runTimestamp=Yt(join(n,"artifacts"));try{tr(join(n,"artifacts"));}catch{}}let s=P();this.detectedOs=
|
|
2580
|
+
`),{navigations:s,apiAssertions:n,networkRequestsFile:o,networkRequestsCount:i,consoleLogsFile:l,consoleLogsCount:d,apiCallsFile:u,apiCallsCount:f}}function ge(e=process.env){let t=Ps(e.TESTRELIC_CLOUD_PLATFORM);if(t)return {cloudPlatform:t,cloudBuildId:e.TESTRELIC_CLOUD_BUILD_ID||void 0,cloudSessionId:e.TESTRELIC_CLOUD_SESSION_ID||void 0};if(e.BROWSERSTACK_USERNAME||e.BROWSERSTACK_ACCESS_KEY)return {cloudPlatform:"browserstack",cloudBuildId:e.BROWSERSTACK_BUILD_NAME||e.BROWSERSTACK_BUILD_ID||void 0,cloudSessionId:e.BROWSERSTACK_SESSION_ID||void 0};if(e.LT_USERNAME||e.LAMBDATEST_USERNAME||e.LT_ACCESS_KEY||e.LAMBDATEST_ACCESS_KEY)return {cloudPlatform:"lambdatest",cloudBuildId:e.LT_BUILD||e.LAMBDATEST_BUILD||void 0,cloudSessionId:e.LT_SESSION_ID||e.LAMBDATEST_SESSION_ID||void 0};let r=e.PLAYWRIGHT_WS_ENDPOINT;if(r){if(/cdp\.lambdatest\.com/i.test(r))return {cloudPlatform:"lambdatest"};if(/cdp\.browserstack\.com/i.test(r))return {cloudPlatform:"browserstack"}}return {}}function Ps(e){if(!e)return;let t=e.toLowerCase().trim();if(t==="browserstack"||t==="bs")return "browserstack";if(t==="lambdatest"||t==="lt")return "lambdatest"}var Os=5,he=[1e3,3e3,9e3],U=3,qs={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp",".webm":"video/webm",".mp4":"video/mp4",".zip":"application/zip"},et=0,tt=[];async function Hs(){et>=Os&&await new Promise(e=>tt.push(e)),et++;}function Us(){et--,tt.length>0&&tt.shift()();}async function me(e){return new Promise(t=>setTimeout(t,e))}function zs(e){let t=e.substring(e.lastIndexOf(".")).toLowerCase();return qs[t]??"application/octet-stream"}function $s(e){try{return statSync(e).size}catch{return 0}}async function js(e,t,r,a,s){let n=`${e}/artifacts/upload-url`,o=JSON.stringify({runId:r.runId,testId:r.testId,fileName:basename(r.filePath),contentType:s,type:r.type,sizeBytes:a});for(let i=0;i<U;i++)try{let l=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:o});if(l.ok)return await l.json();if(l.status>=500&&i<U-1){await me(he[i]);continue}return null}catch{if(i<U-1){await me(he[i]);continue}return null}return null}async function Vs(e,t,r,a){for(let s=0;s<U;s++)try{let n=createReadStream(t),o=Readable.toWeb(n),i=await fetch(e,{method:"PUT",headers:{"Content-Type":r,"Content-Length":String(a)},body:o,duplex:"half"});if(i.ok)return !0;if(i.status>=500&&s<U-1){await me(he[s]);continue}return !1}catch{if(s<U-1){await me(he[s]);continue}return false}return false}async function Gs(e,t,r){let a=`${e}/artifacts/confirm`;try{return (await fetch(a,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({artifactId:r})})).ok}catch{return false}}async function Ws(e,t,r,a){let s=$s(r.filePath);if(s===0)return {success:false,storageKey:null,artifactId:null,error:"file_not_found_or_empty"};if(s>a*1024*1024)return {success:false,storageKey:null,artifactId:null,error:"file_too_large"};let n=zs(r.filePath);await Hs();try{let o=await js(e,t,r,s,n);if(!o)return {success:!1,storageKey:null,artifactId:null,error:"upload_url_request_failed"};if(!await Vs(o.uploadUrl,r.filePath,n,s))return {success:!1,storageKey:o.storageKey,artifactId:o.artifactId,error:"presigned_put_failed"};let l=await Gs(e,t,o.artifactId);return {success:l,storageKey:o.storageKey,artifactId:o.artifactId,error:l?null:"confirm_failed"}}finally{Us();}}async function Rr(e,t,r,a){let s=new Map,n=r.map(async o=>{let i=await Ws(e,t,o,a);s.set(o.filePath,i);});return await Promise.allSettled(n),s}function rt(e){let r=e.getGitMetadata()?.remoteUrl;return r?Z(r):e.getConfig()?.projectName??Q(process.cwd())}async function _r(e,t,r,a,s){if(!e.isCloudMode()||t!=="realtime"&&t!=="both"||!await e.ensureValidToken())return null;let o=e.getGitMetadata(),i=P(),l=ge(),d=ce();return (await kr(e.getEndpoint(),e.getAccessToken(),{runId:r,repoGitId:rt(e),branch:o?.branch??null,commit:o?.commitSha??null,commitMessage:o?.commitMessage??null,commitAuthor:o?.commitAuthor??null,startedAt:a,totalTests:null,ciProvider:i?.provider??null,ciRunUrl:i?.runUrl??null,environment:s??l.cloudPlatform??null,cloudPlatform:l.cloudPlatform??null,cloudBuildId:l.cloudBuildId??null,cloudSessionId:l.cloudSessionId??null,...d?{runType:d}:{},testType:"e2e"}))?.runId??null}var Ks=true,Js=50;async function Ys(e,t,r,a){let s=new Map;if(!(t?.uploadArtifacts??Ks)||!await e.ensureValidToken())return s;let i=[],l=new Map;for(let c of a){if(c.artifacts.screenshot){let p=join(c.outputDir,c.artifacts.screenshot);i.push({filePath:p,runId:r,testId:c.testId,type:"screenshot"}),l.set(p,{testId:c.testId,field:"screenshot"});}if(c.artifacts.video){let p=join(c.outputDir,c.artifacts.video);i.push({filePath:p,runId:r,testId:c.testId,type:"video"}),l.set(p,{testId:c.testId,field:"video"});}}if(i.length===0)return s;let d=t?.artifactMaxSizeMb??Js,u=await Rr(e.getEndpoint(),e.getAccessToken(),i,d),f=new Map;for(let[c,p]of u){if(!p.success||!p.storageKey)continue;let g=l.get(c);if(!g)continue;let h=f.get(g.testId)??{};g.field==="screenshot"&&(h.screenshotKey=p.storageKey),g.field==="video"&&(h.videoKey=p.storageKey),f.set(g.testId,h);}for(let c of a){let p=f.get(c.testId);s.set(c.testId,{...c.artifacts,...p?.screenshotKey?{screenshotKey:p.screenshotKey}:{},...p?.videoKey?{videoKey:p.videoKey}:{}});}let m=Array.from(u.values()).filter(c=>c.success).length;return m>0&&process.stderr.write(`\u2713 TestRelic: Uploaded ${m} artifact(s) to cloud storage.
|
|
2581
|
+
`),s}async function at(e,t,r,a,s,n,o,i,l,d,u){try{if(e.isCloudMode()&&s&&(r==="realtime"||r==="both")){let m=await e.ensureValidToken(),c=e.getGitMetadata(),p=Ze(u),g={finishedAt:o,duration:i,summary:l,...c?.commitMessage?{commitMessage:c.commitMessage}:{},testType:p.testType,...p.testType==="api"?{apiProtocol:p.apiProtocol,metadata:p.metadata}:{}};if(m){if(!await Sr(e.getEndpoint(),e.getAccessToken(),s,g)){let w=t?.queueDirectory??".testrelic/queue";X(w,a,"finalize","finalize_failed_after_retries",`${e.getEndpoint()}/runs/${s}/finalize`,"POST",g,{"Content-Type":"application/json"});}}else {let h=t?.queueDirectory??".testrelic/queue";X(h,a,"finalize",e.getFailureReason()??"token_invalid",`${e.getEndpoint()}/runs/${s}/finalize`,"POST",g,{"Content-Type":"application/json"});}}let f=new Map;if(e.isCloudMode()&&d&&d.length>0&&(f=await Ys(e,t,a,d)),e.isCloudMode()&&(!r||r==="batch"||r==="both"))if(await e.ensureValidToken()){let c=e.getGitMetadata(),p=P(),g=rt(e),h=Zs(n,f),w=Qe(h,g,c,p,u,ge()),S=await wr(e.getEndpoint(),e.getAccessToken(),w,async()=>await e.ensureValidToken()?e.getAccessToken():null);if(!S.success){let y=S,b=t?.queueDirectory??".testrelic/queue";X(b,a,"batch",y.reason,y.targetEndpoint,y.method,y.payload,{"Content-Type":"application/json"});}}else {let c=t?.queueDirectory??".testrelic/queue",p=e.getGitMetadata(),g=P(),h=rt(e),w=Qe(n,h,p,g,void 0,ge());X(c,a,"batch",e.getFailureReason()??"token_invalid",`${e.getEndpoint()}/runs`,"POST",w,{"Content-Type":"application/json"});}await e.dispose();}catch{try{await e.dispose();}catch{}}}function Zs(e,t){if(t.size===0)return e;let r=e.timeline.map(a=>{let s=a.testId;if(!s)return a;let n=t.get(s);return n?{...a,...n.screenshotKey?{screenshotKey:n.screenshotKey}:{},...n.videoKey?{videoKey:n.videoKey}:{}}:a});return {...e,timeline:r}}function z(e){try{return readFileSync(e,"utf-8").split(`
|
|
2582
|
+
`).filter(r=>r.length>0).map(r=>{try{return JSON.parse(r)}catch{return null}}).filter(r=>r!==null)}catch{return []}}function nt(e){switch(e){case "passed":return "passed";case "failed":return "failed";case "timedOut":return "timedout";case "skipped":return "skipped";case "interrupted":return "failed";default:return "failed"}}function sn(e,t,r){let a=`${e}::${t}::${r}`;return createHash("sha256").update(a).digest("hex").substring(0,16)}function nn(e){return e.length<=3?"":e[e.length-2]}function on(e){let t=e.findIndex(r=>r.status==="passed");return t>0?`passed on retry ${t}`:null}function ln(e){return process.env.BROWSERSTACK_USERNAME?"BrowserStack":e?{"github-actions":"GitHub Actions","gitlab-ci":"GitLab CI",jenkins:"Jenkins",circleci:"CircleCI","bitbucket-pipelines":"Bitbucket Pipelines"}[e.provider]??"CI":"Local"}function dn(){let e=platform();return e==="win32"?"Windows":e==="darwin"?"macOS":"Linux"}function cn(e){let t=e.parent?.project?.()?.use;if(!t)return;let{browserName:r,channel:a}=t;if(a){let n=a.toLowerCase();return n==="chrome"||n==="chrome-stable"?"Chrome":n.startsWith("chrome-beta")?"Chrome Beta":n.startsWith("chrome-dev")?"Chrome Dev":n.startsWith("chrome-canary")?"Chrome Canary":n==="msedge"||n==="msedge-stable"?"Microsoft Edge":n.startsWith("msedge-beta")?"Edge Beta":n.startsWith("msedge-dev")?"Edge Dev":n.startsWith("msedge-canary")?"Edge Canary":a}if(r){let n=r.toLowerCase();return n==="chromium"?"Chromium":n==="firefox"?"Firefox":n==="webkit"?"WebKit":r}let s=t.defaultBrowserType;if(s){let n=s.toLowerCase();return n==="chromium"?"Chromium":n==="firefox"?"Firefox":n==="webkit"?"WebKit":s}}function pn(e,t,r){let a=r.parent?.project?.(),s="unknown";a!==void 0&&(a.use.isMobile===true?s="mobile":s="e2e");let n=["e2e","api","unit","mobile"];for(let l of n)if(e.some(d=>d===`@${l}`||d===l))return l;let o=t.replace(/\\/g,"/"),i=["e2e","api","unit"];for(let l of i)if(o.includes(`/${l}/`))return l;return s}var un="__testrelic_api_config";function fn(e){return JSON.stringify(e,(t,r)=>r instanceof RegExp?{__regexp:true,source:r.source,flags:r.flags}:r)}var ve=class{constructor(t){this.rootDir="";this.startedAt="";this.testRunId="";this.collectedTests=[];this.fixtureDataReceived=false;this.testCount=0;this.cloudClient=null;this.cloudRunId=null;this.runTimestamp="";this.pendingArtifactEntries=[];this.detectedOs="";this.detectedSource="";this.cloudTestsBuffer=[];this.activeReportMode="embedded";this.streamingWriter=null;this.testIndex=[];this.summaryCounters={total:0,passed:0,failed:0,flaky:0,skipped:0,timedOut:0,interrupted:0,totalApiCalls:0,totalAssertions:0,totalNavigations:0,totalNetworkRequests:0,totalConsoleLogs:0,totalActionSteps:0};this.config=wt(t),this.apiConfig=xt(t);}async onBegin(t,r){try{if(this.rootDir=t.rootDir,this.startedAt=new Date().toISOString(),this.testRunId=this.config.testRunId??randomUUID(),this.config.reportMode==="auto"){let n=r.allTests().length;this.activeReportMode=n>=this.config.streamingThreshold?"streaming":"embedded";}else this.activeReportMode=this.config.reportMode;let a=r.allTests().length;if(process.stderr.write(`[testrelic] Report mode: ${this.activeReportMode} (${a} tests)
|
|
2583
|
+
`),this.activeReportMode==="streaming"){let n=dirname(this.config.outputPath);this.streamingWriter=new G(n);let o=()=>{if(this.streamingWriter){try{this.streamingWriter.writeIndex(this.testIndex);let i={testRunId:this.testRunId??"",startedAt:this.startedAt,completedAt:new Date().toISOString(),totalDuration:Date.now()-new Date(this.startedAt).getTime(),...this.summaryCounters,metadata:null,reportMode:"streaming",files:[],enrichedSummary:null,validation:null,writeErrors:this.streamingWriter.getWriteErrors()};this.streamingWriter.writeSummary(i);}catch{}this.streamingWriter.dispose();}try{if(this.cloudClient?.isCloudMode()&&this.cloudRunId){let i=this.cloudClient.getAccessToken();i&&Xe(this.cloudClient.getEndpoint(),i,this.cloudRunId,this.startedAt);}}catch{}};process.on("SIGTERM",o),process.on("SIGINT",o);}if(this.config.includeArtifacts){let n=dirname(this.config.outputPath);this.runTimestamp=Yt(join(n,"artifacts"));try{tr(join(n,"artifacts"));}catch{}}let s=P();this.detectedOs=dn(),this.detectedSource=ln(s);try{this.cloudClient=new de(this.config.cloud),await this.cloudClient.initialize(),this.cloudRunId=await _r(this.cloudClient,this.config.cloud?.uploadStrategy,this.testRunId,this.startedAt,this.detectedSource);}catch{this.cloudClient?.switchToLocalMode("init_error");}if(this.activeReportMode!=="streaming"){let n=()=>{try{if(this.cloudClient?.isCloudMode()&&this.cloudRunId){let o=this.cloudClient.getAccessToken();o&&Xe(this.cloudClient.getEndpoint(),o,this.cloudRunId,this.startedAt);}}catch{}};process.on("SIGTERM",n),process.on("SIGINT",n);}}catch{}}onTestBegin(t,r){try{t.annotations.push({type:un,description:fn(this.apiConfig)});}catch{}}onTestEnd(t,r){try{let a=r,s=t.outcome(),n;s==="flaky"?n="flaky":s==="skipped"?n="skipped":n=nt(a.status);let o=a.startTime.toISOString(),i=new Date(a.startTime.getTime()+a.duration).toISOString(),l=t.tags?[...t.tags]:t.annotations.filter(R=>R.type==="tag").map(R=>R.description??""),d=Cr(a.attachments,t.annotations,t.title,n==="skipped");(d.navigations.length>0||d.networkRequestsCount>0||d.apiCallsCount>0)&&(this.fixtureDataReceived=!0);let u=null;if(n==="failed"||n==="flaky"){let C=(n==="flaky"?t.results.find(B=>B.status!=="passed")?.errors??[]:a.errors)[0];if(C){let B=Tt(this.config.redactPatterns),$=C.location?.line??null,j=null;this.config.includeCodeSnippets&&$!==null&&C.location?.file&&(j=kt(C.location.file,$,this.config.codeContextLines),j&&(j=B(j))),u={message:B(C.message??"Unknown error"),line:$,code:j,stack:this.config.includeStackTrace&&C.stack?B(C.stack):null};}}let f=t.titlePath().filter(Boolean),m=relative(this.rootDir||".",t.location.file),c=nn(f),p=f.join(" > "),g=m,h=sn(g,c,p),w=pn(l,g,t),S=w!=="mobile"&&d.apiCallsCount>0?"api":w,y=s==="flaky",b=on(t.results),T=nt(t.expectedStatus),v=nt(a.status),k=null;if(this.config.includeArtifacts&&n!=="skipped"&&a.attachments){let R=dirname(this.config.outputPath),C=f[f.length-1]??t.title;k=Zt(a.attachments,C,a.retry,R,this.runTimestamp||void 0);}let A=this.config.includeActionSteps&&a.steps?ir(a.steps,a.startTime):null,q=cn(t),M={titlePath:f,title:p,status:n,duration:a.duration,startedAt:o,completedAt:i,retryCount:t.results.length-1,retry:a.retry,tags:l,failure:u,specFile:m,navigations:d.navigations,testId:h,filePath:g,suiteName:c,testType:S,isFlaky:y,retryStatus:b,expectedStatus:T,actualStatus:v,artifacts:k,apiAssertions:d.apiAssertions,actions:A,networkRequestsFile:d.networkRequestsFile,networkRequestsCount:d.networkRequestsCount,consoleLogsFile:d.consoleLogsFile,consoleLogsCount:d.consoleLogsCount,apiCallsFile:d.apiCallsFile,apiCallsCount:d.apiCallsCount,browser:q,os:this.detectedOs||void 0,source:this.detectedSource||void 0},it=d.consoleLogsFile&&d.consoleLogsCount>0?z(d.consoleLogsFile):void 0,ot=d.networkRequestsFile&&d.networkRequestsCount>0?z(d.networkRequestsFile):void 0;this.cloudTestsBuffer.push({testId:h,title:p,status:n,duration:a.duration,suiteName:c,filePath:g,testType:S,isFlaky:y,startedAt:o,completedAt:i,tags:l,failure:u,retry:a.retry,retryCount:t.results.length-1,retryStatus:b,browser:q,os:this.detectedOs||void 0,source:this.detectedSource||void 0,...A&&A.length>0?{actions:A}:{},...it?{consoleLogs:it}:{},...ot?{networkRequests:ot}:{}});try{let R=this.config.cloud?.uploadStrategy;if(this.cloudClient?.isCloudMode()&&this.cloudRunId&&(R==="realtime"||R==="both")){let C=this.cloudClient.getAccessToken();C&&Tr(this.cloudClient.getEndpoint(),C,this.cloudRunId,this.buildCloudTestPayload(M));}}catch{}if(this.activeReportMode==="streaming"&&this.streamingWriter){let R=ye(g,f,t.id??"",a.retry),C=a.retry===t.results.length-1,B={id:R,navigations:d.navigations,apiAssertions:d.apiAssertions,actions:A??[],artifacts:k,failureDiagnostic:u,hasNetworkFile:d.networkRequestsFile!==null,networkRequestsCount:d.networkRequestsCount,hasConsoleFile:d.consoleLogsFile!==null,consoleLogsCount:d.consoleLogsCount,hasApiCallsFile:d.apiCallsFile!==null,apiCallsCount:d.apiCallsCount,startedAt:o};this.streamingWriter.writeTestDetail(R,B,{networkRequestsFile:d.networkRequestsFile,consoleLogsFile:d.consoleLogsFile,apiCallsFile:d.apiCallsFile});let $={id:R,title:f[f.length-1]??t.title,titlePath:f,filePath:g,status:n,duration:a.duration,project:f[1]??"",retryIndex:a.retry,tags:l,hasNetworkData:d.networkRequestsCount>0,hasConsoleData:d.consoleLogsCount>0,hasArtifacts:k!==null,hasActionSteps:(A?.length??0)>0,errorMessage:u?.message?.split(`
|
|
2584
2584
|
`)[0]??null,networkCount:d.networkRequestsCount,consoleCount:d.consoleLogsCount,actionCount:A?.length??0,isRetry:!C};if(this.testIndex.push($),C)switch(this.summaryCounters.total++,n){case "passed":this.summaryCounters.passed++;break;case "failed":this.summaryCounters.failed++;break;case "flaky":this.summaryCounters.flaky++;break;case "skipped":this.summaryCounters.skipped++;break;case "timedout":this.summaryCounters.timedOut++;break;default:this.summaryCounters.interrupted++;break}this.summaryCounters.totalApiCalls+=d.apiCallsCount,this.summaryCounters.totalAssertions+=d.apiAssertions.length,this.summaryCounters.totalNavigations+=d.navigations.length,this.summaryCounters.totalNetworkRequests+=d.networkRequestsCount,this.summaryCounters.totalConsoleLogs+=d.consoleLogsCount,this.summaryCounters.totalActionSteps+=A?.length??0,k&&this.pendingArtifactEntries.push({testId:h,artifacts:k,outputDir:dirname(this.config.outputPath)}),M.actions=null,M.apiAssertions=null,M.navigations=[],M.artifacts=null,M.failure=null,M.networkRequestsFile=null,M.consoleLogsFile=null,M.apiCallsFile=null;}this.activeReportMode!=="streaming"&&this.collectedTests.push(M),this.testCount++,this.checkMemoryPressure();}catch{}}async onEnd(t){try{let r=new Date().toISOString(),a=new Date(this.startedAt).getTime(),n=new Date(r).getTime()-a,o=this.activeReportMode==="streaming"?this.testIndex.some(l=>l.status!=="skipped"):this.collectedTests.some(l=>l.status!=="skipped");!this.fixtureDataReceived&&o&&process.stderr.write(`
|
|
2585
2585
|
\u26A0 TestRelic: No fixture data received from any test.
|
|
2586
2586
|
To enable network logs, video sync, and timeline tracking, import the TestRelic fixture:
|
|
@@ -2588,8 +2588,8 @@ ${s}
|
|
|
2588
2588
|
instead of:
|
|
2589
2589
|
import { test, expect } from '@playwright/test';
|
|
2590
2590
|
|
|
2591
|
-
`),await Jt();let i=null;if(this.config.includeArtifacts&&this.runTimestamp)try{let l=dirname(this.config.outputPath);i=Xt(l,this.runTimestamp);let d=join(l,"artifact-manifest.json"),
|
|
2592
|
-
`);let h={total:this.summaryCounters.total,passed:this.summaryCounters.passed,failed:this.summaryCounters.failed,flaky:this.summaryCounters.flaky,skipped:this.summaryCounters.skipped,timedout:this.summaryCounters.timedOut,totalApiCalls:this.summaryCounters.totalApiCalls,uniqueApiUrls:0,apiCallsByMethod:{},apiCallsByStatusRange:{"2xx":0,"3xx":0,"4xx":0,"5xx":0,error:0},apiResponseTime:null,totalAssertions:this.summaryCounters.totalAssertions,passedAssertions:0,failedAssertions:0,totalNavigations:this.summaryCounters.totalNavigations,uniqueNavigationUrls:0,totalTimelineSteps:this.testIndex.length,totalActionSteps:this.summaryCounters.totalActionSteps,actionStepsByCategory:{}},w={testRunId:this.testRunId,startedAt:this.startedAt,completedAt:t,totalDuration:r,...this.summaryCounters,metadata:this.config.metadata,reportMode:"streaming",files:
|
|
2591
|
+
`),await Jt();let i=null;if(this.config.includeArtifacts&&this.runTimestamp)try{let l=dirname(this.config.outputPath);i=Xt(l,this.runTimestamp);let d=join(l,"artifact-manifest.json"),u=JSON.stringify(i),f=d+".tmp";writeFileSync(f,u,"utf-8"),renameSync(f,d);}catch{}if(this.activeReportMode==="streaming"&&this.streamingWriter)await this.finalizeStreamingReport(r,n,t,i);else {let l=this.buildTimeline(),d=new Map;for(let c of this.collectedTests){let p=d.get(c.testId);(!p||c.retry>p.retry)&&d.set(c.testId,c);}let u=Array.from(d.values()),f=nr(u,l.length),m={schemaVersion:xe,testRunId:this.testRunId,startedAt:this.startedAt,completedAt:r,totalDuration:n,summary:f,ci:P(),metadata:this.config.metadata,timeline:l,shardRunIds:null};if(this.writeReport(m),await Ne(m,this.config,i),ze(f,this.config.outputPath,this.config.htmlReportPath,this.config.quiet),this.cloudClient){let c=this.collectArtifactEntries(),p=this.cloudTestsBuffer.slice();this.cloudTestsBuffer.length=0,await at(this.cloudClient,this.config.cloud,this.config.cloud?.uploadStrategy,this.testRunId,this.cloudRunId,m,r,n,f,c,p);}}}catch{}}collectArtifactEntries(){if(this.activeReportMode==="streaming")return this.pendingArtifactEntries;let t=dirname(this.config.outputPath),r=[];for(let a of this.collectedTests)a.artifacts&&r.push({testId:a.testId,artifacts:a.artifacts,outputDir:t});return r}async finalizeStreamingReport(t,r,a,s){if(!this.streamingWriter)return;let{totalApiCalls:n,totalAssertions:o,totalNavigations:i,totalNetworkRequests:l,totalConsoleLogs:d,totalActionSteps:u}=this.summaryCounters;this.summaryCounters={total:0,passed:0,failed:0,flaky:0,skipped:0,timedOut:0,interrupted:0,totalApiCalls:n,totalAssertions:o,totalNavigations:i,totalNetworkRequests:l,totalConsoleLogs:d,totalActionSteps:u};let f=new Map,m=new Map;for(let b of this.testIndex){let T=`${b.filePath}::${b.titlePath.join(" > ")}`,v=f.get(T);(v===void 0||b.retryIndex>v)&&f.set(T,b.retryIndex);}let c=0;for(let b of this.testIndex){let T=`${b.filePath}::${b.titlePath.join(" > ")}`,v=f.get(T);if(b.isRetry=b.retryIndex<v,b.isRetry)continue;switch(c++,this.summaryCounters.total++,b.status){case "passed":this.summaryCounters.passed++;break;case "failed":this.summaryCounters.failed++;break;case "flaky":this.summaryCounters.flaky++;break;case "skipped":this.summaryCounters.skipped++;break;case "timedout":this.summaryCounters.timedOut++;break;default:this.summaryCounters.interrupted++;break}let k=m.get(b.filePath);switch(k||(k={filePath:b.filePath,total:0,passed:0,failed:0,flaky:0,skipped:0,timedOut:0},m.set(b.filePath,k)),k.total++,b.status){case "passed":k.passed++;break;case "failed":k.failed++;break;case "flaky":k.flaky++;break;case "skipped":k.skipped++;break;case "timedout":k.timedOut++;break}}let p=Array.from(m.values());this.streamingWriter.writeIndex(this.testIndex),this.streamingWriter.writeCompactIndex(this.testIndex);let g={reporterTotal:this.summaryCounters.total,indexTotal:c,playwrightStatus:a.status,isValid:this.summaryCounters.total===c};g.isValid||process.stderr.write(`[testrelic] WARNING: Summary count mismatch \u2014 reporter: ${g.reporterTotal}, index: ${g.indexTotal}
|
|
2592
|
+
`);let h={total:this.summaryCounters.total,passed:this.summaryCounters.passed,failed:this.summaryCounters.failed,flaky:this.summaryCounters.flaky,skipped:this.summaryCounters.skipped,timedout:this.summaryCounters.timedOut,totalApiCalls:this.summaryCounters.totalApiCalls,uniqueApiUrls:0,apiCallsByMethod:{},apiCallsByStatusRange:{"2xx":0,"3xx":0,"4xx":0,"5xx":0,error:0},apiResponseTime:null,totalAssertions:this.summaryCounters.totalAssertions,passedAssertions:0,failedAssertions:0,totalNavigations:this.summaryCounters.totalNavigations,uniqueNavigationUrls:0,totalTimelineSteps:this.testIndex.length,totalActionSteps:this.summaryCounters.totalActionSteps,actionStepsByCategory:{}},w={testRunId:this.testRunId,startedAt:this.startedAt,completedAt:t,totalDuration:r,...this.summaryCounters,metadata:this.config.metadata,reportMode:"streaming",files:p,enrichedSummary:h,validation:g,writeErrors:this.streamingWriter.getWriteErrors()};this.streamingWriter.writeSummary(w);let S={schemaVersion:"2.0",generatedAt:new Date().toISOString(),reportMode:"streaming",summaryFile:"summary.json",indexFile:"index.json",testsDir:"tests",artifactsDir:"artifacts",testCount:this.testIndex.length,totalSizeBytes:this.streamingWriter.computeTotalSize()};this.streamingWriter.writeManifest(S),await Ne(this.buildStreamingReport(w,h,t,r),this.config,s),ze(h,this.config.outputPath,this.config.htmlReportPath,this.config.quiet);let y=this.streamingWriter.getReportDir();if(process.stderr.write(`[testrelic] Streaming report written to ${y}
|
|
2593
2593
|
[testrelic] ${this.testIndex.length} test detail files on disk
|
|
2594
2594
|
[testrelic] View report: npx testrelic serve ${y}
|
|
2595
2595
|
`),this.streamingWriter.getWriteErrors().length>0&&process.stderr.write(`[testrelic] WARNING: ${this.streamingWriter.getWriteErrors().length} write error(s) during streaming
|