@piwitests/reporter 0.53.0 → 0.55.0
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/index.js +1442 -294
- package/dist/global-setup-module.js +71 -42
- package/dist/index.d.ts +21 -9
- package/dist/index.js +2421 -1555
- package/dist/internal/capture/attachments.d.ts +2 -0
- package/dist/internal/capture/attachments.js +2 -0
- package/dist/internal/capture/capture-fixtures.d.ts +47 -21
- package/dist/internal/capture/capture-fixtures.js +1027 -391
- package/package.json +1 -1
- package/templates/skills/investigate-failure/SKILL.md +1 -1
- package/templates/skills/stabilize-flaky-tests/SKILL.md +1 -1
- package/templates/skills/write-the-missing-test/SKILL.md +32 -6
|
@@ -2037,6 +2037,8 @@ var ATTACHMENT_NAMES = {
|
|
|
2037
2037
|
pageInventory: "piwi-page-inventory",
|
|
2038
2038
|
locatorPages: "piwi-locator-pages",
|
|
2039
2039
|
codeReach: "piwi-code-reach",
|
|
2040
|
+
browserCoverage: "piwi-browser-coverage",
|
|
2041
|
+
browserFunctions: "piwi-browser-functions",
|
|
2040
2042
|
serverCoverageRequests: "piwi-server-coverage-requests",
|
|
2041
2043
|
webVitals: "piwi-web-vitals",
|
|
2042
2044
|
locatorSuggestion: "piwi-locator-suggestion",
|
|
@@ -3501,6 +3503,438 @@ function joinErrorMessages(errors) {
|
|
|
3501
3503
|
return parts.join("\n---\n") || null;
|
|
3502
3504
|
}
|
|
3503
3505
|
|
|
3506
|
+
// ../core/src/coverage.ts
|
|
3507
|
+
var MAX_COVERAGE_FILES = 2e3;
|
|
3508
|
+
var MAX_COVERAGE_RANGES = 1e4;
|
|
3509
|
+
var MAX_LINE = 1e6;
|
|
3510
|
+
var MAX_PATH_LENGTH = 500;
|
|
3511
|
+
var BLOB_ID = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
3512
|
+
function isRecord(value) {
|
|
3513
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3514
|
+
}
|
|
3515
|
+
function isRuntime(value) {
|
|
3516
|
+
return value === "browser" || value === "server";
|
|
3517
|
+
}
|
|
3518
|
+
function isLine(value) {
|
|
3519
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= MAX_LINE;
|
|
3520
|
+
}
|
|
3521
|
+
function validRanges(raw) {
|
|
3522
|
+
if (!Array.isArray(raw)) return [];
|
|
3523
|
+
const out = [];
|
|
3524
|
+
for (const item of raw) {
|
|
3525
|
+
if (!Array.isArray(item) || item.length !== 2) continue;
|
|
3526
|
+
const [start, end] = item;
|
|
3527
|
+
if (isLine(start) && isLine(end) && start <= end) out.push([start, end]);
|
|
3528
|
+
}
|
|
3529
|
+
return out.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
3530
|
+
}
|
|
3531
|
+
function normalizeLineRanges(ranges) {
|
|
3532
|
+
const out = [];
|
|
3533
|
+
for (const range of validRanges(ranges)) {
|
|
3534
|
+
const last = out[out.length - 1];
|
|
3535
|
+
if (last && range[0] <= last[1] + 1) last[1] = Math.max(last[1], range[1]);
|
|
3536
|
+
else out.push(range);
|
|
3537
|
+
}
|
|
3538
|
+
return out;
|
|
3539
|
+
}
|
|
3540
|
+
function normalizeFunctionRanges(ranges) {
|
|
3541
|
+
const out = [];
|
|
3542
|
+
for (const range of validRanges(ranges)) {
|
|
3543
|
+
const last = out[out.length - 1];
|
|
3544
|
+
if (!last || last[0] !== range[0] || last[1] !== range[1]) out.push(range);
|
|
3545
|
+
}
|
|
3546
|
+
return out;
|
|
3547
|
+
}
|
|
3548
|
+
function normalizeCoveragePath(raw) {
|
|
3549
|
+
if (typeof raw !== "string" || raw.length > MAX_PATH_LENGTH || raw.includes("\0")) return null;
|
|
3550
|
+
const path14 = raw.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
3551
|
+
if (!path14 || path14.startsWith("../") || path14.startsWith("/") || /^[A-Za-z]:\//.test(path14)) return null;
|
|
3552
|
+
if (/(^|\/)node_modules\//.test(path14)) return null;
|
|
3553
|
+
return path14;
|
|
3554
|
+
}
|
|
3555
|
+
function fileEntry(path14, runtime, blob, functions = [], lines = []) {
|
|
3556
|
+
const file = { path: path14, runtime };
|
|
3557
|
+
if (blob && (functions.length > 0 || lines.length > 0)) file.blob = blob;
|
|
3558
|
+
if (functions.length > 0) file.functions = functions;
|
|
3559
|
+
if (lines.length > 0) file.lines = lines;
|
|
3560
|
+
return file;
|
|
3561
|
+
}
|
|
3562
|
+
function captureOf(files, incomplete) {
|
|
3563
|
+
return incomplete ? { v: 1, files, incomplete: true } : { v: 1, files };
|
|
3564
|
+
}
|
|
3565
|
+
function compareFiles(a, b) {
|
|
3566
|
+
if (a.path !== b.path) return a.path < b.path ? -1 : 1;
|
|
3567
|
+
if (a.runtime !== b.runtime) return a.runtime < b.runtime ? -1 : 1;
|
|
3568
|
+
return 0;
|
|
3569
|
+
}
|
|
3570
|
+
function sanitizeCoverage(raw) {
|
|
3571
|
+
if (!isRecord(raw) || raw.v !== 1 || !Array.isArray(raw.files)) return null;
|
|
3572
|
+
const files = [];
|
|
3573
|
+
for (const item of raw.files) {
|
|
3574
|
+
if (!isRecord(item) || !isRuntime(item.runtime)) continue;
|
|
3575
|
+
const path14 = normalizeCoveragePath(item.path);
|
|
3576
|
+
if (path14 === null) continue;
|
|
3577
|
+
const blob = typeof item.blob === "string" && BLOB_ID.test(item.blob) ? item.blob : void 0;
|
|
3578
|
+
files.push(
|
|
3579
|
+
fileEntry(path14, item.runtime, blob, normalizeFunctionRanges(item.functions), normalizeLineRanges(item.lines))
|
|
3580
|
+
);
|
|
3581
|
+
}
|
|
3582
|
+
const merged = mergeCoverage(captureOf(files, raw.incomplete === true));
|
|
3583
|
+
if (!merged) return null;
|
|
3584
|
+
let incomplete = merged.incomplete === true || merged.files.length > MAX_COVERAGE_FILES;
|
|
3585
|
+
let budget = MAX_COVERAGE_RANGES;
|
|
3586
|
+
const capped = merged.files.slice(0, MAX_COVERAGE_FILES).map((file) => {
|
|
3587
|
+
const count = (file.functions?.length ?? 0) + (file.lines?.length ?? 0);
|
|
3588
|
+
if (count <= budget) {
|
|
3589
|
+
budget -= count;
|
|
3590
|
+
return file;
|
|
3591
|
+
}
|
|
3592
|
+
incomplete = true;
|
|
3593
|
+
return fileEntry(file.path, file.runtime);
|
|
3594
|
+
});
|
|
3595
|
+
return captureOf(capped, incomplete);
|
|
3596
|
+
}
|
|
3597
|
+
function mergeCoverage(...captures2) {
|
|
3598
|
+
const entries = /* @__PURE__ */ new Map();
|
|
3599
|
+
let incomplete = false;
|
|
3600
|
+
for (const capture of captures2) {
|
|
3601
|
+
if (!capture) continue;
|
|
3602
|
+
if (capture.incomplete) incomplete = true;
|
|
3603
|
+
for (const file of capture.files) {
|
|
3604
|
+
const key = `${file.runtime}:${file.path}`;
|
|
3605
|
+
let entry = entries.get(key);
|
|
3606
|
+
if (!entry) {
|
|
3607
|
+
entry = { path: file.path, runtime: file.runtime, blobs: /* @__PURE__ */ new Set(), functions: [], lines: [] };
|
|
3608
|
+
entries.set(key, entry);
|
|
3609
|
+
}
|
|
3610
|
+
if (file.blob) entry.blobs.add(file.blob);
|
|
3611
|
+
for (const range of file.functions ?? []) entry.functions.push(range);
|
|
3612
|
+
for (const range of file.lines ?? []) entry.lines.push(range);
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
if (entries.size === 0) return null;
|
|
3616
|
+
const files = [...entries.values()].map((entry) => {
|
|
3617
|
+
if (entry.blobs.size > 1) incomplete = true;
|
|
3618
|
+
const blob = entry.blobs.size === 1 ? [...entry.blobs][0] : void 0;
|
|
3619
|
+
return fileEntry(
|
|
3620
|
+
entry.path,
|
|
3621
|
+
entry.runtime,
|
|
3622
|
+
blob,
|
|
3623
|
+
normalizeFunctionRanges(entry.functions),
|
|
3624
|
+
normalizeLineRanges(entry.lines)
|
|
3625
|
+
);
|
|
3626
|
+
});
|
|
3627
|
+
return captureOf(files.sort(compareFiles), incomplete);
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
// ../core/src/source-map.ts
|
|
3631
|
+
var BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
3632
|
+
var BASE64_VALUE = new Int8Array(128).fill(-1);
|
|
3633
|
+
for (let i = 0; i < BASE64.length; i++) BASE64_VALUE[BASE64.charCodeAt(i)] = i;
|
|
3634
|
+
function decodeMappings(mappings, sourceBase) {
|
|
3635
|
+
const lines = [];
|
|
3636
|
+
let current = { columns: [], sources: [], originalLines: [] };
|
|
3637
|
+
let column = 0;
|
|
3638
|
+
let source = 0;
|
|
3639
|
+
let originalLine = 0;
|
|
3640
|
+
const fields = [];
|
|
3641
|
+
let value = 0;
|
|
3642
|
+
let shift = 0;
|
|
3643
|
+
const flush = () => {
|
|
3644
|
+
if (fields.length === 0) return;
|
|
3645
|
+
column += fields[0];
|
|
3646
|
+
if (fields.length >= 4) {
|
|
3647
|
+
source += fields[1];
|
|
3648
|
+
originalLine += fields[2];
|
|
3649
|
+
current.columns.push(column);
|
|
3650
|
+
current.sources.push(sourceBase + source);
|
|
3651
|
+
current.originalLines.push(originalLine);
|
|
3652
|
+
} else {
|
|
3653
|
+
current.columns.push(column);
|
|
3654
|
+
current.sources.push(-1);
|
|
3655
|
+
current.originalLines.push(-1);
|
|
3656
|
+
}
|
|
3657
|
+
fields.length = 0;
|
|
3658
|
+
};
|
|
3659
|
+
for (let i = 0; i <= mappings.length; i++) {
|
|
3660
|
+
const ch = i < mappings.length ? mappings.charCodeAt(i) : 59;
|
|
3661
|
+
if (ch === 59 || ch === 44) {
|
|
3662
|
+
flush();
|
|
3663
|
+
if (ch === 59) {
|
|
3664
|
+
lines.push(current);
|
|
3665
|
+
current = { columns: [], sources: [], originalLines: [] };
|
|
3666
|
+
column = 0;
|
|
3667
|
+
}
|
|
3668
|
+
continue;
|
|
3669
|
+
}
|
|
3670
|
+
const digit = ch < 128 ? BASE64_VALUE[ch] : -1;
|
|
3671
|
+
if (digit < 0) continue;
|
|
3672
|
+
value += (digit & 31) << shift;
|
|
3673
|
+
if (digit & 32) {
|
|
3674
|
+
shift += 5;
|
|
3675
|
+
continue;
|
|
3676
|
+
}
|
|
3677
|
+
fields.push(value & 1 ? -(value >>> 1) : value >>> 1);
|
|
3678
|
+
value = 0;
|
|
3679
|
+
shift = 0;
|
|
3680
|
+
}
|
|
3681
|
+
for (const line of lines) {
|
|
3682
|
+
if (line.columns.every((c, i) => i === 0 || c >= line.columns[i - 1])) continue;
|
|
3683
|
+
const order = line.columns.map((_, i) => i).sort((a, b) => line.columns[a] - line.columns[b]);
|
|
3684
|
+
line.columns = order.map((i) => line.columns[i]);
|
|
3685
|
+
line.sources = order.map((i) => line.sources[i]);
|
|
3686
|
+
line.originalLines = order.map((i) => line.originalLines[i]);
|
|
3687
|
+
}
|
|
3688
|
+
return lines;
|
|
3689
|
+
}
|
|
3690
|
+
function joinRoot(root, source) {
|
|
3691
|
+
if (!root || /^[a-z][\w+.-]*:/i.test(source) || source.startsWith("/")) return source;
|
|
3692
|
+
return root.endsWith("/") ? root + source : `${root}/${source}`;
|
|
3693
|
+
}
|
|
3694
|
+
function decodeLines(raw) {
|
|
3695
|
+
const sources = [];
|
|
3696
|
+
const decoded = [];
|
|
3697
|
+
const add = (map, lineOffset, columnOffset) => {
|
|
3698
|
+
if (map.sections) {
|
|
3699
|
+
for (const section of map.sections) {
|
|
3700
|
+
if (!add(section.map, section.offset.line, section.offset.column)) return false;
|
|
3701
|
+
}
|
|
3702
|
+
return true;
|
|
3703
|
+
}
|
|
3704
|
+
if (typeof map.mappings !== "string" || !Array.isArray(map.sources)) return false;
|
|
3705
|
+
const base = sources.length;
|
|
3706
|
+
for (const s of map.sources) sources.push(joinRoot(map.sourceRoot, s ?? ""));
|
|
3707
|
+
decodeMappings(map.mappings, base).forEach((segments, i) => {
|
|
3708
|
+
const at = lineOffset + i;
|
|
3709
|
+
const shifted = i === 0 && columnOffset ? segments.columns.map((c) => c + columnOffset) : segments.columns;
|
|
3710
|
+
const existing = decoded[at];
|
|
3711
|
+
if (!existing)
|
|
3712
|
+
decoded[at] = { columns: shifted, sources: segments.sources, originalLines: segments.originalLines };
|
|
3713
|
+
else {
|
|
3714
|
+
existing.columns.push(...shifted);
|
|
3715
|
+
existing.sources.push(...segments.sources);
|
|
3716
|
+
existing.originalLines.push(...segments.originalLines);
|
|
3717
|
+
}
|
|
3718
|
+
});
|
|
3719
|
+
return true;
|
|
3720
|
+
};
|
|
3721
|
+
return add(raw, 0, 0) ? { sources, lines: decoded } : null;
|
|
3722
|
+
}
|
|
3723
|
+
function compactSegments(lines) {
|
|
3724
|
+
let total = 0;
|
|
3725
|
+
for (const line of lines) total += line?.columns.length ?? 0;
|
|
3726
|
+
const segments = {
|
|
3727
|
+
lineStarts: new Int32Array(lines.length + 1),
|
|
3728
|
+
columns: new Int32Array(total),
|
|
3729
|
+
sources: new Int32Array(total),
|
|
3730
|
+
originalLines: new Int32Array(total)
|
|
3731
|
+
};
|
|
3732
|
+
let at = 0;
|
|
3733
|
+
for (let n = 0; n < lines.length; n++) {
|
|
3734
|
+
segments.lineStarts[n] = at;
|
|
3735
|
+
const line = lines[n];
|
|
3736
|
+
if (!line) continue;
|
|
3737
|
+
segments.columns.set(line.columns, at);
|
|
3738
|
+
segments.sources.set(line.sources, at);
|
|
3739
|
+
segments.originalLines.set(line.originalLines, at);
|
|
3740
|
+
at += line.columns.length;
|
|
3741
|
+
}
|
|
3742
|
+
segments.lineStarts[lines.length] = at;
|
|
3743
|
+
return segments;
|
|
3744
|
+
}
|
|
3745
|
+
function decodeSourceMap(raw) {
|
|
3746
|
+
if (raw.version !== void 0 && raw.version !== 3) return null;
|
|
3747
|
+
const decoded = decodeLines(raw);
|
|
3748
|
+
if (!decoded) return null;
|
|
3749
|
+
const { sources } = decoded;
|
|
3750
|
+
const segments = compactSegments(decoded.lines);
|
|
3751
|
+
const lineCount = segments.lineStarts.length - 1;
|
|
3752
|
+
const originalAt = (line, column) => {
|
|
3753
|
+
if (line < 0 || line >= lineCount) return null;
|
|
3754
|
+
let lo = segments.lineStarts[line];
|
|
3755
|
+
let hi = segments.lineStarts[line + 1] - 1;
|
|
3756
|
+
if (lo > hi || segments.columns[lo] > column) return null;
|
|
3757
|
+
while (lo < hi) {
|
|
3758
|
+
const mid = lo + hi + 1 >> 1;
|
|
3759
|
+
if (segments.columns[mid] <= column) lo = mid;
|
|
3760
|
+
else hi = mid - 1;
|
|
3761
|
+
}
|
|
3762
|
+
const source = segments.sources[lo];
|
|
3763
|
+
return source < 0 ? null : { source, line: segments.originalLines[lo] };
|
|
3764
|
+
};
|
|
3765
|
+
return {
|
|
3766
|
+
sources,
|
|
3767
|
+
sourceAt(line, column) {
|
|
3768
|
+
return originalAt(line, column)?.source ?? null;
|
|
3769
|
+
},
|
|
3770
|
+
originalAt,
|
|
3771
|
+
mapsBetween(source, start, end) {
|
|
3772
|
+
let found = false;
|
|
3773
|
+
eachSegmentBetween(segments, start, end, (i) => {
|
|
3774
|
+
found = segments.sources[i] === source;
|
|
3775
|
+
return found;
|
|
3776
|
+
});
|
|
3777
|
+
return found;
|
|
3778
|
+
},
|
|
3779
|
+
firstOriginalLineBetween(source, start, end) {
|
|
3780
|
+
let first = null;
|
|
3781
|
+
eachSegmentBetween(segments, start, end, (i) => {
|
|
3782
|
+
if (segments.sources[i] !== source || segments.originalLines[i] < 0) return false;
|
|
3783
|
+
first = segments.originalLines[i];
|
|
3784
|
+
return true;
|
|
3785
|
+
});
|
|
3786
|
+
return first;
|
|
3787
|
+
},
|
|
3788
|
+
originalLinesBetween(source, start, end, fromLine = 0) {
|
|
3789
|
+
let first = Number.POSITIVE_INFINITY;
|
|
3790
|
+
let last = -1;
|
|
3791
|
+
eachSegmentBetween(segments, start, end, (i) => {
|
|
3792
|
+
const line = segments.originalLines[i];
|
|
3793
|
+
if (segments.sources[i] === source && line >= fromLine) {
|
|
3794
|
+
if (line < first) first = line;
|
|
3795
|
+
if (line > last) last = line;
|
|
3796
|
+
}
|
|
3797
|
+
return false;
|
|
3798
|
+
});
|
|
3799
|
+
return last < 0 ? null : [first, last];
|
|
3800
|
+
}
|
|
3801
|
+
};
|
|
3802
|
+
}
|
|
3803
|
+
function eachSegmentBetween(segments, start, end, visit) {
|
|
3804
|
+
const lineCount = segments.lineStarts.length - 1;
|
|
3805
|
+
for (let line = Math.max(start.line, 0); line <= end.line && line < lineCount; line++) {
|
|
3806
|
+
let i = segments.lineStarts[line];
|
|
3807
|
+
const stop = segments.lineStarts[line + 1];
|
|
3808
|
+
if (line === start.line) {
|
|
3809
|
+
let hi = stop;
|
|
3810
|
+
while (i < hi) {
|
|
3811
|
+
const mid = i + hi >> 1;
|
|
3812
|
+
if (segments.columns[mid] < start.column) i = mid + 1;
|
|
3813
|
+
else hi = mid;
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
for (; i < stop; i++) {
|
|
3817
|
+
if (line === end.line && segments.columns[i] >= end.column) return;
|
|
3818
|
+
if (visit(i)) return;
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
function lineStarts(text) {
|
|
3823
|
+
const starts = [0];
|
|
3824
|
+
for (let i = 0; i < text.length; i++) if (text.charCodeAt(i) === 10) starts.push(i + 1);
|
|
3825
|
+
return starts;
|
|
3826
|
+
}
|
|
3827
|
+
function offsetToPosition(starts, offset) {
|
|
3828
|
+
let lo = 0;
|
|
3829
|
+
let hi = starts.length - 1;
|
|
3830
|
+
while (lo < hi) {
|
|
3831
|
+
const mid = lo + hi + 1 >> 1;
|
|
3832
|
+
if (starts[mid] <= offset) lo = mid;
|
|
3833
|
+
else hi = mid - 1;
|
|
3834
|
+
}
|
|
3835
|
+
return { line: lo, column: offset - starts[lo] };
|
|
3836
|
+
}
|
|
3837
|
+
function sourceMappingUrl(script) {
|
|
3838
|
+
const tail = script.slice(-4096);
|
|
3839
|
+
const matches = [...tail.matchAll(/(?:\/\/|\/\*)[#@]\s*sourceMappingURL=([^\s*'"]+)/g)];
|
|
3840
|
+
return matches.length ? matches[matches.length - 1][1] : null;
|
|
3841
|
+
}
|
|
3842
|
+
function decodeDataUrl(url) {
|
|
3843
|
+
const m = /^data:([^,]*?)(;base64)?,(.*)$/s.exec(url);
|
|
3844
|
+
if (!m) return null;
|
|
3845
|
+
try {
|
|
3846
|
+
if (!m[2]) return decodeURIComponent(m[3]);
|
|
3847
|
+
const binary = globalThis.atob(m[3]);
|
|
3848
|
+
const bytes = new Uint8Array(binary.length);
|
|
3849
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
3850
|
+
return new TextDecoder().decode(bytes);
|
|
3851
|
+
} catch {
|
|
3852
|
+
return null;
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3855
|
+
function normalizeSourcePath(source) {
|
|
3856
|
+
let s = source.replace(/[?#].*$/, "");
|
|
3857
|
+
s = s.replace(/^webpack:\/\/[^/]*\//, "").replace(/^webpack:\/\//, "");
|
|
3858
|
+
s = s.replace(/^(?:vite|rollup|turbopack|ng):\/*/, "");
|
|
3859
|
+
s = s.replace(/^\[project\]\//, "");
|
|
3860
|
+
s = s.replace(/^file:\/\//, "");
|
|
3861
|
+
s = s.replace(/^\/@fs\//, "/");
|
|
3862
|
+
s = s.replace(/^\/@id\//, "");
|
|
3863
|
+
try {
|
|
3864
|
+
s = decodeURIComponent(s);
|
|
3865
|
+
} catch {
|
|
3866
|
+
}
|
|
3867
|
+
s = s.replace(/\\/g, "/");
|
|
3868
|
+
if (/^\/[A-Za-z]:\//.test(s)) s = s.slice(1);
|
|
3869
|
+
s = s.replace(/(^|\/)\.\//g, "$1");
|
|
3870
|
+
return s;
|
|
3871
|
+
}
|
|
3872
|
+
|
|
3873
|
+
// ../core/src/code-reach.ts
|
|
3874
|
+
var MAX_CODE_REACH_FILES = 2e3;
|
|
3875
|
+
function scriptText(source) {
|
|
3876
|
+
return { length: source.length, starts: Uint32Array.from(lineStarts(source)) };
|
|
3877
|
+
}
|
|
3878
|
+
var textOf = (entry, text) => text ?? (entry.source === void 0 ? void 0 : scriptText(entry.source));
|
|
3879
|
+
function executedFunctionRanges(entry, length, all = false) {
|
|
3880
|
+
length ??= entry.source?.length ?? Number.POSITIVE_INFINITY;
|
|
3881
|
+
const out = [];
|
|
3882
|
+
entry.functions.forEach((fn, i) => {
|
|
3883
|
+
const whole = fn.ranges[0];
|
|
3884
|
+
if (!whole || !all && whole.count <= 0) return;
|
|
3885
|
+
const topLevel = whole.startOffset === 0 && (whole.endOffset >= length || i === 0 && fn.functionName === "");
|
|
3886
|
+
if (topLevel) return;
|
|
3887
|
+
out.push({ start: whole.startOffset, end: whole.endOffset });
|
|
3888
|
+
});
|
|
3889
|
+
return out;
|
|
3890
|
+
}
|
|
3891
|
+
function scriptRan(entry, text) {
|
|
3892
|
+
return executedFunctionRanges(entry, text?.length).length > 0;
|
|
3893
|
+
}
|
|
3894
|
+
function reachedFunctionLines(entry, map, text) {
|
|
3895
|
+
return functionLines(entry, map, text, false);
|
|
3896
|
+
}
|
|
3897
|
+
function knownFunctionLines(entry, map, text) {
|
|
3898
|
+
return functionLines(entry, map, text, true);
|
|
3899
|
+
}
|
|
3900
|
+
function functionLines(entry, map, text, all) {
|
|
3901
|
+
const out = /* @__PURE__ */ new Map();
|
|
3902
|
+
const read = textOf(entry, text);
|
|
3903
|
+
const ranges = executedFunctionRanges(entry, read?.length, all);
|
|
3904
|
+
if (!ranges.length || !read) return out;
|
|
3905
|
+
const add = (source, range) => {
|
|
3906
|
+
const list = out.get(source);
|
|
3907
|
+
if (list) list.push(range);
|
|
3908
|
+
else out.set(source, [range]);
|
|
3909
|
+
};
|
|
3910
|
+
const starts = read.starts;
|
|
3911
|
+
for (const range of ranges) {
|
|
3912
|
+
const start = offsetToPosition(starts, range.start);
|
|
3913
|
+
if (!map) {
|
|
3914
|
+
const last = offsetToPosition(starts, Math.max(range.start, range.end - 1));
|
|
3915
|
+
add(-1, [start.line + 1, last.line + 1]);
|
|
3916
|
+
continue;
|
|
3917
|
+
}
|
|
3918
|
+
const at = map.originalAt(start.line, start.column);
|
|
3919
|
+
if (at === null) continue;
|
|
3920
|
+
const end = offsetToPosition(starts, range.end);
|
|
3921
|
+
const first = map.firstOriginalLineBetween(at.source, start, end);
|
|
3922
|
+
if (first === null) continue;
|
|
3923
|
+
const header = first === at.line || first === at.line + 1 ? at.line : first;
|
|
3924
|
+
const lines = map.originalLinesBetween(at.source, start, end, header);
|
|
3925
|
+
if (lines) add(at.source, [header + 1, lines[1] + 1]);
|
|
3926
|
+
}
|
|
3927
|
+
return out;
|
|
3928
|
+
}
|
|
3929
|
+
function finalizeCodeReach(files) {
|
|
3930
|
+
const out = /* @__PURE__ */ new Set();
|
|
3931
|
+
for (const raw of files) {
|
|
3932
|
+
const file = normalizeCoveragePath(raw);
|
|
3933
|
+
if (file !== null) out.add(file);
|
|
3934
|
+
}
|
|
3935
|
+
return [...out].sort().slice(0, MAX_CODE_REACH_FILES);
|
|
3936
|
+
}
|
|
3937
|
+
|
|
3504
3938
|
// src/internal/capture/inspect-on-failure.ts
|
|
3505
3939
|
function isCi(ci) {
|
|
3506
3940
|
return ci !== void 0 && ci !== "" && ci !== "false";
|
|
@@ -3909,7 +4343,45 @@ function isServerGranularity(value) {
|
|
|
3909
4343
|
return typeof value === "string" && SERVER_VALUES.has(value);
|
|
3910
4344
|
}
|
|
3911
4345
|
function browserCoverageInWorker() {
|
|
3912
|
-
|
|
4346
|
+
const browser = process.env[PIWI_ENV_KEYS.coverageBrowser];
|
|
4347
|
+
return process.env[PIWI_ENV_KEYS.captureCodeReach] === "true" || browser === "files" || browser === "functions";
|
|
4348
|
+
}
|
|
4349
|
+
function browserFunctionsInWorker() {
|
|
4350
|
+
return process.env[PIWI_ENV_KEYS.coverageBrowser] === "functions";
|
|
4351
|
+
}
|
|
4352
|
+
function browserFunctionCoverage(files) {
|
|
4353
|
+
return sanitizeCoverage({
|
|
4354
|
+
v: 1,
|
|
4355
|
+
files: [...files].map(([path14, functions]) => ({ path: path14, runtime: "browser", functions }))
|
|
4356
|
+
});
|
|
4357
|
+
}
|
|
4358
|
+
function newKnownFunctions(known, sent) {
|
|
4359
|
+
const parts = [];
|
|
4360
|
+
let files = [];
|
|
4361
|
+
let budget = MAX_COVERAGE_RANGES;
|
|
4362
|
+
const close = () => {
|
|
4363
|
+
if (files.length > 0) parts.push({ v: 1, files: files.sort((a, b) => a.path < b.path ? -1 : 1) });
|
|
4364
|
+
files = [];
|
|
4365
|
+
budget = MAX_COVERAGE_RANGES;
|
|
4366
|
+
};
|
|
4367
|
+
for (const [raw, ranges] of known) {
|
|
4368
|
+
const path14 = normalizeCoveragePath(raw);
|
|
4369
|
+
if (path14 === null) continue;
|
|
4370
|
+
let named = sent.get(path14);
|
|
4371
|
+
let fresh = normalizeFunctionRanges(ranges).filter((range) => !named?.has(`${range[0]}:${range[1]}`));
|
|
4372
|
+
if (fresh.length === 0) continue;
|
|
4373
|
+
if (!named) sent.set(path14, named = /* @__PURE__ */ new Set());
|
|
4374
|
+
for (const range of fresh) named.add(`${range[0]}:${range[1]}`);
|
|
4375
|
+
while (fresh.length > 0) {
|
|
4376
|
+
if (budget === 0 || files.length >= MAX_COVERAGE_FILES) close();
|
|
4377
|
+
const part = fresh.slice(0, budget);
|
|
4378
|
+
fresh = fresh.slice(part.length);
|
|
4379
|
+
budget -= part.length;
|
|
4380
|
+
files.push({ path: path14, allFunctions: part });
|
|
4381
|
+
}
|
|
4382
|
+
}
|
|
4383
|
+
close();
|
|
4384
|
+
return parts;
|
|
3913
4385
|
}
|
|
3914
4386
|
function serverCoverageInWorker() {
|
|
3915
4387
|
return isServerGranularity(process.env[PIWI_ENV_KEYS.coverageServer]);
|
|
@@ -3971,320 +4443,95 @@ var path8 = __toESM(require("path"));
|
|
|
3971
4443
|
var CANDIDATE_OFFSETS = ["..", "../../.."];
|
|
3972
4444
|
function findOwnPackageJson(fromDir) {
|
|
3973
4445
|
for (const offset of CANDIDATE_OFFSETS) {
|
|
3974
|
-
try {
|
|
3975
|
-
const pkgPath = path8.resolve(fromDir, offset, "package.json");
|
|
3976
|
-
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
3977
|
-
const { name, version } = pkg;
|
|
3978
|
-
if (name === "@piwitests/reporter" && typeof version === "string")
|
|
3979
|
-
return { version, root: path8.dirname(pkgPath) };
|
|
3980
|
-
} catch {
|
|
3981
|
-
}
|
|
3982
|
-
}
|
|
3983
|
-
return void 0;
|
|
3984
|
-
}
|
|
3985
|
-
|
|
3986
|
-
// src/internal/capture/quiet-capture.ts
|
|
3987
|
-
var BOXED_PREFIX_EXPORTS = [
|
|
3988
|
-
[
|
|
3989
|
-
"playwright-core/lib/coreBundle",
|
|
3990
|
-
(mod) => mod?.utils?.setBoxedStackPrefixes
|
|
3991
|
-
],
|
|
3992
|
-
["playwright-core/lib/utils", (mod) => mod?.setBoxedStackPrefixes]
|
|
3993
|
-
];
|
|
3994
|
-
var boxing;
|
|
3995
|
-
var resolvedPlaywrightPackageJson;
|
|
3996
|
-
function resolvePlaywrightPackageJson() {
|
|
3997
|
-
if (resolvedPlaywrightPackageJson !== void 0) return resolvedPlaywrightPackageJson;
|
|
3998
|
-
try {
|
|
3999
|
-
const testPackageJson = (0, import_node_module.createRequire)(__filename).resolve("@playwright/test/package.json");
|
|
4000
|
-
resolvedPlaywrightPackageJson = (0, import_node_module.createRequire)(testPackageJson).resolve("playwright/package.json");
|
|
4001
|
-
} catch {
|
|
4002
|
-
resolvedPlaywrightPackageJson = null;
|
|
4003
|
-
}
|
|
4004
|
-
return resolvedPlaywrightPackageJson;
|
|
4005
|
-
}
|
|
4006
|
-
function resolveBoxing() {
|
|
4007
|
-
try {
|
|
4008
|
-
const root = findOwnPackageJson(__dirname)?.root;
|
|
4009
|
-
if (!root) return null;
|
|
4010
|
-
const playwrightPackageJson = resolvePlaywrightPackageJson();
|
|
4011
|
-
if (!playwrightPackageJson) return null;
|
|
4012
|
-
const requireFromPlaywright = (0, import_node_module.createRequire)(playwrightPackageJson);
|
|
4013
|
-
for (const [moduleId, pick] of BOXED_PREFIX_EXPORTS) {
|
|
4014
|
-
let set;
|
|
4015
|
-
try {
|
|
4016
|
-
set = pick(requireFromPlaywright(moduleId));
|
|
4017
|
-
} catch {
|
|
4018
|
-
continue;
|
|
4019
|
-
}
|
|
4020
|
-
if (typeof set !== "function") continue;
|
|
4021
|
-
return {
|
|
4022
|
-
set,
|
|
4023
|
-
prefixes: [path9.dirname(playwrightPackageJson), path9.join(root, "dist") + path9.sep]
|
|
4024
|
-
};
|
|
4025
|
-
}
|
|
4026
|
-
} catch {
|
|
4027
|
-
}
|
|
4028
|
-
return null;
|
|
4029
|
-
}
|
|
4030
|
-
function boxCaptureFrames(extraPrefixes = []) {
|
|
4031
|
-
const first = boxing === void 0;
|
|
4032
|
-
if (first) boxing = resolveBoxing();
|
|
4033
|
-
if (!boxing) return;
|
|
4034
|
-
const added = extraPrefixes.filter((prefix) => !boxing.prefixes.includes(prefix));
|
|
4035
|
-
if (!first && added.length === 0) return;
|
|
4036
|
-
boxing.prefixes.push(...added);
|
|
4037
|
-
try {
|
|
4038
|
-
boxing.set([...boxing.prefixes]);
|
|
4039
|
-
} catch {
|
|
4040
|
-
}
|
|
4041
|
-
}
|
|
4042
|
-
function internalCall(owner, run) {
|
|
4043
|
-
const wrap = owner?._wrapApiCall;
|
|
4044
|
-
if (typeof wrap !== "function") return run();
|
|
4045
|
-
return wrap.call(owner, run, { internal: true, title: "Piwi capture" });
|
|
4046
|
-
}
|
|
4047
|
-
|
|
4048
|
-
// src/internal/capture/code-reach.ts
|
|
4049
|
-
var crypto2 = __toESM(require("crypto"));
|
|
4050
|
-
var fs6 = __toESM(require("fs"));
|
|
4051
|
-
var path10 = __toESM(require("path"));
|
|
4052
|
-
var import_node_child_process = require("child_process");
|
|
4053
|
-
|
|
4054
|
-
// ../core/src/coverage.ts
|
|
4055
|
-
var MAX_PATH_LENGTH = 500;
|
|
4056
|
-
function normalizeCoveragePath(raw) {
|
|
4057
|
-
if (typeof raw !== "string" || raw.length > MAX_PATH_LENGTH || raw.includes("\0")) return null;
|
|
4058
|
-
const path14 = raw.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
4059
|
-
if (!path14 || path14.startsWith("../") || path14.startsWith("/") || /^[A-Za-z]:\//.test(path14)) return null;
|
|
4060
|
-
if (/(^|\/)node_modules\//.test(path14)) return null;
|
|
4061
|
-
return path14;
|
|
4062
|
-
}
|
|
4063
|
-
|
|
4064
|
-
// ../core/src/source-map.ts
|
|
4065
|
-
var BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
4066
|
-
var BASE64_VALUE = new Int8Array(128).fill(-1);
|
|
4067
|
-
for (let i = 0; i < BASE64.length; i++) BASE64_VALUE[BASE64.charCodeAt(i)] = i;
|
|
4068
|
-
function decodeMappings(mappings, sourceBase) {
|
|
4069
|
-
const lines = [];
|
|
4070
|
-
let current = { columns: [], sources: [] };
|
|
4071
|
-
let column = 0;
|
|
4072
|
-
let source = 0;
|
|
4073
|
-
const fields = [];
|
|
4074
|
-
let value = 0;
|
|
4075
|
-
let shift = 0;
|
|
4076
|
-
const flush = () => {
|
|
4077
|
-
if (fields.length === 0) return;
|
|
4078
|
-
column += fields[0];
|
|
4079
|
-
if (fields.length >= 4) {
|
|
4080
|
-
source += fields[1];
|
|
4081
|
-
current.columns.push(column);
|
|
4082
|
-
current.sources.push(sourceBase + source);
|
|
4083
|
-
} else {
|
|
4084
|
-
current.columns.push(column);
|
|
4085
|
-
current.sources.push(-1);
|
|
4086
|
-
}
|
|
4087
|
-
fields.length = 0;
|
|
4088
|
-
};
|
|
4089
|
-
for (let i = 0; i <= mappings.length; i++) {
|
|
4090
|
-
const ch = i < mappings.length ? mappings.charCodeAt(i) : 59;
|
|
4091
|
-
if (ch === 59 || ch === 44) {
|
|
4092
|
-
flush();
|
|
4093
|
-
if (ch === 59) {
|
|
4094
|
-
lines.push(current);
|
|
4095
|
-
current = { columns: [], sources: [] };
|
|
4096
|
-
column = 0;
|
|
4097
|
-
}
|
|
4098
|
-
continue;
|
|
4099
|
-
}
|
|
4100
|
-
const digit = ch < 128 ? BASE64_VALUE[ch] : -1;
|
|
4101
|
-
if (digit < 0) continue;
|
|
4102
|
-
value += (digit & 31) << shift;
|
|
4103
|
-
if (digit & 32) {
|
|
4104
|
-
shift += 5;
|
|
4105
|
-
continue;
|
|
4106
|
-
}
|
|
4107
|
-
fields.push(value & 1 ? -(value >>> 1) : value >>> 1);
|
|
4108
|
-
value = 0;
|
|
4109
|
-
shift = 0;
|
|
4110
|
-
}
|
|
4111
|
-
for (const line of lines) {
|
|
4112
|
-
if (line.columns.every((c, i) => i === 0 || c >= line.columns[i - 1])) continue;
|
|
4113
|
-
const order = line.columns.map((_, i) => i).sort((a, b) => line.columns[a] - line.columns[b]);
|
|
4114
|
-
line.columns = order.map((i) => line.columns[i]);
|
|
4115
|
-
line.sources = order.map((i) => line.sources[i]);
|
|
4116
|
-
}
|
|
4117
|
-
return lines;
|
|
4118
|
-
}
|
|
4119
|
-
function joinRoot(root, source) {
|
|
4120
|
-
if (!root || /^[a-z][\w+.-]*:/i.test(source) || source.startsWith("/")) return source;
|
|
4121
|
-
return root.endsWith("/") ? root + source : `${root}/${source}`;
|
|
4122
|
-
}
|
|
4123
|
-
function decodeSourceMap(raw) {
|
|
4124
|
-
const sources = [];
|
|
4125
|
-
const lines = [];
|
|
4126
|
-
const add = (map, lineOffset, columnOffset) => {
|
|
4127
|
-
if (map.sections) {
|
|
4128
|
-
for (const section of map.sections) {
|
|
4129
|
-
if (!add(section.map, section.offset.line, section.offset.column)) return false;
|
|
4130
|
-
}
|
|
4131
|
-
return true;
|
|
4132
|
-
}
|
|
4133
|
-
if (typeof map.mappings !== "string" || !Array.isArray(map.sources)) return false;
|
|
4134
|
-
const base = sources.length;
|
|
4135
|
-
for (const s of map.sources) sources.push(joinRoot(map.sourceRoot, s ?? ""));
|
|
4136
|
-
decodeMappings(map.mappings, base).forEach((segments, i) => {
|
|
4137
|
-
const at = lineOffset + i;
|
|
4138
|
-
const shifted = i === 0 && columnOffset ? segments.columns.map((c) => c + columnOffset) : segments.columns;
|
|
4139
|
-
const existing = lines[at];
|
|
4140
|
-
if (!existing) lines[at] = { columns: shifted, sources: segments.sources };
|
|
4141
|
-
else {
|
|
4142
|
-
existing.columns.push(...shifted);
|
|
4143
|
-
existing.sources.push(...segments.sources);
|
|
4144
|
-
}
|
|
4145
|
-
});
|
|
4146
|
-
return true;
|
|
4147
|
-
};
|
|
4148
|
-
if (raw.version !== void 0 && raw.version !== 3) return null;
|
|
4149
|
-
if (!add(raw, 0, 0)) return null;
|
|
4150
|
-
return {
|
|
4151
|
-
sources,
|
|
4152
|
-
sourceAt(line, column) {
|
|
4153
|
-
const segments = lines[line];
|
|
4154
|
-
if (!segments || segments.columns.length === 0) return null;
|
|
4155
|
-
let lo = 0;
|
|
4156
|
-
let hi = segments.columns.length - 1;
|
|
4157
|
-
if (segments.columns[0] > column) return null;
|
|
4158
|
-
while (lo < hi) {
|
|
4159
|
-
const mid = lo + hi + 1 >> 1;
|
|
4160
|
-
if (segments.columns[mid] <= column) lo = mid;
|
|
4161
|
-
else hi = mid - 1;
|
|
4162
|
-
}
|
|
4163
|
-
const source = segments.sources[lo];
|
|
4164
|
-
return source < 0 ? null : source;
|
|
4165
|
-
},
|
|
4166
|
-
mapsBetween(source, start, end) {
|
|
4167
|
-
for (let line = start.line; line <= end.line && line < lines.length; line++) {
|
|
4168
|
-
const segments = lines[line];
|
|
4169
|
-
if (!segments) continue;
|
|
4170
|
-
let i = 0;
|
|
4171
|
-
if (line === start.line) {
|
|
4172
|
-
let hi = segments.columns.length;
|
|
4173
|
-
while (i < hi) {
|
|
4174
|
-
const mid = i + hi >> 1;
|
|
4175
|
-
if (segments.columns[mid] < start.column) i = mid + 1;
|
|
4176
|
-
else hi = mid;
|
|
4177
|
-
}
|
|
4178
|
-
}
|
|
4179
|
-
for (; i < segments.columns.length; i++) {
|
|
4180
|
-
if (line === end.line && segments.columns[i] >= end.column) return false;
|
|
4181
|
-
if (segments.sources[i] === source) return true;
|
|
4182
|
-
}
|
|
4183
|
-
}
|
|
4184
|
-
return false;
|
|
4185
|
-
}
|
|
4186
|
-
};
|
|
4187
|
-
}
|
|
4188
|
-
function lineStarts(text) {
|
|
4189
|
-
const starts = [0];
|
|
4190
|
-
for (let i = 0; i < text.length; i++) if (text.charCodeAt(i) === 10) starts.push(i + 1);
|
|
4191
|
-
return starts;
|
|
4192
|
-
}
|
|
4193
|
-
function offsetToPosition(starts, offset) {
|
|
4194
|
-
let lo = 0;
|
|
4195
|
-
let hi = starts.length - 1;
|
|
4196
|
-
while (lo < hi) {
|
|
4197
|
-
const mid = lo + hi + 1 >> 1;
|
|
4198
|
-
if (starts[mid] <= offset) lo = mid;
|
|
4199
|
-
else hi = mid - 1;
|
|
4446
|
+
try {
|
|
4447
|
+
const pkgPath = path8.resolve(fromDir, offset, "package.json");
|
|
4448
|
+
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
4449
|
+
const { name, version } = pkg;
|
|
4450
|
+
if (name === "@piwitests/reporter" && typeof version === "string")
|
|
4451
|
+
return { version, root: path8.dirname(pkgPath) };
|
|
4452
|
+
} catch {
|
|
4453
|
+
}
|
|
4200
4454
|
}
|
|
4201
|
-
return
|
|
4202
|
-
}
|
|
4203
|
-
function sourceMappingUrl(script) {
|
|
4204
|
-
const tail = script.slice(-4096);
|
|
4205
|
-
const matches = [...tail.matchAll(/(?:\/\/|\/\*)[#@]\s*sourceMappingURL=([^\s*'"]+)/g)];
|
|
4206
|
-
return matches.length ? matches[matches.length - 1][1] : null;
|
|
4455
|
+
return void 0;
|
|
4207
4456
|
}
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4457
|
+
|
|
4458
|
+
// src/internal/capture/quiet-capture.ts
|
|
4459
|
+
var BOXED_PREFIX_EXPORTS = [
|
|
4460
|
+
[
|
|
4461
|
+
"playwright-core/lib/coreBundle",
|
|
4462
|
+
(mod) => mod?.utils?.setBoxedStackPrefixes
|
|
4463
|
+
],
|
|
4464
|
+
["playwright-core/lib/utils", (mod) => mod?.setBoxedStackPrefixes]
|
|
4465
|
+
];
|
|
4466
|
+
var boxing;
|
|
4467
|
+
var resolvedPlaywrightPackageJson;
|
|
4468
|
+
function resolvePlaywrightPackageJson() {
|
|
4469
|
+
if (resolvedPlaywrightPackageJson !== void 0) return resolvedPlaywrightPackageJson;
|
|
4211
4470
|
try {
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
const bytes = new Uint8Array(binary.length);
|
|
4215
|
-
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
4216
|
-
return new TextDecoder().decode(bytes);
|
|
4471
|
+
const testPackageJson = (0, import_node_module.createRequire)(__filename).resolve("@playwright/test/package.json");
|
|
4472
|
+
resolvedPlaywrightPackageJson = (0, import_node_module.createRequire)(testPackageJson).resolve("playwright/package.json");
|
|
4217
4473
|
} catch {
|
|
4218
|
-
|
|
4474
|
+
resolvedPlaywrightPackageJson = null;
|
|
4219
4475
|
}
|
|
4476
|
+
return resolvedPlaywrightPackageJson;
|
|
4220
4477
|
}
|
|
4221
|
-
function
|
|
4222
|
-
let s = source.replace(/[?#].*$/, "");
|
|
4223
|
-
s = s.replace(/^webpack:\/\/[^/]*\//, "").replace(/^webpack:\/\//, "");
|
|
4224
|
-
s = s.replace(/^(?:vite|rollup|turbopack|ng):\/*/, "");
|
|
4225
|
-
s = s.replace(/^\[project\]\//, "");
|
|
4226
|
-
s = s.replace(/^file:\/\//, "");
|
|
4227
|
-
s = s.replace(/^\/@fs\//, "/");
|
|
4228
|
-
s = s.replace(/^\/@id\//, "");
|
|
4478
|
+
function resolveBoxing() {
|
|
4229
4479
|
try {
|
|
4230
|
-
|
|
4480
|
+
const root = findOwnPackageJson(__dirname)?.root;
|
|
4481
|
+
if (!root) return null;
|
|
4482
|
+
const playwrightPackageJson = resolvePlaywrightPackageJson();
|
|
4483
|
+
if (!playwrightPackageJson) return null;
|
|
4484
|
+
const requireFromPlaywright = (0, import_node_module.createRequire)(playwrightPackageJson);
|
|
4485
|
+
for (const [moduleId, pick] of BOXED_PREFIX_EXPORTS) {
|
|
4486
|
+
let set;
|
|
4487
|
+
try {
|
|
4488
|
+
set = pick(requireFromPlaywright(moduleId));
|
|
4489
|
+
} catch {
|
|
4490
|
+
continue;
|
|
4491
|
+
}
|
|
4492
|
+
if (typeof set !== "function") continue;
|
|
4493
|
+
return {
|
|
4494
|
+
set,
|
|
4495
|
+
prefixes: [path9.dirname(playwrightPackageJson), path9.join(root, "dist") + path9.sep]
|
|
4496
|
+
};
|
|
4497
|
+
}
|
|
4231
4498
|
} catch {
|
|
4232
4499
|
}
|
|
4233
|
-
|
|
4234
|
-
if (/^\/[A-Za-z]:\//.test(s)) s = s.slice(1);
|
|
4235
|
-
s = s.replace(/(^|\/)\.\//g, "$1");
|
|
4236
|
-
return s;
|
|
4237
|
-
}
|
|
4238
|
-
|
|
4239
|
-
// ../core/src/code-reach.ts
|
|
4240
|
-
var MAX_CODE_REACH_FILES = 2e3;
|
|
4241
|
-
function executedFunctionRanges(entry) {
|
|
4242
|
-
const length = entry.source?.length ?? Number.POSITIVE_INFINITY;
|
|
4243
|
-
const out = [];
|
|
4244
|
-
entry.functions.forEach((fn, i) => {
|
|
4245
|
-
const whole = fn.ranges[0];
|
|
4246
|
-
if (!whole || whole.count <= 0) return;
|
|
4247
|
-
const topLevel = whole.startOffset === 0 && (whole.endOffset >= length || i === 0 && fn.functionName === "");
|
|
4248
|
-
if (topLevel) return;
|
|
4249
|
-
out.push({ start: whole.startOffset, end: whole.endOffset });
|
|
4250
|
-
});
|
|
4251
|
-
return out;
|
|
4252
|
-
}
|
|
4253
|
-
function executedFunctionOffsets(entry) {
|
|
4254
|
-
return executedFunctionRanges(entry).map((range) => range.start);
|
|
4255
|
-
}
|
|
4256
|
-
function scriptRan(entry) {
|
|
4257
|
-
return executedFunctionOffsets(entry).length > 0;
|
|
4500
|
+
return null;
|
|
4258
4501
|
}
|
|
4259
|
-
function
|
|
4260
|
-
const
|
|
4261
|
-
|
|
4262
|
-
if (!
|
|
4263
|
-
const
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4502
|
+
function boxCaptureFrames(extraPrefixes = []) {
|
|
4503
|
+
const first = boxing === void 0;
|
|
4504
|
+
if (first) boxing = resolveBoxing();
|
|
4505
|
+
if (!boxing) return;
|
|
4506
|
+
const added = extraPrefixes.filter((prefix) => !boxing.prefixes.includes(prefix));
|
|
4507
|
+
if (!first && added.length === 0) return;
|
|
4508
|
+
boxing.prefixes.push(...added);
|
|
4509
|
+
try {
|
|
4510
|
+
boxing.set([...boxing.prefixes]);
|
|
4511
|
+
} catch {
|
|
4269
4512
|
}
|
|
4270
|
-
return reached;
|
|
4271
4513
|
}
|
|
4272
|
-
function
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
return
|
|
4514
|
+
function callMethod(target, prop, args) {
|
|
4515
|
+
return target[prop](...args);
|
|
4516
|
+
}
|
|
4517
|
+
function internalCall(owner, run) {
|
|
4518
|
+
const wrap = owner?._wrapApiCall;
|
|
4519
|
+
if (typeof wrap !== "function") return run();
|
|
4520
|
+
return wrap.call(owner, run, { internal: true, title: "Piwi capture" });
|
|
4279
4521
|
}
|
|
4280
4522
|
|
|
4281
4523
|
// src/internal/capture/code-reach.ts
|
|
4524
|
+
var crypto2 = __toESM(require("crypto"));
|
|
4525
|
+
var fs6 = __toESM(require("fs"));
|
|
4526
|
+
var path10 = __toESM(require("path"));
|
|
4527
|
+
var import_node_child_process = require("child_process");
|
|
4282
4528
|
var MAX_MAP_BYTES = 20 * 1024 * 1024;
|
|
4283
4529
|
var MAP_TIMEOUT_MS = 5e3;
|
|
4284
4530
|
var SOURCE_EXTENSIONS = /\.(?:[cm]?[jt]sx?|vue|svelte|astro|marko|html?)$/i;
|
|
4285
4531
|
var REPLAYED_SCRIPT = /^(?:about|rsc):/i;
|
|
4532
|
+
var NEVER_REACHED = /\/node_modules\/|\/@vite\//;
|
|
4286
4533
|
var BUILD_DIRS = /(?:^|\/)(?:dist|build|out|\.output|\.next|\.nuxt|\.svelte-kit|public|static|wwwroot)\//;
|
|
4287
|
-
var MAX_CACHED_SCRIPTS =
|
|
4534
|
+
var MAX_CACHED_SCRIPTS = 4e3;
|
|
4288
4535
|
var LatestByUrl = class {
|
|
4289
4536
|
constructor() {
|
|
4290
4537
|
this.entries = /* @__PURE__ */ new Map();
|
|
@@ -4324,8 +4571,32 @@ function repositoryRoot(from) {
|
|
|
4324
4571
|
}
|
|
4325
4572
|
function codeReachRoots(configDir, configured) {
|
|
4326
4573
|
const repoRoot = repositoryRoot(configDir);
|
|
4327
|
-
const
|
|
4328
|
-
|
|
4574
|
+
const resolve3 = (dirs) => (Array.isArray(dirs) ? dirs : dirs ? [dirs] : []).filter(Boolean).map((r) => path10.resolve(configDir, r));
|
|
4575
|
+
const global = Array.isArray(configured) ? resolve3(configured) : resolve3(configured?.["*"]);
|
|
4576
|
+
const byOrigin = /* @__PURE__ */ new Map();
|
|
4577
|
+
if (configured && !Array.isArray(configured)) {
|
|
4578
|
+
for (const [key, dirs] of Object.entries(configured)) {
|
|
4579
|
+
const origin = key === "*" ? null : originOf2(key);
|
|
4580
|
+
const resolved = resolve3(dirs);
|
|
4581
|
+
if (origin && resolved.length) byOrigin.set(origin, resolved);
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4584
|
+
const roots = global.length ? global : [configDir];
|
|
4585
|
+
return { roots: [.../* @__PURE__ */ new Set([...roots, repoRoot])], byOrigin, repoRoot };
|
|
4586
|
+
}
|
|
4587
|
+
function originOf2(url) {
|
|
4588
|
+
if (!url) return null;
|
|
4589
|
+
try {
|
|
4590
|
+
const origin = new URL(url).origin;
|
|
4591
|
+
return origin === "null" ? null : origin;
|
|
4592
|
+
} catch {
|
|
4593
|
+
return null;
|
|
4594
|
+
}
|
|
4595
|
+
}
|
|
4596
|
+
function scriptRoots(roots, scriptUrl, pageUrl) {
|
|
4597
|
+
if (!roots.byOrigin?.size) return roots.roots;
|
|
4598
|
+
const own = roots.byOrigin.get(originOf2(scriptUrl) ?? "") ?? roots.byOrigin.get(originOf2(pageUrl) ?? "");
|
|
4599
|
+
return own ? [.../* @__PURE__ */ new Set([...own, ...roots.roots])] : roots.roots;
|
|
4329
4600
|
}
|
|
4330
4601
|
function isFile(file) {
|
|
4331
4602
|
try {
|
|
@@ -4409,50 +4680,96 @@ async function resolveScript(entry, roots, bases, fetchMap) {
|
|
|
4409
4680
|
const version = scriptVersion(entry);
|
|
4410
4681
|
const cached = scriptCache.get(entry.url, version);
|
|
4411
4682
|
if (cached) return cached;
|
|
4412
|
-
const file = fileForUrl(entry.url, roots
|
|
4683
|
+
const file = fileForUrl(entry.url, roots, bases);
|
|
4413
4684
|
const lookup = await loadMap(entry, fetchMap);
|
|
4414
4685
|
let resolution;
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4686
|
+
const text = entry.source === void 0 ? void 0 : scriptText(entry.source);
|
|
4687
|
+
if (file) resolution = { kind: "file", file, lookup, text };
|
|
4688
|
+
else resolution = lookup ? { kind: "map", lookup, files: [], text } : { kind: "none" };
|
|
4689
|
+
if (text) scriptCache.set(entry.url, version, resolution);
|
|
4418
4690
|
return resolution;
|
|
4419
4691
|
}
|
|
4420
|
-
function
|
|
4421
|
-
|
|
4422
|
-
|
|
4692
|
+
function textOf2(entry, resolution) {
|
|
4693
|
+
return resolution.text ?? (entry.source === void 0 ? void 0 : scriptText(entry.source));
|
|
4694
|
+
}
|
|
4695
|
+
async function resolveCodeReach(entries, roots, fetchMap, pageUrl) {
|
|
4696
|
+
return finalizeCodeReach((await resolveReach(entries, roots, fetchMap, pageUrl)).keys());
|
|
4423
4697
|
}
|
|
4424
|
-
async function
|
|
4425
|
-
|
|
4698
|
+
async function resolveCodeReachFunctions(entries, roots, fetchMap, pageUrl, options = {}) {
|
|
4699
|
+
return resolveFunctions(entries, roots, fetchMap, pageUrl, options.known === true);
|
|
4700
|
+
}
|
|
4701
|
+
async function resolveReach(entries, roots, fetchMap, pageUrl) {
|
|
4702
|
+
return (await resolveFunctions(entries, roots, fetchMap, pageUrl, false)).reached;
|
|
4703
|
+
}
|
|
4704
|
+
var knownCache = new LatestByUrl();
|
|
4705
|
+
async function resolveFunctions(entries, roots, fetchMap, pageUrl, withKnown) {
|
|
4706
|
+
const files = /* @__PURE__ */ new Map();
|
|
4707
|
+
const known = /* @__PURE__ */ new Map();
|
|
4708
|
+
const addTo = (into, file, functions) => {
|
|
4709
|
+
const path14 = relativeToRepo(file, roots.repoRoot);
|
|
4710
|
+
const listed = into.get(path14);
|
|
4711
|
+
if (listed) listed.push(...functions);
|
|
4712
|
+
else into.set(path14, [...functions]);
|
|
4713
|
+
};
|
|
4714
|
+
const add = (file, functions) => addTo(files, file, functions);
|
|
4426
4715
|
const bases = viteBases(entries);
|
|
4427
4716
|
const scripts = entries.filter((entry) => entry.url && entry.functions?.length && !REPLAYED_SCRIPT.test(entry.url));
|
|
4428
|
-
const
|
|
4717
|
+
const cached = scripts.map((entry) => scriptCache.get(entry.url, scriptVersion(entry)));
|
|
4429
4718
|
for (const [i, covered] of scripts.entries()) {
|
|
4430
|
-
const
|
|
4719
|
+
const dirs = scriptRoots(roots, covered.url, covered.pageUrl ?? pageUrl);
|
|
4720
|
+
const resolution = cached[i] ?? await resolveScript(covered, dirs, bases, fetchMap);
|
|
4721
|
+
const mapFile = (resolved, index) => {
|
|
4722
|
+
if (resolved.files[index] === void 0) {
|
|
4723
|
+
const source = resolved.lookup.sources[index] ?? "";
|
|
4724
|
+
let absolute = resolveSourcePath(source, dirs);
|
|
4725
|
+
if (!absolute && !/^[a-z][\w+.-]*:/i.test(source)) {
|
|
4726
|
+
try {
|
|
4727
|
+
absolute = fileForUrl(new URL(source, covered.url).href, dirs, bases);
|
|
4728
|
+
} catch {
|
|
4729
|
+
absolute = null;
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
resolved.files[index] = absolute;
|
|
4733
|
+
}
|
|
4734
|
+
return resolved.files[index] ?? null;
|
|
4735
|
+
};
|
|
4431
4736
|
if (resolution.kind === "file") {
|
|
4432
|
-
|
|
4433
|
-
const
|
|
4434
|
-
if (resolution.
|
|
4737
|
+
if (!resolution.file) continue;
|
|
4738
|
+
const text = textOf2(covered, resolution);
|
|
4739
|
+
if (resolution.lookup) {
|
|
4740
|
+
const reached = reachedFunctionLines(covered, resolution.lookup, text);
|
|
4741
|
+
if (reached.size > 0) add(resolution.file, [...reached.values()].flat());
|
|
4742
|
+
} else if (scriptRan(covered, text)) {
|
|
4743
|
+
add(resolution.file, reachedFunctionLines(covered, null, text).get(-1) ?? []);
|
|
4744
|
+
}
|
|
4435
4745
|
} else if (resolution.kind === "map") {
|
|
4436
|
-
const
|
|
4437
|
-
|
|
4438
|
-
if (
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4746
|
+
for (const [index, functions] of reachedFunctionLines(covered, resolution.lookup, textOf2(covered, resolution))) {
|
|
4747
|
+
const file = mapFile(resolution, index);
|
|
4748
|
+
if (file) add(file, functions);
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
if (!withKnown || resolution.kind === "none") continue;
|
|
4752
|
+
const version = scriptVersion(covered);
|
|
4753
|
+
let listed = knownCache.get(covered.url, version);
|
|
4754
|
+
if (!listed || listed.count < covered.functions.length) {
|
|
4755
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
4756
|
+
const text = textOf2(covered, resolution);
|
|
4757
|
+
if (resolution.kind === "file" && resolution.file) {
|
|
4758
|
+
const lines = knownFunctionLines(covered, resolution.lookup, text);
|
|
4759
|
+
const all = resolution.lookup ? [...lines.values()].flat() : lines.get(-1) ?? [];
|
|
4760
|
+
if (all.length > 0) byFile.set(resolution.file, all);
|
|
4761
|
+
} else if (resolution.kind === "map") {
|
|
4762
|
+
for (const [index, functions] of knownFunctionLines(covered, resolution.lookup, text)) {
|
|
4763
|
+
const file = mapFile(resolution, index);
|
|
4764
|
+
if (file) byFile.set(file, [...byFile.get(file) ?? [], ...functions]);
|
|
4449
4765
|
}
|
|
4450
|
-
const file = resolution.files[index];
|
|
4451
|
-
if (file) files.add(relativeToRepo(file, roots.repoRoot));
|
|
4452
4766
|
}
|
|
4767
|
+
listed = { count: covered.functions.length, files: byFile };
|
|
4768
|
+
knownCache.set(covered.url, version, listed);
|
|
4453
4769
|
}
|
|
4770
|
+
for (const [file, functions] of listed.files) addTo(known, file, functions);
|
|
4454
4771
|
}
|
|
4455
|
-
return
|
|
4772
|
+
return { reached: files, known };
|
|
4456
4773
|
}
|
|
4457
4774
|
function pageMapFetcher(page) {
|
|
4458
4775
|
return async (url) => {
|
|
@@ -4476,12 +4793,57 @@ function isChromiumPage(page) {
|
|
|
4476
4793
|
}
|
|
4477
4794
|
}
|
|
4478
4795
|
var captures = /* @__PURE__ */ new WeakMap();
|
|
4796
|
+
var MARK_WORLD = "piwi-code-reach";
|
|
4797
|
+
var MARK_FUNCTION = "piwiCodeReachMark";
|
|
4798
|
+
var MARK_SOURCE = `addEventListener('beforeunload', function ${MARK_FUNCTION}() {});`;
|
|
4799
|
+
var markedDocuments = /* @__PURE__ */ new WeakMap();
|
|
4800
|
+
function marksOf(page) {
|
|
4801
|
+
let marks = markedDocuments.get(page);
|
|
4802
|
+
if (!marks) {
|
|
4803
|
+
marks = /* @__PURE__ */ new Set();
|
|
4804
|
+
markedDocuments.set(page, marks);
|
|
4805
|
+
}
|
|
4806
|
+
return marks;
|
|
4807
|
+
}
|
|
4808
|
+
async function markCurrentDocument(page, cdp) {
|
|
4809
|
+
try {
|
|
4810
|
+
const { frameTree } = await internalCall(page, () => cdp.send("Page.getFrameTree"));
|
|
4811
|
+
const marks = marksOf(page);
|
|
4812
|
+
if (marks.has(frameTree.frame.loaderId)) return;
|
|
4813
|
+
const world = await internalCall(
|
|
4814
|
+
page,
|
|
4815
|
+
() => cdp.send("Page.createIsolatedWorld", { frameId: frameTree.frame.id, worldName: MARK_WORLD })
|
|
4816
|
+
);
|
|
4817
|
+
await internalCall(
|
|
4818
|
+
page,
|
|
4819
|
+
() => cdp.send("Runtime.evaluate", { expression: MARK_SOURCE, contextId: world.executionContextId })
|
|
4820
|
+
);
|
|
4821
|
+
marks.add(frameTree.frame.loaderId);
|
|
4822
|
+
} catch {
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4825
|
+
function onPaused(page, capture, event) {
|
|
4826
|
+
const leaving = event.reason === "EventListener" && event.data?.eventName === "listener:beforeunload" && (event.callFrames?.[0]?.functionName ?? MARK_FUNCTION) === MARK_FUNCTION;
|
|
4827
|
+
const read = leaving ? readCoverage(page, capture.cdp) : null;
|
|
4828
|
+
if (read) {
|
|
4829
|
+
const merging = read.then(
|
|
4830
|
+
(coverage) => mergeCoverageRead(capture.ran, capture.scripts, coverage, capture.keepIdle)
|
|
4831
|
+
);
|
|
4832
|
+
capture.reading.add(merging);
|
|
4833
|
+
void merging.finally(() => capture.reading.delete(merging));
|
|
4834
|
+
}
|
|
4835
|
+
void (read ?? Promise.resolve()).finally(() => {
|
|
4836
|
+
internalCall(page, () => capture.cdp.send("Debugger.resume")).catch(() => {
|
|
4837
|
+
});
|
|
4838
|
+
});
|
|
4839
|
+
}
|
|
4479
4840
|
function versionKey(url, hash) {
|
|
4480
4841
|
return `${hash} ${url}`;
|
|
4481
4842
|
}
|
|
4482
4843
|
function onScriptParsed(page, capture, event) {
|
|
4483
4844
|
if (!event.url || REPLAYED_SCRIPT.test(event.url)) return;
|
|
4484
|
-
capture.scripts.set(event.scriptId, { url: event.url, hash: event.hash });
|
|
4845
|
+
capture.scripts.set(event.scriptId, { url: event.url, hash: event.hash, pageUrl: capture.pageUrl });
|
|
4846
|
+
if (NEVER_REACHED.test(event.url)) return;
|
|
4485
4847
|
const key = versionKey(event.url, event.hash);
|
|
4486
4848
|
if (capture.sources.has(key) || scriptCache.get(event.url, event.hash)) return;
|
|
4487
4849
|
const source = internalCall(
|
|
@@ -4496,6 +4858,44 @@ function onScriptParsed(page, capture, event) {
|
|
|
4496
4858
|
);
|
|
4497
4859
|
capture.sources.set(key, source);
|
|
4498
4860
|
}
|
|
4861
|
+
var READ_WAIT_MS = 5e3;
|
|
4862
|
+
function readCoverage(page, cdp) {
|
|
4863
|
+
let timer;
|
|
4864
|
+
const late = new Promise((resolve3) => {
|
|
4865
|
+
timer = setTimeout(() => resolve3([]), READ_WAIT_MS);
|
|
4866
|
+
});
|
|
4867
|
+
const taken = internalCall(page, () => cdp.send("Profiler.takePreciseCoverage")).then(
|
|
4868
|
+
(answer) => answer.result,
|
|
4869
|
+
() => []
|
|
4870
|
+
);
|
|
4871
|
+
return Promise.race([taken, late]).finally(() => clearTimeout(timer));
|
|
4872
|
+
}
|
|
4873
|
+
function mergeCoverageRead(ran, scripts, read, keepIdle = true) {
|
|
4874
|
+
for (const covered of read) {
|
|
4875
|
+
const script = scripts.get(covered.scriptId);
|
|
4876
|
+
if (!script) continue;
|
|
4877
|
+
const key = versionKey(script.url, script.hash);
|
|
4878
|
+
let known = ran.get(key);
|
|
4879
|
+
if (!known) {
|
|
4880
|
+
known = { script, functions: [], byRange: /* @__PURE__ */ new Map() };
|
|
4881
|
+
ran.set(key, known);
|
|
4882
|
+
}
|
|
4883
|
+
for (const fn of covered.functions) {
|
|
4884
|
+
const whole = fn.ranges[0];
|
|
4885
|
+
if (!whole) continue;
|
|
4886
|
+
if (!keepIdle && whole.count <= 0) continue;
|
|
4887
|
+
const range = `${whole.startOffset}:${whole.endOffset}`;
|
|
4888
|
+
const seen = known.byRange.get(range);
|
|
4889
|
+
if (seen) {
|
|
4890
|
+
if (whole.count > 0 && seen.ranges[0].count <= 0) seen.ranges[0].count = whole.count;
|
|
4891
|
+
continue;
|
|
4892
|
+
}
|
|
4893
|
+
const copy = { ...fn, ranges: fn.ranges.map((r) => ({ ...r })) };
|
|
4894
|
+
known.byRange.set(range, copy);
|
|
4895
|
+
known.functions.push(copy);
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
}
|
|
4499
4899
|
async function detach(page, cdp) {
|
|
4500
4900
|
try {
|
|
4501
4901
|
await internalCall(page, () => cdp.detach());
|
|
@@ -4507,21 +4907,35 @@ async function startCodeReach(page) {
|
|
|
4507
4907
|
let cdp = null;
|
|
4508
4908
|
try {
|
|
4509
4909
|
cdp = await internalCall(page, () => page.context().newCDPSession(page));
|
|
4510
|
-
const capture = {
|
|
4910
|
+
const capture = {
|
|
4911
|
+
cdp,
|
|
4912
|
+
scripts: /* @__PURE__ */ new Map(),
|
|
4913
|
+
sources: /* @__PURE__ */ new Map(),
|
|
4914
|
+
ran: /* @__PURE__ */ new Map(),
|
|
4915
|
+
keepIdle: browserFunctionsInWorker(),
|
|
4916
|
+
reading: /* @__PURE__ */ new Set(),
|
|
4917
|
+
pageUrl: page.url()
|
|
4918
|
+
};
|
|
4919
|
+
const marks = marksOf(page);
|
|
4511
4920
|
cdp.on("Debugger.scriptParsed", (event) => onScriptParsed(page, capture, event));
|
|
4512
|
-
cdp.on("Debugger.paused", () =>
|
|
4513
|
-
|
|
4514
|
-
|
|
4921
|
+
cdp.on("Debugger.paused", (event) => onPaused(page, capture, event));
|
|
4922
|
+
cdp.on("Page.frameNavigated", (event) => {
|
|
4923
|
+
if (event.frame.parentId) return;
|
|
4924
|
+
marks.add(event.frame.loaderId);
|
|
4925
|
+
capture.pageUrl = event.frame.url;
|
|
4515
4926
|
});
|
|
4516
4927
|
await internalCall(
|
|
4517
4928
|
page,
|
|
4518
4929
|
() => Promise.all([
|
|
4519
4930
|
capture.cdp.send("Debugger.enable"),
|
|
4520
|
-
capture.cdp.send("Debugger.setSkipAllPauses", { skip: true }),
|
|
4521
4931
|
capture.cdp.send("Profiler.enable"),
|
|
4522
|
-
capture.cdp.send("Profiler.startPreciseCoverage", { callCount: false, detailed: false })
|
|
4932
|
+
capture.cdp.send("Profiler.startPreciseCoverage", { callCount: false, detailed: false }),
|
|
4933
|
+
capture.cdp.send("Page.enable"),
|
|
4934
|
+
capture.cdp.send("Page.addScriptToEvaluateOnNewDocument", { source: MARK_SOURCE, worldName: MARK_WORLD }),
|
|
4935
|
+
capture.cdp.send("DOMDebugger.setEventListenerBreakpoint", { eventName: "beforeunload" })
|
|
4523
4936
|
])
|
|
4524
4937
|
);
|
|
4938
|
+
await markCurrentDocument(page, capture.cdp);
|
|
4525
4939
|
captures.set(page, capture);
|
|
4526
4940
|
return true;
|
|
4527
4941
|
} catch {
|
|
@@ -4535,17 +4949,17 @@ async function stopCodeReach(page) {
|
|
|
4535
4949
|
captures.delete(page);
|
|
4536
4950
|
const { cdp } = capture;
|
|
4537
4951
|
try {
|
|
4538
|
-
const
|
|
4952
|
+
const last = await internalCall(page, async () => {
|
|
4539
4953
|
const taken = await cdp.send("Profiler.takePreciseCoverage");
|
|
4540
4954
|
await cdp.send("Profiler.stopPreciseCoverage");
|
|
4541
|
-
return taken;
|
|
4955
|
+
return taken.result;
|
|
4542
4956
|
});
|
|
4957
|
+
await Promise.all(capture.reading);
|
|
4958
|
+
mergeCoverageRead(capture.ran, capture.scripts, last, capture.keepIdle);
|
|
4543
4959
|
const entries = [];
|
|
4544
|
-
for (const script of
|
|
4545
|
-
const
|
|
4546
|
-
|
|
4547
|
-
const entry = { url: parsed2.url, hash: parsed2.hash, functions: script.functions };
|
|
4548
|
-
const source = await capture.sources.get(versionKey(parsed2.url, parsed2.hash));
|
|
4960
|
+
for (const [key, { script, functions }] of capture.ran) {
|
|
4961
|
+
const entry = { url: script.url, hash: script.hash, functions, pageUrl: script.pageUrl };
|
|
4962
|
+
const source = await capture.sources.get(key);
|
|
4549
4963
|
if (source != null) entry.source = source;
|
|
4550
4964
|
entries.push(entry);
|
|
4551
4965
|
}
|
|
@@ -5522,6 +5936,143 @@ function readPageInteraction() {
|
|
|
5522
5936
|
return out;
|
|
5523
5937
|
}
|
|
5524
5938
|
|
|
5939
|
+
// src/internal/capture/api-requests.ts
|
|
5940
|
+
var SENDING_METHODS = /* @__PURE__ */ new Set(["fetch", "get", "post", "put", "patch", "delete", "head"]);
|
|
5941
|
+
var PROXIES = /* @__PURE__ */ new WeakMap();
|
|
5942
|
+
var OBSERVED = /* @__PURE__ */ new WeakSet();
|
|
5943
|
+
var PATCHED_REQUESTS = /* @__PURE__ */ new WeakSet();
|
|
5944
|
+
function observeApiRequests(playwright, recorder) {
|
|
5945
|
+
const apiRequest = playwright?.request;
|
|
5946
|
+
if (!apiRequest || typeof apiRequest !== "object" || typeof apiRequest.newContext !== "function") return;
|
|
5947
|
+
if (PATCHED_REQUESTS.has(apiRequest)) return;
|
|
5948
|
+
PATCHED_REQUESTS.add(apiRequest);
|
|
5949
|
+
const create = apiRequest.newContext.bind(apiRequest);
|
|
5950
|
+
apiRequest.newContext = async function newContext(...args) {
|
|
5951
|
+
const context = await create(...args);
|
|
5952
|
+
return observeApiRequestContext(context, recorder, baseUrlOption(args[0]));
|
|
5953
|
+
};
|
|
5954
|
+
}
|
|
5955
|
+
function observeApiRequestContext(context, recorder, baseURL = null) {
|
|
5956
|
+
if (!context || typeof context !== "object" || OBSERVED.has(context)) return context;
|
|
5957
|
+
const known = PROXIES.get(context);
|
|
5958
|
+
if (known) return known;
|
|
5959
|
+
const forwarders = /* @__PURE__ */ new Map();
|
|
5960
|
+
const proxy = new Proxy(context, {
|
|
5961
|
+
get(target, prop) {
|
|
5962
|
+
const value = Reflect.get(target, prop);
|
|
5963
|
+
if (typeof value !== "function" || prop === "constructor") return value;
|
|
5964
|
+
const cached = forwarders.get(prop);
|
|
5965
|
+
if (cached?.method === value) return cached.forward;
|
|
5966
|
+
const forward = typeof prop === "string" && SENDING_METHODS.has(prop) ? sendingMethod(target, prop, recorder, baseURL) : value.bind(target);
|
|
5967
|
+
forwarders.set(prop, { method: value, forward });
|
|
5968
|
+
return forward;
|
|
5969
|
+
}
|
|
5970
|
+
});
|
|
5971
|
+
PROXIES.set(context, proxy);
|
|
5972
|
+
OBSERVED.add(proxy);
|
|
5973
|
+
return proxy;
|
|
5974
|
+
}
|
|
5975
|
+
function sendingMethod(target, prop, recorder, baseURL) {
|
|
5976
|
+
return function send(...args) {
|
|
5977
|
+
const record = recorder();
|
|
5978
|
+
if (!record) return callMethod(target, prop, args);
|
|
5979
|
+
const startedAt = Date.now();
|
|
5980
|
+
const result = callMethod(target, prop, args);
|
|
5981
|
+
if (typeof result?.then !== "function") return result;
|
|
5982
|
+
const report = (response, error) => {
|
|
5983
|
+
try {
|
|
5984
|
+
record({
|
|
5985
|
+
method: methodOf(prop, args),
|
|
5986
|
+
url: urlOf(args[0]),
|
|
5987
|
+
baseURL,
|
|
5988
|
+
startedAt,
|
|
5989
|
+
endedAt: Date.now(),
|
|
5990
|
+
response,
|
|
5991
|
+
error
|
|
5992
|
+
});
|
|
5993
|
+
} catch {
|
|
5994
|
+
}
|
|
5995
|
+
};
|
|
5996
|
+
return result.then(
|
|
5997
|
+
(response) => {
|
|
5998
|
+
report(response);
|
|
5999
|
+
return response;
|
|
6000
|
+
},
|
|
6001
|
+
(error) => {
|
|
6002
|
+
report(null, error);
|
|
6003
|
+
throw error;
|
|
6004
|
+
}
|
|
6005
|
+
);
|
|
6006
|
+
};
|
|
6007
|
+
}
|
|
6008
|
+
function methodOf(prop, args) {
|
|
6009
|
+
if (prop !== "fetch") return prop.toUpperCase();
|
|
6010
|
+
const option = args[1]?.method;
|
|
6011
|
+
if (typeof option === "string" && option) return option.toUpperCase();
|
|
6012
|
+
const request = args[0];
|
|
6013
|
+
const method = typeof request?.method === "function" ? request.method() : null;
|
|
6014
|
+
return typeof method === "string" && method ? method.toUpperCase() : "GET";
|
|
6015
|
+
}
|
|
6016
|
+
function urlOf(target) {
|
|
6017
|
+
if (typeof target === "string") return target;
|
|
6018
|
+
const url = typeof target?.url === "function" ? target.url() : null;
|
|
6019
|
+
return typeof url === "string" ? url : null;
|
|
6020
|
+
}
|
|
6021
|
+
function baseUrlOption(options) {
|
|
6022
|
+
const baseURL = options?.baseURL;
|
|
6023
|
+
return typeof baseURL === "string" && baseURL ? baseURL : null;
|
|
6024
|
+
}
|
|
6025
|
+
var MAX_FAILURE_LENGTH = 300;
|
|
6026
|
+
var API_ERROR_PREFIX = /^apiRequestContext\.\w+: /;
|
|
6027
|
+
var STATUS_FAILURE = /^(\d{3}) /;
|
|
6028
|
+
function apiCallEntry(call, baseURL) {
|
|
6029
|
+
const response = call.response;
|
|
6030
|
+
if (response) {
|
|
6031
|
+
const timing = responseTiming(response) ?? { startTime: call.startedAt, duration: call.endedAt - call.startedAt };
|
|
6032
|
+
return {
|
|
6033
|
+
method: call.method,
|
|
6034
|
+
url: response.url(),
|
|
6035
|
+
status: response.status(),
|
|
6036
|
+
duration: timing.duration,
|
|
6037
|
+
startTime: timing.startTime
|
|
6038
|
+
};
|
|
6039
|
+
}
|
|
6040
|
+
const url = resolveUrl(call.url, call.baseURL ?? baseURL);
|
|
6041
|
+
if (!url) return null;
|
|
6042
|
+
const failure = failureText(call.error);
|
|
6043
|
+
const status = STATUS_FAILURE.exec(failure);
|
|
6044
|
+
return {
|
|
6045
|
+
method: call.method,
|
|
6046
|
+
url,
|
|
6047
|
+
status: status ? Number(status[1]) : 0,
|
|
6048
|
+
duration: Math.max(0, call.endedAt - call.startedAt),
|
|
6049
|
+
startTime: call.startedAt,
|
|
6050
|
+
...status ? {} : { failure }
|
|
6051
|
+
};
|
|
6052
|
+
}
|
|
6053
|
+
function responseTiming(response) {
|
|
6054
|
+
if (typeof response.timing !== "function") return null;
|
|
6055
|
+
const timing = response.timing();
|
|
6056
|
+
if (!(timing.startTime > 0) || !(timing.responseEnd >= 0)) return null;
|
|
6057
|
+
return {
|
|
6058
|
+
startTime: timing.startTime,
|
|
6059
|
+
duration: Math.max(0, Math.round(timing.responseEnd - Math.max(0, timing.requestStart)))
|
|
6060
|
+
};
|
|
6061
|
+
}
|
|
6062
|
+
function resolveUrl(url, baseURL) {
|
|
6063
|
+
if (!url) return null;
|
|
6064
|
+
try {
|
|
6065
|
+
return new URL(url, baseURL ?? void 0).toString();
|
|
6066
|
+
} catch {
|
|
6067
|
+
return url;
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
function failureText(error) {
|
|
6071
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6072
|
+
const line = message.split("\n", 1)[0].replace(API_ERROR_PREFIX, "").trim();
|
|
6073
|
+
return (line || "failed").slice(0, MAX_FAILURE_LENGTH);
|
|
6074
|
+
}
|
|
6075
|
+
|
|
5525
6076
|
// src/internal/capture/capture-fixtures.ts
|
|
5526
6077
|
function computeCoreVitals(lcpEntries, shiftEntries, eventEntries) {
|
|
5527
6078
|
const lastLcp = lcpEntries && lcpEntries.length > 0 ? lcpEntries[lcpEntries.length - 1] : null;
|
|
@@ -5581,51 +6132,89 @@ function createSink() {
|
|
|
5581
6132
|
userPick: null,
|
|
5582
6133
|
pause: null,
|
|
5583
6134
|
locatorPages: new LocatorPageLog(),
|
|
5584
|
-
codeReach:
|
|
5585
|
-
serverCoverage: /* @__PURE__ */ new Map()
|
|
6135
|
+
codeReach: { sessions: /* @__PURE__ */ new Map(), files: /* @__PURE__ */ new Map(), known: /* @__PURE__ */ new Map() },
|
|
6136
|
+
serverCoverage: /* @__PURE__ */ new Map(),
|
|
6137
|
+
baseURL: null
|
|
5586
6138
|
};
|
|
5587
6139
|
}
|
|
5588
|
-
|
|
6140
|
+
var MAX_CODE_REACH_PAGES = 16;
|
|
6141
|
+
var CODE_REACH_START_WAIT_MS = 2e3;
|
|
6142
|
+
function maybeStartCodeReach(page, alreadyOpen = false) {
|
|
5589
6143
|
const sink = currentSink;
|
|
5590
|
-
if (!sink || !browserCoverageInWorker()) return Promise.resolve(false);
|
|
5591
|
-
|
|
6144
|
+
if (!sink || !browserCoverageInWorker() || isPageClosed(page)) return Promise.resolve(false);
|
|
6145
|
+
const known = sink.codeReach.sessions.get(page);
|
|
6146
|
+
if (known) return known.started;
|
|
6147
|
+
if (alreadyOpen) {
|
|
6148
|
+
let recording = 0;
|
|
6149
|
+
for (const [other, session] of sink.codeReach.sessions) if (!session.stopped && !isPageClosed(other)) recording++;
|
|
6150
|
+
if (recording >= MAX_CODE_REACH_PAGES) return Promise.resolve(false);
|
|
6151
|
+
}
|
|
5592
6152
|
const started = startCodeReach(page);
|
|
5593
|
-
sink.codeReach
|
|
6153
|
+
sink.codeReach.sessions.set(page, { started, stopped: false });
|
|
5594
6154
|
return started;
|
|
5595
6155
|
}
|
|
6156
|
+
async function startCodeReachOnOpenPages() {
|
|
6157
|
+
if (!browserCoverageInWorker() || OPEN_PAGES.size === 0) return;
|
|
6158
|
+
const started = Promise.all([...OPEN_PAGES].map((page) => maybeStartCodeReach(page, true)));
|
|
6159
|
+
await Promise.race([started, new Promise((resolve3) => setTimeout(resolve3, CODE_REACH_START_WAIT_MS).unref())]);
|
|
6160
|
+
}
|
|
5596
6161
|
function configuredCodeReachRoots() {
|
|
5597
6162
|
const raw = process.env.PIWI_CODE_REACH_ROOTS?.trim();
|
|
5598
6163
|
if (!raw) return null;
|
|
6164
|
+
const dirs = (value) => (Array.isArray(value) ? value : [value]).filter((r) => typeof r === "string" && r !== "");
|
|
5599
6165
|
try {
|
|
5600
6166
|
const parsed2 = JSON.parse(raw);
|
|
5601
|
-
if (Array.isArray(parsed2)) return parsed2
|
|
6167
|
+
if (Array.isArray(parsed2)) return dirs(parsed2);
|
|
6168
|
+
if (parsed2 && typeof parsed2 === "object") {
|
|
6169
|
+
return Object.fromEntries(Object.entries(parsed2).map(([origin, value]) => [origin, dirs(value)]));
|
|
6170
|
+
}
|
|
5602
6171
|
} catch {
|
|
5603
6172
|
}
|
|
5604
6173
|
return raw.split(path13.delimiter).filter(Boolean);
|
|
5605
6174
|
}
|
|
5606
6175
|
async function stopSinkCodeReach(sink, closing) {
|
|
5607
|
-
const
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
6176
|
+
const stopping = [];
|
|
6177
|
+
const underway = [];
|
|
6178
|
+
for (const [page, session] of sink.codeReach.sessions) {
|
|
6179
|
+
if (session.stopped) {
|
|
6180
|
+
if (!closing && session.stopping) underway.push(session.stopping);
|
|
6181
|
+
continue;
|
|
6182
|
+
}
|
|
6183
|
+
if (closing && closing.page !== page && (!closing.context || pageContext(page) !== closing.context)) continue;
|
|
6184
|
+
session.stopped = true;
|
|
6185
|
+
stopping.push([page, session]);
|
|
5611
6186
|
}
|
|
5612
|
-
|
|
5613
|
-
try {
|
|
5614
|
-
if (!await session.started || isPageClosed(session.page)) return;
|
|
5615
|
-
const entries = await stopCodeReach(session.page);
|
|
5616
|
-
if (!entries) return;
|
|
6187
|
+
if (stopping.length > 0) {
|
|
5617
6188
|
const info = sink.testInfo;
|
|
5618
6189
|
const configFile = info?.config.configFile;
|
|
5619
6190
|
const configDir = configFile ? path13.dirname(configFile) : info?.config.rootDir ?? process.cwd();
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
6191
|
+
const roots = codeReachRoots(configDir, configuredCodeReachRoots());
|
|
6192
|
+
for (const [page, session] of stopping) {
|
|
6193
|
+
session.stopping = stopPageCodeReach(sink, page, session, roots);
|
|
6194
|
+
underway.push(session.stopping);
|
|
6195
|
+
}
|
|
6196
|
+
}
|
|
6197
|
+
await Promise.all(underway);
|
|
6198
|
+
}
|
|
6199
|
+
async function stopPageCodeReach(sink, page, session, roots) {
|
|
6200
|
+
try {
|
|
6201
|
+
if (!await session.started || isPageClosed(page)) return;
|
|
6202
|
+
const entries = await stopCodeReach(page);
|
|
6203
|
+
if (!entries) return;
|
|
6204
|
+
const { reached, known } = await resolveCodeReachFunctions(entries, roots, pageMapFetcher(page), page.url(), {
|
|
6205
|
+
known: browserFunctionsInWorker()
|
|
6206
|
+
});
|
|
6207
|
+
for (const [file, functions] of reached) {
|
|
6208
|
+
sink.codeReach.files.set(file, [...sink.codeReach.files.get(file) ?? [], ...functions]);
|
|
6209
|
+
}
|
|
6210
|
+
for (const [file, functions] of known) {
|
|
6211
|
+
sink.codeReach.known.set(file, [...sink.codeReach.known.get(file) ?? [], ...functions]);
|
|
6212
|
+
}
|
|
5625
6213
|
} catch {
|
|
5626
6214
|
}
|
|
5627
6215
|
}
|
|
5628
6216
|
var currentSink = null;
|
|
6217
|
+
var KNOWN_FUNCTIONS_SENT = /* @__PURE__ */ new Map();
|
|
5629
6218
|
var PENDING_PROBES = /* @__PURE__ */ new Set();
|
|
5630
6219
|
async function drainPendingProbes(capMs) {
|
|
5631
6220
|
if (PENDING_PROBES.size === 0) return;
|
|
@@ -5922,6 +6511,7 @@ async function maybeOpenPicker(sink, closing) {
|
|
|
5922
6511
|
applyPickToSnapshots(sink.capturedLocators, pick);
|
|
5923
6512
|
}
|
|
5924
6513
|
var INSTRUMENTED_PAGES = /* @__PURE__ */ new WeakSet();
|
|
6514
|
+
var OPEN_PAGES = /* @__PURE__ */ new Set();
|
|
5925
6515
|
var INSTRUMENTED_CONTEXTS = /* @__PURE__ */ new WeakSet();
|
|
5926
6516
|
var PATCHED_BROWSERS = /* @__PURE__ */ new WeakSet();
|
|
5927
6517
|
var CHAIN_METHOD_SET = new Set(CHAIN_METHODS);
|
|
@@ -6037,9 +6627,6 @@ function startElementCapture(sink, page, target, seq, callerLocation, used) {
|
|
|
6037
6627
|
})();
|
|
6038
6628
|
sink.capturePromises.push(resolveAttrs);
|
|
6039
6629
|
}
|
|
6040
|
-
function callMethod(target, prop, args) {
|
|
6041
|
-
return target[prop](...args);
|
|
6042
|
-
}
|
|
6043
6630
|
function recordLocatorPage(sink, page, locator, location, arrival) {
|
|
6044
6631
|
try {
|
|
6045
6632
|
sink.locatorPages.record({ location, locator: String(locator), url: page.url(), arrival });
|
|
@@ -6205,7 +6792,12 @@ var CANCELLED_REQUEST = /^net::ERR_ABORTED$|^NS_BINDING_ABORTED$|^NS_ERROR_ABORT
|
|
|
6205
6792
|
function instrumentPage(page) {
|
|
6206
6793
|
if (!page || INSTRUMENTED_PAGES.has(page)) return;
|
|
6207
6794
|
INSTRUMENTED_PAGES.add(page);
|
|
6795
|
+
if (typeof page.once === "function") {
|
|
6796
|
+
OPEN_PAGES.add(page);
|
|
6797
|
+
page.once("close", () => OPEN_PAGES.delete(page));
|
|
6798
|
+
}
|
|
6208
6799
|
void maybeStartCodeReach(page);
|
|
6800
|
+
observeOwnApiRequests(page);
|
|
6209
6801
|
const probeSink = currentSink;
|
|
6210
6802
|
if (probeSink?.probeItem && !probeSink.probeInterception) {
|
|
6211
6803
|
void installProbeInterception(page, probeSink.probeItem).then((interception) => {
|
|
@@ -6365,25 +6957,7 @@ function recordRequest(sink, request, end, response) {
|
|
|
6365
6957
|
if (timing.requestStart < 0 && typeof response.serverAddr === "function") {
|
|
6366
6958
|
if (await response.serverAddr().catch(() => void 0) === null) entry.fulfilled = true;
|
|
6367
6959
|
}
|
|
6368
|
-
|
|
6369
|
-
const contentType = headers["content-type"];
|
|
6370
|
-
if (contentType) entry.contentType = contentType.split(";")[0].trim();
|
|
6371
|
-
const logHeader = headers["x-piwi-logs"];
|
|
6372
|
-
if (logHeader) {
|
|
6373
|
-
try {
|
|
6374
|
-
entry.serverLogs = JSON.parse((0, import_node_zlib2.gunzipSync)(Buffer.from(logHeader, "base64")).toString("utf-8"));
|
|
6375
|
-
} catch {
|
|
6376
|
-
}
|
|
6377
|
-
}
|
|
6378
|
-
const coverageHeader = headers["x-piwi-coverage"];
|
|
6379
|
-
if (coverageHeader && serverCoverageInWorker()) recordServerCoverageIds(sink, url, coverageHeader);
|
|
6380
|
-
const traceHeader = headers["x-piwi-trace"];
|
|
6381
|
-
if (traceHeader && process.env.PIWI_CAPTURE_SERVER_TRACES !== "false") {
|
|
6382
|
-
try {
|
|
6383
|
-
entry.serverTraces = JSON.parse((0, import_node_zlib2.gunzipSync)(Buffer.from(traceHeader, "base64")).toString("utf-8"));
|
|
6384
|
-
} catch {
|
|
6385
|
-
}
|
|
6386
|
-
}
|
|
6960
|
+
readResponseHeaders(sink, entry, url, response.headers());
|
|
6387
6961
|
}
|
|
6388
6962
|
sink.networkRequests.push(entry);
|
|
6389
6963
|
} catch {
|
|
@@ -6391,6 +6965,48 @@ function recordRequest(sink, request, end, response) {
|
|
|
6391
6965
|
})();
|
|
6392
6966
|
sink.pendingHandlers.push(p);
|
|
6393
6967
|
}
|
|
6968
|
+
function readResponseHeaders(sink, entry, url, headers) {
|
|
6969
|
+
const contentType = headers["content-type"];
|
|
6970
|
+
if (contentType) entry.contentType = contentType.split(";")[0].trim();
|
|
6971
|
+
const logHeader = headers["x-piwi-logs"];
|
|
6972
|
+
if (logHeader) {
|
|
6973
|
+
try {
|
|
6974
|
+
entry.serverLogs = JSON.parse((0, import_node_zlib2.gunzipSync)(Buffer.from(logHeader, "base64")).toString("utf-8"));
|
|
6975
|
+
} catch {
|
|
6976
|
+
}
|
|
6977
|
+
}
|
|
6978
|
+
const coverageHeader = headers["x-piwi-coverage"];
|
|
6979
|
+
if (coverageHeader && serverCoverageInWorker()) recordServerCoverageIds(sink, url, coverageHeader);
|
|
6980
|
+
const traceHeader = headers["x-piwi-trace"];
|
|
6981
|
+
if (traceHeader && process.env.PIWI_CAPTURE_SERVER_TRACES !== "false") {
|
|
6982
|
+
try {
|
|
6983
|
+
entry.serverTraces = JSON.parse((0, import_node_zlib2.gunzipSync)(Buffer.from(traceHeader, "base64")).toString("utf-8"));
|
|
6984
|
+
} catch {
|
|
6985
|
+
}
|
|
6986
|
+
}
|
|
6987
|
+
}
|
|
6988
|
+
function recordApiCall(sink, call) {
|
|
6989
|
+
try {
|
|
6990
|
+
const entry = apiCallEntry(call, sink.baseURL);
|
|
6991
|
+
if (!entry) return;
|
|
6992
|
+
if (call.response) readResponseHeaders(sink, entry, String(entry.url), call.response.headers());
|
|
6993
|
+
sink.networkRequests.push(entry);
|
|
6994
|
+
} catch {
|
|
6995
|
+
}
|
|
6996
|
+
}
|
|
6997
|
+
function apiCallRecorder() {
|
|
6998
|
+
const sink = currentSink;
|
|
6999
|
+
return sink ? (call) => recordApiCall(sink, call) : null;
|
|
7000
|
+
}
|
|
7001
|
+
function observeOwnApiRequests(owner) {
|
|
7002
|
+
try {
|
|
7003
|
+
const holder = owner;
|
|
7004
|
+
if (holder.request && typeof holder.request === "object") {
|
|
7005
|
+
holder.request = observeApiRequestContext(holder.request, apiCallRecorder);
|
|
7006
|
+
}
|
|
7007
|
+
} catch {
|
|
7008
|
+
}
|
|
7009
|
+
}
|
|
6394
7010
|
function recordOpenRequests(sink) {
|
|
6395
7011
|
for (const [request, response] of sink.openResponses) {
|
|
6396
7012
|
RECORDED_OPEN.add(request);
|
|
@@ -6404,6 +7020,7 @@ async function awaitFlakeConditions() {
|
|
|
6404
7020
|
function instrumentContext(context) {
|
|
6405
7021
|
if (!context || INSTRUMENTED_CONTEXTS.has(context)) return;
|
|
6406
7022
|
INSTRUMENTED_CONTEXTS.add(context);
|
|
7023
|
+
observeOwnApiRequests(context);
|
|
6407
7024
|
if (process.env.PIWI_CAPTURE_LOCATORS !== "false" && typeof context.addInitScript === "function") {
|
|
6408
7025
|
void Promise.resolve(context.addInitScript({ content: PROBE_INIT_SCRIPT })).catch(() => {
|
|
6409
7026
|
});
|
|
@@ -6515,10 +7132,24 @@ async function flushSink(sink, testInfo) {
|
|
|
6515
7132
|
});
|
|
6516
7133
|
}
|
|
6517
7134
|
await stopSinkCodeReach(sink);
|
|
6518
|
-
if (sink.codeReach
|
|
7135
|
+
if (sink.codeReach.files.size > 0) {
|
|
6519
7136
|
await testInfo.attach(ATTACHMENT_NAMES.codeReach, {
|
|
6520
7137
|
contentType: "application/json",
|
|
6521
|
-
body: Buffer.from(JSON.stringify(sink.codeReach.files))
|
|
7138
|
+
body: Buffer.from(JSON.stringify(finalizeCodeReach(sink.codeReach.files.keys())))
|
|
7139
|
+
});
|
|
7140
|
+
const functions = browserFunctionsInWorker() ? browserFunctionCoverage(sink.codeReach.files) : null;
|
|
7141
|
+
if (functions) {
|
|
7142
|
+
await testInfo.attach(ATTACHMENT_NAMES.browserCoverage, {
|
|
7143
|
+
contentType: "application/json",
|
|
7144
|
+
body: Buffer.from(JSON.stringify(functions))
|
|
7145
|
+
});
|
|
7146
|
+
}
|
|
7147
|
+
}
|
|
7148
|
+
const known = browserFunctionsInWorker() ? newKnownFunctions(sink.codeReach.known, KNOWN_FUNCTIONS_SENT) : [];
|
|
7149
|
+
for (const part of known) {
|
|
7150
|
+
await testInfo.attach(ATTACHMENT_NAMES.browserFunctions, {
|
|
7151
|
+
contentType: "application/json",
|
|
7152
|
+
body: Buffer.from(JSON.stringify(part))
|
|
6522
7153
|
});
|
|
6523
7154
|
}
|
|
6524
7155
|
if (sink.serverCoverage.size > 0) {
|
|
@@ -6658,14 +7289,17 @@ async function flushSink(sink, testInfo) {
|
|
|
6658
7289
|
}
|
|
6659
7290
|
}
|
|
6660
7291
|
var piwiFixtures = {
|
|
6661
|
-
// Auto, worker-scoped: the worker's resource ledger
|
|
6662
|
-
//
|
|
6663
|
-
//
|
|
6664
|
-
//
|
|
7292
|
+
// Auto, worker-scoped: the worker's resource ledger, and the API request
|
|
7293
|
+
// contexts its tests create, handed out behind the call proxy. Set up before
|
|
7294
|
+
// any hook (worker fixtures come first), so a `beforeAll` context is in the
|
|
7295
|
+
// ledger and records the calls the tests make through it, and torn down after
|
|
7296
|
+
// the browser closed, so its census sees what only the worker's shutdown
|
|
7297
|
+
// closed. Depends on `playwright` alone, which launches nothing.
|
|
6665
7298
|
piwiResources: [
|
|
6666
7299
|
async ({ playwright }, use, workerInfo) => {
|
|
6667
7300
|
boxCaptureFrames();
|
|
6668
7301
|
startResourceLedger(playwright, workerInfo.workerIndex);
|
|
7302
|
+
observeApiRequests(playwright, apiCallRecorder);
|
|
6669
7303
|
try {
|
|
6670
7304
|
await use();
|
|
6671
7305
|
} finally {
|
|
@@ -6696,12 +7330,13 @@ var piwiFixtures = {
|
|
|
6696
7330
|
// (attach the collected data) at teardown. Runs for every test without being
|
|
6697
7331
|
// requested, so suites that never destructure `page` are still captured.
|
|
6698
7332
|
piwiCapture: [
|
|
6699
|
-
async ({ screenshot }, use, testInfo) => {
|
|
7333
|
+
async ({ screenshot, baseURL }, use, testInfo) => {
|
|
6700
7334
|
boxCaptureFrames();
|
|
6701
7335
|
const conflict = labModeConflict();
|
|
6702
7336
|
if (conflict) throw new Error(conflict);
|
|
6703
7337
|
const sink = createSink();
|
|
6704
7338
|
sink.testInfo = testInfo;
|
|
7339
|
+
sink.baseURL = baseURL ?? null;
|
|
6705
7340
|
if (keepsFailureScreenshots(screenshot, testInfo.retry)) sink.failureScreenshot = screenshot;
|
|
6706
7341
|
const pauseSet = currentPauseSet();
|
|
6707
7342
|
if (pauseSet.size) {
|
|
@@ -6731,6 +7366,7 @@ var piwiFixtures = {
|
|
|
6731
7366
|
activeResourceLedger()?.testStarted(testInfo);
|
|
6732
7367
|
currentSink = sink;
|
|
6733
7368
|
try {
|
|
7369
|
+
await startCodeReachOnOpenPages();
|
|
6734
7370
|
await use();
|
|
6735
7371
|
} finally {
|
|
6736
7372
|
currentSink = null;
|