@piwitests/reporter 0.60.0 → 0.61.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 +68 -6
- package/dist/global-setup-module.js +66 -9
- package/dist/index.d.ts +20 -2
- package/dist/index.js +758 -123
- package/dist/internal/capture/attachments.d.ts +2 -0
- package/dist/internal/capture/attachments.js +2 -0
- package/dist/internal/capture/capture-fixtures.js +433 -62
- package/package.json +1 -1
|
@@ -1678,7 +1678,7 @@ var TEST_DATA_ATTRS = /* @__PURE__ */ new Set(["data-test", "data-test-id", "dat
|
|
|
1678
1678
|
function generateAlternatives(attrs, options = {}) {
|
|
1679
1679
|
const alts = [];
|
|
1680
1680
|
const seen = /* @__PURE__ */ new Set();
|
|
1681
|
-
const
|
|
1681
|
+
const add2 = (loc) => {
|
|
1682
1682
|
if (!seen.has(loc.locator)) {
|
|
1683
1683
|
seen.add(loc.locator);
|
|
1684
1684
|
alts.push(loc);
|
|
@@ -1695,7 +1695,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1695
1695
|
const selectable = (name2) => name2.toLowerCase() !== testIdAttribute.toLowerCase();
|
|
1696
1696
|
const testId = attr(attrs, testIdAttribute);
|
|
1697
1697
|
if (testId && isUnique(counts?.testId)) {
|
|
1698
|
-
|
|
1698
|
+
add2({
|
|
1699
1699
|
locator: `getByTestId(${quoteJsString(testId)})`,
|
|
1700
1700
|
method: "getByTestId",
|
|
1701
1701
|
args: { testId },
|
|
@@ -1706,7 +1706,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1706
1706
|
const levelPart = level != null ? `, level: ${level}` : "";
|
|
1707
1707
|
const withLevel = (base) => level != null ? { ...base, level } : base;
|
|
1708
1708
|
if (role && accessibleName) {
|
|
1709
|
-
|
|
1709
|
+
add2({
|
|
1710
1710
|
locator: `getByRole(${quoteJsString(role)}, { name: ${quoteJsString(accessibleName)}${levelPart} })`,
|
|
1711
1711
|
method: "getByRole",
|
|
1712
1712
|
args: withLevel({ role, name: accessibleName }),
|
|
@@ -1715,7 +1715,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1715
1715
|
}
|
|
1716
1716
|
const ariaLabel = attr(attrs, "aria-label");
|
|
1717
1717
|
if (role && ariaLabel && ariaLabel !== accessibleName) {
|
|
1718
|
-
|
|
1718
|
+
add2({
|
|
1719
1719
|
locator: `getByRole(${quoteJsString(role)}, { name: ${quoteJsString(ariaLabel)}${levelPart} })`,
|
|
1720
1720
|
method: "getByRole",
|
|
1721
1721
|
args: withLevel({ role, name: ariaLabel }),
|
|
@@ -1726,7 +1726,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1726
1726
|
const label = attr(attrs, "aria-label");
|
|
1727
1727
|
const labelBacked = attrs.hasLabel === void 0 ? true : attrs.hasLabel === true || label === accessibleName;
|
|
1728
1728
|
if (labelBacked) {
|
|
1729
|
-
|
|
1729
|
+
add2({
|
|
1730
1730
|
locator: `getByLabel(${quoteJsString(accessibleName)})`,
|
|
1731
1731
|
method: "getByLabel",
|
|
1732
1732
|
args: { label: accessibleName },
|
|
@@ -1736,7 +1736,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1736
1736
|
}
|
|
1737
1737
|
const placeholder = attr(attrs, "placeholder");
|
|
1738
1738
|
if (placeholder) {
|
|
1739
|
-
|
|
1739
|
+
add2({
|
|
1740
1740
|
locator: `getByPlaceholder(${quoteJsString(placeholder)})`,
|
|
1741
1741
|
method: "getByPlaceholder",
|
|
1742
1742
|
args: { placeholder },
|
|
@@ -1744,7 +1744,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1744
1744
|
});
|
|
1745
1745
|
}
|
|
1746
1746
|
if (text && text.length < 80) {
|
|
1747
|
-
|
|
1747
|
+
add2({
|
|
1748
1748
|
locator: `getByText(${quoteJsString(text)})`,
|
|
1749
1749
|
method: "getByText",
|
|
1750
1750
|
args: { text },
|
|
@@ -1754,7 +1754,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1754
1754
|
const id = attr(attrs, "id");
|
|
1755
1755
|
if (id && selectable("id") && !isAutoGenerated(id) && isUnique(counts?.id)) {
|
|
1756
1756
|
const selector = isCssSafeId(id) ? `#${id}` : `[id="${escCssAttrValue(id)}"]`;
|
|
1757
|
-
|
|
1757
|
+
add2({
|
|
1758
1758
|
locator: `locator(${quoteJsString(selector)})`,
|
|
1759
1759
|
method: "locator",
|
|
1760
1760
|
args: { selector },
|
|
@@ -1764,7 +1764,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1764
1764
|
const name = attr(attrs, "name");
|
|
1765
1765
|
if (name && selectable("name") && isUnique(counts?.name)) {
|
|
1766
1766
|
const selector = `[name="${escCssAttrValue(name)}"]`;
|
|
1767
|
-
|
|
1767
|
+
add2({
|
|
1768
1768
|
locator: `locator(${quoteJsString(selector)})`,
|
|
1769
1769
|
method: "locator",
|
|
1770
1770
|
args: { selector },
|
|
@@ -1773,7 +1773,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1773
1773
|
}
|
|
1774
1774
|
const alt = attr(attrs, "alt");
|
|
1775
1775
|
if (alt) {
|
|
1776
|
-
|
|
1776
|
+
add2({
|
|
1777
1777
|
locator: `getByAltText(${quoteJsString(alt)})`,
|
|
1778
1778
|
method: "getByAltText",
|
|
1779
1779
|
args: { text: alt },
|
|
@@ -1782,7 +1782,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1782
1782
|
}
|
|
1783
1783
|
const title = attr(attrs, "title");
|
|
1784
1784
|
if (title) {
|
|
1785
|
-
|
|
1785
|
+
add2({
|
|
1786
1786
|
locator: `getByTitle(${quoteJsString(title)})`,
|
|
1787
1787
|
method: "getByTitle",
|
|
1788
1788
|
args: { title },
|
|
@@ -1800,7 +1800,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1800
1800
|
if (scopedCount(anc) !== 1) continue;
|
|
1801
1801
|
if (!testIdAnchorDone && anc.testId && anc.testIdCount === 1) {
|
|
1802
1802
|
testIdAnchorDone = true;
|
|
1803
|
-
|
|
1803
|
+
add2({
|
|
1804
1804
|
locator: `getByTestId(${quoteJsString(anc.testId)}).${leaf.expr}`,
|
|
1805
1805
|
method: leaf.method,
|
|
1806
1806
|
args: { ...leaf.args, anchorTestId: anc.testId },
|
|
@@ -1810,7 +1810,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1810
1810
|
if (!idAnchorDone && anc.id && selectable("id") && !isAutoGenerated(anc.id) && anc.idCount === 1) {
|
|
1811
1811
|
idAnchorDone = true;
|
|
1812
1812
|
const anchorSelector = isCssSafeId(anc.id) ? `#${anc.id}` : `[id="${escCssAttrValue(anc.id)}"]`;
|
|
1813
|
-
|
|
1813
|
+
add2({
|
|
1814
1814
|
locator: `locator(${quoteJsString(anchorSelector)}).${leaf.expr}`,
|
|
1815
1815
|
method: leaf.method,
|
|
1816
1816
|
args: { ...leaf.args, anchorSelector },
|
|
@@ -1821,7 +1821,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1821
1821
|
dataAnchorDone = true;
|
|
1822
1822
|
const { name: name2, value } = anc.dataAttr;
|
|
1823
1823
|
const anchorSelector = `[${name2}="${escCssAttrValue(value)}"]`;
|
|
1824
|
-
|
|
1824
|
+
add2({
|
|
1825
1825
|
locator: `locator(${quoteJsString(anchorSelector)}).${leaf.expr}`,
|
|
1826
1826
|
method: leaf.method,
|
|
1827
1827
|
args: { ...leaf.args, anchorSelector },
|
|
@@ -1831,7 +1831,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1831
1831
|
const ancestorRole = anc.role || TAG_TO_ROLE[anc.tag] || null;
|
|
1832
1832
|
if (!roleAnchorDone && ancestorRole && ancestorRole !== leaf.role && anc.roleCount === 1) {
|
|
1833
1833
|
roleAnchorDone = true;
|
|
1834
|
-
|
|
1834
|
+
add2({
|
|
1835
1835
|
locator: `getByRole(${quoteJsString(ancestorRole)}).${leaf.expr}`,
|
|
1836
1836
|
method: leaf.method,
|
|
1837
1837
|
args: { ...leaf.args, anchorRole: ancestorRole },
|
|
@@ -1840,7 +1840,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1840
1840
|
}
|
|
1841
1841
|
if (!filterAnchorDone && ancestorRole && anc.filterText && anc.filterRoleCount === 1) {
|
|
1842
1842
|
filterAnchorDone = true;
|
|
1843
|
-
|
|
1843
|
+
add2({
|
|
1844
1844
|
locator: `getByRole(${quoteJsString(ancestorRole)}).filter({ hasText: ${quoteJsString(anc.filterText)} }).${leaf.expr}`,
|
|
1845
1845
|
method: leaf.method,
|
|
1846
1846
|
args: { ...leaf.args, anchorRole: ancestorRole, anchorHasText: anc.filterText },
|
|
@@ -1859,7 +1859,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1859
1859
|
);
|
|
1860
1860
|
const pos = attrs.rolePosition;
|
|
1861
1861
|
if (pos && pos.role === role && (pos.count === 1 || level != null && pos.levelCount === 1)) {
|
|
1862
|
-
|
|
1862
|
+
add2({
|
|
1863
1863
|
locator: `getByRole(${rolePart})`,
|
|
1864
1864
|
method: "getByRole",
|
|
1865
1865
|
args: leafArgs,
|
|
@@ -1878,7 +1878,7 @@ function generateAlternatives(attrs, options = {}) {
|
|
|
1878
1878
|
const countsClasses = counts?.classes;
|
|
1879
1879
|
const classes = clsStr.split(/\s+/).filter((c) => c.length > 1 && /^[A-Za-z_-][A-Za-z0-9_-]*$/.test(c) && isUnique(countsClasses?.[c])).map((cls) => ({ cls, score: classifyCssStability(cls) })).sort((a, b) => b.score - a.score).slice(0, 3);
|
|
1880
1880
|
for (const { cls, score } of classes) {
|
|
1881
|
-
|
|
1881
|
+
add2({
|
|
1882
1882
|
locator: `locator(${quoteJsString(`.${cls}`)})`,
|
|
1883
1883
|
method: "locator",
|
|
1884
1884
|
args: { selector: `.${cls}` },
|
|
@@ -1935,7 +1935,7 @@ function generateAnchoredAlternatives(leaf, anchors, chainLeafCount) {
|
|
|
1935
1935
|
const seen = /* @__PURE__ */ new Set();
|
|
1936
1936
|
const rolePart = leaf.level != null ? `${quoteJsString(leaf.role)}, { level: ${leaf.level} }` : quoteJsString(leaf.role);
|
|
1937
1937
|
const leafArgs = leaf.level != null ? { role: leaf.role, level: leaf.level } : { role: leaf.role };
|
|
1938
|
-
const
|
|
1938
|
+
const add2 = (l) => {
|
|
1939
1939
|
if (!seen.has(l.locator)) {
|
|
1940
1940
|
seen.add(l.locator);
|
|
1941
1941
|
out.push(l);
|
|
@@ -1945,7 +1945,7 @@ function generateAnchoredAlternatives(leaf, anchors, chainLeafCount) {
|
|
|
1945
1945
|
if (anchor.scopedLeafCount !== 1) continue;
|
|
1946
1946
|
const seg = anchorSegment(anchor);
|
|
1947
1947
|
if (!seg) continue;
|
|
1948
|
-
|
|
1948
|
+
add2({
|
|
1949
1949
|
locator: `${seg.code}.getByRole(${rolePart})`,
|
|
1950
1950
|
method: "getByRole",
|
|
1951
1951
|
args: { ...leafArgs, ...seg.flatArgs },
|
|
@@ -1958,7 +1958,7 @@ function generateAnchoredAlternatives(leaf, anchors, chainLeafCount) {
|
|
|
1958
1958
|
if (segments.every((s) => s !== null)) {
|
|
1959
1959
|
const innermost = segments[segments.length - 1];
|
|
1960
1960
|
const score = Math.min(...segments.map((s) => ANCHOR_KIND_SCORES[s.kind]));
|
|
1961
|
-
|
|
1961
|
+
add2({
|
|
1962
1962
|
locator: `${segments.map((s) => s.code).join(".")}.getByRole(${rolePart})`,
|
|
1963
1963
|
method: "getByRole",
|
|
1964
1964
|
// Flat args describe the innermost anchor (what the healing fingerprint
|
|
@@ -2401,6 +2401,8 @@ var ATTACHMENT_NAMES = {
|
|
|
2401
2401
|
codeReach: "piwi-code-reach",
|
|
2402
2402
|
browserCoverage: "piwi-browser-coverage",
|
|
2403
2403
|
browserFunctions: "piwi-browser-functions",
|
|
2404
|
+
cssCoverage: "piwi-css-coverage",
|
|
2405
|
+
cssRules: "piwi-css-rules",
|
|
2404
2406
|
serverCoverageRequests: "piwi-server-coverage-requests",
|
|
2405
2407
|
webVitals: "piwi-web-vitals",
|
|
2406
2408
|
locatorSuggestion: "piwi-locator-suggestion",
|
|
@@ -2718,6 +2720,7 @@ var PIWI_ENV_KEYS = {
|
|
|
2718
2720
|
codeReachRoots: "PIWI_CODE_REACH_ROOTS",
|
|
2719
2721
|
coverageBrowser: "PIWI_COVERAGE_BROWSER",
|
|
2720
2722
|
coverageServer: "PIWI_COVERAGE_SERVER",
|
|
2723
|
+
coverageCss: "PIWI_COVERAGE_CSS",
|
|
2721
2724
|
externalIdPattern: "PIWI_EXTERNAL_ID_PATTERN",
|
|
2722
2725
|
externalIdOnDuplicate: "PIWI_EXTERNAL_ID_ON_DUPLICATE",
|
|
2723
2726
|
captureServerTraces: "PIWI_CAPTURE_SERVER_TRACES",
|
|
@@ -3807,10 +3810,10 @@ function joinRoot(root, source) {
|
|
|
3807
3810
|
function decodeLines(raw) {
|
|
3808
3811
|
const sources = [];
|
|
3809
3812
|
const decoded = [];
|
|
3810
|
-
const
|
|
3813
|
+
const add2 = (map, lineOffset, columnOffset) => {
|
|
3811
3814
|
if (map.sections) {
|
|
3812
3815
|
for (const section of map.sections) {
|
|
3813
|
-
if (!
|
|
3816
|
+
if (!add2(section.map, section.offset.line, section.offset.column)) return false;
|
|
3814
3817
|
}
|
|
3815
3818
|
return true;
|
|
3816
3819
|
}
|
|
@@ -3831,7 +3834,7 @@ function decodeLines(raw) {
|
|
|
3831
3834
|
});
|
|
3832
3835
|
return true;
|
|
3833
3836
|
};
|
|
3834
|
-
return
|
|
3837
|
+
return add2(raw, 0, 0) ? { sources, lines: decoded } : null;
|
|
3835
3838
|
}
|
|
3836
3839
|
function compactSegments(lines) {
|
|
3837
3840
|
let total = 0;
|
|
@@ -4007,7 +4010,7 @@ function functionLines(entry, map, text, all) {
|
|
|
4007
4010
|
const read = textOf(entry, text);
|
|
4008
4011
|
const ranges = executedFunctionRanges(entry, read?.length, all);
|
|
4009
4012
|
if (!ranges.length || !read) return out;
|
|
4010
|
-
const
|
|
4013
|
+
const add2 = (source, range) => {
|
|
4011
4014
|
const list = out.get(source);
|
|
4012
4015
|
if (list) list.push(range);
|
|
4013
4016
|
else out.set(source, [range]);
|
|
@@ -4017,7 +4020,7 @@ function functionLines(entry, map, text, all) {
|
|
|
4017
4020
|
const start = offsetToPosition(starts, range.start);
|
|
4018
4021
|
if (!map) {
|
|
4019
4022
|
const last = offsetToPosition(starts, Math.max(range.start, range.end - 1));
|
|
4020
|
-
|
|
4023
|
+
add2(-1, [start.line + 1, last.line + 1]);
|
|
4021
4024
|
continue;
|
|
4022
4025
|
}
|
|
4023
4026
|
const at = map.originalAt(start.line, start.column);
|
|
@@ -4027,7 +4030,7 @@ function functionLines(entry, map, text, all) {
|
|
|
4027
4030
|
if (first === null) continue;
|
|
4028
4031
|
const header = first === at.line || first === at.line + 1 ? at.line : first;
|
|
4029
4032
|
const lines = map.originalLinesBetween(at.source, start, end, header);
|
|
4030
|
-
if (lines)
|
|
4033
|
+
if (lines) add2(at.source, [header + 1, lines[1] + 1]);
|
|
4031
4034
|
}
|
|
4032
4035
|
return out;
|
|
4033
4036
|
}
|
|
@@ -4934,6 +4937,9 @@ var SERVER_VALUES = /* @__PURE__ */ new Set(["files", "functions", "lines"]);
|
|
|
4934
4937
|
function isServerGranularity(value) {
|
|
4935
4938
|
return typeof value === "string" && SERVER_VALUES.has(value);
|
|
4936
4939
|
}
|
|
4940
|
+
function cssCoverageInWorker() {
|
|
4941
|
+
return process.env[PIWI_ENV_KEYS.coverageCss] === "rules";
|
|
4942
|
+
}
|
|
4937
4943
|
function browserCoverageInWorker() {
|
|
4938
4944
|
const browser = process.env[PIWI_ENV_KEYS.coverageBrowser];
|
|
4939
4945
|
return process.env[PIWI_ENV_KEYS.captureCodeReach] === "true" || browser === "files" || browser === "functions";
|
|
@@ -5215,7 +5221,7 @@ async function resolveFunctions(entries, roots, fetchMap, pageUrl, withKnown) {
|
|
|
5215
5221
|
if (listed) listed.push(...functions);
|
|
5216
5222
|
else into.set(path15, [...functions]);
|
|
5217
5223
|
};
|
|
5218
|
-
const
|
|
5224
|
+
const add2 = (file, functions) => addTo(files, file, functions);
|
|
5219
5225
|
const bases = viteBases(entries);
|
|
5220
5226
|
const scripts = entries.filter((entry) => entry.url && entry.functions?.length && !REPLAYED_SCRIPT.test(entry.url));
|
|
5221
5227
|
const cached = scripts.map((entry) => scriptCache.get(entry.url, scriptVersion(entry)));
|
|
@@ -5242,14 +5248,14 @@ async function resolveFunctions(entries, roots, fetchMap, pageUrl, withKnown) {
|
|
|
5242
5248
|
const text = textOf2(covered, resolution);
|
|
5243
5249
|
if (resolution.lookup) {
|
|
5244
5250
|
const reached = reachedFunctionLines(covered, resolution.lookup, text);
|
|
5245
|
-
if (reached.size > 0)
|
|
5251
|
+
if (reached.size > 0) add2(resolution.file, [...reached.values()].flat());
|
|
5246
5252
|
} else if (scriptRan(covered, text)) {
|
|
5247
|
-
|
|
5253
|
+
add2(resolution.file, reachedFunctionLines(covered, null, text).get(-1) ?? []);
|
|
5248
5254
|
}
|
|
5249
5255
|
} else if (resolution.kind === "map") {
|
|
5250
5256
|
for (const [index, functions] of reachedFunctionLines(covered, resolution.lookup, textOf2(covered, resolution))) {
|
|
5251
5257
|
const file = mapFile(resolution, index);
|
|
5252
|
-
if (file)
|
|
5258
|
+
if (file) add2(file, functions);
|
|
5253
5259
|
}
|
|
5254
5260
|
}
|
|
5255
5261
|
if (!withKnown || resolution.kind === "none") continue;
|
|
@@ -5328,10 +5334,13 @@ async function markCurrentDocument(page, cdp) {
|
|
|
5328
5334
|
}
|
|
5329
5335
|
function onPaused(page, capture, event) {
|
|
5330
5336
|
const leaving = event.reason === "EventListener" && event.data?.eventName === "listener:beforeunload" && (event.callFrames?.[0]?.functionName ?? MARK_FUNCTION) === MARK_FUNCTION;
|
|
5331
|
-
const read = leaving ?
|
|
5337
|
+
const read = leaving ? Promise.all([
|
|
5338
|
+
capture.js ? readCoverage(page, capture.cdp) : Promise.resolve([]),
|
|
5339
|
+
capture.css ? readCssUsage(page, capture.cdp, capture.css) : Promise.resolve()
|
|
5340
|
+
]) : null;
|
|
5332
5341
|
if (read) {
|
|
5333
5342
|
const merging = read.then(
|
|
5334
|
-
(coverage) => mergeCoverageRead(capture.ran, capture.scripts, coverage, capture.keepIdle)
|
|
5343
|
+
([coverage]) => mergeCoverageRead(capture.ran, capture.scripts, coverage, capture.keepIdle)
|
|
5335
5344
|
);
|
|
5336
5345
|
capture.reading.add(merging);
|
|
5337
5346
|
void merging.finally(() => capture.reading.delete(merging));
|
|
@@ -5345,7 +5354,7 @@ function versionKey(url, hash) {
|
|
|
5345
5354
|
return `${hash} ${url}`;
|
|
5346
5355
|
}
|
|
5347
5356
|
function onScriptParsed(page, capture, event) {
|
|
5348
|
-
if (!event.url || REPLAYED_SCRIPT.test(event.url)) return;
|
|
5357
|
+
if (!capture.js || !event.url || REPLAYED_SCRIPT.test(event.url)) return;
|
|
5349
5358
|
capture.scripts.set(event.scriptId, { url: event.url, hash: event.hash, pageUrl: capture.pageUrl });
|
|
5350
5359
|
if (NEVER_REACHED.test(event.url)) return;
|
|
5351
5360
|
const key = versionKey(event.url, event.hash);
|
|
@@ -5374,6 +5383,53 @@ function readCoverage(page, cdp) {
|
|
|
5374
5383
|
);
|
|
5375
5384
|
return Promise.race([taken, late]).finally(() => clearTimeout(timer));
|
|
5376
5385
|
}
|
|
5386
|
+
function onStyleSheetAdded(page, cdp, css, pageUrl, header) {
|
|
5387
|
+
if (header.isConstructed) return;
|
|
5388
|
+
const sheet = {
|
|
5389
|
+
sourceURL: header.sourceURL,
|
|
5390
|
+
...header.sourceMapURL ? { sourceMapURL: header.sourceMapURL } : {},
|
|
5391
|
+
isInline: header.isInline,
|
|
5392
|
+
startLine: header.startLine,
|
|
5393
|
+
pageUrl,
|
|
5394
|
+
used: /* @__PURE__ */ new Map()
|
|
5395
|
+
};
|
|
5396
|
+
css.sheets.set(header.styleSheetId, sheet);
|
|
5397
|
+
const reading = (async () => {
|
|
5398
|
+
try {
|
|
5399
|
+
if (header.ownerNode !== void 0 && !/\.css(?:[?#]|$)/i.test(header.sourceURL)) {
|
|
5400
|
+
const { node } = await internalCall(
|
|
5401
|
+
page,
|
|
5402
|
+
() => cdp.send("DOM.describeNode", { backendNodeId: header.ownerNode })
|
|
5403
|
+
);
|
|
5404
|
+
const attributes = node.attributes ?? [];
|
|
5405
|
+
const at = attributes.indexOf("data-vite-dev-id");
|
|
5406
|
+
if (at >= 0 && typeof attributes[at + 1] === "string") sheet.devId = attributes[at + 1];
|
|
5407
|
+
}
|
|
5408
|
+
if (/\/node_modules\//.test(sheet.devId ?? header.sourceURL)) return;
|
|
5409
|
+
const { text } = await internalCall(
|
|
5410
|
+
page,
|
|
5411
|
+
() => cdp.send("CSS.getStyleSheetText", { styleSheetId: header.styleSheetId })
|
|
5412
|
+
);
|
|
5413
|
+
sheet.text = text;
|
|
5414
|
+
} catch {
|
|
5415
|
+
}
|
|
5416
|
+
})();
|
|
5417
|
+
css.reading.add(reading);
|
|
5418
|
+
void reading.finally(() => css.reading.delete(reading));
|
|
5419
|
+
}
|
|
5420
|
+
async function readCssUsage(page, cdp, css) {
|
|
5421
|
+
try {
|
|
5422
|
+
const { coverage } = await internalCall(page, () => cdp.send("CSS.takeCoverageDelta"));
|
|
5423
|
+
for (const rule of coverage) {
|
|
5424
|
+
if (!rule.used) continue;
|
|
5425
|
+
css.sheets.get(rule.styleSheetId)?.used.set(`${rule.startOffset}:${rule.endOffset}`, {
|
|
5426
|
+
startOffset: rule.startOffset,
|
|
5427
|
+
endOffset: rule.endOffset
|
|
5428
|
+
});
|
|
5429
|
+
}
|
|
5430
|
+
} catch {
|
|
5431
|
+
}
|
|
5432
|
+
}
|
|
5377
5433
|
function mergeCoverageRead(ran, scripts, read, keepIdle = true) {
|
|
5378
5434
|
for (const covered of read) {
|
|
5379
5435
|
const script = scripts.get(covered.scriptId);
|
|
@@ -5408,11 +5464,15 @@ async function detach(page, cdp) {
|
|
|
5408
5464
|
}
|
|
5409
5465
|
async function startCodeReach(page) {
|
|
5410
5466
|
if (!isChromiumPage(page) || captures.has(page)) return false;
|
|
5467
|
+
const withCss = cssCoverageInWorker();
|
|
5468
|
+
const js = browserCoverageInWorker() || !withCss;
|
|
5411
5469
|
let cdp = null;
|
|
5412
5470
|
try {
|
|
5413
5471
|
cdp = await internalCall(page, () => page.context().newCDPSession(page));
|
|
5414
5472
|
const capture = {
|
|
5415
5473
|
cdp,
|
|
5474
|
+
js,
|
|
5475
|
+
css: withCss ? { sheets: /* @__PURE__ */ new Map(), reading: /* @__PURE__ */ new Set() } : null,
|
|
5416
5476
|
scripts: /* @__PURE__ */ new Map(),
|
|
5417
5477
|
sources: /* @__PURE__ */ new Map(),
|
|
5418
5478
|
ran: /* @__PURE__ */ new Map(),
|
|
@@ -5422,6 +5482,9 @@ async function startCodeReach(page) {
|
|
|
5422
5482
|
};
|
|
5423
5483
|
const marks = marksOf(page);
|
|
5424
5484
|
cdp.on("Debugger.scriptParsed", (event) => onScriptParsed(page, capture, event));
|
|
5485
|
+
const css = capture.css;
|
|
5486
|
+
if (css)
|
|
5487
|
+
cdp.on("CSS.styleSheetAdded", (event) => onStyleSheetAdded(page, cdp, css, capture.pageUrl, event.header));
|
|
5425
5488
|
cdp.on("Debugger.paused", (event) => onPaused(page, capture, event));
|
|
5426
5489
|
cdp.on("Page.frameNavigated", (event) => {
|
|
5427
5490
|
if (event.frame.parentId) return;
|
|
@@ -5432,8 +5495,17 @@ async function startCodeReach(page) {
|
|
|
5432
5495
|
page,
|
|
5433
5496
|
() => Promise.all([
|
|
5434
5497
|
capture.cdp.send("Debugger.enable"),
|
|
5435
|
-
|
|
5436
|
-
|
|
5498
|
+
...js ? [
|
|
5499
|
+
capture.cdp.send("Profiler.enable"),
|
|
5500
|
+
capture.cdp.send("Profiler.startPreciseCoverage", { callCount: false, detailed: false })
|
|
5501
|
+
] : [],
|
|
5502
|
+
// The style sheets' rule usage records nothing without the DOM and CSS domains. A failure there leaves the
|
|
5503
|
+
// scripts' coverage running.
|
|
5504
|
+
...css ? [
|
|
5505
|
+
capture.cdp.send("DOM.enable").then(() => capture.cdp.send("CSS.enable")).then(() => capture.cdp.send("CSS.startRuleUsageTracking")).catch(() => {
|
|
5506
|
+
capture.css = null;
|
|
5507
|
+
})
|
|
5508
|
+
] : [],
|
|
5437
5509
|
capture.cdp.send("Page.enable"),
|
|
5438
5510
|
capture.cdp.send("Page.addScriptToEvaluateOnNewDocument", { source: MARK_SOURCE, worldName: MARK_WORLD }),
|
|
5439
5511
|
capture.cdp.send("DOMDebugger.setEventListenerBreakpoint", { eventName: "beforeunload" })
|
|
@@ -5448,17 +5520,28 @@ async function startCodeReach(page) {
|
|
|
5448
5520
|
}
|
|
5449
5521
|
}
|
|
5450
5522
|
async function stopCodeReach(page) {
|
|
5523
|
+
return (await stopPageCoverage(page)).scripts;
|
|
5524
|
+
}
|
|
5525
|
+
async function stopPageCoverage(page) {
|
|
5451
5526
|
const capture = captures.get(page);
|
|
5452
|
-
if (!capture) return null;
|
|
5527
|
+
if (!capture) return { scripts: null, sheets: [] };
|
|
5453
5528
|
captures.delete(page);
|
|
5454
5529
|
const { cdp } = capture;
|
|
5455
|
-
|
|
5530
|
+
const sheets = async () => {
|
|
5531
|
+
if (!capture.css) return [];
|
|
5532
|
+
await readCssUsage(page, cdp, capture.css);
|
|
5533
|
+
await Promise.all(capture.css.reading);
|
|
5534
|
+
await internalCall(page, () => cdp.send("CSS.stopRuleUsageTracking")).catch(() => {
|
|
5535
|
+
});
|
|
5536
|
+
return [...capture.css.sheets.values()].filter((sheet) => sheet.used.size > 0 || sheet.text !== void 0);
|
|
5537
|
+
};
|
|
5538
|
+
const scripts = async () => {
|
|
5539
|
+
if (!capture.js) return null;
|
|
5456
5540
|
const last = await internalCall(page, async () => {
|
|
5457
5541
|
const taken = await cdp.send("Profiler.takePreciseCoverage");
|
|
5458
5542
|
await cdp.send("Profiler.stopPreciseCoverage");
|
|
5459
5543
|
return taken.result;
|
|
5460
5544
|
});
|
|
5461
|
-
await Promise.all(capture.reading);
|
|
5462
5545
|
mergeCoverageRead(capture.ran, capture.scripts, last, capture.keepIdle);
|
|
5463
5546
|
const entries = [];
|
|
5464
5547
|
for (const [key, { script, functions }] of capture.ran) {
|
|
@@ -5468,15 +5551,279 @@ async function stopCodeReach(page) {
|
|
|
5468
5551
|
entries.push(entry);
|
|
5469
5552
|
}
|
|
5470
5553
|
return entries;
|
|
5471
|
-
}
|
|
5472
|
-
|
|
5554
|
+
};
|
|
5555
|
+
try {
|
|
5556
|
+
await Promise.all(capture.reading);
|
|
5557
|
+
const [read, styles] = await Promise.all([scripts().catch(() => null), sheets().catch(() => [])]);
|
|
5558
|
+
return { scripts: read, sheets: styles };
|
|
5473
5559
|
} finally {
|
|
5474
5560
|
await detach(page, cdp);
|
|
5475
5561
|
}
|
|
5476
5562
|
}
|
|
5477
5563
|
|
|
5564
|
+
// src/internal/capture/css-coverage.ts
|
|
5565
|
+
var crypto3 = __toESM(require("crypto"));
|
|
5566
|
+
var fs9 = __toESM(require("fs"));
|
|
5567
|
+
|
|
5568
|
+
// ../core/src/css-rules.ts
|
|
5569
|
+
var GROUPING = /* @__PURE__ */ new Set(["media", "supports", "layer", "container", "scope", "starting-style", "document"]);
|
|
5570
|
+
function parseCssRules(text) {
|
|
5571
|
+
const rules = [];
|
|
5572
|
+
const open = [];
|
|
5573
|
+
let preludeStart = -1;
|
|
5574
|
+
for (let i = 0; i < text.length; i++) {
|
|
5575
|
+
const c = text[i];
|
|
5576
|
+
if (c === "/" && text[i + 1] === "*") {
|
|
5577
|
+
const end = text.indexOf("*/", i + 2);
|
|
5578
|
+
i = end < 0 ? text.length : end + 1;
|
|
5579
|
+
continue;
|
|
5580
|
+
}
|
|
5581
|
+
if (c === '"' || c === "'") {
|
|
5582
|
+
if (preludeStart < 0) preludeStart = i;
|
|
5583
|
+
for (i++; i < text.length && text[i] !== c && text[i] !== "\n"; i++) if (text[i] === "\\") i++;
|
|
5584
|
+
continue;
|
|
5585
|
+
}
|
|
5586
|
+
if (c === "\\") {
|
|
5587
|
+
if (preludeStart < 0) preludeStart = i;
|
|
5588
|
+
i++;
|
|
5589
|
+
continue;
|
|
5590
|
+
}
|
|
5591
|
+
if ((c === "u" || c === "U") && /^url\(/i.test(text.slice(i, i + 4)) && !/[\w-]/.test(text[i - 1] ?? "")) {
|
|
5592
|
+
let j = i + 4;
|
|
5593
|
+
while (j < text.length && /\s/.test(text[j])) j++;
|
|
5594
|
+
if (text[j] !== '"' && text[j] !== "'") {
|
|
5595
|
+
if (preludeStart < 0) preludeStart = i;
|
|
5596
|
+
for (; j < text.length && text[j] !== ")"; j++) if (text[j] === "\\") j++;
|
|
5597
|
+
i = j;
|
|
5598
|
+
continue;
|
|
5599
|
+
}
|
|
5600
|
+
}
|
|
5601
|
+
if (text.startsWith("<!--", i) || text.startsWith("-->", i)) {
|
|
5602
|
+
i += text.startsWith("<!--", i) ? 3 : 2;
|
|
5603
|
+
continue;
|
|
5604
|
+
}
|
|
5605
|
+
if (c === "{") {
|
|
5606
|
+
const start = preludeStart < 0 ? i : preludeStart;
|
|
5607
|
+
const atRule = /^@([\w-]+)/.exec(text.slice(start, i));
|
|
5608
|
+
const parent = open[open.length - 1];
|
|
5609
|
+
const kind = atRule ? GROUPING.has(atRule[1].toLowerCase()) ? "group" : "other" : parent?.kind === "other" ? "other" : "style";
|
|
5610
|
+
open.push({ kind, start, open: i });
|
|
5611
|
+
preludeStart = -1;
|
|
5612
|
+
continue;
|
|
5613
|
+
}
|
|
5614
|
+
if (c === "}") {
|
|
5615
|
+
const block = open.pop();
|
|
5616
|
+
if (block && block.kind !== "other")
|
|
5617
|
+
rules.push({ kind: block.kind, start: block.start, open: block.open, end: i + 1 });
|
|
5618
|
+
preludeStart = -1;
|
|
5619
|
+
continue;
|
|
5620
|
+
}
|
|
5621
|
+
if (c === ";") {
|
|
5622
|
+
preludeStart = -1;
|
|
5623
|
+
continue;
|
|
5624
|
+
}
|
|
5625
|
+
if (preludeStart < 0 && !/\s/.test(c)) preludeStart = i;
|
|
5626
|
+
}
|
|
5627
|
+
for (const block of open) {
|
|
5628
|
+
if (block.kind !== "other")
|
|
5629
|
+
rules.push({ kind: block.kind, start: block.start, open: block.open, end: text.length });
|
|
5630
|
+
}
|
|
5631
|
+
return rules.sort((a, b) => a.start - b.start);
|
|
5632
|
+
}
|
|
5633
|
+
function usedCssRules(rules, used) {
|
|
5634
|
+
const styleAt = /* @__PURE__ */ new Map();
|
|
5635
|
+
const groups = [];
|
|
5636
|
+
for (const rule of rules) {
|
|
5637
|
+
if (rule.kind === "style") styleAt.set(rule.start, rule);
|
|
5638
|
+
else groups.push(rule);
|
|
5639
|
+
}
|
|
5640
|
+
const out = /* @__PURE__ */ new Map();
|
|
5641
|
+
for (const range of used) {
|
|
5642
|
+
const style = styleAt.get(range.startOffset);
|
|
5643
|
+
if (style) {
|
|
5644
|
+
out.set(`${style.start}`, [style.start, style.end]);
|
|
5645
|
+
continue;
|
|
5646
|
+
}
|
|
5647
|
+
const group = groups.find((rule) => rule.start <= range.startOffset && range.startOffset <= rule.open);
|
|
5648
|
+
if (group) out.set(`${group.start}`, [group.start, group.start + 1]);
|
|
5649
|
+
}
|
|
5650
|
+
return [...out.values()].sort((a, b) => a[0] - b[0]);
|
|
5651
|
+
}
|
|
5652
|
+
function allCssRules(rules) {
|
|
5653
|
+
return rules.map((rule) => rule.kind === "style" ? [rule.start, rule.end] : [rule.start, rule.start + 1]);
|
|
5654
|
+
}
|
|
5655
|
+
function cssRuleLines(text, offsets, options = {}) {
|
|
5656
|
+
const starts = lineStarts(text);
|
|
5657
|
+
const lineOffset = options.lineOffset ?? 0;
|
|
5658
|
+
const out = /* @__PURE__ */ new Map();
|
|
5659
|
+
const add2 = (source, range) => {
|
|
5660
|
+
const ranges = out.get(source);
|
|
5661
|
+
if (ranges) ranges.push(range);
|
|
5662
|
+
else out.set(source, [range]);
|
|
5663
|
+
};
|
|
5664
|
+
for (const [startOffset, endOffset] of offsets) {
|
|
5665
|
+
const start = offsetToPosition(starts, startOffset);
|
|
5666
|
+
const end = offsetToPosition(starts, Math.max(startOffset, endOffset - 1));
|
|
5667
|
+
if (!options.lookup) {
|
|
5668
|
+
add2(-1, [start.line + lineOffset + 1, end.line + lineOffset + 1]);
|
|
5669
|
+
continue;
|
|
5670
|
+
}
|
|
5671
|
+
const first = options.lookup.originalAt(start.line, start.column);
|
|
5672
|
+
if (!first) continue;
|
|
5673
|
+
const last = options.lookup.originalAt(end.line, end.column);
|
|
5674
|
+
const lastLine = last && last.source === first.source && last.line >= first.line ? last.line : first.line;
|
|
5675
|
+
add2(first.source, [first.line + 1, lastLine + 1]);
|
|
5676
|
+
}
|
|
5677
|
+
return out;
|
|
5678
|
+
}
|
|
5679
|
+
|
|
5680
|
+
// src/internal/capture/css-coverage.ts
|
|
5681
|
+
var STYLE_SOURCES = /\.(?:css|scss|sass|less|styl|stylus|pcss|postcss|vue|svelte|astro|html?)$/i;
|
|
5682
|
+
var BUILD_DIRS2 = /(?:^|\/)(?:dist|build|out|\.output|\.next|\.nuxt|\.svelte-kit)\//;
|
|
5683
|
+
var resolutions = /* @__PURE__ */ new Map();
|
|
5684
|
+
var MAX_CACHED_SHEETS = 2e3;
|
|
5685
|
+
function fileForStyleUrl(url, roots) {
|
|
5686
|
+
let pathname;
|
|
5687
|
+
try {
|
|
5688
|
+
const parsed2 = new URL(url);
|
|
5689
|
+
if (parsed2.protocol !== "http:" && parsed2.protocol !== "https:" && parsed2.protocol !== "file:") return null;
|
|
5690
|
+
pathname = parsed2.pathname;
|
|
5691
|
+
} catch {
|
|
5692
|
+
return null;
|
|
5693
|
+
}
|
|
5694
|
+
return sourceFile(decodeURIComponent(pathname), roots);
|
|
5695
|
+
}
|
|
5696
|
+
function sourceFile(source, roots) {
|
|
5697
|
+
const bare = source.replace(/[?#].*$/, "");
|
|
5698
|
+
if (!STYLE_SOURCES.test(bare) || /(?:^|\/)node_modules\//.test(bare)) return null;
|
|
5699
|
+
const file = resolveSourcePath(bare, roots);
|
|
5700
|
+
if (!file || BUILD_DIRS2.test(file.replace(/\\/g, "/")) || /(?:^|[\\/])node_modules[\\/]/.test(file)) return null;
|
|
5701
|
+
return file;
|
|
5702
|
+
}
|
|
5703
|
+
function styleBlockLine(file, index) {
|
|
5704
|
+
let text;
|
|
5705
|
+
try {
|
|
5706
|
+
text = fs9.readFileSync(file, "utf-8");
|
|
5707
|
+
} catch {
|
|
5708
|
+
return 0;
|
|
5709
|
+
}
|
|
5710
|
+
const blocks = [...text.matchAll(/^<style\b[^>]*>/gim)];
|
|
5711
|
+
const block = blocks[index];
|
|
5712
|
+
if (!block) return 0;
|
|
5713
|
+
return text.slice(0, block.index + block[0].length).split("\n").length - 1;
|
|
5714
|
+
}
|
|
5715
|
+
async function sheetMap(sheet, text, fetchMap) {
|
|
5716
|
+
const ref = sourceMappingUrl(text) ?? sheet.sourceMapURL ?? null;
|
|
5717
|
+
if (!ref) return null;
|
|
5718
|
+
let raw;
|
|
5719
|
+
if (ref.startsWith("data:")) raw = decodeDataUrl(ref);
|
|
5720
|
+
else {
|
|
5721
|
+
try {
|
|
5722
|
+
raw = await fetchMap(new URL(ref, sheet.sourceURL).href);
|
|
5723
|
+
} catch {
|
|
5724
|
+
raw = null;
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
if (!raw) return null;
|
|
5728
|
+
try {
|
|
5729
|
+
return decodeSourceMap(JSON.parse(raw));
|
|
5730
|
+
} catch {
|
|
5731
|
+
return null;
|
|
5732
|
+
}
|
|
5733
|
+
}
|
|
5734
|
+
function sameLines(text, rules, file, lineOffset) {
|
|
5735
|
+
let source;
|
|
5736
|
+
try {
|
|
5737
|
+
source = fs9.readFileSync(file, "utf-8");
|
|
5738
|
+
} catch {
|
|
5739
|
+
return false;
|
|
5740
|
+
}
|
|
5741
|
+
const fileLines = source.split(/\r?\n/);
|
|
5742
|
+
const sheetLineStarts = [0];
|
|
5743
|
+
for (let i = 0; i < text.length; i++) if (text[i] === "\n") sheetLineStarts.push(i + 1);
|
|
5744
|
+
let low = 0;
|
|
5745
|
+
for (const rule of rules) {
|
|
5746
|
+
while (low + 1 < sheetLineStarts.length && sheetLineStarts[low + 1] <= rule.start) low++;
|
|
5747
|
+
const head = text.slice(rule.start, rule.open).split("\n")[0].trim();
|
|
5748
|
+
if (head && !(fileLines[low + lineOffset] ?? "").includes(head)) return false;
|
|
5749
|
+
}
|
|
5750
|
+
return true;
|
|
5751
|
+
}
|
|
5752
|
+
async function resolveSheet(sheet, text, dirs, fetchMap) {
|
|
5753
|
+
const lookup = await sheetMap(sheet, text, fetchMap);
|
|
5754
|
+
const rules = parseCssRules(text);
|
|
5755
|
+
if (lookup) {
|
|
5756
|
+
const sources = lookup.sources.map((source) => {
|
|
5757
|
+
const direct = sourceFile(source, dirs);
|
|
5758
|
+
if (direct) return direct;
|
|
5759
|
+
try {
|
|
5760
|
+
return /^[a-z][\w+.-]*:/i.test(source) ? null : fileForStyleUrl(new URL(source, sheet.sourceURL).href, dirs);
|
|
5761
|
+
} catch {
|
|
5762
|
+
return null;
|
|
5763
|
+
}
|
|
5764
|
+
});
|
|
5765
|
+
return { rules, lookup, file: null, lineOffset: 0, sources, exact: true };
|
|
5766
|
+
}
|
|
5767
|
+
if (sheet.devId) {
|
|
5768
|
+
const file2 = sourceFile(sheet.devId, dirs);
|
|
5769
|
+
if (!file2) return null;
|
|
5770
|
+
const index = /[?&]index=(\d+)/.exec(sheet.devId);
|
|
5771
|
+
const lineOffset2 = /\.(?:vue|svelte|astro)$/i.test(file2) && index ? styleBlockLine(file2, Number(index[1])) : 0;
|
|
5772
|
+
return { rules, lookup: null, file: file2, lineOffset: lineOffset2, sources: [], exact: sameLines(text, rules, file2, lineOffset2) };
|
|
5773
|
+
}
|
|
5774
|
+
const file = fileForStyleUrl(sheet.sourceURL, dirs);
|
|
5775
|
+
if (!file) return null;
|
|
5776
|
+
const lineOffset = sheet.isInline ? sheet.startLine : 0;
|
|
5777
|
+
return { rules, lookup: null, file, lineOffset, sources: [], exact: sameLines(text, rules, file, lineOffset) };
|
|
5778
|
+
}
|
|
5779
|
+
function cachedResolution(sheet, text, dirs, fetchMap) {
|
|
5780
|
+
const version = crypto3.createHash("sha1").update(text).digest("hex");
|
|
5781
|
+
const key = [sheet.devId ?? sheet.sourceURL, sheet.isInline ? sheet.startLine : "", dirs.join("\0"), version].join(
|
|
5782
|
+
" "
|
|
5783
|
+
);
|
|
5784
|
+
let pending = resolutions.get(key);
|
|
5785
|
+
if (!pending) {
|
|
5786
|
+
pending = resolveSheet(sheet, text, dirs, fetchMap).catch(() => null);
|
|
5787
|
+
resolutions.set(key, pending);
|
|
5788
|
+
if (resolutions.size > MAX_CACHED_SHEETS) resolutions.delete(resolutions.keys().next().value);
|
|
5789
|
+
}
|
|
5790
|
+
return pending;
|
|
5791
|
+
}
|
|
5792
|
+
function add(into, file, ranges, repoRoot, fileLevel = false) {
|
|
5793
|
+
if (ranges.length === 0 && !fileLevel) return;
|
|
5794
|
+
const key = relativeToRepo(file, repoRoot);
|
|
5795
|
+
into.set(key, [...into.get(key) ?? [], ...ranges]);
|
|
5796
|
+
}
|
|
5797
|
+
async function resolveCssCoverage(sheets, roots, fetchMap) {
|
|
5798
|
+
const reached = /* @__PURE__ */ new Map();
|
|
5799
|
+
const known = /* @__PURE__ */ new Map();
|
|
5800
|
+
for (const sheet of sheets) {
|
|
5801
|
+
if (sheet.text === void 0) continue;
|
|
5802
|
+
const dirs = scriptRoots(roots, sheet.sourceURL, sheet.pageUrl);
|
|
5803
|
+
const resolution = await cachedResolution(sheet, sheet.text, dirs, fetchMap);
|
|
5804
|
+
if (!resolution) continue;
|
|
5805
|
+
const used = usedCssRules(resolution.rules, [...sheet.used.values()]);
|
|
5806
|
+
if (!resolution.exact) {
|
|
5807
|
+
if (resolution.file && used.length > 0) add(reached, resolution.file, [], roots.repoRoot, true);
|
|
5808
|
+
continue;
|
|
5809
|
+
}
|
|
5810
|
+
const options = { lookup: resolution.lookup, lineOffset: resolution.lineOffset };
|
|
5811
|
+
const fileOf = (source) => source === -1 ? resolution.file : resolution.sources[source] ?? null;
|
|
5812
|
+
const matched = cssRuleLines(sheet.text, used, options);
|
|
5813
|
+
for (const [source, ranges] of matched) {
|
|
5814
|
+
const file = fileOf(source);
|
|
5815
|
+
if (file) add(reached, file, ranges, roots.repoRoot);
|
|
5816
|
+
}
|
|
5817
|
+
for (const [source, ranges] of cssRuleLines(sheet.text, allCssRules(resolution.rules), options)) {
|
|
5818
|
+
const file = fileOf(source);
|
|
5819
|
+
if (file) add(known, file, ranges, roots.repoRoot);
|
|
5820
|
+
}
|
|
5821
|
+
}
|
|
5822
|
+
return { reached, known };
|
|
5823
|
+
}
|
|
5824
|
+
|
|
5478
5825
|
// src/internal/capture/resource-ledger.ts
|
|
5479
|
-
var
|
|
5826
|
+
var fs11 = __toESM(require("fs"));
|
|
5480
5827
|
var path13 = __toESM(require("path"));
|
|
5481
5828
|
var import_node_module2 = require("module");
|
|
5482
5829
|
|
|
@@ -5484,12 +5831,12 @@ var import_node_module2 = require("module");
|
|
|
5484
5831
|
var import_node_perf_hooks = require("perf_hooks");
|
|
5485
5832
|
|
|
5486
5833
|
// src/internal/support/system-readers.ts
|
|
5487
|
-
var
|
|
5834
|
+
var fs10 = __toESM(require("fs"));
|
|
5488
5835
|
var path12 = __toESM(require("path"));
|
|
5489
5836
|
var CLK_TCK = 100;
|
|
5490
5837
|
function readText(file) {
|
|
5491
5838
|
try {
|
|
5492
|
-
return
|
|
5839
|
+
return fs10.readFileSync(file, "utf8");
|
|
5493
5840
|
} catch {
|
|
5494
5841
|
return null;
|
|
5495
5842
|
}
|
|
@@ -5571,14 +5918,14 @@ var ProcFs = class {
|
|
|
5571
5918
|
}
|
|
5572
5919
|
/** Whether this looks like a Linux `/proc` at all. */
|
|
5573
5920
|
available() {
|
|
5574
|
-
return
|
|
5921
|
+
return fs10.existsSync(path12.join(this.root, "stat"));
|
|
5575
5922
|
}
|
|
5576
5923
|
/** Every process, keyed by pid. */
|
|
5577
5924
|
table() {
|
|
5578
5925
|
const out = /* @__PURE__ */ new Map();
|
|
5579
5926
|
let names;
|
|
5580
5927
|
try {
|
|
5581
|
-
names =
|
|
5928
|
+
names = fs10.readdirSync(this.root);
|
|
5582
5929
|
} catch {
|
|
5583
5930
|
return out;
|
|
5584
5931
|
}
|
|
@@ -5609,7 +5956,7 @@ var ProcFs = class {
|
|
|
5609
5956
|
/** Proportional set size: shared pages split between the processes sharing them, so a sum counts them once. */
|
|
5610
5957
|
async pssKb(pid) {
|
|
5611
5958
|
try {
|
|
5612
|
-
return parseKbField(await
|
|
5959
|
+
return parseKbField(await fs10.promises.readFile(path12.join(this.root, String(pid), "smaps_rollup"), "utf8"), "Pss");
|
|
5613
5960
|
} catch {
|
|
5614
5961
|
return null;
|
|
5615
5962
|
}
|
|
@@ -5617,7 +5964,7 @@ var ProcFs = class {
|
|
|
5617
5964
|
/** Reset a process's peak RSS (`VmHWM`) to its current RSS. */
|
|
5618
5965
|
resetPeakRss(pid) {
|
|
5619
5966
|
try {
|
|
5620
|
-
|
|
5967
|
+
fs10.writeFileSync(path12.join(this.root, String(pid), "clear_refs"), "5");
|
|
5621
5968
|
return true;
|
|
5622
5969
|
} catch {
|
|
5623
5970
|
return false;
|
|
@@ -5639,7 +5986,7 @@ var ProcFs = class {
|
|
|
5639
5986
|
/** Open file descriptors of a process. */
|
|
5640
5987
|
fdCount(pid) {
|
|
5641
5988
|
try {
|
|
5642
|
-
return
|
|
5989
|
+
return fs10.readdirSync(path12.join(this.root, String(pid), "fd")).length;
|
|
5643
5990
|
} catch {
|
|
5644
5991
|
return null;
|
|
5645
5992
|
}
|
|
@@ -6018,7 +6365,7 @@ var ResourceLedger = class {
|
|
|
6018
6365
|
const census = this.census(null);
|
|
6019
6366
|
if (this.resultsFile) {
|
|
6020
6367
|
try {
|
|
6021
|
-
|
|
6368
|
+
fs11.appendFileSync(this.resultsFile, `${JSON.stringify(census)}
|
|
6022
6369
|
`);
|
|
6023
6370
|
} catch {
|
|
6024
6371
|
}
|
|
@@ -6643,16 +6990,19 @@ function createSink() {
|
|
|
6643
6990
|
onReleasedRequestsEnded: null,
|
|
6644
6991
|
releasedQuietSince: 0,
|
|
6645
6992
|
locatorPages: new LocatorPageLog(),
|
|
6646
|
-
codeReach: { sessions: /* @__PURE__ */ new Map(), files: /* @__PURE__ */ new Map(), known: /* @__PURE__ */ new Map() },
|
|
6993
|
+
codeReach: { sessions: /* @__PURE__ */ new Map(), files: /* @__PURE__ */ new Map(), known: /* @__PURE__ */ new Map(), cssFiles: /* @__PURE__ */ new Map(), cssKnown: /* @__PURE__ */ new Map() },
|
|
6647
6994
|
serverCoverage: /* @__PURE__ */ new Map(),
|
|
6648
6995
|
baseURL: null
|
|
6649
6996
|
};
|
|
6650
6997
|
}
|
|
6651
6998
|
var MAX_CODE_REACH_PAGES = 16;
|
|
6652
6999
|
var CODE_REACH_START_WAIT_MS = 2e3;
|
|
7000
|
+
function pageCoverageInWorker() {
|
|
7001
|
+
return browserCoverageInWorker() || cssCoverageInWorker();
|
|
7002
|
+
}
|
|
6653
7003
|
function maybeStartCodeReach(page, alreadyOpen = false) {
|
|
6654
7004
|
const sink = currentSink;
|
|
6655
|
-
if (!sink || !
|
|
7005
|
+
if (!sink || !pageCoverageInWorker() || isPageClosed(page)) return Promise.resolve(false);
|
|
6656
7006
|
const known = sink.codeReach.sessions.get(page);
|
|
6657
7007
|
if (known) return known.started;
|
|
6658
7008
|
if (alreadyOpen) {
|
|
@@ -6665,7 +7015,7 @@ function maybeStartCodeReach(page, alreadyOpen = false) {
|
|
|
6665
7015
|
return started;
|
|
6666
7016
|
}
|
|
6667
7017
|
async function startCodeReachOnOpenPages() {
|
|
6668
|
-
if (!
|
|
7018
|
+
if (!pageCoverageInWorker() || OPEN_PAGES.size === 0) return;
|
|
6669
7019
|
const started = Promise.all([...OPEN_PAGES].map((page) => maybeStartCodeReach(page, true)));
|
|
6670
7020
|
await Promise.race([started, new Promise((resolve5) => setTimeout(resolve5, CODE_REACH_START_WAIT_MS).unref())]);
|
|
6671
7021
|
}
|
|
@@ -6708,22 +7058,28 @@ async function stopSinkCodeReach(sink, closing) {
|
|
|
6708
7058
|
async function stopPageCodeReach(sink, page, session, roots) {
|
|
6709
7059
|
try {
|
|
6710
7060
|
if (!await session.started || isPageClosed(page)) return;
|
|
6711
|
-
const
|
|
6712
|
-
|
|
6713
|
-
const
|
|
7061
|
+
const { scripts, sheets } = await stopPageCoverage(page);
|
|
7062
|
+
const fetchMap = pageMapFetcher(page);
|
|
7063
|
+
const append = (into, from) => {
|
|
7064
|
+
for (const [file, ranges] of from) into.set(file, [...into.get(file) ?? [], ...ranges]);
|
|
7065
|
+
};
|
|
7066
|
+
if (sheets.length > 0) {
|
|
7067
|
+
const rules = await resolveCssCoverage(sheets, roots, fetchMap);
|
|
7068
|
+
append(sink.codeReach.cssFiles, rules.reached);
|
|
7069
|
+
append(sink.codeReach.cssKnown, rules.known);
|
|
7070
|
+
}
|
|
7071
|
+
if (!scripts) return;
|
|
7072
|
+
const { reached, known } = await resolveCodeReachFunctions(scripts, roots, fetchMap, page.url(), {
|
|
6714
7073
|
known: browserFunctionsInWorker()
|
|
6715
7074
|
});
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
}
|
|
6719
|
-
for (const [file, functions] of known) {
|
|
6720
|
-
sink.codeReach.known.set(file, [...sink.codeReach.known.get(file) ?? [], ...functions]);
|
|
6721
|
-
}
|
|
7075
|
+
append(sink.codeReach.files, reached);
|
|
7076
|
+
append(sink.codeReach.known, known);
|
|
6722
7077
|
} catch {
|
|
6723
7078
|
}
|
|
6724
7079
|
}
|
|
6725
7080
|
var currentSink = null;
|
|
6726
7081
|
var KNOWN_FUNCTIONS_SENT = /* @__PURE__ */ new Map();
|
|
7082
|
+
var KNOWN_CSS_RULES_SENT = /* @__PURE__ */ new Map();
|
|
6727
7083
|
var PENDING_PROBES = /* @__PURE__ */ new Set();
|
|
6728
7084
|
async function drainPendingProbes(capMs) {
|
|
6729
7085
|
if (PENDING_PROBES.size === 0) return;
|
|
@@ -7879,6 +8235,21 @@ async function flushSink(sink, testInfo) {
|
|
|
7879
8235
|
body: Buffer.from(JSON.stringify(part))
|
|
7880
8236
|
});
|
|
7881
8237
|
}
|
|
8238
|
+
if (cssCoverageInWorker()) {
|
|
8239
|
+
const rules = sink.codeReach.cssFiles.size > 0 ? browserFunctionCoverage(sink.codeReach.cssFiles) : null;
|
|
8240
|
+
if (rules) {
|
|
8241
|
+
await testInfo.attach(ATTACHMENT_NAMES.cssCoverage, {
|
|
8242
|
+
contentType: "application/json",
|
|
8243
|
+
body: Buffer.from(JSON.stringify(rules))
|
|
8244
|
+
});
|
|
8245
|
+
}
|
|
8246
|
+
for (const part of newKnownFunctions(sink.codeReach.cssKnown, KNOWN_CSS_RULES_SENT)) {
|
|
8247
|
+
await testInfo.attach(ATTACHMENT_NAMES.cssRules, {
|
|
8248
|
+
contentType: "application/json",
|
|
8249
|
+
body: Buffer.from(JSON.stringify(part))
|
|
8250
|
+
});
|
|
8251
|
+
}
|
|
8252
|
+
}
|
|
7882
8253
|
if (sink.serverCoverage.size > 0) {
|
|
7883
8254
|
const requests = {};
|
|
7884
8255
|
for (const [origin, ids] of sink.serverCoverage) requests[origin] = [...ids];
|