@testrelic/playwright-analytics 2.14.0 → 2.16.0-next.121
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -0
- package/dist/api-fixture.cjs +6 -4
- package/dist/api-fixture.cjs.map +1 -1
- package/dist/api-fixture.js +6 -4
- package/dist/api-fixture.js.map +1 -1
- package/dist/cli.cjs +55 -4
- package/dist/fixture.cjs +6 -4
- package/dist/fixture.cjs.map +1 -1
- package/dist/fixture.d.cts +7 -5
- package/dist/fixture.d.ts +7 -5
- package/dist/fixture.js +6 -4
- package/dist/fixture.js.map +1 -1
- package/dist/index.cjs +1902 -196
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1902 -196
- package/dist/index.js.map +1 -1
- package/dist/reporter-entry.cjs +1898 -194
- package/dist/reporter-entry.cjs.map +1 -1
- package/dist/reporter-entry.d.cts +11 -0
- package/dist/reporter-entry.d.ts +11 -0
- package/dist/reporter-entry.js +1898 -194
- package/dist/reporter-entry.js.map +1 -1
- package/dist/visual.cjs +3 -0
- package/dist/visual.cjs.map +1 -0
- package/dist/visual.d.cts +68 -0
- package/dist/visual.d.ts +68 -0
- package/dist/visual.js +3 -0
- package/dist/visual.js.map +1 -0
- package/package.json +17 -3
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ When you run your Playwright tests with the TestRelic reporter and fixture, you
|
|
|
16
16
|
- **Action steps** — Captures Playwright test steps (actions, assertions, custom steps) with video timestamp sync
|
|
17
17
|
- **Console log capture** — Collects browser console messages (log, warn, error) per test
|
|
18
18
|
- **Screenshots & videos** — Automatically captures screenshots and video recordings, organized per test in an `artifacts/` folder alongside the report
|
|
19
|
+
- **Visual regression** — Captures `toHaveScreenshot()` comparisons with baseline, actual and pixel diff, plus the differing-pixel count and exact ratio
|
|
19
20
|
- **HTML report** — Self-contained interactive HTML report with test grid, filter bar, and expandable drawer showing navigation timeline, API call details, and artifacts
|
|
20
21
|
|
|
21
22
|
## Quick Start
|
|
@@ -585,6 +586,87 @@ test-results/
|
|
|
585
586
|
- The HTML report embeds links to these artifacts with inline image/video viewers
|
|
586
587
|
- Disable artifact collection with `includeArtifacts: false`
|
|
587
588
|
|
|
589
|
+
## Visual Regression
|
|
590
|
+
|
|
591
|
+
Screenshot comparisons appear in the report automatically. If your suite already uses Playwright's `toHaveScreenshot()` or `toMatchSnapshot()`, upgrade the reporter and they show up — no import to add, no assertion to rewrite.
|
|
592
|
+
|
|
593
|
+
```typescript
|
|
594
|
+
import { test, expect } from '@playwright/test';
|
|
595
|
+
|
|
596
|
+
test('dashboard matches its baseline', async ({ page }) => {
|
|
597
|
+
await page.goto('/dashboard');
|
|
598
|
+
await expect(page).toHaveScreenshot('dashboard.png');
|
|
599
|
+
});
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
The report's drawer gains a **Visual comparisons** section: baseline, actual and diff, viewable side by side, through a wipe slider, as an onion-skin blend, or as Playwright's diff image — with the differing-pixel count and the exact ratio.
|
|
603
|
+
|
|
604
|
+
TestRelic does not compare images itself. Playwright already ships a pixel comparator, so this adds no dependencies to your project.
|
|
605
|
+
|
|
606
|
+
### Recording passing comparisons
|
|
607
|
+
|
|
608
|
+
`toHaveScreenshot` attaches nothing when images match, so a green visual check is invisible to any reporter. `toMatchVisualBaseline` runs the same comparator with the same options and additionally records the baseline it asserted against:
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
import { test, expect } from '@testrelic/playwright-analytics/fixture';
|
|
612
|
+
|
|
613
|
+
await expect(page).toMatchVisualBaseline('release-health-dashboard', {
|
|
614
|
+
maxDiffPixelRatio: 0.01,
|
|
615
|
+
mask: [page.locator('.live-ticker')],
|
|
616
|
+
tags: ['critical-path'],
|
|
617
|
+
});
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
Every `toHaveScreenshot` option applies unchanged. The `.png` extension is optional here; `toHaveScreenshot` requires it. Using Playwright's own `expect`? Import `@testrelic/playwright-analytics/visual` once to register the matcher.
|
|
621
|
+
|
|
622
|
+
### Baselines
|
|
623
|
+
|
|
624
|
+
Baselines stay where Playwright puts them — in `<spec>-snapshots/`, committed to git, so a baseline change arrives as a reviewable diff in the pull request. Update them with `npx playwright test --update-snapshots`.
|
|
625
|
+
|
|
626
|
+
Run artifacts land under `artifacts/<timestamp>/<test>/visual/<snapshot>/` as `baseline.png`, `actual.png` and `diff.png`. That folder is gitignored and pruned with old runs — never keep baselines there.
|
|
627
|
+
|
|
628
|
+
Three things keep a visual suite from going flaky: pin `use.viewport`, leave a small tolerance (`maxDiffPixelRatio: 0.002`) since anti-aliasing varies between runs, and generate baselines on the platform that will check them — a macOS baseline will not match a Linux CI run.
|
|
629
|
+
|
|
630
|
+
See `examples/visual-regression/` for a working suite.
|
|
631
|
+
|
|
632
|
+
### What changed, not just that it changed
|
|
633
|
+
|
|
634
|
+
A pixel diff says 80,858 pixels moved. It cannot say why. Alongside each screenshot TestRelic captures a DOM snapshot — element geometry, a curated set of computed styles, text and identity attributes — diffs it against a committed baseline, and names the elements responsible:
|
|
635
|
+
|
|
636
|
+
```
|
|
637
|
+
div.card.accent restyled · moved 7.9%
|
|
638
|
+
background-color rgb(3, 183, 156) → rgb(224, 108, 79)
|
|
639
|
+
margin-top (initial) → 24px
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
The report gains an **Elements** view beside the image modes. Hovering a row draws that element's box over the actual screenshot, so the semantic change and the pixels it caused are connected.
|
|
643
|
+
|
|
644
|
+
Elements that only *moved* or *resized* are separated from those that actually changed. One card gaining a 24px margin shifts everything below it; the report leads with the card and folds the knock-on shifts beneath it, rather than presenting eleven equal findings. Seven kinds are reported: `added`, `removed`, `moved`, `resized`, `restyled`, `text` and `attrs`.
|
|
645
|
+
|
|
646
|
+
This needs the fixture's `expect`, since it is the assertion call that gets intercepted:
|
|
647
|
+
|
|
648
|
+
```typescript
|
|
649
|
+
import { test, expect } from '@testrelic/playwright-analytics/fixture';
|
|
650
|
+
|
|
651
|
+
await expect(page).toHaveScreenshot('dashboard.png');
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
Both `toHaveScreenshot()` and `toMatchVisualBaseline()` are covered. Playwright's own `expect` keeps working and keeps comparing pixels — you just get no Elements view.
|
|
655
|
+
|
|
656
|
+
Three things to know:
|
|
657
|
+
|
|
658
|
+
- **Name your snapshots.** `toHaveScreenshot()` with no argument auto-generates a name from the test title, which cannot be resolved to a path ahead of the call, so DOM capture is skipped for those.
|
|
659
|
+
- **DOM comparison needs Playwright >= 1.51**, which is where `testInfo.snapshotPath(name, { kind })` arrived. The peer range stays `>=1.35.0` and everything else works there; on an older version this one feature announces itself as unavailable, once, on stderr.
|
|
660
|
+
- **Masked regions are excluded from the DOM comparison too.** The `mask` locators you already pass to `toHaveScreenshot` are resolved to boxes, and any element intersecting one is skipped — so a masked ticker stops failing the pixel check *and* stops generating text-change noise, from one option.
|
|
661
|
+
|
|
662
|
+
A capture is capped at 1,500 elements and 1 MiB of JSON. Hitting either is reported in the UI as a truncation badge and counted, never silently dropped.
|
|
663
|
+
|
|
664
|
+
#### DOM baselines in git
|
|
665
|
+
|
|
666
|
+
The baseline is written as `<name>.dom.json` beside the image baseline, in the same `<spec>-snapshots/` directory, and belongs in git for the same reason the PNG does — a restyle then arrives as a readable text diff in the pull request instead of an opaque binary change. `--update-snapshots` rewrites both files together, and a first run writes both and reports nothing as changed, because nothing was compared.
|
|
667
|
+
|
|
668
|
+
Expect a style change to touch two files, not one. That is the point of it.
|
|
669
|
+
|
|
588
670
|
## Action Steps
|
|
589
671
|
|
|
590
672
|
TestRelic captures Playwright test steps — actions (`click`, `fill`, `goto`), assertions (`expect`), and custom steps (`test.step()`). Steps are timestamped and synced with the video timeline so you can jump to the exact moment in the recording.
|
package/dist/api-fixture.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
'use strict';var test=require('@playwright/test'),perf_hooks=require('perf_hooks'),fs=require('fs'),path=require('path'),os=require('os'),crypto=require('crypto');require('readline');var url=require('url'),core=require('@testrelic/core');var
|
|
2
|
-
`;fs.writeSync(this.fd,
|
|
3
|
-
`);return
|
|
4
|
-
`);for(let n of e){if(n.includes("/assertion-tracker.")||n.includes("/api-request-tracker.")||n.includes("node:internal")||n.includes(" at new Error")||n.includes(" at captureAssertionLocation")||n.includes(" at expectWrapper")||n.includes(" at wrappedMatcher"))continue;let s=n.match(/at\s+(?:.*?\s+\()?(.+?):(\d+):(\d+)\)?$/);if(s)return {file:s[1],line:parseInt(s[2],10),column:parseInt(s[3],10)}}return null}function ft(r,t){if(!r)return "custom";let e=r.toLowerCase();return e.includes(".status()")&&!e.includes(".statustext()")?e.includes(".ok()")?"statusOk":"status":e.includes(".ok()")?"statusOk":e.includes(".headers()")||e.includes(".headersarray()")?"header":e.includes("tomatchobject")||e.includes("toequal")?"bodyMatch":e.includes("tocontain")||e.includes("tostringcontaining")?"bodyContains":e.includes(".json()")||e.includes(".text()")?"bodyField":"custom"}function ht(r,t,e){let n=function(o,...p){let u=r(),d=t();if(!u||!d)return e(o,...p);let l=null;if(o!=null&&typeof o=="object"){let a=o[B];typeof a=="string"&&(l=a);}if(!l&&o!==null&&o!==void 0&&typeof o=="object"&&(l=C.get(o)??null),l||(l=d.getCallIdForValue(o)),l||(l=u.getCurrentCallId()),!l)return e(o,...p);let i=gt(),g;if(i){let a=pt(i.file);a&&i.line>0&&i.line<=a.length&&(g=a[i.line-1].trim());}let c=e(o,...p);return l?$(c,{tracker:u,callId:l,received:o,location:i??{file:"unknown",line:0},expression:g}):c};for(let s of Object.keys(e))n[s]=e[s];return n}var G=null,Y=null;function v(r,t){G=r,Y=t;}function S(r){return ht(()=>G,()=>Y,r)}function $(r,t){return new Proxy(r,{get(e,n,s){let o=Reflect.get(e,n,s);return typeof o!="function"||typeof n=="symbol"?o:n==="not"?$(o,t):function(...u){let d=u[0],l="passed",i=t.received,g=null;try{let c=o.apply(this??e,u);return c&&typeof c=="object"&&typeof c.then=="function"?c.then(()=>{L(t,n,d,i,"passed");},a=>{throw L(t,n,d,i,"failed"),a}):c}catch(c){if(l="failed",g=c,c instanceof Error&&c.message){let a=c.message.match(/Received:\s*(.+)/);a&&(i=a[1]);}throw c}finally{(g!==null||l==="passed")&&L(t,n,d,i,l);}}}})}function L(r,t,e,n,s){let o=ft(r.expression,r.received),p={callId:r.callId,type:o,expected:U(e),actual:U(n),status:s,location:r.location,expression:r.expression};r.tracker.recordAssertion(p);}function U(r){if(r==null||typeof r=="string"||typeof r=="number"||typeof r=="boolean")return r;try{return JSON.stringify(r),r}catch{return String(r)}}var z="[REDACTED]";function O(r,t){if(r===null||t.length===0)return r;let e=new Set(t.map(s=>s.toLowerCase())),n={};for(let s of Object.keys(r))Object.hasOwn(r,s)&&(n[s]=e.has(s.toLowerCase())?z:r[s]);return n}function q(r,t){if(r===null||t.length===0)return r;let e;try{e=JSON.parse(r);}catch{return r}if(typeof e!="object"||e===null)return r;let n=new Set(t.map(o=>o.toLowerCase())),s=F(e,n);return JSON.stringify(s)}function F(r,t){if(Array.isArray(r))return r.map(e=>F(e,t));if(typeof r=="object"&&r!==null){let e={};for(let n of Object.keys(r)){if(!Object.hasOwn(r,n))continue;let s=r[n];t.has(n.toLowerCase())?e[n]=z:e[n]=F(s,t);}return e}return r}var yt=new Set(["access_token","api_key","token","sig"]);function E(r){if(!r)return r;let t;try{t=new URL(r);}catch{return r}let e=false;for(let n of [...t.searchParams.keys()])yt.has(n.toLowerCase())&&(t.searchParams.delete(n),e=true);return e?t.toString():r}function K(r,t,e){if(e.length>0){for(let n of e)if(V(r,n))return false}if(t.length>0){for(let n of t)if(V(r,n))return true;return false}return true}function V(r,t){try{return t instanceof RegExp?t.test(r):wt(t).test(r)}catch{return console.warn(`[testrelic] Invalid URL filter pattern: ${String(t)}`),false}}function wt(r){let t="",e=0;for(;e<r.length;){let n=r[e];n==="*"&&r[e+1]==="*"?(t+=".*",e+=2,r[e]==="/"&&e++):n==="*"?(t+="[^/]*",e++):n==="?"?(t+="[^/]",e++):".+^${}()|[]\\".includes(n)?(t+="\\"+n,e++):(t+=n,e++);}return new RegExp(t)}var kt=["get","post","put","patch","delete","head","fetch"],mt=["text/","application/json","application/xml","application/javascript","application/x-www-form-urlencoded","application/graphql"];function Ct(r){let t=r.toLowerCase();return mt.some(e=>t.includes(e))}function Rt(r){if(!r)return null;if(r.data!==void 0&&r.data!==null){let t=r.data;return typeof t=="string"?t:Buffer.isBuffer(t)?t.toString("base64"):JSON.stringify(t)}if(r.form!==void 0&&r.form!==null)return JSON.stringify(r.form);if(r.multipart!==void 0&&r.multipart!==null){let t=r.multipart,e={};for(let[n,s]of Object.entries(t))typeof s=="string"||typeof s=="number"||typeof s=="boolean"?e[n]=String(s):s&&typeof s=="object"&&"name"in s?e[n]=`[file: ${s.name}]`:e[n]="[binary]";return JSON.stringify(e)}return null}function Q(r){return r?"\u2022\u2022\u2022\u2022":""}function Tt(r){if(!r)return [];let t;for(let[n,s]of Object.entries(r))if(n.toLowerCase()==="cookie"){t=s;break}if(!t)return [];let e=[];for(let n of t.split(";")){let s=n.trim();if(!s)continue;let o=s.indexOf("="),p=o===-1?s:s.slice(0,o).trim(),u=o===-1?"":s.slice(o+1).trim();p&&e.push({name:p,value:Q(u)});}return e}function At(r){let t=[];for(let e of r)for(let n of e.split(/,(?=\s*[A-Za-z0-9_.\-]+=)/)){let s=n.trim();if(!s)continue;let o=s.split(";"),p=o[0].indexOf("=");if(p===-1)continue;let u=o[0].slice(0,p).trim(),d=o[0].slice(p+1).trim(),l=o.slice(1).map(i=>i.trim()).filter(Boolean).join(" \xB7 ")||void 0;u&&t.push({name:u,value:Q(d),attributes:l});}return t}var P=class P{constructor(t,e,n){this.originals=new Map;this.apiCallWriter=null;this.callCounter=0;this.apiCallCount=0;this.disposed=false;this._lastCallId=null;this.primitiveCallIds=new Map;this.context=t,this.assertionTracker=e??null,this.apiConfig=n??P.DEFAULT_API_CONFIG;try{this.apiCallWriter=new h("api-calls");}catch{}}get lastCallId(){return this._lastCallId}getCallIdForValue(t){return t!=null&&typeof t=="object"?C.get(t)??null:this.primitiveCallIds.get(t)??null}intercept(){for(let e of kt){let n=this.context[e].bind(this.context);this.originals.set(e,n),this.context[e]=this.createWrapper(e,n);}let t=this.context.dispose.bind(this.context);this.originals.set("dispose",t),this.context.dispose=async e=>(this.disposed=true,t(e));}getFileData(){return this.apiCallWriter?.close(),{apiCallsFile:this.apiCallWriter?.getCount()?this.apiCallWriter.getPath():null,apiCallsCount:this.apiCallWriter?.getCount()??0}}flushLegacyAnnotations(t){}dispose(){for(let[t,e]of this.originals)this.context[t]=e;this.originals.clear(),this.apiCallWriter?.close(),this.callCounter=0,this.apiCallCount=0,this._lastCallId=null,this.primitiveCallIds.clear();}get isDisposed(){return this.disposed}getCapturedCallCount(){return this.apiCallCount}tagResponseMethods(t,e){let n=this,s=t.headers.bind(t);t.headers=function(){let a=s();return C.set(a,e),a};let o=t.headersArray.bind(t);t.headersArray=function(){let a=o();return C.set(a,e),a};let p=t.json.bind(t);t.json=async function(){let a=await p();return a!=null&&typeof a=="object"&&C.set(a,e),a};let u=t.status.bind(t);t.status=function(){let a=u();return n.primitiveCallIds.set(a,e),a};let d=t.statusText.bind(t);t.statusText=function(){let a=d();return n.primitiveCallIds.set(a,e),a};let l=t.ok.bind(t);t.ok=function(){let a=l();return n.primitiveCallIds.set(a,e),a};let i=t.text.bind(t);t.text=async function(){let a=await i();return n.primitiveCallIds.set(a,e),a};let g=t.body.bind(t);t.body=async function(){let a=await g();return C.set(a,e),a};}createWrapper(t,e){let n=this;return async function(o,p){if(!K(o,n.apiConfig.apiIncludeUrls,n.apiConfig.apiExcludeUrls))return e(o,p);let u=`api-call-${n.callCounter++}`,d=new Date().toISOString(),l=t==="fetch"?(p?.method??"GET").toUpperCase():t.toUpperCase(),i=Rt(p),g=perf_hooks.performance.now();n._lastCallId=u,n.assertionTracker&&n.assertionTracker.setCurrentCallId(u);let c;try{c=await e(o,p);}catch(a){let y=perf_hooks.performance.now();try{let R=n.apiConfig.captureRequestBody?q(i,n.apiConfig.redactBodyFields):null,m={id:u,timestamp:d,method:l,url:E(o),requestHeaders:null,requestBody:R,responseStatusCode:null,responseStatusText:null,responseHeaders:null,responseBody:null,responseTimeMs:Math.round((y-g)*100)/100,isBinary:!1,error:a instanceof Error?a.message:String(a)};n.apiCallWriter&&(n.apiCallWriter.append(m),n.apiCallCount++);}catch{}throw a}try{let a=perf_hooks.performance.now(),y=c.headers(),R=y["content-type"]??null,m=R?!Ct(R):!1,T=null;n.apiConfig.captureRequestHeaders&&p?.headers&&(T=p.headers);let f=null;n.apiConfig.captureResponseHeaders&&(f=y);let w=n.apiConfig.captureRequestBody?i:null,k=null;if(n.apiConfig.captureResponseBody)try{m?k=(await c.body()).toString("base64"):k=await c.text();}catch{}let x=[],_=[];try{if(n.apiConfig.captureRequestHeaders&&(x=Tt(p?.headers)),n.apiConfig.captureResponseHeaders){let M=c.headersArray().filter(I=>I.name.toLowerCase()==="set-cookie").map(I=>I.value);_=At(M);}}catch{}T=O(T,n.apiConfig.redactHeaders),f=O(f,n.apiConfig.redactHeaders),w=q(w,n.apiConfig.redactBodyFields),k=q(k,n.apiConfig.redactBodyFields);let et={id:u,timestamp:d,method:l,url:E(c.url()),requestHeaders:T,requestBody:w,responseStatusCode:c.status(),responseStatusText:c.statusText(),responseHeaders:f,responseBody:k,requestCookies:x.length?x:null,responseCookies:_.length?_:null,responseTimeMs:Math.round((a-g)*100)/100,isBinary:m,error:null};n.apiCallWriter&&(n.apiCallWriter.append(et),n.apiCallCount++);}catch{}try{c[B]=u,n.tagResponseMethods(c,u);}catch{}return c}}};P.DEFAULT_API_CONFIG=Object.freeze({trackApiCalls:true,captureRequestHeaders:true,captureResponseHeaders:true,captureRequestBody:true,captureResponseBody:true,captureAssertions:true,redactHeaders:["authorization","cookie","set-cookie","x-api-key"],redactBodyFields:["password","secret","token","apiKey","api_key"],apiIncludeUrls:[],apiExcludeUrls:[]});var b=P;var vt=["text/","application/json","application/xml","application/javascript","application/x-www-form-urlencoded","application/graphql"];function bt(r){let t=r.toLowerCase();return vt.some(e=>t.includes(e))}var N=class r{constructor(t,e){this.page=t;this.records=[];this.listeners=[];this.navCounters=[];this.pendingRequests=new Map;this.networkWriter=null;this.consoleWriter=null;this.pendingBodyReads=[];this.requestIdCounter=0;this.networkRequestCount=0;this.consoleLogCount=0;this.includeNetworkStats=e?.includeNetworkStats??true,this.origGoto=t.goto.bind(t),this.origGoBack=t.goBack.bind(t),this.origGoForward=t.goForward.bind(t),this.origReload=t.reload.bind(t);try{this.includeNetworkStats&&(this.networkWriter=new h("network")),this.consoleWriter=new h("console");}catch{}this.interceptMethods(),this.attachListeners();}async init(){await this.injectSPADetection();}async finalizeCapturedRequests(){await Promise.allSettled(this.pendingBodyReads),this.pendingBodyReads=[];for(let[,t]of this.pendingRequests)this.networkWriter&&(this.networkWriter.append({url:t.url,method:t.method,resourceType:t.resourceType,statusCode:0,responseTimeMs:Date.now()-t.startTimeMs,startedAt:t.startedAt,requestHeaders:t.headers,requestBody:t.postData,responseBody:null,responseHeaders:null,contentType:null,responseSize:0,requestBodyTruncated:t.postDataTruncated,responseBodyTruncated:false,isBinary:false,error:"incomplete"}),this.networkRequestCount++);this.pendingRequests.clear(),this.networkWriter?.close(),this.consoleWriter?.close();}static mapConsoleType(t){switch(t){case "log":return "log";case "warning":return "warn";case "error":return "error";case "info":return "info";case "debug":return "debug";default:return "log"}}async getFileData(){this.assembleNetworkStats();let t=this.records.map(e=>({url:e.url,navigationType:e.navigationType,timestamp:e.timestamp,domContentLoadedAt:e.domContentLoadedAt,networkIdleAt:e.networkIdleAt,networkStats:e.networkStats}));return this.includeNetworkStats?await this.finalizeCapturedRequests():this.consoleWriter?.close(),{navigations:t,networkRequestsFile:this.networkWriter?.getCount()?this.networkWriter.getPath():null,networkRequestsCount:this.networkWriter?.getCount()??0,consoleLogsFile:this.consoleWriter?.getCount()?this.consoleWriter.getPath():null,consoleLogsCount:this.consoleWriter?.getCount()??0}}async flushLegacyAnnotations(t){}dispose(){this.page.goto=this.origGoto,this.page.goBack=this.origGoBack,this.page.goForward=this.origGoForward,this.page.reload=this.origReload;for(let{event:t,handler:e}of this.listeners)this.page.off(t,e);this.listeners=[],this.records=[],this.pendingRequests.clear(),this.pendingBodyReads=[],this.networkWriter?.close(),this.consoleWriter?.close();}getRecords(){return this.assembleNetworkStats(),this.records}interceptMethods(){let t=this,e=this.page;e.goto=async function(n,s){return t.recordNavigation(n,"goto"),t.origGoto(n,s)},e.goBack=async function(n){let s=await t.origGoBack(n);return t.recordNavigation(e.url(),"back"),s},e.goForward=async function(n){let s=await t.origGoForward(n);return t.recordNavigation(e.url(),"forward"),s},e.reload=async function(n){return t.recordNavigation(e.url(),"refresh"),t.origReload(n)};}attachListeners(){let t=()=>{this.lastDomContentLoaded=new Date().toISOString(),this.records.length>0&&(this.records[this.records.length-1].domContentLoadedAt=this.lastDomContentLoaded);};this.page.on("domcontentloaded",t),this.listeners.push({event:"domcontentloaded",handler:t});let e=s=>{try{let o=s;if(typeof o.parentFrame=="function"&&o.parentFrame()!==null)return;let p=o.url(),u=this.records[this.records.length-1];if(u&&Date.now()-new Date(u.timestamp).getTime()<50&&u.url===p)return;this.recordNavigation(p,"navigation");}catch{}};this.page.on("framenavigated",e),this.listeners.push({event:"framenavigated",handler:e});let n=s=>{try{let o=s,p=o.type(),u=o.text();if(p==="debug"&&u.startsWith("__testrelic_nav:")){try{let d=JSON.parse(u.slice(16));d.type&&d.url&&this.recordNavigation(d.url,d.type);}catch{}return}{let d=null;try{let i=o.location();i&&i.url&&(d=`${i.url}:${i.lineNumber}:${i.columnNumber}`);}catch{}let l=r.mapConsoleType(p);this.consoleWriter&&(this.consoleWriter.append({level:l,text:u,timestamp:new Date().toISOString(),location:d}),this.consoleLogCount++);}}catch{}};if(this.page.on("console",n),this.listeners.push({event:"console",handler:n}),this.includeNetworkStats){let s=u=>{let d=this.records.length-1,l=this.counterForNav(d);l&&l.totalRequests++;try{let i=u,g=String(this.requestIdCounter++),c=i.postData()??null,a={url:i.url(),method:i.method(),resourceType:this.mapResourceType(i.resourceType()),headers:i.headers(),postData:c,postDataTruncated:!1,startedAt:new Date().toISOString(),startTimeMs:Date.now(),navIndex:d};this.pendingRequests.set(g,a),u.__testrelic_id=g;}catch{}};this.page.on("request",s),this.listeners.push({event:"request",handler:s});let o=u=>{try{let d=u,l=d.request().__testrelic_id,i=l?this.pendingRequests.get(l):void 0,g=this.counterForNav(i?i.navIndex:this.records.length-1);if(g){let f=d.status();f>=400&&(g.failedRequests++,g.failedRequestUrls.push(f+" "+d.url()));let w=d.headers()["content-length"];w&&(g.totalBytes+=parseInt(w,10)||0);let k=d.request().resourceType(),x=this.mapResourceType(k);g.byType[x]++;}if(!l||!i)return;this.pendingRequests.delete(l);let c=Date.now()-i.startTimeMs,a=d.headers(),y=a["content-type"]??null,R=parseInt(a["content-length"]??"0",10)||0,m=y?!bt(y):!1,T=(async()=>{let f=null;if(!m)try{f=(await d.body()).toString("utf-8");}catch{}let w={url:i.url,method:i.method,resourceType:i.resourceType,statusCode:d.status(),responseTimeMs:c,startedAt:i.startedAt,requestHeaders:i.headers,requestBody:i.postData,responseBody:f,responseHeaders:a,contentType:y,responseSize:R,requestBodyTruncated:i.postDataTruncated,responseBodyTruncated:!1,isBinary:m,error:null};this.networkWriter&&(this.networkWriter.append(w),this.networkRequestCount++);})();this.pendingBodyReads.push(T);}catch{}};this.page.on("response",o),this.listeners.push({event:"response",handler:o});let p=u=>{let d=u.__testrelic_id,l=d?this.pendingRequests.get(d):void 0,i=this.counterForNav(l?l.navIndex:this.records.length-1);if(i){i.failedRequests++;try{let g=u;i.failedRequestUrls.push("ERR "+g.url());}catch{}}try{let g=u;if(!d||!l)return;this.pendingRequests.delete(d);let c={url:l.url,method:l.method,resourceType:l.resourceType,statusCode:0,responseTimeMs:Date.now()-l.startTimeMs,startedAt:l.startedAt,requestHeaders:l.headers,requestBody:l.postData,responseBody:null,responseHeaders:null,contentType:null,responseSize:0,requestBodyTruncated:l.postDataTruncated,responseBodyTruncated:!1,isBinary:!1,error:g.failure()?.errorText??"Unknown error"};this.networkWriter&&(this.networkWriter.append(c),this.networkRequestCount++);}catch{}};this.page.on("requestfailed",p),this.listeners.push({event:"requestfailed",handler:p});}}async injectSPADetection(){try{await this.page.addInitScript(()=>{let t=history.pushState.bind(history),e=history.replaceState.bind(history);history.pushState=function(...n){t(...n),console.debug("__testrelic_nav:"+JSON.stringify({type:"spa_route",url:location.href}));},history.replaceState=function(...n){e(...n),console.debug("__testrelic_nav:"+JSON.stringify({type:"spa_replace",url:location.href}));},window.addEventListener("popstate",()=>{console.debug("__testrelic_nav:"+JSON.stringify({type:"popstate",url:location.href}));}),window.addEventListener("hashchange",()=>{console.debug("__testrelic_nav:"+JSON.stringify({type:"hash_change",url:location.href}));});});}catch{}}recordNavigation(t,e){this.records.push({url:t,navigationType:e,timestamp:new Date().toISOString()}),this.includeNetworkStats&&this.navCounters.push(this.createNetworkCounter());}createNetworkCounter(){return {totalRequests:0,failedRequests:0,failedRequestUrls:[],totalBytes:0,byType:{xhr:0,document:0,script:0,stylesheet:0,image:0,font:0,other:0}}}counterForNav(t){return t>=0?this.navCounters[t]:void 0}assembleNetworkStats(){if(this.includeNetworkStats)for(let t=0;t<this.records.length;t++){let e=this.navCounters[t];e&&(this.records[t].networkStats={totalRequests:e.totalRequests,failedRequests:e.failedRequests,failedRequestUrls:[...e.failedRequestUrls],totalBytes:e.totalBytes,byType:{...e.byType}});}}mapResourceType(t){switch(t){case "xhr":case "fetch":return "xhr";case "document":return "document";case "script":return "script";case "stylesheet":return "stylesheet";case "image":return "image";case "font":return "font";default:return "other"}}};S(test.expect);var qt="__testrelic_api_config",Pt="__testrelic_config_trackApiCalls";function W(r){let t=r.annotations.find(n=>n.type===qt&&n.description!==void 0);if(t)try{return JSON.parse(t.description,Nt)}catch{}let e=r.annotations.find(n=>n.type===Pt&&n.description!==void 0);return e?{trackApiCalls:e.description!=="false",captureRequestHeaders:true,captureResponseHeaders:true,captureRequestBody:true,captureResponseBody:true,captureAssertions:true,redactHeaders:["authorization","cookie","set-cookie","x-api-key"],redactBodyFields:["password","secret","token","apiKey","api_key"],apiIncludeUrls:[],apiExcludeUrls:[]}:{trackApiCalls:true,captureRequestHeaders:true,captureResponseHeaders:true,captureRequestBody:true,captureResponseBody:true,captureAssertions:true,redactHeaders:["authorization","cookie","set-cookie","x-api-key"],redactBodyFields:["password","secret","token","apiKey","api_key"],apiIncludeUrls:[],apiExcludeUrls:[]}}function Nt(r,t){if(typeof t=="object"&&t!==null&&t.__regexp===true&&typeof t.source=="string"){let{source:e,flags:n}=t;return new RegExp(e,n)}return t}var _t={page:async({page:r},t,e)=>{let n=new N(r);try{await n.init();}catch{}await t(r);try{let{navigations:s,networkRequestsFile:o,networkRequestsCount:p,consoleLogsFile:u,consoleLogsCount:d}=await n.getFileData(),l={testRelicData:!0,version:core.PAYLOAD_VERSION,navigations:s,apiAssertions:[],networkRequestsFile:o,networkRequestsCount:p,consoleLogsFile:u,consoleLogsCount:d,apiCallsFile:null,apiCallsCount:0};await e.attach(core.ATTACHMENT_NAME,{body:Buffer.from(JSON.stringify(l)),contentType:core.ATTACHMENT_CONTENT_TYPE});}catch{}n.dispose();},request:async({request:r},t,e)=>{let n=W(e);if(!n.trackApiCalls){await t(r);return}let s=new A,o=new b(r,s,n);o.intercept(),v(s,o);try{await t(r);}finally{v(null,null);}try{let{apiCallsFile:p,apiCallsCount:u}=o.getFileData(),d=n.captureAssertions?s.getData():[],l={testRelicData:!0,version:core.PAYLOAD_VERSION,navigations:[],apiAssertions:d,networkRequestsFile:null,networkRequestsCount:0,consoleLogsFile:null,consoleLogsCount:0,apiCallsFile:p,apiCallsCount:u};await e.attach(core.ATTACHMENT_NAME,{body:Buffer.from(JSON.stringify(l)),contentType:core.ATTACHMENT_CONTENT_TYPE});}catch{}o.dispose(),s.dispose();}};test.test.extend(_t);var we=S(test.expect),ke={request:async({request:r},t,e)=>{let n=W(e);if(!n.trackApiCalls){await t(r);return}let s=new A,o=new b(r,s,n);o.intercept(),v(s,o);let p=null;try{p=new h("api-console");}catch{}let u=0,d=process.stdout.write,l=process.stderr.write;process.stdout.write=function(i,...g){try{let c=typeof i=="string"?i:Buffer.isBuffer(i)?i.toString("utf-8"):String(i);!c.startsWith("[testrelic]")&&!c.startsWith("\u2139 TestRelic")&&!c.startsWith("\u2713 TestRelic")&&!c.startsWith("\u26A0 TestRelic")&&p&&(p.append({level:"stdout",text:c.replace(/\n$/,""),timestamp:new Date().toISOString(),location:null}),u++);}catch{}return d.apply(process.stdout,[i,...g])},process.stderr.write=function(i,...g){try{let c=typeof i=="string"?i:Buffer.isBuffer(i)?i.toString("utf-8"):String(i);!c.startsWith("[testrelic]")&&!c.startsWith("\u26A0 TestRelic")&&p&&(p.append({level:"stderr",text:c.replace(/\n$/,""),timestamp:new Date().toISOString(),location:null}),u++);}catch{}return l.apply(process.stderr,[i,...g])};try{await t(r);}finally{process.stdout.write=d,process.stderr.write=l,v(null,null);}try{p?.close();let{apiCallsFile:i,apiCallsCount:g}=o.getFileData(),c=n.captureAssertions?s.getData():[],a={testRelicData:!0,version:core.PAYLOAD_VERSION,navigations:[],apiAssertions:c,networkRequestsFile:null,networkRequestsCount:0,consoleLogsFile:u>0?p?.getPath()??null:null,consoleLogsCount:u,apiCallsFile:i,apiCallsCount:g};await e.attach(core.ATTACHMENT_NAME,{body:Buffer.from(JSON.stringify(a)),contentType:core.ATTACHMENT_CONTENT_TYPE});}catch{}o.dispose(),s.dispose();}};exports.expect=we;exports.testRelicApiFixture=ke;//# sourceMappingURL=api-fixture.cjs.map
|
|
1
|
+
'use strict';var test=require('@playwright/test'),perf_hooks=require('perf_hooks'),fs=require('fs'),path=require('path'),os=require('os'),crypto=require('crypto');require('readline');var url=require('url'),core=require('@testrelic/core');var ct=path.join(os.tmpdir(),"testrelic-data"),v=class{constructor(t){this.count=0;this.closed=false;fs.mkdirSync(ct,{recursive:true}),this.filePath=path.join(ct,`${t}-${crypto.randomUUID().substring(0,8)}.jsonl`),this.fd=fs.openSync(this.filePath,"w");}append(t){if(this.closed)return;let n=JSON.stringify(t)+`
|
|
2
|
+
`;fs.writeSync(this.fd,n),this.count++;}getPath(){return this.filePath}getCount(){return this.count}close(){if(!this.closed){this.closed=true;try{fs.closeSync(this.fd);}catch{}}}cleanup(){try{fs.unlinkSync(this.filePath);}catch{}}};var $t=new RegExp("[\x1B\x9B][[\\]()#;?]*(?:\\d{1,4}(?:;\\d{0,4})*)?[0-9A-PR-TZcf-nqry=><]","g");function dt(e){return !e||e.indexOf("\x1B")===-1&&e.indexOf("\x9B")===-1?e:e.replace($t,"")}var z=Symbol.for("__testrelic_call_id"),Xt="__testrelic_api_assertions",D=new WeakMap,M=class{constructor(){this.assertions=[];this.currentCallId=null;}recordAssertion(t){this.assertions.push(t);}getAssertions(){return this.assertions}setCurrentCallId(t){this.currentCallId=t;}getCurrentCallId(){return this.currentCallId}getData(){return [...this.assertions]}flushLegacyAnnotations(t){this.assertions.length!==0&&t.annotations.push({type:Xt,description:JSON.stringify(this.assertions)});}dispose(){this.assertions=[],this.currentCallId=null;}},pt=new Map;function Kt(e){let t=pt.get(e);if(t)return t;try{let n=e.startsWith("file://")?url.fileURLToPath(e):e,o=fs.readFileSync(n,"utf-8").split(`
|
|
3
|
+
`);return pt.set(e,o),o}catch{return null}}var ft=(()=>{let e=new Error().stack;if(!e)return null;for(let t of e.split(`
|
|
4
|
+
`)){let n=t.match(/at\s+(?:.*?\s+\()?(.+?):\d+:\d+\)?$/);if(!n||n[1].startsWith("node:"))continue;let r=Math.max(n[1].lastIndexOf("/"),n[1].lastIndexOf("\\"));return r>0?n[1].slice(0,r+1):null}return null})();function Zt(e){if(!e.startsWith("file:"))return e;try{return url.fileURLToPath(e)}catch{return e}}function Qt(){let t=new Error().stack;if(!t)return null;let n=t.split(`
|
|
5
|
+
`);return gt(n,true)??gt(n,false)}function gt(e,t){for(let n of e){if(t&&ft!==null&&n.includes(ft)||n.includes("node_modules")||n.includes("node:internal")||n.includes(" at new Error")||n.includes(" at captureAssertionLocation")||n.includes(" at findFrame")||n.includes(" at expectWrapper")||n.includes(" at wrappedMatcher"))continue;let r=n.match(/at\s+(?:.*?\s+\()?(.+?):(\d+):(\d+)\)?$/);if(r)return {file:Zt(r[1]),line:parseInt(r[2],10),column:parseInt(r[3],10)}}return null}function te(e,t){if(!e)return "custom";let n=e.toLowerCase();return n.includes(".status()")&&!n.includes(".statustext()")?n.includes(".ok()")?"statusOk":"status":n.includes(".ok()")?"statusOk":n.includes(".headers()")||n.includes(".headersarray()")?"header":n.includes("tomatchobject")||n.includes("toequal")?"bodyMatch":n.includes("tocontain")||n.includes("tostringcontaining")?"bodyContains":n.includes(".json()")||n.includes(".text()")?"bodyField":"custom"}function ee(e,t,n){let r=function(a,...d){let f=e(),c=t();if(!f||!c)return n(a,...d);let u=null;if(a!=null&&typeof a=="object"){let h=a[z];typeof h=="string"&&(u=h);}if(!u&&a!==null&&a!==void 0&&typeof a=="object"&&(u=D.get(a)??null),u||(u=c.getCallIdForValue(a)),u||(u=f.getCurrentCallId()),!u)return n(a,...d);let i=Qt(),p;if(i){let h=Kt(i.file);h&&i.line>0&&i.line<=h.length&&(p=h[i.line-1].trim());}let g=n(a,...d);return u?wt(g,{tracker:f,callId:u,received:a,location:i??{file:"unknown",line:0},expression:p}):g},o=n,s=r;for(let l of Object.keys(o))s[l]=o[l];return r}var ht=Symbol.for("testrelic.activeAssertionContext");function J(){let e=globalThis,t=e[ht];return t||(t={tracker:null,apiTracker:null},e[ht]=t),t}function B(e,t){let n=J();n.tracker=e,n.apiTracker=t;}function O(e){return ee(()=>J().tracker,()=>J().apiTracker,e)}function wt(e,t){return new Proxy(e,{get(n,r,o){let s=Reflect.get(n,r,o);return typeof s!="function"||typeof r=="symbol"?s:r==="not"?wt(s,t):function(...a){let d=a[0],f="passed",c=t.received,u=null;try{let i=s.apply(this??n,a);return i&&typeof i=="object"&&typeof i.then=="function"?i.then(()=>{U(t,r,d,c,"passed");},p=>{throw U(t,r,d,c,"failed"),p}):i}catch(i){if(f="failed",u=i,i instanceof Error&&i.message){let p=i.message.match(/Received:\s*(.+)/);p&&(c=p[1]);}throw i}finally{(u!==null||f==="passed")&&U(t,r,d,c,f);}}}})}function U(e,t,n,r,o){let s=te(e.expression,e.received),l={callId:e.callId,type:s,expected:mt(n),actual:mt(r),status:o,location:e.location,expression:e.expression};e.tracker.recordAssertion(l);}function mt(e){if(e==null)return e;if(typeof e=="string")return dt(e);if(typeof e=="number"||typeof e=="boolean")return e;try{return JSON.stringify(e),e}catch{return String(e)}}var kt="[REDACTED]";function $(e,t){if(e===null||t.length===0)return e;let n=new Set(t.map(o=>o.toLowerCase())),r={};for(let o of Object.keys(e))Object.prototype.hasOwnProperty.call(e,o)&&(r[o]=n.has(o.toLowerCase())?kt:e[o]);return r}function L(e,t){if(e===null||t.length===0)return e;let n;try{n=JSON.parse(e);}catch{return e}if(typeof n!="object"||n===null)return e;let r=new Set(t.map(s=>s.toLowerCase())),o=G(n,r);return JSON.stringify(o)}function G(e,t){if(Array.isArray(e))return e.map(n=>G(n,t));if(typeof e=="object"&&e!==null){let n={};for(let r of Object.keys(e)){if(!Object.prototype.hasOwnProperty.call(e,r))continue;let o=e[r];t.has(r.toLowerCase())?n[r]=kt:n[r]=G(o,t);}return n}return e}var ne=new Set(["access_token","api_key","token","sig"]);function Y(e){if(!e)return e;let t;try{t=new URL(e);}catch{return e}let n=false;for(let r of [...t.searchParams.keys()])ne.has(r.toLowerCase())&&(t.searchParams.delete(r),n=true);return n?t.toString():e}function xt(e,t,n){if(n.length>0){for(let r of n)if(bt(e,r))return false}if(t.length>0){for(let r of t)if(bt(e,r))return true;return false}return true}function bt(e,t){try{return t instanceof RegExp?t.test(e):re(t).test(e)}catch{return console.warn(`[testrelic] Invalid URL filter pattern: ${String(t)}`),false}}function re(e){let t="",n=0;for(;n<e.length;){let r=e[n];r==="*"&&e[n+1]==="*"?(t+=".*",n+=2,e[n]==="/"&&n++):r==="*"?(t+="[^/]*",n++):r==="?"?(t+="[^/]",n++):".+^${}()|[]\\".includes(r)?(t+="\\"+r,n++):(t+=r,n++);}return new RegExp(t)}var oe=["get","post","put","patch","delete","head","fetch"],se=["text/","application/json","application/xml","application/javascript","application/x-www-form-urlencoded","application/graphql"];function ie(e){let t=e.toLowerCase();return se.some(n=>t.includes(n))}function ae(e){if(!e)return null;if(e.data!==void 0&&e.data!==null){let t=e.data;return typeof t=="string"?t:Buffer.isBuffer(t)?t.toString("base64"):JSON.stringify(t)}if(e.form!==void 0&&e.form!==null)return JSON.stringify(e.form);if(e.multipart!==void 0&&e.multipart!==null){let t=e.multipart,n={};for(let[r,o]of Object.entries(t))typeof o=="string"||typeof o=="number"||typeof o=="boolean"?n[r]=String(o):o&&typeof o=="object"&&"name"in o?n[r]=`[file: ${o.name}]`:n[r]="[binary]";return JSON.stringify(n)}return null}function Ct(e){return e?"\u2022\u2022\u2022\u2022":""}function ue(e){if(!e)return [];let t;for(let[r,o]of Object.entries(e))if(r.toLowerCase()==="cookie"){t=o;break}if(!t)return [];let n=[];for(let r of t.split(";")){let o=r.trim();if(!o)continue;let s=o.indexOf("="),l=s===-1?o:o.slice(0,s).trim(),a=s===-1?"":o.slice(s+1).trim();l&&n.push({name:l,value:Ct(a)});}return n}function ce(e){let t=[];for(let n of e)for(let r of n.split(/,(?=\s*[A-Za-z0-9_.\-]+=)/)){let o=r.trim();if(!o)continue;let s=o.split(";"),l=s[0].indexOf("=");if(l===-1)continue;let a=s[0].slice(0,l).trim(),d=s[0].slice(l+1).trim(),f=s.slice(1).map(c=>c.trim()).filter(Boolean).join(" \xB7 ")||void 0;a&&t.push({name:a,value:Ct(d),attributes:f});}return t}var q=class q{constructor(t,n,r){this.originals=new Map;this.apiCallWriter=null;this.statusMix={"2xx":0,"3xx":0,"4xx":0,"5xx":0,error:0};this.callCounter=0;this.apiCallCount=0;this.disposed=false;this._lastCallId=null;this.primitiveCallIds=new Map;this.context=t,this.assertionTracker=n??null,this.apiConfig=r??q.DEFAULT_API_CONFIG;try{this.apiCallWriter=new v("api-calls");}catch{}}get lastCallId(){return this._lastCallId}getCallIdForValue(t){return t!=null&&typeof t=="object"?D.get(t)??null:this.primitiveCallIds.get(t)??null}intercept(){for(let n of oe){let r=this.context[n].bind(this.context);this.originals.set(n,r),this.context[n]=this.createWrapper(n,r);}let t=this.context.dispose.bind(this.context);this.originals.set("dispose",t),this.context.dispose=async n=>(this.disposed=true,t(n));}tallyStatus(t){if(t===null||t<=0){this.statusMix.error++;return}if(t>=500){this.statusMix["5xx"]++;return}if(t>=400){this.statusMix["4xx"]++;return}if(t>=300){this.statusMix["3xx"]++;return}if(t>=200){this.statusMix["2xx"]++;return}this.statusMix.error++;}getFileData(){return this.apiCallWriter?.close(),{apiCallsFile:this.apiCallWriter?.getCount()?this.apiCallWriter.getPath():null,apiCallsCount:this.apiCallWriter?.getCount()??0,apiStatusMix:{...this.statusMix}}}flushLegacyAnnotations(t){}dispose(){for(let[t,n]of this.originals)this.context[t]=n;this.originals.clear(),this.apiCallWriter?.close(),this.callCounter=0,this.apiCallCount=0,this._lastCallId=null,this.primitiveCallIds.clear();}get isDisposed(){return this.disposed}getCapturedCallCount(){return this.apiCallCount}tagResponseMethods(t,n){let r=this,o=t.headers.bind(t);t.headers=function(){let p=o();return D.set(p,n),p};let s=t.headersArray.bind(t);t.headersArray=function(){let p=s();return D.set(p,n),p};let l=t.json.bind(t);t.json=async function(){let p=await l();return p!=null&&typeof p=="object"&&D.set(p,n),p};let a=t.status.bind(t);t.status=function(){let p=a();return r.primitiveCallIds.set(p,n),p};let d=t.statusText.bind(t);t.statusText=function(){let p=d();return r.primitiveCallIds.set(p,n),p};let f=t.ok.bind(t);t.ok=function(){let p=f();return r.primitiveCallIds.set(p,n),p};let c=t.text.bind(t);t.text=async function(){let p=await c();return r.primitiveCallIds.set(p,n),p};let u=t.body.bind(t);t.body=async function(){let p=await u();return D.set(p,n),p};}createWrapper(t,n){let r=this;return async function(s,l){if(!xt(s,r.apiConfig.apiIncludeUrls,r.apiConfig.apiExcludeUrls))return n(s,l);let a=`api-call-${r.callCounter++}`,d=new Date().toISOString(),f=t==="fetch"?(l?.method??"GET").toUpperCase():t.toUpperCase(),c=ae(l),u=perf_hooks.performance.now();r._lastCallId=a,r.assertionTracker&&r.assertionTracker.setCurrentCallId(a);let i;try{i=await n(s,l);}catch(p){let g=perf_hooks.performance.now();try{let h=r.apiConfig.captureRequestBody?L(c,r.apiConfig.redactBodyFields):null,w={id:a,timestamp:d,method:f,url:Y(s),requestHeaders:null,requestBody:h,responseStatusCode:null,responseStatusText:null,responseHeaders:null,responseBody:null,responseTimeMs:Math.round((g-u)*100)/100,isBinary:!1,error:p instanceof Error?p.message:String(p)};r.apiCallWriter&&(r.apiCallWriter.append(w),r.apiCallCount++,r.tallyStatus(null));}catch{}throw p}try{let p=perf_hooks.performance.now(),g=i.headers(),h=g["content-type"]??null,w=h?!ie(h):!1,k=null;r.apiConfig.captureRequestHeaders&&l?.headers&&(k=l.headers);let x=null;r.apiConfig.captureResponseHeaders&&(x=g);let R=r.apiConfig.captureRequestBody?c:null,A=null;if(r.apiConfig.captureResponseBody)try{w?A=(await i.body()).toString("base64"):A=await i.text();}catch{}let P=[],N=[];try{if(r.apiConfig.captureRequestHeaders&&(P=ue(l?.headers)),r.apiConfig.captureResponseHeaders){let m=i.headersArray().filter(b=>b.name.toLowerCase()==="set-cookie").map(b=>b.value);N=ce(m);}}catch{}k=$(k,r.apiConfig.redactHeaders),x=$(x,r.apiConfig.redactHeaders),R=L(R,r.apiConfig.redactBodyFields),A=L(A,r.apiConfig.redactBodyFields);let y={id:a,timestamp:d,method:f,url:Y(i.url()),requestHeaders:k,requestBody:R,responseStatusCode:i.status(),responseStatusText:i.statusText(),responseHeaders:x,responseBody:A,requestCookies:P.length?P:null,responseCookies:N.length?N:null,responseTimeMs:Math.round((p-u)*100)/100,isBinary:w,error:null};r.apiCallWriter&&(r.apiCallWriter.append(y),r.apiCallCount++,r.tallyStatus(y.responseStatusCode));}catch{}try{i[z]=a,r.tagResponseMethods(i,a);}catch{}return i}}};q.DEFAULT_API_CONFIG=Object.freeze({trackApiCalls:true,captureRequestHeaders:true,captureResponseHeaders:true,captureRequestBody:true,captureResponseBody:true,captureAssertions:true,redactHeaders:["authorization","cookie","set-cookie","x-api-key"],redactBodyFields:["password","secret","token","apiKey","api_key"],apiIncludeUrls:[],apiExcludeUrls:[]});var I=q;var le=["text/","application/json","application/xml","application/javascript","application/x-www-form-urlencoded","application/graphql"];function de(e){let t=e.toLowerCase();return le.some(n=>t.includes(n))}var F=class e{constructor(t,n){this.page=t;this.records=[];this.listeners=[];this.navCounters=[];this.pendingRequests=new Map;this.networkWriter=null;this.consoleWriter=null;this.pendingBodyReads=[];this.requestIdCounter=0;this.networkRequestCount=0;this.consoleLogCount=0;this.includeNetworkStats=n?.includeNetworkStats??true,this.origGoto=t.goto.bind(t),this.origGoBack=t.goBack.bind(t),this.origGoForward=t.goForward.bind(t),this.origReload=t.reload.bind(t);try{this.includeNetworkStats&&(this.networkWriter=new v("network")),this.consoleWriter=new v("console");}catch{}this.interceptMethods(),this.attachListeners();}async init(){await this.injectSPADetection();}async finalizeCapturedRequests(){await Promise.allSettled(this.pendingBodyReads),this.pendingBodyReads=[];for(let[,t]of this.pendingRequests)this.networkWriter&&(this.networkWriter.append({url:t.url,method:t.method,resourceType:t.resourceType,statusCode:0,responseTimeMs:Date.now()-t.startTimeMs,startedAt:t.startedAt,requestHeaders:t.headers,requestBody:t.postData,responseBody:null,responseHeaders:null,contentType:null,responseSize:0,requestBodyTruncated:t.postDataTruncated,responseBodyTruncated:false,isBinary:false,error:"incomplete"}),this.networkRequestCount++);this.pendingRequests.clear(),this.networkWriter?.close(),this.consoleWriter?.close();}static mapConsoleType(t){switch(t){case "log":return "log";case "warning":return "warn";case "error":return "error";case "info":return "info";case "debug":return "debug";default:return "log"}}async getFileData(){this.assembleNetworkStats();let t=this.records.map(n=>({url:n.url,navigationType:n.navigationType,timestamp:n.timestamp,domContentLoadedAt:n.domContentLoadedAt,networkIdleAt:n.networkIdleAt,networkStats:n.networkStats}));return this.includeNetworkStats?await this.finalizeCapturedRequests():this.consoleWriter?.close(),{navigations:t,networkRequestsFile:this.networkWriter?.getCount()?this.networkWriter.getPath():null,networkRequestsCount:this.networkWriter?.getCount()??0,consoleLogsFile:this.consoleWriter?.getCount()?this.consoleWriter.getPath():null,consoleLogsCount:this.consoleWriter?.getCount()??0}}async flushLegacyAnnotations(t){}dispose(){let t=e.asPatchable(this.page);t.goto=this.origGoto,t.goBack=this.origGoBack,t.goForward=this.origGoForward,t.reload=this.origReload;for(let{event:n,handler:r}of this.listeners)this.page.off(n,r);this.listeners=[],this.records=[],this.pendingRequests.clear(),this.pendingBodyReads=[],this.networkWriter?.close(),this.consoleWriter?.close();}getRecords(){return this.assembleNetworkStats(),this.records}static asPatchable(t){return t}interceptMethods(){let t=this,n=this.page,r=e.asPatchable(n);r.goto=async function(o,s){return t.recordNavigation(o,"goto"),t.origGoto(o,s)},r.goBack=async function(o){let s=await t.origGoBack(o);return t.recordNavigation(n.url(),"back"),s},r.goForward=async function(o){let s=await t.origGoForward(o);return t.recordNavigation(n.url(),"forward"),s},r.reload=async function(o){return t.recordNavigation(n.url(),"refresh"),t.origReload(o)};}attachListeners(){let t=()=>{this.lastDomContentLoaded=new Date().toISOString(),this.records.length>0&&(this.records[this.records.length-1].domContentLoadedAt=this.lastDomContentLoaded);};this.page.on("domcontentloaded",t),this.listeners.push({event:"domcontentloaded",handler:t});let n=o=>{try{let s=o;if(typeof s.parentFrame=="function"&&s.parentFrame()!==null)return;let l=s.url(),a=this.records[this.records.length-1];if(a&&Date.now()-new Date(a.timestamp).getTime()<50&&a.url===l)return;this.recordNavigation(l,"navigation");}catch{}};this.page.on("framenavigated",n),this.listeners.push({event:"framenavigated",handler:n});let r=o=>{try{let s=o,l=s.type(),a=s.text();if(l==="debug"&&a.startsWith("__testrelic_nav:")){try{let d=JSON.parse(a.slice(16));d.type&&d.url&&this.recordNavigation(d.url,d.type);}catch{}return}{let d=null;try{let c=s.location();c&&c.url&&(d=`${c.url}:${c.lineNumber}:${c.columnNumber}`);}catch{}let f=e.mapConsoleType(l);this.consoleWriter&&(this.consoleWriter.append({level:f,text:a,timestamp:new Date().toISOString(),location:d}),this.consoleLogCount++);}}catch{}};if(this.page.on("console",r),this.listeners.push({event:"console",handler:r}),this.includeNetworkStats){let o=a=>{let d=this.records.length-1,f=this.counterForNav(d);f&&f.totalRequests++;try{let c=a,u=String(this.requestIdCounter++),i=c.postData()??null,p={url:c.url(),method:c.method(),resourceType:this.mapResourceType(c.resourceType()),headers:c.headers(),postData:i,postDataTruncated:!1,startedAt:new Date().toISOString(),startTimeMs:Date.now(),navIndex:d};this.pendingRequests.set(u,p),a.__testrelic_id=u;}catch{}};this.page.on("request",o),this.listeners.push({event:"request",handler:o});let s=a=>{try{let d=a,f=d.request().__testrelic_id,c=f?this.pendingRequests.get(f):void 0,u=this.counterForNav(c?c.navIndex:this.records.length-1);if(u){let x=d.status();x>=400&&(u.failedRequests++,u.failedRequestUrls.push(x+" "+d.url()));let R=d.headers()["content-length"];R&&(u.totalBytes+=parseInt(R,10)||0);let A=d.request().resourceType(),P=this.mapResourceType(A);u.byType[P]++;}if(!f||!c)return;this.pendingRequests.delete(f);let i=Date.now()-c.startTimeMs,p=d.headers(),g=p["content-type"]??null,h=parseInt(p["content-length"]??"0",10)||0,w=g?!de(g):!1,k=(async()=>{let x=null;if(!w)try{x=(await d.body()).toString("utf-8");}catch{}let R={url:c.url,method:c.method,resourceType:c.resourceType,statusCode:d.status(),responseTimeMs:i,startedAt:c.startedAt,requestHeaders:c.headers,requestBody:c.postData,responseBody:x,responseHeaders:p,contentType:g,responseSize:h,requestBodyTruncated:c.postDataTruncated,responseBodyTruncated:!1,isBinary:w,error:null};this.networkWriter&&(this.networkWriter.append(R),this.networkRequestCount++);})();this.pendingBodyReads.push(k);}catch{}};this.page.on("response",s),this.listeners.push({event:"response",handler:s});let l=a=>{let d=a.__testrelic_id,f=d?this.pendingRequests.get(d):void 0,c=this.counterForNav(f?f.navIndex:this.records.length-1);if(c){c.failedRequests++;try{let u=a;c.failedRequestUrls.push("ERR "+u.url());}catch{}}try{let u=a;if(!d||!f)return;this.pendingRequests.delete(d);let i={url:f.url,method:f.method,resourceType:f.resourceType,statusCode:0,responseTimeMs:Date.now()-f.startTimeMs,startedAt:f.startedAt,requestHeaders:f.headers,requestBody:f.postData,responseBody:null,responseHeaders:null,contentType:null,responseSize:0,requestBodyTruncated:f.postDataTruncated,responseBodyTruncated:!1,isBinary:!1,error:u.failure()?.errorText??"Unknown error"};this.networkWriter&&(this.networkWriter.append(i),this.networkRequestCount++);}catch{}};this.page.on("requestfailed",l),this.listeners.push({event:"requestfailed",handler:l});}}async injectSPADetection(){try{await this.page.addInitScript(()=>{let t=history.pushState.bind(history),n=history.replaceState.bind(history);history.pushState=function(...r){t(...r),console.debug("__testrelic_nav:"+JSON.stringify({type:"spa_route",url:location.href}));},history.replaceState=function(...r){n(...r),console.debug("__testrelic_nav:"+JSON.stringify({type:"spa_replace",url:location.href}));},window.addEventListener("popstate",()=>{console.debug("__testrelic_nav:"+JSON.stringify({type:"popstate",url:location.href}));}),window.addEventListener("hashchange",()=>{console.debug("__testrelic_nav:"+JSON.stringify({type:"hash_change",url:location.href}));});});}catch{}}recordNavigation(t,n){this.records.push({url:t,navigationType:n,timestamp:new Date().toISOString()}),this.includeNetworkStats&&this.navCounters.push(this.createNetworkCounter());}createNetworkCounter(){return {totalRequests:0,failedRequests:0,failedRequestUrls:[],totalBytes:0,byType:{xhr:0,document:0,script:0,stylesheet:0,image:0,font:0,other:0}}}counterForNav(t){return t>=0?this.navCounters[t]:void 0}assembleNetworkStats(){if(this.includeNetworkStats)for(let t=0;t<this.records.length;t++){let n=this.navCounters[t];n&&(this.records[t].networkStats={totalRequests:n.totalRequests,failedRequests:n.failedRequests,failedRequestUrls:[...n.failedRequestUrls],totalBytes:n.totalBytes,byType:{...n.byType}});}}mapResourceType(t){switch(t){case "xhr":case "fetch":return "xhr";case "document":return "document";case "script":return "script";case "stylesheet":return "stylesheet";case "image":return "image";case "font":return "font";default:return "other"}}};Buffer.from([137,80,78,71,13,10,26,10]);var pe=/Expected an image (\d+)px by (\d+)px, received (\d+)px by (\d+)px/;function K(e){return e?pe.test(e):false}function Rt(e){let{styleProps:t,defaults:n,inherited:r,maxNodes:o,maxText:s,maskBoxes:l,viewportBox:a,fullPage:d}=e,f={};for(let y=0;y<r.length;y++)f[r[y]]=true;let c=window.scrollX||window.pageXOffset||0,u=window.scrollY||window.pageYOffset||0,i=[],p=0,g=[];for(let y=0;y<l.length;y++){let m=l[y];g.push([m[0]+c,m[1]+u,m[2],m[3]]);}function h(y,m,b,S){for(let C=0;C<g.length;C++){let T=g[C];if(y<T[0]+T[2]&&y+b>T[0]&&m<T[1]+T[3]&&m+S>T[1])return true}return false}function w(y){let m=y.parentElement;if(!m)return 0;let b=m.children,S=0;for(let C=0;C<b.length;C++){let T=b[C];if(T===y)break;T.tagName===y.tagName&&S++;}return S}function k(y){let m="",b=y.childNodes;for(let S=0;S<b.length;S++){let C=b[S];C.nodeType===3&&(m+=C.nodeValue??"");}return m=m.replace(/\s+/g," ").trim(),m.length>s?m.slice(0,s):m}function x(y,m){let b;for(let S=0;S<t.length;S++){let C=t[S],T=y.getPropertyValue(C);if(T){if(f[C]){if(m&&m.getPropertyValue(C)===T)continue}else if(n[C]===T)continue;b||(b={}),b[C]=T;}}return b}let R=[{el:document.body,parentPath:"",parentComputed:null}];for(;R.length>0;){let y=R.pop(),m=y.el,b=window.getComputedStyle(m);if(b.display==="none")continue;let S=m.tagName.toLowerCase(),C=m.id?"#"+m.id:y.parentPath?y.parentPath+">"+S+"["+w(m)+"]":S,T=m.children;for(let V=T.length-1;V>=0;V--)R.push({el:T[V],parentPath:C,parentComputed:b});if(b.visibility==="hidden")continue;let _=m.getBoundingClientRect();if(_.width<=0||_.height<=0)continue;if(i.length>=o){p++;continue}let nt=Math.round(_.left+c),rt=Math.round(_.top+u),ot=Math.round(_.width),st=Math.round(_.height),E={p:C,t:S,r:[nt,rt,ot,st]};m.id&&(E.i=m.id);let it=m.getAttribute("class");it&&(E.c=it.replace(/\s+/g," ").trim());let at=k(m);at&&(E.x=at);let ut=x(b,y.parentComputed);ut&&(E.s=ut),h(nt,rt,ot,st)&&(E.m=1),i.push(E);}let A=Math.max(document.documentElement.scrollWidth,window.innerWidth),P=Math.max(document.documentElement.scrollHeight,window.innerHeight),N;return a?N=[Math.round(a[0]+c),Math.round(a[1]+u),Math.round(a[2]),Math.round(a[3])]:d?N=[0,0,A,P]:N=[Math.round(c),Math.round(u),window.innerWidth,window.innerHeight],{version:"1.0",url:location.href,viewport:{width:window.innerWidth,height:window.innerHeight},content:{width:A,height:P},captureBox:N,nodes:i,truncatedNodes:p}}var we=["display","position","color","background-color","background-image","border-top-width","border-right-width","border-bottom-width","border-left-width","border-color","border-style","border-radius","box-shadow","opacity","transform","margin-top","margin-right","margin-bottom","margin-left","padding-top","padding-right","padding-bottom","padding-left","font-family","font-size","font-weight","line-height","letter-spacing","text-align","text-decoration-line","text-transform","flex-direction","justify-content","align-items","gap","z-index","overflow"],ke={position:"static","background-color":"rgba(0, 0, 0, 0)","background-image":"none","border-top-width":"0px","border-right-width":"0px","border-bottom-width":"0px","border-left-width":"0px","border-style":"none","border-radius":"0px","box-shadow":"none",opacity:"1",transform:"none","margin-top":"0px","margin-right":"0px","margin-bottom":"0px","margin-left":"0px","padding-top":"0px","padding-right":"0px","padding-bottom":"0px","padding-left":"0px","text-decoration-line":"none","flex-direction":"row","justify-content":"normal","align-items":"normal",gap:"normal","z-index":"auto",overflow:"visible"},be=["color","font-family","font-size","font-weight","line-height","letter-spacing","text-align","text-transform"],xe=1024*1024,Ce=1500,Re=200;async function Te(e){let t=[];for(let n of e)try{let r=await n.boundingBox();r&&t.push([r.x,r.y,r.width,r.height]);}catch{}return t}async function Tt(e){let t=e.config??{};try{let n=e.maskLocators?.length?await Te(e.maskLocators):[],r=null;if(e.bounded){let s=await e.bounded.boundingBox();s&&(r=[s.x,s.y,s.width,s.height]);}let o={styleProps:we,defaults:ke,inherited:be,maxNodes:t.maxNodes??Ce,maxText:t.maxText??Re,maskBoxes:n,viewportBox:r,fullPage:e.fullPage===!0};return await e.target.evaluate(Rt,o)}catch{return null}}function St(e,t=xe){let n=JSON.stringify(e);if(Buffer.byteLength(n,"utf-8")<=t)return {json:n,truncatedBytes:false};let r=e.nodes.slice(),o=0;for(;r.length>0&&Buffer.byteLength(n,"utf-8")>t;){let s=Math.max(1,Math.floor(r.length*.1));r.splice(-s,s),o+=s,n=JSON.stringify({...e,nodes:r,truncatedNodes:e.truncatedNodes+o});}return {json:n,truncatedBytes:true}}function At(e){return e.replace(/\.[A-Za-z0-9]+$/,"")+".dom.json"}function vt(e){try{if(!fs.existsSync(e))return null;let t=JSON.parse(fs.readFileSync(e,"utf-8"));return Se(t)?t:null}catch{return null}}function Pt(e,t){try{return fs.mkdirSync(path.dirname(e),{recursive:!0}),fs.writeFileSync(e,t,"utf-8"),!0}catch{return false}}function Se(e){if(typeof e!="object"||e===null)return false;let t=e;return typeof t.version=="string"&&Array.isArray(t.nodes)&&Array.isArray(t.captureBox)&&t.captureBox.length===4}var Ae=new Set(["moved","resized"]);function W(e){return e.some(t=>!Ae.has(t))}function Z(e){let t=e.t;if(e.i&&(t+="#"+e.i),e.c){let n=e.c.split(" ").filter(Boolean).slice(0,3);n.length>0&&(t+="."+n.join("."));}return t}function ve(e,t){let n=[],r=Math.abs(e.r[0]-t.r[0]),o=Math.abs(e.r[1]-t.r[1]),s=Math.abs(e.r[2]-t.r[2]),l=Math.abs(e.r[3]-t.r[3]);return (r>1||o>1)&&n.push("moved"),(s>1||l>1)&&n.push("resized"),n}function Pe(e,t){let n=e.s??{},r=t.s??{},o=new Set([...Object.keys(n),...Object.keys(r)]),s=[];for(let l of o){let a=n[l],d=r[l];a!==d&&s.push({prop:l,from:a??"(initial)",to:d??"(initial)"});}return s.sort((l,a)=>l.prop.localeCompare(a.prop)),s}function Nt(e){return e.i?"i:"+e.t+"#"+e.i:e.c?"c:"+e.t+"."+e.c:null}function Ne(e,t){let n=new Map;for(let u of e)n.set(u.p,u);let r=[],o=[],s=new Set;for(let u of t){let i=n.get(u.p);if(i&&i.t!==u.t){o.push(u);continue}i&&!s.has(i)?(s.add(i),r.push({before:i,after:u})):o.push(u);}let l=e.filter(u=>!s.has(u)),a=new Map;for(let u of l){let i=Nt(u);if(!i)continue;let p=a.get(i);p?p.push(u):a.set(i,[u]);}let d=[],f=new Set;for(let u of o){let i=Nt(u),g=(i?a.get(i):void 0)?.find(h=>!f.has(h));g?(f.add(g),r.push({before:g,after:u})):d.push(u);}let c=l.filter(u=>!f.has(u));return {pairs:r,removed:c,added:d}}function De(e,t){let n=[];return (e.i??"")!==(t.i??"")&&n.push({prop:"id",from:e.i??"(none)",to:t.i??"(none)"}),(e.c??"")!==(t.c??"")&&n.push({prop:"class",from:e.c??"(none)",to:t.c??"(none)"}),n}function Q(e,t){let n=(t[2]??0)*(t[3]??0);return n<=0?0:Math.min(1,e.r[2]*e.r[3]/n)}function Dt(e,t,n={}){let r=n.maxChanges??50,o=n.includeText!==false,s=n.includeStyles!==false,l=e.nodes.filter(g=>g.m!==1),a=t.nodes.filter(g=>g.m!==1),{pairs:d,removed:f,added:c}=Ne(l,a),u=[];for(let{before:g,after:h}of d){let w=ve(g,h),k=s?Pe(g,h):[];k.length>0&&w.push("restyled");let x=De(g,h);x.length>0&&w.push("attrs");let R=o&&(g.x??"")!==(h.x??"");if(R&&w.push("text"),w.length===0)continue;let A=Q(h,t.captureBox);u.push({score:A,change:{path:h.p,label:Z(h),kinds:w,rect:h.r,baselineRect:g.r,areaPct:A,...x.length>0?{attrs:x}:{},...k.length>0?{styles:k}:{},...R?{text:{from:g.x??"",to:h.x??""}}:{}}});}for(let g of c){let h=Q(g,t.captureBox);u.push({score:h,change:{path:g.p,label:Z(g),kinds:["added"],rect:g.r,areaPct:h}});}for(let g of f){let h=Q(g,e.captureBox);u.push({score:h,change:{path:g.p,label:Z(g),kinds:["removed"],baselineRect:g.r,areaPct:h}});}let i=u.filter(g=>W(g.change.kinds)).length;return u.sort((g,h)=>{if(i>0){let w=W(g.change.kinds)?1:0,k=W(h.change.kinds)?1:0;if(w!==k)return k-w}return h.score-g.score}),{changes:u.slice(0,r).map((g,h)=>({rank:h+1,...g.change,...i>0&&!W(g.change.kinds)?{consequence:true}:{}})),total:u.length,rootCauses:i}}var _t="__testrelic_snapshot_path_probe__.png",Et=Symbol.for("testrelic.snapshotPathWarned");function _e(){let e=globalThis,t=e[Et];return t||(t={warned:false},e[Et]=t),t}function H(e){try{let t=e.snapshotPath(_t,{kind:"screenshot"});return typeof t=="string"&&t.endsWith(_t)}catch{return false}}function j(e){let t=_e();t.warned||(t.warned=true,process.stderr.write(`[testrelic] ${e} needs Playwright >= 1.51 for testInfo.snapshotPath(name, { kind }); this project's Playwright resolves that call the older way. Everything else in the reporter is unaffected \u2014 upgrade Playwright to enable it.
|
|
6
|
+
`));}var Mt="testrelic-visual-dom",Oe=new Set(["toHaveScreenshot","toMatchVisualBaseline"]);function Bt(e){if(typeof e!="object"||e===null)return false;let t=e;return typeof t.boundingBox=="function"&&typeof t.page=="function"}function Le(e){if(typeof e!="object"||e===null)return false;let t=e;return typeof t.evaluate=="function"&&typeof t.screenshot=="function"}function qe(e){let t=e[0];if(typeof t=="string")return t;if(Array.isArray(t))return t.join("/");if(typeof t=="object"&&t!==null){let n=t.name;if(typeof n=="string")return n}return null}function Fe(e){for(let t of e)if(typeof t=="object"&&t!==null&&!Array.isArray(t))return t;return {}}function We(e){let t=e.mask;return Array.isArray(t)?t.filter(n=>typeof n=="object"&&n!==null&&typeof n.boundingBox=="function"):[]}function He(e){return /\.(png|jpg|jpeg)$/i.test(e)?e:`${e}.png`}var je=/^(.+)-(expected|actual|diff|previous)\.[A-Za-z0-9]+$/;function Ve(e,t,n){let r=e.attachments;for(let o=t;o<r.length;o++){let s=je.exec(r[o]?.name??"");if(s)return s[1]}return n}async function Ue(e){let t=Fe(e.args),n=Bt(e.received)?e.received:null,r=n?n.page():e.received;return Tt({target:r,bounded:n,maskLocators:We(t),fullPage:t.fullPage===true})}function Je(e){let t=e.config?.updateSnapshots;return t==="all"||t==="changed"}async function ze(e,t){let n=qe(e.args);if(!n)return;if(!H(e.testInfo)){j("DOM comparison");return}let r=e.testInfo.snapshotPath(He(n),{kind:"screenshot"}),o=At(r),s=Ve(e.testInfo,e.attachedBefore,path.basename(r,path.extname(r))),l=St(t),d=Je(e.testInfo)?null:vt(o);if(!d){Pt(o,l.json);return}let{changes:f,total:c,rootCauses:u}=Dt(d,t),i;try{i=e.testInfo.outputPath(`${s}.actual.dom.json`),fs.writeFileSync(i,l.json,"utf-8");}catch{i=void 0;}let p={stem:s,changes:f,total:c,rootCauses:u,truncated:l.truncatedBytes||t.truncatedNodes>0,captureBox:t.captureBox,...fs.existsSync(o)?{baselineDomPath:o}:{},...i?{actualDomPath:i}:{}};await e.testInfo.attach(Mt,{body:Buffer.from(JSON.stringify([p])),contentType:"application/json"});}function It(e,t){let n=function(l,...a){let d=e(l,...a);return !(Bt(l)||Le(l))||typeof d!="object"||d===null?d:new Proxy(d,{get(c,u,i){let p=Reflect.get(c,u,i);if(typeof u!="string"||!Oe.has(u)||typeof p!="function")return p;let g=p;return async function(...w){let k=t(),x=null,R=k?.attachments.length??0;if(k)try{x=await Ue({received:l,args:w,testInfo:k,attachedBefore:R});}catch{}try{return await g.apply(c,w)}finally{if(k&&x)try{await ze({received:l,args:w,testInfo:k,attachedBefore:R},x);}catch{}}}}})},r=e,o=n;for(let s of Object.keys(r))o[s]=r[s];return n}var Ot="testrelic-visual";var Ke=[".png",".jpg",".jpeg"];function Ze(e){let t=e.toLowerCase();return Ke.some(n=>t.endsWith(n))?e:`${e}.png`}function Qe(){try{return test.test.info()}catch{return null}}function tn(e,t){for(let n=t;n<e.length;n++){let r=/^(.+)-(expected|actual|diff|previous)\.[A-Za-z0-9]+$/.exec(e[n]?.name??"");if(r)return r[1]}return null}async function en(e,t){if(!H(e))return j("Attaching the baseline of a passing comparison"),null;try{let n=e.snapshotPath(t,{kind:"screenshot"}),r=path.extname(n)||".png",o=path.basename(n,r);return await e.attach(`${o}-expected${r}`,{path:n}),o}catch{return null}}async function nn(e,t){try{await e.attach(Ot,{body:Buffer.from(JSON.stringify([t])),contentType:"application/json"});}catch{}}async function tt(e,t,n={}){let r={...n};delete r.tags;let o=Ze(t),s=Qe(),l=s?.attachments.length??0,a=true,d="";try{await test.expect(e).toHaveScreenshot(o,r);}catch(f){a=false,d=f instanceof Error?f.message:String(f);}if(s){let f=a?await en(s,o):tn(s.attachments,l);if(f){let c=!a&&!K(d)&&!s.attachments.some((u,i)=>i>=l&&u.name.startsWith(`${f}-diff.`));await nn(s,{stem:f,name:t,status:a?"passed":c?"new":"failed"});}}return {pass:a,name:"toMatchVisualBaseline",message:()=>a?`Expected "${t}" to differ from its visual baseline, but it matched.`:d}}test.expect.extend({toMatchVisualBaseline:tt});function sn(){try{return test.test.info()}catch{return null}}It(O(test.expect),sn);var an="__testrelic_api_config",un="__testrelic_config_trackApiCalls";function et(e){let t=e.annotations.find(r=>r.type===an&&r.description!==void 0);if(t)try{return JSON.parse(t.description,cn)}catch{}let n=e.annotations.find(r=>r.type===un&&r.description!==void 0);return n?{trackApiCalls:n.description!=="false",captureRequestHeaders:true,captureResponseHeaders:true,captureRequestBody:true,captureResponseBody:true,captureAssertions:true,redactHeaders:["authorization","cookie","set-cookie","x-api-key"],redactBodyFields:["password","secret","token","apiKey","api_key"],apiIncludeUrls:[],apiExcludeUrls:[]}:{trackApiCalls:true,captureRequestHeaders:true,captureResponseHeaders:true,captureRequestBody:true,captureResponseBody:true,captureAssertions:true,redactHeaders:["authorization","cookie","set-cookie","x-api-key"],redactBodyFields:["password","secret","token","apiKey","api_key"],apiIncludeUrls:[],apiExcludeUrls:[]}}function cn(e,t){if(typeof t=="object"&&t!==null&&t.__regexp===true&&typeof t.source=="string"){let{source:n,flags:r}=t;return new RegExp(n,r)}return t}var ln={page:async({page:e},t,n)=>{let r=new F(e);try{await r.init();}catch{}await t(e);try{let{navigations:o,networkRequestsFile:s,networkRequestsCount:l,consoleLogsFile:a,consoleLogsCount:d}=await r.getFileData(),f={testRelicData:!0,version:core.PAYLOAD_VERSION,navigations:o,apiAssertions:[],networkRequestsFile:s,networkRequestsCount:l,consoleLogsFile:a,consoleLogsCount:d,apiCallsFile:null,apiCallsCount:0};await n.attach(core.ATTACHMENT_NAME,{body:Buffer.from(JSON.stringify(f)),contentType:core.ATTACHMENT_CONTENT_TYPE});}catch{}r.dispose();},request:async({request:e},t,n)=>{let r=et(n);if(!r.trackApiCalls){await t(e);return}let o=new M,s=new I(e,o,r);s.intercept(),B(o,s);try{await t(e);}finally{B(null,null);}try{let{apiCallsFile:l,apiCallsCount:a,apiStatusMix:d}=s.getFileData(),f=r.captureAssertions?o.getData():[],c={testRelicData:!0,version:core.PAYLOAD_VERSION,navigations:[],apiAssertions:f,networkRequestsFile:null,networkRequestsCount:0,consoleLogsFile:null,consoleLogsCount:0,apiCallsFile:l,apiCallsCount:a,apiStatusMix:d};await n.attach(core.ATTACHMENT_NAME,{body:Buffer.from(JSON.stringify(c)),contentType:core.ATTACHMENT_CONTENT_TYPE});}catch{}s.dispose(),o.dispose();}};test.test.extend(ln);var Dr=O(test.expect),_r={request:async({request:e},t,n)=>{let r=et(n);if(!r.trackApiCalls){await t(e);return}let o=new M,s=new I(e,o,r);s.intercept(),B(o,s);let l=null;try{l=new v("api-console");}catch{}let a=0,d=process.stdout.write,f=process.stderr.write;process.stdout.write=function(c,...u){try{let i=typeof c=="string"?c:Buffer.isBuffer(c)?c.toString("utf-8"):String(c);!i.startsWith("[testrelic]")&&!i.startsWith("\u2139 TestRelic")&&!i.startsWith("\u2713 TestRelic")&&!i.startsWith("\u26A0 TestRelic")&&l&&(l.append({level:"stdout",text:i.replace(/\n$/,""),timestamp:new Date().toISOString(),location:null}),a++);}catch{}return d.apply(process.stdout,[c,...u])},process.stderr.write=function(c,...u){try{let i=typeof c=="string"?c:Buffer.isBuffer(c)?c.toString("utf-8"):String(c);!i.startsWith("[testrelic]")&&!i.startsWith("\u26A0 TestRelic")&&l&&(l.append({level:"stderr",text:i.replace(/\n$/,""),timestamp:new Date().toISOString(),location:null}),a++);}catch{}return f.apply(process.stderr,[c,...u])};try{await t(e);}finally{process.stdout.write=d,process.stderr.write=f,B(null,null);}try{l?.close();let{apiCallsFile:c,apiCallsCount:u,apiStatusMix:i}=s.getFileData(),p=r.captureAssertions?o.getData():[],g={testRelicData:!0,version:core.PAYLOAD_VERSION,navigations:[],apiAssertions:p,networkRequestsFile:null,networkRequestsCount:0,consoleLogsFile:a>0?l?.getPath()??null:null,consoleLogsCount:a,apiCallsFile:c,apiCallsCount:u,apiStatusMix:i};await n.attach(core.ATTACHMENT_NAME,{body:Buffer.from(JSON.stringify(g)),contentType:core.ATTACHMENT_CONTENT_TYPE});}catch{}s.dispose(),o.dispose();}};exports.expect=Dr;exports.testRelicApiFixture=_r;//# sourceMappingURL=api-fixture.cjs.map
|
|
5
7
|
//# sourceMappingURL=api-fixture.cjs.map
|