playwright-network-metrics 0.2.0 → 0.2.2
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 +3 -1
- package/dist/index.js +18 -16
- package/dist/index.mjs +18 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ The `defineNetworkMetricsFixture` function and `NetworkMetricsReporter` accept a
|
|
|
70
70
|
| Option | Type | Default | Description |
|
|
71
71
|
| ------------------- | -------------------- | ------------------------------------- | ---------------------------------------------------- |
|
|
72
72
|
| `outDir` | `string` | `"playwright-report/network-metrics"` | Directory where the reports will be written. |
|
|
73
|
-
| `html` | `boolean` | `
|
|
73
|
+
| `html` | `boolean` | `true` | Whether to generate an interactive HTML report. |
|
|
74
74
|
| `urlMatch` | `string \| string[]` | `"**"` | Glob pattern(s) to match URLs (e.g., `**/api/**`). |
|
|
75
75
|
| `resourceTypes` | `string[]` | `[...]` | List of resource types to track. |
|
|
76
76
|
| `redactQueryParams` | `string[]` | `[]` | List of query parameters to redact from stored URLs. |
|
|
@@ -78,6 +78,8 @@ The `defineNetworkMetricsFixture` function and `NetworkMetricsReporter` accept a
|
|
|
78
78
|
| `routeGroupRules` | `array` | `[]` | Rules for grouping URLs into logical categories. |
|
|
79
79
|
| `routeGroupFn` | `function` | `undefined` | Custom hook for route grouping. |
|
|
80
80
|
|
|
81
|
+
> **Note**: If the `PLAYWRIGHT_REPORT_FOLDER` environment variable is set, the reporter will automatically adjust the output directory to be distinct inside that folder (e.g. `${PLAYWRIGHT_REPORT_FOLDER}/network-metrics`).
|
|
82
|
+
|
|
81
83
|
### Custom Route Grouping
|
|
82
84
|
|
|
83
85
|
You can provide custom rules to group similar URLs together:
|
package/dist/index.js
CHANGED
|
@@ -89,7 +89,7 @@ var NetworkMetricsCollector = class {
|
|
|
89
89
|
*/
|
|
90
90
|
async handleRequestFinished(request) {
|
|
91
91
|
if (!this.shouldTrack(request)) return;
|
|
92
|
-
const response = await request.response();
|
|
92
|
+
const response = await request.response().catch(() => null);
|
|
93
93
|
if (!response) return;
|
|
94
94
|
this.addMetric(request, response);
|
|
95
95
|
}
|
|
@@ -381,13 +381,13 @@ function generateHtmlReport(report) {
|
|
|
381
381
|
|
|
382
382
|
.grid-wrapper { background: var(--card-bg); border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); border: 1px solid var(--border); overflow: hidden; }
|
|
383
383
|
.grid-table { overflow-x: auto; }
|
|
384
|
-
.grid-header, .grid-row { display: grid; grid-template-columns: minmax(300px, 4fr) 110px
|
|
385
|
-
.grid-header { background: #f8fafc; font-weight: 700; color: var(--secondary); font-size: 0.85rem; text-transform: uppercase; width: fit-content; }
|
|
384
|
+
.grid-header, .grid-row { display: grid; grid-template-columns: minmax(300px, 4fr) 110px 160px 160px 160px 160px 120px; align-items: center; border-bottom: 1px solid var(--border); }
|
|
385
|
+
.grid-header { background: #f8fafc; font-weight: 700; color: var(--secondary); font-size: 0.85rem; text-transform: uppercase; width: fit-content; min-width: 100%; }
|
|
386
386
|
.grid-cell { padding: 14px 18px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
387
387
|
.grid-header .grid-cell { cursor: pointer; position: relative; display: flex; align-items: center; gap: 4px; }
|
|
388
388
|
.grid-header .grid-cell:hover { background: #f1f5f9; }
|
|
389
389
|
|
|
390
|
-
#grid-body { width: fit-content; }
|
|
390
|
+
#grid-body { width: fit-content; min-width: 100%; }
|
|
391
391
|
|
|
392
392
|
.grid-row { cursor: pointer; transition: background 0.1s; background: white; }
|
|
393
393
|
.grid-row:hover { background: #fcfdfe; }
|
|
@@ -477,12 +477,10 @@ function generateHtmlReport(report) {
|
|
|
477
477
|
const columnMeta = {
|
|
478
478
|
key: { label: 'Endpoint / Key', tooltip: 'The unique identifier for this aggregation group.' },
|
|
479
479
|
count: { label: 'Count', tooltip: 'Total number of requests made.' },
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
p99: { label: 'P99', tooltip: '99th Percentile: Only 1% of requests were slower than this value. Critical for tail latency optimization.' },
|
|
485
|
-
totalDurationMs: { label: 'Total Time', tooltip: 'Sum of all response times for this group.' },
|
|
480
|
+
avgLoadTimeMs: { label: 'Avg Load', tooltip: 'Resource Load Time: The time taken to download the resource body (responseEnd - responseStart).' },
|
|
481
|
+
avgDurationMs: { label: 'Avg Duration', tooltip: 'The average response time in milliseconds. This represents the total time from request start to the end of the response body.' },
|
|
482
|
+
totalLoadTimeMs: { label: 'Total Load', tooltip: 'Sum of all load times for this group.' },
|
|
483
|
+
totalDurationMs: { label: 'Total Duration', tooltip: 'Sum of all response times for this group.' },
|
|
486
484
|
errorCount: { label: 'Errors', tooltip: 'Number of failed requests (non-2xx response or network error).' }
|
|
487
485
|
};
|
|
488
486
|
|
|
@@ -529,7 +527,7 @@ function generateHtmlReport(report) {
|
|
|
529
527
|
const header = document.getElementById('grid-header');
|
|
530
528
|
const body = document.getElementById('grid-body');
|
|
531
529
|
|
|
532
|
-
const columns = ['key', 'count', '
|
|
530
|
+
const columns = ['key', 'count', 'avgLoadTimeMs', 'totalLoadTimeMs', 'avgDurationMs', 'totalDurationMs', 'errorCount'];
|
|
533
531
|
|
|
534
532
|
header.innerHTML = columns.map(k => \`
|
|
535
533
|
<div class="grid-cell" onclick="handleSort('\${k}')">
|
|
@@ -550,11 +548,9 @@ function generateHtmlReport(report) {
|
|
|
550
548
|
<span class="endpoint-key" title="\${item.key}">\${item.method ? item.key.replace(new RegExp(\`^\${item.method}\\\\s+\`), '') : item.key}</span>
|
|
551
549
|
</div>
|
|
552
550
|
<div class="grid-cell">\${item.count}</div>
|
|
553
|
-
<div class="grid-cell">\${formatMs(item.avgDurationMs)}</div>
|
|
554
551
|
<div class="grid-cell">\${formatMs(item.avgLoadTimeMs)}</div>
|
|
555
|
-
<div class="grid-cell">\${formatMs(item.
|
|
556
|
-
<div class="grid-cell">\${formatMs(item.
|
|
557
|
-
<div class="grid-cell">\${formatMs(item.p99)}</div>
|
|
552
|
+
<div class="grid-cell">\${formatMs(item.totalLoadTimeMs)}</div>
|
|
553
|
+
<div class="grid-cell">\${formatMs(item.avgDurationMs)}</div>
|
|
558
554
|
<div class="grid-cell">\${formatMs(item.totalDurationMs)}</div>
|
|
559
555
|
<div class="grid-cell \${item.errorCount > 0 ? 'error-tag' : ''}">\${item.errorCount}</div>
|
|
560
556
|
</div>
|
|
@@ -628,8 +624,14 @@ var NetworkMetricsReporter = class {
|
|
|
628
624
|
html: true
|
|
629
625
|
};
|
|
630
626
|
onBegin(config) {
|
|
627
|
+
if (process.env.PLAYWRIGHT_REPORT_FOLDER) {
|
|
628
|
+
this.config.outDir = import_node_path.default.join(
|
|
629
|
+
process.env.PLAYWRIGHT_REPORT_FOLDER,
|
|
630
|
+
"network-metrics"
|
|
631
|
+
);
|
|
632
|
+
}
|
|
631
633
|
const reporterEntry = config.reporter.find(
|
|
632
|
-
([name]) => name
|
|
634
|
+
([name]) => name.includes("playwright-network-metrics")
|
|
633
635
|
);
|
|
634
636
|
if (reporterEntry && typeof reporterEntry[1] === "object") {
|
|
635
637
|
this.config = {
|
package/dist/index.mjs
CHANGED
|
@@ -51,7 +51,7 @@ var NetworkMetricsCollector = class {
|
|
|
51
51
|
*/
|
|
52
52
|
async handleRequestFinished(request) {
|
|
53
53
|
if (!this.shouldTrack(request)) return;
|
|
54
|
-
const response = await request.response();
|
|
54
|
+
const response = await request.response().catch(() => null);
|
|
55
55
|
if (!response) return;
|
|
56
56
|
this.addMetric(request, response);
|
|
57
57
|
}
|
|
@@ -343,13 +343,13 @@ function generateHtmlReport(report) {
|
|
|
343
343
|
|
|
344
344
|
.grid-wrapper { background: var(--card-bg); border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); border: 1px solid var(--border); overflow: hidden; }
|
|
345
345
|
.grid-table { overflow-x: auto; }
|
|
346
|
-
.grid-header, .grid-row { display: grid; grid-template-columns: minmax(300px, 4fr) 110px
|
|
347
|
-
.grid-header { background: #f8fafc; font-weight: 700; color: var(--secondary); font-size: 0.85rem; text-transform: uppercase; width: fit-content; }
|
|
346
|
+
.grid-header, .grid-row { display: grid; grid-template-columns: minmax(300px, 4fr) 110px 160px 160px 160px 160px 120px; align-items: center; border-bottom: 1px solid var(--border); }
|
|
347
|
+
.grid-header { background: #f8fafc; font-weight: 700; color: var(--secondary); font-size: 0.85rem; text-transform: uppercase; width: fit-content; min-width: 100%; }
|
|
348
348
|
.grid-cell { padding: 14px 18px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
349
349
|
.grid-header .grid-cell { cursor: pointer; position: relative; display: flex; align-items: center; gap: 4px; }
|
|
350
350
|
.grid-header .grid-cell:hover { background: #f1f5f9; }
|
|
351
351
|
|
|
352
|
-
#grid-body { width: fit-content; }
|
|
352
|
+
#grid-body { width: fit-content; min-width: 100%; }
|
|
353
353
|
|
|
354
354
|
.grid-row { cursor: pointer; transition: background 0.1s; background: white; }
|
|
355
355
|
.grid-row:hover { background: #fcfdfe; }
|
|
@@ -439,12 +439,10 @@ function generateHtmlReport(report) {
|
|
|
439
439
|
const columnMeta = {
|
|
440
440
|
key: { label: 'Endpoint / Key', tooltip: 'The unique identifier for this aggregation group.' },
|
|
441
441
|
count: { label: 'Count', tooltip: 'Total number of requests made.' },
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
p99: { label: 'P99', tooltip: '99th Percentile: Only 1% of requests were slower than this value. Critical for tail latency optimization.' },
|
|
447
|
-
totalDurationMs: { label: 'Total Time', tooltip: 'Sum of all response times for this group.' },
|
|
442
|
+
avgLoadTimeMs: { label: 'Avg Load', tooltip: 'Resource Load Time: The time taken to download the resource body (responseEnd - responseStart).' },
|
|
443
|
+
avgDurationMs: { label: 'Avg Duration', tooltip: 'The average response time in milliseconds. This represents the total time from request start to the end of the response body.' },
|
|
444
|
+
totalLoadTimeMs: { label: 'Total Load', tooltip: 'Sum of all load times for this group.' },
|
|
445
|
+
totalDurationMs: { label: 'Total Duration', tooltip: 'Sum of all response times for this group.' },
|
|
448
446
|
errorCount: { label: 'Errors', tooltip: 'Number of failed requests (non-2xx response or network error).' }
|
|
449
447
|
};
|
|
450
448
|
|
|
@@ -491,7 +489,7 @@ function generateHtmlReport(report) {
|
|
|
491
489
|
const header = document.getElementById('grid-header');
|
|
492
490
|
const body = document.getElementById('grid-body');
|
|
493
491
|
|
|
494
|
-
const columns = ['key', 'count', '
|
|
492
|
+
const columns = ['key', 'count', 'avgLoadTimeMs', 'totalLoadTimeMs', 'avgDurationMs', 'totalDurationMs', 'errorCount'];
|
|
495
493
|
|
|
496
494
|
header.innerHTML = columns.map(k => \`
|
|
497
495
|
<div class="grid-cell" onclick="handleSort('\${k}')">
|
|
@@ -512,11 +510,9 @@ function generateHtmlReport(report) {
|
|
|
512
510
|
<span class="endpoint-key" title="\${item.key}">\${item.method ? item.key.replace(new RegExp(\`^\${item.method}\\\\s+\`), '') : item.key}</span>
|
|
513
511
|
</div>
|
|
514
512
|
<div class="grid-cell">\${item.count}</div>
|
|
515
|
-
<div class="grid-cell">\${formatMs(item.avgDurationMs)}</div>
|
|
516
513
|
<div class="grid-cell">\${formatMs(item.avgLoadTimeMs)}</div>
|
|
517
|
-
<div class="grid-cell">\${formatMs(item.
|
|
518
|
-
<div class="grid-cell">\${formatMs(item.
|
|
519
|
-
<div class="grid-cell">\${formatMs(item.p99)}</div>
|
|
514
|
+
<div class="grid-cell">\${formatMs(item.totalLoadTimeMs)}</div>
|
|
515
|
+
<div class="grid-cell">\${formatMs(item.avgDurationMs)}</div>
|
|
520
516
|
<div class="grid-cell">\${formatMs(item.totalDurationMs)}</div>
|
|
521
517
|
<div class="grid-cell \${item.errorCount > 0 ? 'error-tag' : ''}">\${item.errorCount}</div>
|
|
522
518
|
</div>
|
|
@@ -590,8 +586,14 @@ var NetworkMetricsReporter = class {
|
|
|
590
586
|
html: true
|
|
591
587
|
};
|
|
592
588
|
onBegin(config) {
|
|
589
|
+
if (process.env.PLAYWRIGHT_REPORT_FOLDER) {
|
|
590
|
+
this.config.outDir = path.join(
|
|
591
|
+
process.env.PLAYWRIGHT_REPORT_FOLDER,
|
|
592
|
+
"network-metrics"
|
|
593
|
+
);
|
|
594
|
+
}
|
|
593
595
|
const reporterEntry = config.reporter.find(
|
|
594
|
-
([name]) => name
|
|
596
|
+
([name]) => name.includes("playwright-network-metrics")
|
|
595
597
|
);
|
|
596
598
|
if (reporterEntry && typeof reporterEntry[1] === "object") {
|
|
597
599
|
this.config = {
|
package/package.json
CHANGED