@testrelic/playwright-analytics 2.3.2 → 2.3.3

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/cli.cjs CHANGED
@@ -10,6 +10,45 @@ var __export = (target, all) => {
10
10
  __defProp(target, name, { get: all[name], enumerable: true });
11
11
  };
12
12
 
13
+ // src/jsonl-stream.ts
14
+ async function readJsonlPage(filePath, page, pageSize, knownTotal) {
15
+ const skip = (page - 1) * pageSize;
16
+ const items = [];
17
+ let lineCount = 0;
18
+ const rl = (0, import_node_readline.createInterface)({
19
+ input: (0, import_node_fs2.createReadStream)(filePath, { encoding: "utf-8" }),
20
+ crlfDelay: Infinity
21
+ });
22
+ for await (const line of rl) {
23
+ if (line.length === 0) continue;
24
+ if (lineCount >= skip && items.length < pageSize) {
25
+ try {
26
+ items.push(JSON.parse(line));
27
+ } catch {
28
+ }
29
+ }
30
+ lineCount++;
31
+ if (items.length >= pageSize && knownTotal !== void 0) {
32
+ break;
33
+ }
34
+ }
35
+ const total = knownTotal ?? lineCount;
36
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
37
+ return { items, total, page, pageSize, totalPages };
38
+ }
39
+ var import_node_fs2, import_node_path2, import_node_os, import_node_crypto2, import_node_readline, TESTRELIC_TMP_DIR;
40
+ var init_jsonl_stream = __esm({
41
+ "src/jsonl-stream.ts"() {
42
+ "use strict";
43
+ import_node_fs2 = require("fs");
44
+ import_node_path2 = require("path");
45
+ import_node_os = require("os");
46
+ import_node_crypto2 = require("crypto");
47
+ import_node_readline = require("readline");
48
+ TESTRELIC_TMP_DIR = (0, import_node_path2.join)((0, import_node_os.tmpdir)(), "testrelic-data");
49
+ }
50
+ });
51
+
13
52
  // src/report-server-routes.ts
14
53
  function sendJson(res, status, body) {
15
54
  res.writeHead(status, { "Content-Type": "application/json" });
@@ -22,8 +61,8 @@ function setCorsHeaders(res) {
22
61
  }
23
62
  function readJsonFile(filePath) {
24
63
  try {
25
- if (!(0, import_node_fs2.existsSync)(filePath)) return null;
26
- return JSON.parse((0, import_node_fs2.readFileSync)(filePath, "utf-8"));
64
+ if (!(0, import_node_fs3.existsSync)(filePath)) return null;
65
+ return JSON.parse((0, import_node_fs3.readFileSync)(filePath, "utf-8"));
27
66
  } catch {
28
67
  return null;
29
68
  }
@@ -31,11 +70,11 @@ function readJsonFile(filePath) {
31
70
  function getDirSize(dirPath) {
32
71
  let size = 0;
33
72
  try {
34
- const entries = (0, import_node_fs2.readdirSync)(dirPath, { withFileTypes: true });
73
+ const entries = (0, import_node_fs3.readdirSync)(dirPath, { withFileTypes: true });
35
74
  for (const entry of entries) {
36
- const fullPath = (0, import_node_path2.join)(dirPath, entry.name);
75
+ const fullPath = (0, import_node_path3.join)(dirPath, entry.name);
37
76
  if (entry.isFile()) {
38
- size += (0, import_node_fs2.statSync)(fullPath).size;
77
+ size += (0, import_node_fs3.statSync)(fullPath).size;
39
78
  } else if (entry.isDirectory()) {
40
79
  size += getDirSize(fullPath);
41
80
  }
@@ -45,7 +84,7 @@ function getDirSize(dirPath) {
45
84
  return size;
46
85
  }
47
86
  function handleHealth(_req, res, reportDir, startTime) {
48
- const index = readJsonFile((0, import_node_path2.join)(reportDir, "index.json"));
87
+ const index = readJsonFile((0, import_node_path3.join)(reportDir, "index.json"));
49
88
  sendJson(res, 200, {
50
89
  status: "ok",
51
90
  reportMode: "streaming",
@@ -54,7 +93,7 @@ function handleHealth(_req, res, reportDir, startTime) {
54
93
  });
55
94
  }
56
95
  function handleSummary(_req, res, reportDir) {
57
- const summary = readJsonFile((0, import_node_path2.join)(reportDir, "summary.json"));
96
+ const summary = readJsonFile((0, import_node_path3.join)(reportDir, "summary.json"));
58
97
  if (!summary) {
59
98
  sendJson(res, 404, { error: "Summary not found" });
60
99
  return;
@@ -62,7 +101,7 @@ function handleSummary(_req, res, reportDir) {
62
101
  sendJson(res, 200, summary);
63
102
  }
64
103
  function handleTests(req, res, reportDir) {
65
- const index = readJsonFile((0, import_node_path2.join)(reportDir, "index.json"));
104
+ const index = readJsonFile((0, import_node_path3.join)(reportDir, "index.json"));
66
105
  if (!index) {
67
106
  sendJson(res, 404, { error: "Test index not found" });
68
107
  return;
@@ -131,21 +170,68 @@ function handleTestDetail(_req, res, reportDir, testId) {
131
170
  sendJson(res, 400, { error: "Invalid test ID format" });
132
171
  return;
133
172
  }
134
- const filePath = (0, import_node_path2.join)(reportDir, "tests", `${testId}.json`);
135
- if (!(0, import_node_fs2.existsSync)(filePath)) {
173
+ const metaPath = (0, import_node_path3.join)(reportDir, "tests", testId, "meta.json");
174
+ const legacyPath = (0, import_node_path3.join)(reportDir, "tests", `${testId}.json`);
175
+ const filePath = (0, import_node_fs3.existsSync)(metaPath) ? metaPath : (0, import_node_fs3.existsSync)(legacyPath) ? legacyPath : null;
176
+ if (!filePath) {
136
177
  sendJson(res, 404, { error: `Test not found: ${testId}` });
137
178
  return;
138
179
  }
139
180
  try {
140
- const data = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
181
+ const data = (0, import_node_fs3.readFileSync)(filePath, "utf-8");
141
182
  res.writeHead(200, { "Content-Type": "application/json" });
142
183
  res.end(data);
143
184
  } catch (err) {
144
185
  sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
145
186
  }
146
187
  }
188
+ async function handleTestDataFile(req, res, reportDir, testId, dataType) {
189
+ if (!SAFE_ID_PATTERN.test(testId)) {
190
+ sendJson(res, 400, { error: "Invalid test ID format" });
191
+ return;
192
+ }
193
+ const fileNames = {
194
+ "network": "network.jsonl",
195
+ "console": "console.jsonl",
196
+ "api-calls": "api-calls.jsonl"
197
+ };
198
+ const jsonlPath = (0, import_node_path3.join)(reportDir, "tests", testId, fileNames[dataType]);
199
+ if (!(0, import_node_fs3.existsSync)(jsonlPath)) {
200
+ sendJson(res, 200, { items: [], total: 0, page: 1, pageSize: 50, totalPages: 0 });
201
+ return;
202
+ }
203
+ try {
204
+ const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
205
+ const params = url.searchParams;
206
+ const page = Math.max(1, parseInt(params.get("page") ?? "1", 10) || 1);
207
+ const pageSize = Math.min(MAX_PAGE_SIZE, Math.max(1, parseInt(params.get("pageSize") ?? "50", 10) || 50));
208
+ let knownTotal;
209
+ const metaPath = (0, import_node_path3.join)(reportDir, "tests", testId, "meta.json");
210
+ if ((0, import_node_fs3.existsSync)(metaPath)) {
211
+ try {
212
+ const meta = JSON.parse((0, import_node_fs3.readFileSync)(metaPath, "utf-8"));
213
+ switch (dataType) {
214
+ case "network":
215
+ knownTotal = meta.networkRequestsCount;
216
+ break;
217
+ case "console":
218
+ knownTotal = meta.consoleLogsCount;
219
+ break;
220
+ case "api-calls":
221
+ knownTotal = meta.apiCallsCount;
222
+ break;
223
+ }
224
+ } catch {
225
+ }
226
+ }
227
+ const result = await readJsonlPage(jsonlPath, page, pageSize, knownTotal);
228
+ sendJson(res, 200, result);
229
+ } catch (err) {
230
+ sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
231
+ }
232
+ }
147
233
  function handleFiles(_req, res, reportDir) {
148
- const index = readJsonFile((0, import_node_path2.join)(reportDir, "index.json"));
234
+ const index = readJsonFile((0, import_node_path3.join)(reportDir, "index.json"));
149
235
  if (!index) {
150
236
  sendJson(res, 404, { error: "Test index not found" });
151
237
  return;
@@ -181,19 +267,19 @@ function handleFiles(_req, res, reportDir) {
181
267
  sendJson(res, 200, { files });
182
268
  }
183
269
  function handleArtifactsList(_req, res, artifactsDir) {
184
- if (!(0, import_node_fs2.existsSync)(artifactsDir)) {
270
+ if (!(0, import_node_fs3.existsSync)(artifactsDir)) {
185
271
  sendJson(res, 200, { runs: [], totalSizeBytes: 0 });
186
272
  return;
187
273
  }
188
274
  try {
189
275
  const runs = [];
190
276
  let totalSizeBytes = 0;
191
- const entries = (0, import_node_fs2.readdirSync)(artifactsDir, { withFileTypes: true });
277
+ const entries = (0, import_node_fs3.readdirSync)(artifactsDir, { withFileTypes: true });
192
278
  for (const entry of entries) {
193
279
  if (!entry.isDirectory() || !TIMESTAMP_PATTERN.test(entry.name)) continue;
194
- const runDir = (0, import_node_path2.join)(artifactsDir, entry.name);
280
+ const runDir = (0, import_node_path3.join)(artifactsDir, entry.name);
195
281
  const size = getDirSize(runDir);
196
- const testDirs = (0, import_node_fs2.readdirSync)(runDir, { withFileTypes: true }).filter((e) => e.isDirectory());
282
+ const testDirs = (0, import_node_fs3.readdirSync)(runDir, { withFileTypes: true }).filter((e) => e.isDirectory());
197
283
  runs.push({ folderName: entry.name, totalSizeBytes: size, testCount: testDirs.length });
198
284
  totalSizeBytes += size;
199
285
  }
@@ -207,13 +293,13 @@ function handleArtifactsDeleteAll(_req, res, artifactsDir) {
207
293
  try {
208
294
  let deletedCount = 0;
209
295
  let freedBytes = 0;
210
- if ((0, import_node_fs2.existsSync)(artifactsDir)) {
211
- const entries = (0, import_node_fs2.readdirSync)(artifactsDir, { withFileTypes: true });
296
+ if ((0, import_node_fs3.existsSync)(artifactsDir)) {
297
+ const entries = (0, import_node_fs3.readdirSync)(artifactsDir, { withFileTypes: true });
212
298
  for (const entry of entries) {
213
299
  if (!entry.isDirectory() || !TIMESTAMP_PATTERN.test(entry.name)) continue;
214
- const runDir = (0, import_node_path2.join)(artifactsDir, entry.name);
300
+ const runDir = (0, import_node_path3.join)(artifactsDir, entry.name);
215
301
  const size = getDirSize(runDir);
216
- (0, import_node_fs2.rmSync)(runDir, { recursive: true, force: true });
302
+ (0, import_node_fs3.rmSync)(runDir, { recursive: true, force: true });
217
303
  freedBytes += size;
218
304
  deletedCount++;
219
305
  }
@@ -228,9 +314,9 @@ function handleArtifactDelete(_req, res, artifactsDir, folderName) {
228
314
  sendJson(res, 400, { error: "Invalid folder name" });
229
315
  return;
230
316
  }
231
- const runDir = (0, import_node_path2.join)(artifactsDir, folderName);
317
+ const runDir = (0, import_node_path3.join)(artifactsDir, folderName);
232
318
  try {
233
- const stat = (0, import_node_fs2.statSync)(runDir);
319
+ const stat = (0, import_node_fs3.statSync)(runDir);
234
320
  if (!stat.isDirectory()) {
235
321
  sendJson(res, 404, { error: "Not found" });
236
322
  return;
@@ -241,7 +327,7 @@ function handleArtifactDelete(_req, res, artifactsDir, folderName) {
241
327
  }
242
328
  try {
243
329
  const freedBytes = getDirSize(runDir);
244
- (0, import_node_fs2.rmSync)(runDir, { recursive: true, force: true });
330
+ (0, import_node_fs3.rmSync)(runDir, { recursive: true, force: true });
245
331
  sendJson(res, 200, { deleted: folderName, freedBytes });
246
332
  } catch (err) {
247
333
  sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
@@ -252,13 +338,13 @@ function handleShutdown(_req, res, server) {
252
338
  server.close();
253
339
  }
254
340
  function serveStaticFile(_req, res, filePath) {
255
- if (!(0, import_node_fs2.existsSync)(filePath)) {
341
+ if (!(0, import_node_fs3.existsSync)(filePath)) {
256
342
  sendJson(res, 404, { error: "File not found" });
257
343
  return;
258
344
  }
259
345
  try {
260
- const data = (0, import_node_fs2.readFileSync)(filePath);
261
- const ext = (0, import_node_path2.extname)(filePath).toLowerCase();
346
+ const data = (0, import_node_fs3.readFileSync)(filePath);
347
+ const ext = (0, import_node_path3.extname)(filePath).toLowerCase();
262
348
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
263
349
  res.writeHead(200, { "Content-Type": contentType });
264
350
  res.end(data);
@@ -266,12 +352,13 @@ function serveStaticFile(_req, res, filePath) {
266
352
  sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
267
353
  }
268
354
  }
269
- var import_node_fs2, import_node_path2, SAFE_ID_PATTERN, TIMESTAMP_PATTERN, MAX_PAGE_SIZE, MIME_TYPES;
355
+ var import_node_fs3, import_node_path3, SAFE_ID_PATTERN, TIMESTAMP_PATTERN, MAX_PAGE_SIZE, MIME_TYPES;
270
356
  var init_report_server_routes = __esm({
271
357
  "src/report-server-routes.ts"() {
272
358
  "use strict";
273
- import_node_fs2 = require("fs");
274
- import_node_path2 = require("path");
359
+ import_node_fs3 = require("fs");
360
+ import_node_path3 = require("path");
361
+ init_jsonl_stream();
275
362
  SAFE_ID_PATTERN = /^[a-f0-9]{12}$/;
276
363
  TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}(-\d+)?$/;
277
364
  MAX_PAGE_SIZE = 500;
@@ -303,7 +390,7 @@ function startReportServer(reportDir, options) {
303
390
  const startTime = Date.now();
304
391
  let inactivityTimer;
305
392
  let currentAttempt = 0;
306
- const artifactsDir = (0, import_node_fs3.existsSync)((0, import_node_path3.join)(reportDir, "artifacts")) ? (0, import_node_path3.join)(reportDir, "artifacts") : (0, import_node_fs3.existsSync)((0, import_node_path3.join)(reportDir, "..", "artifacts")) ? (0, import_node_path3.join)(reportDir, "..", "artifacts") : (0, import_node_path3.join)(reportDir, "artifacts");
393
+ const artifactsDir = (0, import_node_fs4.existsSync)((0, import_node_path4.join)(reportDir, "artifacts")) ? (0, import_node_path4.join)(reportDir, "artifacts") : (0, import_node_fs4.existsSync)((0, import_node_path4.join)(reportDir, "..", "artifacts")) ? (0, import_node_path4.join)(reportDir, "..", "artifacts") : (0, import_node_path4.join)(reportDir, "artifacts");
307
394
  function resetTimer() {
308
395
  clearTimeout(inactivityTimer);
309
396
  inactivityTimer = setTimeout(() => {
@@ -312,10 +399,10 @@ function startReportServer(reportDir, options) {
312
399
  }
313
400
  let htmlReportPath = options?.htmlPath ?? null;
314
401
  if (!htmlReportPath) {
315
- const parentDir = (0, import_node_path3.dirname)(reportDir);
402
+ const parentDir = (0, import_node_path4.dirname)(reportDir);
316
403
  try {
317
- const htmlFile = (0, import_node_fs3.readdirSync)(parentDir).find((f) => f.endsWith(".html"));
318
- if (htmlFile) htmlReportPath = (0, import_node_path3.join)(parentDir, htmlFile);
404
+ const htmlFile = (0, import_node_fs4.readdirSync)(parentDir).find((f) => f.endsWith(".html"));
405
+ if (htmlFile) htmlReportPath = (0, import_node_path4.join)(parentDir, htmlFile);
319
406
  } catch {
320
407
  }
321
408
  }
@@ -336,7 +423,7 @@ function startReportServer(reportDir, options) {
336
423
  return;
337
424
  }
338
425
  if (req.method === "GET" && (pathname === "/" || pathname === "/index.html")) {
339
- if (htmlReportPath && (0, import_node_fs3.existsSync)(htmlReportPath)) {
426
+ if (htmlReportPath && (0, import_node_fs4.existsSync)(htmlReportPath)) {
340
427
  serveStaticFile(req, res, htmlReportPath);
341
428
  return;
342
429
  }
@@ -359,6 +446,11 @@ function startReportServer(reportDir, options) {
359
446
  handleFiles(req, res, reportDir);
360
447
  return;
361
448
  }
449
+ const testDataMatch = pathname.match(/^\/api\/tests\/([a-f0-9]+)\/(network|console|api-calls)$/);
450
+ if (req.method === "GET" && testDataMatch) {
451
+ handleTestDataFile(req, res, reportDir, testDataMatch[1], testDataMatch[2]);
452
+ return;
453
+ }
362
454
  const testMatch = pathname.match(/^\/api\/tests\/([a-f0-9]+)$/);
363
455
  if (req.method === "GET" && testMatch) {
364
456
  handleTestDetail(req, res, reportDir, testMatch[1]);
@@ -383,7 +475,7 @@ function startReportServer(reportDir, options) {
383
475
  sendJson(res, 400, { error: "Invalid path" });
384
476
  return;
385
477
  }
386
- serveStaticFile(req, res, (0, import_node_path3.join)(artifactsDir, relPath));
478
+ serveStaticFile(req, res, (0, import_node_path4.join)(artifactsDir, relPath));
387
479
  return;
388
480
  }
389
481
  if (req.method === "POST" && pathname === "/api/shutdown") {
@@ -422,13 +514,13 @@ function startReportServer(reportDir, options) {
422
514
  tryListen(startPort);
423
515
  });
424
516
  }
425
- var import_node_http, import_node_path3, import_node_fs3, DEFAULT_PORT, MAX_PORT_ATTEMPTS, INACTIVITY_TIMEOUT_MS;
517
+ var import_node_http, import_node_path4, import_node_fs4, DEFAULT_PORT, MAX_PORT_ATTEMPTS, INACTIVITY_TIMEOUT_MS;
426
518
  var init_report_server = __esm({
427
519
  "src/report-server.ts"() {
428
520
  "use strict";
429
521
  import_node_http = require("http");
430
- import_node_path3 = require("path");
431
- import_node_fs3 = require("fs");
522
+ import_node_path4 = require("path");
523
+ import_node_fs4 = require("fs");
432
524
  init_report_server_routes();
433
525
  DEFAULT_PORT = 9323;
434
526
  MAX_PORT_ATTEMPTS = 10;
@@ -653,8 +745,8 @@ function recalculateSummaryFromList(summaries, timelineLength) {
653
745
  }
654
746
 
655
747
  // src/cli.ts
656
- var import_node_path4 = require("path");
657
- var import_node_fs4 = require("fs");
748
+ var import_node_path5 = require("path");
749
+ var import_node_fs5 = require("fs");
658
750
  async function main() {
659
751
  const args = process.argv.slice(2);
660
752
  if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
@@ -724,8 +816,8 @@ async function handleServe(serveArgs) {
724
816
  process.stderr.write("Usage: testrelic serve <dir> [--port <port>]\n");
725
817
  process.exit(1);
726
818
  }
727
- const reportDir = (0, import_node_path4.resolve)(dir);
728
- if (!(0, import_node_fs4.existsSync)(reportDir)) {
819
+ const reportDir = (0, import_node_path5.resolve)(dir);
820
+ if (!(0, import_node_fs5.existsSync)(reportDir)) {
729
821
  process.stderr.write(`Error: Directory not found: ${reportDir}
730
822
  `);
731
823
  process.exit(1);
package/dist/fixture.cjs CHANGED
@@ -1,2 +1,3 @@
1
- 'use strict';var test=require('@playwright/test'),perf_hooks=require('perf_hooks');require('fs');var core=require('@testrelic/core');var M=["text/","application/json","application/xml","application/javascript","application/x-www-form-urlencoded","application/graphql"];function j(r){let t=r.toLowerCase();return M.some(e=>t.includes(e))}var T=class r{constructor(t,e){this.page=t;this.records=[];this.listeners=[];this.currentNetworkCounter=null;this.pendingRequests=new Map;this.capturedRequests=[];this.pendingBodyReads=[];this.requestIdCounter=0;this.consoleLogs=[];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),this.interceptMethods(),this.attachListeners();}async init(){await this.injectSPADetection();}async getCapturedRequests(){await Promise.allSettled(this.pendingBodyReads),this.pendingBodyReads=[];for(let[t,e]of this.pendingRequests)this.capturedRequests.push({url:e.url,method:e.method,resourceType:e.resourceType,statusCode:0,responseTimeMs:Date.now()-e.startTimeMs,startedAt:e.startedAt,requestHeaders:e.headers,requestBody:e.postData,responseBody:null,responseHeaders:null,contentType:null,responseSize:0,requestBodyTruncated:e.postDataTruncated,responseBodyTruncated:false,isBinary:false,error:"incomplete"}),this.pendingRequests.delete(t);return [...this.capturedRequests].sort((t,e)=>t.startedAt.localeCompare(e.startedAt))}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 getData(){this.includeNetworkStats&&this.currentNetworkCounter&&this.records.length>0&&(this.records[this.records.length-1].networkStats={totalRequests:this.currentNetworkCounter.totalRequests,failedRequests:this.currentNetworkCounter.failedRequests,failedRequestUrls:[...this.currentNetworkCounter.failedRequestUrls],totalBytes:this.currentNetworkCounter.totalBytes,byType:{...this.currentNetworkCounter.byType}});let t=this.records.map(n=>({url:n.url,navigationType:n.navigationType,timestamp:n.timestamp,domContentLoadedAt:n.domContentLoadedAt,networkIdleAt:n.networkIdleAt,networkStats:n.networkStats})),e=this.includeNetworkStats?await this.getCapturedRequests():[];return {navigations:t,networkRequests:e,consoleLogs:[...this.consoleLogs]}}async flushLegacyAnnotations(t){this.includeNetworkStats&&this.currentNetworkCounter&&this.records.length>0&&(this.records[this.records.length-1].networkStats={totalRequests:this.currentNetworkCounter.totalRequests,failedRequests:this.currentNetworkCounter.failedRequests,failedRequestUrls:[...this.currentNetworkCounter.failedRequestUrls],totalBytes:this.currentNetworkCounter.totalBytes,byType:{...this.currentNetworkCounter.byType}});for(let e of this.records){let n={url:e.url,navigationType:e.navigationType,timestamp:e.timestamp,domContentLoadedAt:e.domContentLoadedAt,networkIdleAt:e.networkIdleAt,networkStats:e.networkStats};t.annotations.push({type:"lambdatest-navigation",description:JSON.stringify(n)});}if(this.includeNetworkStats){let e=await this.getCapturedRequests();e.length>0&&t.annotations.push({type:"__testrelic_network_requests",description:JSON.stringify(e)});}}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.capturedRequests=[],this.pendingBodyReads=[];}getRecords(){return 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 c=s;if(typeof c.parentFrame=="function"&&c.parentFrame()!==null)return;let d=c.url(),u=this.records[this.records.length-1];if(u&&Date.now()-new Date(u.timestamp).getTime()<50&&u.url===d)return;this.recordNavigation(d,"navigation");}catch{}};this.page.on("framenavigated",e),this.listeners.push({event:"framenavigated",handler:e});let n=s=>{try{let c=s,d=c.type(),u=c.text();if(d==="debug"&&u.startsWith("__testrelic_nav:")){try{let i=JSON.parse(u.slice(16));i.type&&i.url&&this.recordNavigation(i.url,i.type);}catch{}return}{let i=null;try{let a=c.location();a&&a.url&&(i=`${a.url}:${a.lineNumber}:${a.columnNumber}`);}catch{}let p=r.mapConsoleType(d);this.consoleLogs.push({level:p,text:u,timestamp:new Date().toISOString(),location:i});}}catch{}};if(this.page.on("console",n),this.listeners.push({event:"console",handler:n}),this.includeNetworkStats){let s=u=>{this.currentNetworkCounter&&this.currentNetworkCounter.totalRequests++;try{let i=u,p=String(this.requestIdCounter++),a=i.postData()??null,g={url:i.url(),method:i.method(),resourceType:this.mapResourceType(i.resourceType()),headers:i.headers(),postData:a,postDataTruncated:!1,startedAt:new Date().toISOString(),startTimeMs:Date.now()};this.pendingRequests.set(p,g),u.__testrelic_id=p;}catch{}};this.page.on("request",s),this.listeners.push({event:"request",handler:s});let c=u=>{try{let i=u;if(this.currentNetworkCounter){let f=i.status();f>=400&&(this.currentNetworkCounter.failedRequests++,this.currentNetworkCounter.failedRequestUrls.push(f+" "+i.url()));let h=i.headers()["content-length"];h&&(this.currentNetworkCounter.totalBytes+=parseInt(h,10)||0);let R=i.request().resourceType(),w=this.mapResourceType(R);this.currentNetworkCounter.byType[w]++;}let p=i.request().__testrelic_id;if(!p)return;let a=this.pendingRequests.get(p);if(!a)return;this.pendingRequests.delete(p);let g=Date.now()-a.startTimeMs,l=i.headers(),o=l["content-type"]??null,k=parseInt(l["content-length"]??"0",10)||0,y=o?!j(o):!1,m=(async()=>{let f=null;if(!y)try{f=(await i.body()).toString("utf-8");}catch{}let h={url:a.url,method:a.method,resourceType:a.resourceType,statusCode:i.status(),responseTimeMs:g,startedAt:a.startedAt,requestHeaders:a.headers,requestBody:a.postData,responseBody:f,responseHeaders:l,contentType:o,responseSize:k,requestBodyTruncated:a.postDataTruncated,responseBodyTruncated:!1,isBinary:y,error:null};this.capturedRequests.push(h);})();this.pendingBodyReads.push(m);}catch{}};this.page.on("response",c),this.listeners.push({event:"response",handler:c});let d=u=>{if(this.currentNetworkCounter){this.currentNetworkCounter.failedRequests++;try{let i=u;this.currentNetworkCounter.failedRequestUrls.push("ERR "+i.url());}catch{}}try{let i=u,p=i.__testrelic_id;if(!p)return;let a=this.pendingRequests.get(p);if(!a)return;this.pendingRequests.delete(p);let g={url:a.url,method:a.method,resourceType:a.resourceType,statusCode:0,responseTimeMs:Date.now()-a.startTimeMs,startedAt:a.startedAt,requestHeaders:a.headers,requestBody:a.postData,responseBody:null,responseHeaders:null,contentType:null,responseSize:0,requestBodyTruncated:a.postDataTruncated,responseBodyTruncated:!1,isBinary:!1,error:i.failure()?.errorText??"Unknown error"};this.capturedRequests.push(g);}catch{}};this.page.on("requestfailed",d),this.listeners.push({event:"requestfailed",handler:d});}}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.includeNetworkStats&&this.currentNetworkCounter&&this.records.length>0&&(this.records[this.records.length-1].networkStats={totalRequests:this.currentNetworkCounter.totalRequests,failedRequests:this.currentNetworkCounter.failedRequests,failedRequestUrls:[...this.currentNetworkCounter.failedRequestUrls],totalBytes:this.currentNetworkCounter.totalBytes,byType:{...this.currentNetworkCounter.byType}}),this.records.push({url:t,navigationType:e,timestamp:new Date().toISOString()}),this.includeNetworkStats&&(this.currentNetworkCounter=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}}}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"}}};var x=Symbol.for("__testrelic_call_id"),H="__testrelic_api_assertions",C=new WeakMap,A=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:H,description:JSON.stringify(this.assertions)});}dispose(){this.assertions=[],this.currentCallId=null;}};var B="[REDACTED]";function S(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())?B:r[s]);return n}function v(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),s=b(e,n);return JSON.stringify(s)}function b(r,t){if(Array.isArray(r))return r.map(e=>b(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)?e[n]=B:e[n]=b(s,t);}return e}return r}function P(r,t,e){if(e.length>0){for(let n of e)if(I(r,n))return false}if(t.length>0){for(let n of t)if(I(r,n))return true;return false}return true}function I(r,t){try{return t instanceof RegExp?t.test(r):F(t).test(r)}catch{return console.warn(`[testrelic] Invalid URL filter pattern: ${String(t)}`),false}}function F(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 U=["get","post","put","patch","delete","head","fetch"],J=["text/","application/json","application/xml","application/javascript","application/x-www-form-urlencoded","application/graphql"],G="__testrelic_api_calls";function z(r){let t=r.toLowerCase();return J.some(e=>t.includes(e))}function Y(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}var q=class q{constructor(t,e,n){this.originals=new Map;this.capturedCalls=[];this.callCounter=0;this.disposed=false;this._lastCallId=null;this.primitiveCallIds=new Map;this.context=t,this.assertionTracker=e??null,this.apiConfig=n??q.DEFAULT_API_CONFIG;}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 U){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));}getData(){return [...this.capturedCalls]}flushLegacyAnnotations(t){this.capturedCalls.length!==0&&t.annotations.push({type:G,description:JSON.stringify(this.capturedCalls)});}dispose(){for(let[t,e]of this.originals)this.context[t]=e;this.originals.clear(),this.capturedCalls=[],this.callCounter=0,this._lastCallId=null,this.primitiveCallIds.clear();}get isDisposed(){return this.disposed}getCapturedCalls(){return this.capturedCalls}tagResponseMethods(t,e){let n=this,s=t.headers.bind(t);t.headers=function(){let o=s();return C.set(o,e),o};let c=t.headersArray.bind(t);t.headersArray=function(){let o=c();return C.set(o,e),o};let d=t.json.bind(t);t.json=async function(){let o=await d();return o!=null&&typeof o=="object"&&C.set(o,e),o};let u=t.status.bind(t);t.status=function(){let o=u();return n.primitiveCallIds.set(o,e),o};let i=t.statusText.bind(t);t.statusText=function(){let o=i();return n.primitiveCallIds.set(o,e),o};let p=t.ok.bind(t);t.ok=function(){let o=p();return n.primitiveCallIds.set(o,e),o};let a=t.text.bind(t);t.text=async function(){let o=await a();return n.primitiveCallIds.set(o,e),o};let g=t.body.bind(t);t.body=async function(){let o=await g();return C.set(o,e),o};}createWrapper(t,e){let n=this;return async function(c,d){if(!P(c,n.apiConfig.apiIncludeUrls,n.apiConfig.apiExcludeUrls))return e(c,d);let u=`api-call-${n.callCounter++}`,i=new Date().toISOString(),p=t==="fetch"?(d?.method??"GET").toUpperCase():t.toUpperCase(),a=Y(d),g=perf_hooks.performance.now();n._lastCallId=u,n.assertionTracker&&n.assertionTracker.setCurrentCallId(u);let l;try{l=await e(c,d);}catch(o){let k=perf_hooks.performance.now();try{let y=n.apiConfig.captureRequestBody?v(a,n.apiConfig.redactBodyFields):null,m={id:u,timestamp:i,method:p,url:c,requestHeaders:null,requestBody:y,responseStatusCode:null,responseStatusText:null,responseHeaders:null,responseBody:null,responseTimeMs:Math.round((k-g)*100)/100,isBinary:!1,error:o instanceof Error?o.message:String(o)};n.capturedCalls.push(m);}catch{}throw o}try{let o=perf_hooks.performance.now(),k=l.headers(),y=k["content-type"]??null,m=y?!z(y):!1,f=null;n.apiConfig.captureRequestHeaders&&d?.headers&&(f=d.headers);let h=null;n.apiConfig.captureResponseHeaders&&(h=k);let R=n.apiConfig.captureRequestBody?a:null,w=null;if(n.apiConfig.captureResponseBody)try{m?w=(await l.body()).toString("base64"):w=await l.text();}catch{}f=S(f,n.apiConfig.redactHeaders),h=S(h,n.apiConfig.redactHeaders),R=v(R,n.apiConfig.redactBodyFields),w=v(w,n.apiConfig.redactBodyFields);let E={id:u,timestamp:i,method:p,url:l.url(),requestHeaders:f,requestBody:R,responseStatusCode:l.status(),responseStatusText:l.statusText(),responseHeaders:h,responseBody:w,responseTimeMs:Math.round((o-g)*100)/100,isBinary:m,error:null};n.capturedCalls.push(E);}catch{}try{l[x]=u,n.tagResponseMethods(l,u);}catch{}return l}}};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 N=q;var W="__testrelic_api_config",K="__testrelic_config_trackApiCalls";function V(r){let t=r.annotations.find(n=>n.type===W&&n.description!==void 0);if(t)try{return JSON.parse(t.description,X)}catch{}let e=r.annotations.find(n=>n.type===K&&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 X(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 Q={page:async({page:r},t,e)=>{let n=new T(r);try{await n.init();}catch{}await t(r);try{let{navigations:s,networkRequests:c,consoleLogs:d}=await n.getData(),u={testRelicData:!0,version:core.PAYLOAD_VERSION,navigations:s,networkRequests:c,apiCalls:[],apiAssertions:[],consoleLogs:d};await e.attach(core.ATTACHMENT_NAME,{body:Buffer.from(JSON.stringify(u)),contentType:core.ATTACHMENT_CONTENT_TYPE});}catch{}try{await n.flushLegacyAnnotations(e);}catch{}n.dispose();},request:async({request:r},t,e)=>{let n=V(e);if(!n.trackApiCalls){await t(r);return}let s=new A,c=new N(r,s,n);c.intercept(),await t(r);try{let d=c.getData(),u=n.captureAssertions?s.getData():[],i={testRelicData:!0,version:core.PAYLOAD_VERSION,navigations:[],networkRequests:[],apiCalls:d,apiAssertions:u,consoleLogs:[]};await e.attach(core.ATTACHMENT_NAME,{body:Buffer.from(JSON.stringify(i)),contentType:core.ATTACHMENT_CONTENT_TYPE});}catch{}try{c.flushLegacyAnnotations(e);}catch{}try{s.flushLegacyAnnotations(e);}catch{}c.dispose(),s.dispose();}},kt=test.test.extend(Q);Object.defineProperty(exports,"expect",{enumerable:true,get:function(){return test.expect}});exports.readApiConfig=V;exports.test=kt;exports.testRelicFixture=Q;//# sourceMappingURL=fixture.cjs.map
1
+ 'use strict';var test=require('@playwright/test'),fs=require('fs'),path=require('path'),os=require('os'),crypto=require('crypto');require('readline');var perf_hooks=require('perf_hooks'),core=require('@testrelic/core');var I=path.join(os.tmpdir(),"testrelic-data"),k=class{constructor(t){this.count=0;this.closed=false;fs.mkdirSync(I,{recursive:true}),this.filePath=path.join(I,`${t}-${crypto.randomUUID().substring(0,8)}.jsonl`),this.fd=fs.openSync(this.filePath,"w");}append(t){if(this.closed)return;let e=JSON.stringify(t)+`
2
+ `;fs.writeSync(this.fd,e),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 z=["text/","application/json","application/xml","application/javascript","application/x-www-form-urlencoded","application/graphql"];function K(r){let t=r.toLowerCase();return z.some(e=>t.includes(e))}var A=class r{constructor(t,e){this.page=t;this.records=[];this.listeners=[];this.currentNetworkCounter=null;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 k("network")),this.consoleWriter=new k("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.includeNetworkStats&&this.currentNetworkCounter&&this.records.length>0&&(this.records[this.records.length-1].networkStats={totalRequests:this.currentNetworkCounter.totalRequests,failedRequests:this.currentNetworkCounter.failedRequests,failedRequestUrls:[...this.currentNetworkCounter.failedRequestUrls],totalBytes:this.currentNetworkCounter.totalBytes,byType:{...this.currentNetworkCounter.byType}});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.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 c=s;if(typeof c.parentFrame=="function"&&c.parentFrame()!==null)return;let d=c.url(),u=this.records[this.records.length-1];if(u&&Date.now()-new Date(u.timestamp).getTime()<50&&u.url===d)return;this.recordNavigation(d,"navigation");}catch{}};this.page.on("framenavigated",e),this.listeners.push({event:"framenavigated",handler:e});let n=s=>{try{let c=s,d=c.type(),u=c.text();if(d==="debug"&&u.startsWith("__testrelic_nav:")){try{let o=JSON.parse(u.slice(16));o.type&&o.url&&this.recordNavigation(o.url,o.type);}catch{}return}{let o=null;try{let a=c.location();a&&a.url&&(o=`${a.url}:${a.lineNumber}:${a.columnNumber}`);}catch{}let p=r.mapConsoleType(d);this.consoleWriter&&(this.consoleWriter.append({level:p,text:u,timestamp:new Date().toISOString(),location:o}),this.consoleLogCount++);}}catch{}};if(this.page.on("console",n),this.listeners.push({event:"console",handler:n}),this.includeNetworkStats){let s=u=>{this.currentNetworkCounter&&this.currentNetworkCounter.totalRequests++;try{let o=u,p=String(this.requestIdCounter++),a=o.postData()??null,g={url:o.url(),method:o.method(),resourceType:this.mapResourceType(o.resourceType()),headers:o.headers(),postData:a,postDataTruncated:!1,startedAt:new Date().toISOString(),startTimeMs:Date.now()};this.pendingRequests.set(p,g),u.__testrelic_id=p;}catch{}};this.page.on("request",s),this.listeners.push({event:"request",handler:s});let c=u=>{try{let o=u;if(this.currentNetworkCounter){let f=o.status();f>=400&&(this.currentNetworkCounter.failedRequests++,this.currentNetworkCounter.failedRequestUrls.push(f+" "+o.url()));let h=o.headers()["content-length"];h&&(this.currentNetworkCounter.totalBytes+=parseInt(h,10)||0);let R=o.request().resourceType(),w=this.mapResourceType(R);this.currentNetworkCounter.byType[w]++;}let p=o.request().__testrelic_id;if(!p)return;let a=this.pendingRequests.get(p);if(!a)return;this.pendingRequests.delete(p);let g=Date.now()-a.startTimeMs,l=o.headers(),i=l["content-type"]??null,m=parseInt(l["content-length"]??"0",10)||0,y=i?!K(i):!1,C=(async()=>{let f=null;if(!y)try{f=(await o.body()).toString("utf-8");}catch{}let h={url:a.url,method:a.method,resourceType:a.resourceType,statusCode:o.status(),responseTimeMs:g,startedAt:a.startedAt,requestHeaders:a.headers,requestBody:a.postData,responseBody:f,responseHeaders:l,contentType:i,responseSize:m,requestBodyTruncated:a.postDataTruncated,responseBodyTruncated:!1,isBinary:y,error:null};this.networkWriter&&(this.networkWriter.append(h),this.networkRequestCount++);})();this.pendingBodyReads.push(C);}catch{}};this.page.on("response",c),this.listeners.push({event:"response",handler:c});let d=u=>{if(this.currentNetworkCounter){this.currentNetworkCounter.failedRequests++;try{let o=u;this.currentNetworkCounter.failedRequestUrls.push("ERR "+o.url());}catch{}}try{let o=u,p=o.__testrelic_id;if(!p)return;let a=this.pendingRequests.get(p);if(!a)return;this.pendingRequests.delete(p);let g={url:a.url,method:a.method,resourceType:a.resourceType,statusCode:0,responseTimeMs:Date.now()-a.startTimeMs,startedAt:a.startedAt,requestHeaders:a.headers,requestBody:a.postData,responseBody:null,responseHeaders:null,contentType:null,responseSize:0,requestBodyTruncated:a.postDataTruncated,responseBodyTruncated:!1,isBinary:!1,error:o.failure()?.errorText??"Unknown error"};this.networkWriter&&(this.networkWriter.append(g),this.networkRequestCount++);}catch{}};this.page.on("requestfailed",d),this.listeners.push({event:"requestfailed",handler:d});}}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.includeNetworkStats&&this.currentNetworkCounter&&this.records.length>0&&(this.records[this.records.length-1].networkStats={totalRequests:this.currentNetworkCounter.totalRequests,failedRequests:this.currentNetworkCounter.failedRequests,failedRequestUrls:[...this.currentNetworkCounter.failedRequestUrls],totalBytes:this.currentNetworkCounter.totalBytes,byType:{...this.currentNetworkCounter.byType}}),this.records.push({url:t,navigationType:e,timestamp:new Date().toISOString()}),this.includeNetworkStats&&(this.currentNetworkCounter=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}}}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"}}};var B=Symbol.for("__testrelic_call_id"),V="__testrelic_api_assertions",T=new WeakMap,v=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:V,description:JSON.stringify(this.assertions)});}dispose(){this.assertions=[],this.currentCallId=null;}};var L="[REDACTED]";function S(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())?L:r[s]);return n}function b(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),s=_(e,n);return JSON.stringify(s)}function _(r,t){if(Array.isArray(r))return r.map(e=>_(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)?e[n]=L:e[n]=_(s,t);}return e}return r}function O(r,t,e){if(e.length>0){for(let n of e)if(F(r,n))return false}if(t.length>0){for(let n of t)if(F(r,n))return true;return false}return true}function F(r,t){try{return t instanceof RegExp?t.test(r):X(t).test(r)}catch{return console.warn(`[testrelic] Invalid URL filter pattern: ${String(t)}`),false}}function X(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 Q=["get","post","put","patch","delete","head","fetch"],Z=["text/","application/json","application/xml","application/javascript","application/x-www-form-urlencoded","application/graphql"];function tt(r){let t=r.toLowerCase();return Z.some(e=>t.includes(e))}function et(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}var q=class q{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??q.DEFAULT_API_CONFIG;try{this.apiCallWriter=new k("api-calls");}catch{}}get lastCallId(){return this._lastCallId}getCallIdForValue(t){return t!=null&&typeof t=="object"?T.get(t)??null:this.primitiveCallIds.get(t)??null}intercept(){for(let e of Q){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 i=s();return T.set(i,e),i};let c=t.headersArray.bind(t);t.headersArray=function(){let i=c();return T.set(i,e),i};let d=t.json.bind(t);t.json=async function(){let i=await d();return i!=null&&typeof i=="object"&&T.set(i,e),i};let u=t.status.bind(t);t.status=function(){let i=u();return n.primitiveCallIds.set(i,e),i};let o=t.statusText.bind(t);t.statusText=function(){let i=o();return n.primitiveCallIds.set(i,e),i};let p=t.ok.bind(t);t.ok=function(){let i=p();return n.primitiveCallIds.set(i,e),i};let a=t.text.bind(t);t.text=async function(){let i=await a();return n.primitiveCallIds.set(i,e),i};let g=t.body.bind(t);t.body=async function(){let i=await g();return T.set(i,e),i};}createWrapper(t,e){let n=this;return async function(c,d){if(!O(c,n.apiConfig.apiIncludeUrls,n.apiConfig.apiExcludeUrls))return e(c,d);let u=`api-call-${n.callCounter++}`,o=new Date().toISOString(),p=t==="fetch"?(d?.method??"GET").toUpperCase():t.toUpperCase(),a=et(d),g=perf_hooks.performance.now();n._lastCallId=u,n.assertionTracker&&n.assertionTracker.setCurrentCallId(u);let l;try{l=await e(c,d);}catch(i){let m=perf_hooks.performance.now();try{let y=n.apiConfig.captureRequestBody?b(a,n.apiConfig.redactBodyFields):null,C={id:u,timestamp:o,method:p,url:c,requestHeaders:null,requestBody:y,responseStatusCode:null,responseStatusText:null,responseHeaders:null,responseBody:null,responseTimeMs:Math.round((m-g)*100)/100,isBinary:!1,error:i instanceof Error?i.message:String(i)};n.apiCallWriter&&(n.apiCallWriter.append(C),n.apiCallCount++);}catch{}throw i}try{let i=perf_hooks.performance.now(),m=l.headers(),y=m["content-type"]??null,C=y?!tt(y):!1,f=null;n.apiConfig.captureRequestHeaders&&d?.headers&&(f=d.headers);let h=null;n.apiConfig.captureResponseHeaders&&(h=m);let R=n.apiConfig.captureRequestBody?a:null,w=null;if(n.apiConfig.captureResponseBody)try{C?w=(await l.body()).toString("base64"):w=await l.text();}catch{}f=S(f,n.apiConfig.redactHeaders),h=S(h,n.apiConfig.redactHeaders),R=b(R,n.apiConfig.redactBodyFields),w=b(w,n.apiConfig.redactBodyFields);let W={id:u,timestamp:o,method:p,url:l.url(),requestHeaders:f,requestBody:R,responseStatusCode:l.status(),responseStatusText:l.statusText(),responseHeaders:h,responseBody:w,responseTimeMs:Math.round((i-g)*100)/100,isBinary:C,error:null};n.apiCallWriter&&(n.apiCallWriter.append(W),n.apiCallCount++);}catch{}try{l[B]=u,n.tagResponseMethods(l,u);}catch{}return l}}};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 N=q;var rt="__testrelic_api_config",st="__testrelic_config_trackApiCalls";function ot(r){let t=r.annotations.find(n=>n.type===rt&&n.description!==void 0);if(t)try{return JSON.parse(t.description,it)}catch{}let e=r.annotations.find(n=>n.type===st&&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 it(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 at={page:async({page:r},t,e)=>{let n=new A(r);try{await n.init();}catch{}await t(r);try{let{navigations:s,networkRequestsFile:c,networkRequestsCount:d,consoleLogsFile:u,consoleLogsCount:o}=await n.getFileData(),p={testRelicData:!0,version:core.PAYLOAD_VERSION,navigations:s,apiAssertions:[],networkRequestsFile:c,networkRequestsCount:d,consoleLogsFile:u,consoleLogsCount:o,apiCallsFile:null,apiCallsCount:0};await e.attach(core.ATTACHMENT_NAME,{body:Buffer.from(JSON.stringify(p)),contentType:core.ATTACHMENT_CONTENT_TYPE});}catch{}n.dispose();},request:async({request:r},t,e)=>{let n=ot(e);if(!n.trackApiCalls){await t(r);return}let s=new v,c=new N(r,s,n);c.intercept(),await t(r);try{let{apiCallsFile:d,apiCallsCount:u}=c.getFileData(),o=n.captureAssertions?s.getData():[],p={testRelicData:!0,version:core.PAYLOAD_VERSION,navigations:[],apiAssertions:o,networkRequestsFile:null,networkRequestsCount:0,consoleLogsFile:null,consoleLogsCount:0,apiCallsFile:d,apiCallsCount:u};await e.attach(core.ATTACHMENT_NAME,{body:Buffer.from(JSON.stringify(p)),contentType:core.ATTACHMENT_CONTENT_TYPE});}catch{}c.dispose(),s.dispose();}},Dt=test.test.extend(at);Object.defineProperty(exports,"expect",{enumerable:true,get:function(){return test.expect}});exports.readApiConfig=ot;exports.test=Dt;exports.testRelicFixture=at;//# sourceMappingURL=fixture.cjs.map
2
3
  //# sourceMappingURL=fixture.cjs.map