@riddledc/riddle-proof 0.7.36 → 0.7.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -1
- package/dist/{chunk-VEK6QHPE.js → chunk-ZB7AFRN3.js} +287 -2
- package/dist/cli.cjs +287 -2
- package/dist/cli.js +1 -1
- package/dist/index.cjs +287 -2
- package/dist/index.js +1 -1
- package/dist/profile.cjs +287 -2
- package/dist/profile.d.cts +2 -1
- package/dist/profile.d.ts +2 -1
- package/dist/profile.js +1 -1
- package/package.json +1 -1
package/dist/profile.cjs
CHANGED
|
@@ -58,6 +58,8 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
58
58
|
"selector_visible",
|
|
59
59
|
"selector_count_at_least",
|
|
60
60
|
"selector_text_order",
|
|
61
|
+
"frame_text_visible",
|
|
62
|
+
"frame_no_horizontal_overflow",
|
|
61
63
|
"text_visible",
|
|
62
64
|
"text_absent",
|
|
63
65
|
"route_inventory",
|
|
@@ -356,6 +358,12 @@ function normalizeCheck(input, index) {
|
|
|
356
358
|
if ((type === "selector_visible" || type === "selector_count_at_least") && !stringValue(input.selector)) {
|
|
357
359
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
358
360
|
}
|
|
361
|
+
if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
|
|
362
|
+
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
363
|
+
}
|
|
364
|
+
if (type === "frame_text_visible" && !stringValue(input.text) && !stringValue(input.pattern)) {
|
|
365
|
+
throw new Error(`checks[${index}] frame_text_visible requires text or pattern.`);
|
|
366
|
+
}
|
|
359
367
|
if ((type === "text_visible" || type === "text_absent") && !stringValue(input.text) && !stringValue(input.pattern)) {
|
|
360
368
|
throw new Error(`checks[${index}] ${type} requires text or pattern.`);
|
|
361
369
|
}
|
|
@@ -506,6 +514,22 @@ function textOrderMatch(texts, expectedTexts) {
|
|
|
506
514
|
}
|
|
507
515
|
return { matched: true, positions };
|
|
508
516
|
}
|
|
517
|
+
function frameEvidenceForSelector(viewport, selector) {
|
|
518
|
+
const container = viewport.frames?.[selector];
|
|
519
|
+
if (!isRecord(container)) return [];
|
|
520
|
+
const frames = Array.isArray(container.frames) ? container.frames : [];
|
|
521
|
+
return frames.filter((frame) => isRecord(frame));
|
|
522
|
+
}
|
|
523
|
+
function frameTextSample(frame) {
|
|
524
|
+
const parts = [
|
|
525
|
+
frame.title,
|
|
526
|
+
frame.text_sample,
|
|
527
|
+
frame.body_text_sample,
|
|
528
|
+
frame.body_text,
|
|
529
|
+
frame.text
|
|
530
|
+
];
|
|
531
|
+
return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
532
|
+
}
|
|
509
533
|
function summarizeRouteInventory(viewport, inventory) {
|
|
510
534
|
const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
|
|
511
535
|
const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
|
|
@@ -670,6 +694,67 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
670
694
|
message: failed ? `Selector ${key} text order failed in ${failed} viewport(s).` : void 0
|
|
671
695
|
};
|
|
672
696
|
}
|
|
697
|
+
if (check.type === "frame_text_visible") {
|
|
698
|
+
const key = selectorKey(check);
|
|
699
|
+
const results = viewports.map((viewport) => {
|
|
700
|
+
const frames = frameEvidenceForSelector(viewport, key);
|
|
701
|
+
const matches = frames.map((frame, index) => ({ index, frame, matched: matchText(frameTextSample(frame), check) })).filter((item) => item.matched);
|
|
702
|
+
return {
|
|
703
|
+
viewport: viewport.name,
|
|
704
|
+
frame_count: frames.length,
|
|
705
|
+
matched_count: matches.length,
|
|
706
|
+
matched: matches.length > 0,
|
|
707
|
+
urls: frames.map((frame) => String(frame.url || "")).filter(Boolean).slice(0, 10),
|
|
708
|
+
samples: matches.map((item) => frameTextSample(item.frame).replace(/\s+/g, " ").trim().slice(0, 240)).slice(0, 3)
|
|
709
|
+
};
|
|
710
|
+
});
|
|
711
|
+
const failed = results.filter((result) => !result.matched).length;
|
|
712
|
+
return {
|
|
713
|
+
type: check.type,
|
|
714
|
+
label: checkLabel(check),
|
|
715
|
+
status: failed ? "failed" : "passed",
|
|
716
|
+
evidence: {
|
|
717
|
+
selector: key,
|
|
718
|
+
text: check.text || null,
|
|
719
|
+
pattern: check.pattern || null,
|
|
720
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
721
|
+
},
|
|
722
|
+
message: failed ? `Frame selector ${key} did not contain expected text in ${failed} viewport(s).` : void 0
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
if (check.type === "frame_no_horizontal_overflow") {
|
|
726
|
+
const key = selectorKey(check);
|
|
727
|
+
const maxOverflow = check.max_overflow_px ?? 4;
|
|
728
|
+
const results = viewports.map((viewport) => {
|
|
729
|
+
const frames = frameEvidenceForSelector(viewport, key);
|
|
730
|
+
const frameOverflows = frames.map((frame, index) => ({
|
|
731
|
+
index,
|
|
732
|
+
url: String(frame.url || ""),
|
|
733
|
+
overflow_px: horizontalBoundsOverflowPx(frame),
|
|
734
|
+
offender_count: boundsOffendersForEvidence(frame).length
|
|
735
|
+
}));
|
|
736
|
+
const maxFrameOverflow = frameOverflows.reduce((max, frame) => Math.max(max, frame.overflow_px), 0);
|
|
737
|
+
return {
|
|
738
|
+
viewport: viewport.name,
|
|
739
|
+
frame_count: frames.length,
|
|
740
|
+
max_overflow_px: roundPixels(maxFrameOverflow),
|
|
741
|
+
failed_frame_count: frameOverflows.filter((frame) => frame.overflow_px > maxOverflow).length,
|
|
742
|
+
frames: frameOverflows.map((frame) => toJsonValue(frame))
|
|
743
|
+
};
|
|
744
|
+
});
|
|
745
|
+
const failed = results.filter((result) => result.frame_count < 1 || result.failed_frame_count > 0).length;
|
|
746
|
+
return {
|
|
747
|
+
type: check.type,
|
|
748
|
+
label: checkLabel(check),
|
|
749
|
+
status: failed ? "failed" : "passed",
|
|
750
|
+
evidence: {
|
|
751
|
+
selector: key,
|
|
752
|
+
max_overflow_px: maxOverflow,
|
|
753
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
754
|
+
},
|
|
755
|
+
message: failed ? `Frame selector ${key} overflow exceeded ${maxOverflow}px or was missing in ${failed} viewport(s).` : void 0
|
|
756
|
+
};
|
|
757
|
+
}
|
|
673
758
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
674
759
|
const key = textKey(check);
|
|
675
760
|
const expectedVisible = check.type === "text_visible";
|
|
@@ -1045,6 +1130,21 @@ function textOrderMatch(texts, expectedTexts) {
|
|
|
1045
1130
|
}
|
|
1046
1131
|
return { matched: true, positions };
|
|
1047
1132
|
}
|
|
1133
|
+
function frameEvidenceForSelector(viewport, selector) {
|
|
1134
|
+
const container = viewport.frames && viewport.frames[selector || ""];
|
|
1135
|
+
if (!container || typeof container !== "object" || Array.isArray(container)) return [];
|
|
1136
|
+
const frames = Array.isArray(container.frames) ? container.frames : [];
|
|
1137
|
+
return frames.filter((frame) => frame && typeof frame === "object" && !Array.isArray(frame));
|
|
1138
|
+
}
|
|
1139
|
+
function frameTextSample(frame) {
|
|
1140
|
+
return [
|
|
1141
|
+
frame && frame.title,
|
|
1142
|
+
frame && frame.text_sample,
|
|
1143
|
+
frame && frame.body_text_sample,
|
|
1144
|
+
frame && frame.body_text,
|
|
1145
|
+
frame && frame.text,
|
|
1146
|
+
].map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
1147
|
+
}
|
|
1048
1148
|
function summarizeRouteInventory(viewport, inventory) {
|
|
1049
1149
|
const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
|
|
1050
1150
|
const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
|
|
@@ -1291,6 +1391,62 @@ function assessProfile(profile, evidence) {
|
|
|
1291
1391
|
});
|
|
1292
1392
|
continue;
|
|
1293
1393
|
}
|
|
1394
|
+
if (check.type === "frame_text_visible") {
|
|
1395
|
+
const selector = check.selector || "";
|
|
1396
|
+
const results = viewports.map((viewport) => {
|
|
1397
|
+
const frames = frameEvidenceForSelector(viewport, selector);
|
|
1398
|
+
const matches = frames
|
|
1399
|
+
.map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
|
|
1400
|
+
.filter((item) => item.matched);
|
|
1401
|
+
return {
|
|
1402
|
+
viewport: viewport.name,
|
|
1403
|
+
frame_count: frames.length,
|
|
1404
|
+
matched_count: matches.length,
|
|
1405
|
+
matched: matches.length > 0,
|
|
1406
|
+
urls: frames.map((frame) => String(frame.url || "")).filter(Boolean).slice(0, 10),
|
|
1407
|
+
samples: matches.map((item) => frameTextSample(item.frame).replace(/\s+/g, " ").trim().slice(0, 240)).slice(0, 3),
|
|
1408
|
+
};
|
|
1409
|
+
});
|
|
1410
|
+
const failed = results.filter((result) => !result.matched).length;
|
|
1411
|
+
checks.push({
|
|
1412
|
+
type: check.type,
|
|
1413
|
+
label: check.label || check.type,
|
|
1414
|
+
status: failed ? "failed" : "passed",
|
|
1415
|
+
evidence: { selector, text: check.text || null, pattern: check.pattern || null, viewports: results },
|
|
1416
|
+
message: failed ? "Frame selector " + selector + " did not contain expected text in " + failed + " viewport(s)." : undefined,
|
|
1417
|
+
});
|
|
1418
|
+
continue;
|
|
1419
|
+
}
|
|
1420
|
+
if (check.type === "frame_no_horizontal_overflow") {
|
|
1421
|
+
const selector = check.selector || "";
|
|
1422
|
+
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
1423
|
+
const results = viewports.map((viewport) => {
|
|
1424
|
+
const frames = frameEvidenceForSelector(viewport, selector);
|
|
1425
|
+
const frameOverflows = frames.map((frame, index) => ({
|
|
1426
|
+
index,
|
|
1427
|
+
url: String(frame.url || ""),
|
|
1428
|
+
overflow_px: horizontalBoundsOverflowPx(frame),
|
|
1429
|
+
offender_count: boundsOffendersForEvidence(frame).length,
|
|
1430
|
+
}));
|
|
1431
|
+
const maxFrameOverflow = frameOverflows.reduce((max, frame) => Math.max(max, frame.overflow_px), 0);
|
|
1432
|
+
return {
|
|
1433
|
+
viewport: viewport.name,
|
|
1434
|
+
frame_count: frames.length,
|
|
1435
|
+
max_overflow_px: roundPixels(maxFrameOverflow),
|
|
1436
|
+
failed_frame_count: frameOverflows.filter((frame) => frame.overflow_px > maxOverflow).length,
|
|
1437
|
+
frames: frameOverflows,
|
|
1438
|
+
};
|
|
1439
|
+
});
|
|
1440
|
+
const failed = results.filter((result) => result.frame_count < 1 || result.failed_frame_count > 0).length;
|
|
1441
|
+
checks.push({
|
|
1442
|
+
type: check.type,
|
|
1443
|
+
label: check.label || check.type,
|
|
1444
|
+
status: failed ? "failed" : "passed",
|
|
1445
|
+
evidence: { selector, max_overflow_px: maxOverflow, viewports: results },
|
|
1446
|
+
message: failed ? "Frame selector " + selector + " overflow exceeded " + maxOverflow + "px or was missing in " + failed + " viewport(s)." : undefined,
|
|
1447
|
+
});
|
|
1448
|
+
continue;
|
|
1449
|
+
}
|
|
1294
1450
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
1295
1451
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
1296
1452
|
const expectedVisible = check.type === "text_visible";
|
|
@@ -1688,6 +1844,108 @@ async function selectorTextSequence(selector) {
|
|
|
1688
1844
|
};
|
|
1689
1845
|
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
1690
1846
|
}
|
|
1847
|
+
async function frameEvidence(selector) {
|
|
1848
|
+
const result = { selector, count: 0, frame_count: 0, frames: [], errors: [] };
|
|
1849
|
+
let handles = [];
|
|
1850
|
+
try {
|
|
1851
|
+
const locator = page.locator(selector);
|
|
1852
|
+
result.count = await locator.count();
|
|
1853
|
+
handles = await locator.elementHandles();
|
|
1854
|
+
} catch (error) {
|
|
1855
|
+
result.errors.push(String(error && error.message ? error.message : error).slice(0, 500));
|
|
1856
|
+
return result;
|
|
1857
|
+
}
|
|
1858
|
+
for (let index = 0; index < handles.length; index += 1) {
|
|
1859
|
+
const handle = handles[index];
|
|
1860
|
+
try {
|
|
1861
|
+
const frame = typeof handle.contentFrame === "function" ? await handle.contentFrame() : null;
|
|
1862
|
+
if (!frame) {
|
|
1863
|
+
result.frames.push({ index, attached: false, error: "content_frame_unavailable" });
|
|
1864
|
+
continue;
|
|
1865
|
+
}
|
|
1866
|
+
const snapshot = await frame.evaluate(() => {
|
|
1867
|
+
const body = document.body;
|
|
1868
|
+
const documentElement = document.documentElement;
|
|
1869
|
+
const text = (body ? body.innerText : "").replace(/\s+/g, " ").trim();
|
|
1870
|
+
const clientWidth = documentElement ? documentElement.clientWidth : window.innerWidth;
|
|
1871
|
+
const clientHeight = documentElement ? documentElement.clientHeight : window.innerHeight;
|
|
1872
|
+
const scrollWidth = documentElement ? documentElement.scrollWidth : 0;
|
|
1873
|
+
const viewportWidth = clientWidth || window.innerWidth;
|
|
1874
|
+
const overflowOffenders = [];
|
|
1875
|
+
function isContainedByHorizontalScroller(element) {
|
|
1876
|
+
let current = element.parentElement;
|
|
1877
|
+
while (current && current !== body && current !== documentElement) {
|
|
1878
|
+
const style = window.getComputedStyle(current);
|
|
1879
|
+
const overflowX = style.overflowX || style.overflow || "";
|
|
1880
|
+
if ((overflowX === "auto" || overflowX === "scroll") && current.scrollWidth > current.clientWidth + 1) {
|
|
1881
|
+
const currentRect = current.getBoundingClientRect();
|
|
1882
|
+
const contained = currentRect.left >= -0.5 && currentRect.right <= viewportWidth + 0.5;
|
|
1883
|
+
if (contained) return true;
|
|
1884
|
+
}
|
|
1885
|
+
current = current.parentElement;
|
|
1886
|
+
}
|
|
1887
|
+
return false;
|
|
1888
|
+
}
|
|
1889
|
+
for (const element of Array.from(body ? body.querySelectorAll("*") : [])) {
|
|
1890
|
+
const rect = element.getBoundingClientRect();
|
|
1891
|
+
if (!rect || rect.width < 1 || rect.height < 1) continue;
|
|
1892
|
+
const style = window.getComputedStyle(element);
|
|
1893
|
+
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") continue;
|
|
1894
|
+
const leftOverflow = Math.max(0, -rect.left);
|
|
1895
|
+
const rightOverflow = Math.max(0, rect.right - viewportWidth);
|
|
1896
|
+
const overflow = Math.max(leftOverflow, rightOverflow);
|
|
1897
|
+
if (overflow <= 0.5) continue;
|
|
1898
|
+
if (isContainedByHorizontalScroller(element)) continue;
|
|
1899
|
+
const tag = element.tagName ? element.tagName.toLowerCase() : "element";
|
|
1900
|
+
const id = element.id ? "#" + element.id : "";
|
|
1901
|
+
const className = typeof element.className === "string"
|
|
1902
|
+
? element.className.trim().split(/\s+/).filter(Boolean).slice(0, 2).join(".")
|
|
1903
|
+
: "";
|
|
1904
|
+
overflowOffenders.push({
|
|
1905
|
+
selector: tag + id + (className ? "." + className : ""),
|
|
1906
|
+
tag,
|
|
1907
|
+
text: (element.textContent || "").replace(/\s+/g, " ").trim().slice(0, 120),
|
|
1908
|
+
overflow,
|
|
1909
|
+
left_overflow_px: leftOverflow,
|
|
1910
|
+
right_overflow_px: rightOverflow,
|
|
1911
|
+
viewport_width: viewportWidth,
|
|
1912
|
+
rect: {
|
|
1913
|
+
left: rect.left,
|
|
1914
|
+
right: rect.right,
|
|
1915
|
+
width: rect.width,
|
|
1916
|
+
},
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1919
|
+
overflowOffenders.sort((a, b) => b.overflow - a.overflow);
|
|
1920
|
+
const boundsOverflowPx = Math.max(
|
|
1921
|
+
0,
|
|
1922
|
+
scrollWidth - (clientWidth || viewportWidth),
|
|
1923
|
+
...overflowOffenders.map((offender) => offender.overflow),
|
|
1924
|
+
);
|
|
1925
|
+
return {
|
|
1926
|
+
url: location.href,
|
|
1927
|
+
title: document.title,
|
|
1928
|
+
text_length: text.length,
|
|
1929
|
+
text_sample: text.slice(0, 8000),
|
|
1930
|
+
body_text_sample: text.slice(0, 8000),
|
|
1931
|
+
viewport_width: viewportWidth,
|
|
1932
|
+
viewport_height: clientHeight || window.innerHeight,
|
|
1933
|
+
scroll_width: scrollWidth,
|
|
1934
|
+
client_width: clientWidth,
|
|
1935
|
+
overflow_px: Math.max(0, scrollWidth - (clientWidth || viewportWidth)),
|
|
1936
|
+
bounds_overflow_px: Math.round(boundsOverflowPx * 100) / 100,
|
|
1937
|
+
overflow_offender_count: overflowOffenders.length,
|
|
1938
|
+
overflow_offenders: overflowOffenders.slice(0, 10),
|
|
1939
|
+
};
|
|
1940
|
+
});
|
|
1941
|
+
result.frames.push({ index, attached: true, ...snapshot });
|
|
1942
|
+
} catch (error) {
|
|
1943
|
+
result.frames.push({ index, attached: false, error: String(error && error.message ? error.message : error).slice(0, 1000) });
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
result.frame_count = result.frames.filter((frame) => frame && frame.attached !== false && !frame.error).length;
|
|
1947
|
+
return result;
|
|
1948
|
+
}
|
|
1691
1949
|
function inventoryAppPathFromUrl(urlLike) {
|
|
1692
1950
|
const url = new URL(String(urlLike), targetUrl);
|
|
1693
1951
|
const mountPrefix = previewMountPrefix(new URL(targetUrl).pathname);
|
|
@@ -1709,6 +1967,13 @@ function inventoryRouteUrl(expectedPath) {
|
|
|
1709
1967
|
function inventorySlugFromPath(path) {
|
|
1710
1968
|
return String(path || "route").split("/").filter(Boolean).pop()?.replace(/[^a-z0-9]+/gi, "-").toLowerCase() || "route";
|
|
1711
1969
|
}
|
|
1970
|
+
function inventorySlugFromViewport(viewport) {
|
|
1971
|
+
return String(viewport && viewport.name || "viewport").replace(/[^a-z0-9]+/gi, "-").replace(/^-+|-+$/g, "").toLowerCase() || "viewport";
|
|
1972
|
+
}
|
|
1973
|
+
function inventoryScreenshotLabel(check, viewport, phase, path) {
|
|
1974
|
+
const viewportPart = check.run_all_viewports ? "-" + inventorySlugFromViewport(viewport) : "";
|
|
1975
|
+
return profileSlug + viewportPart + "-" + phase + "-" + inventorySlugFromPath(path);
|
|
1976
|
+
}
|
|
1712
1977
|
function inventoryCheckTimeout(check) {
|
|
1713
1978
|
const timeout = Number(check && check.timeout_ms);
|
|
1714
1979
|
return Number.isFinite(timeout) && timeout > 0 ? timeout : 45000;
|
|
@@ -1917,7 +2182,7 @@ async function collectRouteInventory(check, viewport) {
|
|
|
1917
2182
|
const snapshot = await collectInventoryRouteSnapshot(expectedRoute, "direct", check, error);
|
|
1918
2183
|
directRoutes.push(snapshot);
|
|
1919
2184
|
if (check.save_route_screenshots) {
|
|
1920
|
-
await saveScreenshot(
|
|
2185
|
+
await saveScreenshot(inventoryScreenshotLabel(check, viewport, "direct", expectedRoute.path)).catch(() => {});
|
|
1921
2186
|
}
|
|
1922
2187
|
if (error) failures.push({ code: "direct_route_unhealthy", name: expectedRoute.name || null, path: expectedRoute.path, error });
|
|
1923
2188
|
else if (snapshot.actual_app_path !== normalizeRoutePath(expectedRoute.path)) failures.push({ code: "direct_route_wrong_path", name: expectedRoute.name || null, path: expectedRoute.path, actual_app_path: snapshot.actual_app_path });
|
|
@@ -1951,7 +2216,7 @@ async function collectRouteInventory(check, viewport) {
|
|
|
1951
2216
|
result.snapshot = await collectInventoryRouteSnapshot(expectedRoute, "clickthrough", check, result.error || "");
|
|
1952
2217
|
clickthroughs.push(result);
|
|
1953
2218
|
if (check.save_route_screenshots) {
|
|
1954
|
-
await saveScreenshot(
|
|
2219
|
+
await saveScreenshot(inventoryScreenshotLabel(check, viewport, "click", expectedRoute.path)).catch(() => {});
|
|
1955
2220
|
}
|
|
1956
2221
|
if (result.error && result.error !== "source_link_not_found") failures.push({ code: "source_link_clickthrough_unhealthy", name: expectedRoute.name || null, path: expectedRoute.path, error: result.error });
|
|
1957
2222
|
else if (result.snapshot.actual_app_path !== normalizeRoutePath(expectedRoute.path)) failures.push({ code: "source_link_clickthrough_wrong_path", name: expectedRoute.name || null, path: expectedRoute.path, actual_app_path: result.snapshot.actual_app_path });
|
|
@@ -2085,6 +2350,7 @@ async function captureViewport(viewport) {
|
|
|
2085
2350
|
evaluation_error: String(error && error.message ? error.message : error).slice(0, 1000),
|
|
2086
2351
|
}));
|
|
2087
2352
|
const selectors = {};
|
|
2353
|
+
const frames = {};
|
|
2088
2354
|
const text_sequences = {};
|
|
2089
2355
|
const text_matches = {};
|
|
2090
2356
|
for (const check of profile.checks || []) {
|
|
@@ -2098,6 +2364,10 @@ async function captureViewport(viewport) {
|
|
|
2098
2364
|
if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
|
|
2099
2365
|
text_matches[textKey(check)] = textMatches(dom.body_text || dom.body_text_sample || "", check);
|
|
2100
2366
|
}
|
|
2367
|
+
if ((check.type === "frame_text_visible" || check.type === "frame_no_horizontal_overflow") && check.selector) {
|
|
2368
|
+
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
2369
|
+
frames[check.selector] = frames[check.selector] || await frameEvidence(check.selector);
|
|
2370
|
+
}
|
|
2101
2371
|
}
|
|
2102
2372
|
const screenshotLabel = profileSlug + "-" + viewport.name;
|
|
2103
2373
|
try {
|
|
@@ -2158,6 +2428,7 @@ async function captureViewport(viewport) {
|
|
|
2158
2428
|
bounds_overflow_px: dom.bounds_overflow_px,
|
|
2159
2429
|
overflow_offenders: dom.overflow_offenders || [],
|
|
2160
2430
|
selectors,
|
|
2431
|
+
frames,
|
|
2161
2432
|
text_sequences,
|
|
2162
2433
|
text_matches,
|
|
2163
2434
|
route_inventory: routeInventory,
|
|
@@ -2193,6 +2464,20 @@ function buildProfileEvidence(currentViewports) {
|
|
|
2193
2464
|
overflow_px: currentViewports.map((viewport) => viewport.overflow_px),
|
|
2194
2465
|
bounds_overflow_px: currentViewports.map((viewport) => viewport.bounds_overflow_px),
|
|
2195
2466
|
overflow_offender_counts: currentViewports.map((viewport) => (viewport.overflow_offenders || []).length),
|
|
2467
|
+
frames: currentViewports
|
|
2468
|
+
.filter((viewport) => viewport.frames)
|
|
2469
|
+
.map((viewport) => ({
|
|
2470
|
+
viewport: viewport.name,
|
|
2471
|
+
selectors: Object.entries(viewport.frames || {}).map(([selector, frameSet]) => ({
|
|
2472
|
+
selector,
|
|
2473
|
+
count: frameSet && frameSet.count,
|
|
2474
|
+
frame_count: frameSet && frameSet.frame_count,
|
|
2475
|
+
max_bounds_overflow_px: Math.max(
|
|
2476
|
+
0,
|
|
2477
|
+
...((frameSet && Array.isArray(frameSet.frames) ? frameSet.frames : [])).map((frame) => horizontalBoundsOverflowPx(frame)),
|
|
2478
|
+
),
|
|
2479
|
+
})),
|
|
2480
|
+
})),
|
|
2196
2481
|
route_inventory: currentViewports
|
|
2197
2482
|
.filter((viewport) => viewport.route_inventory)
|
|
2198
2483
|
.map((viewport) => ({
|
package/dist/profile.d.cts
CHANGED
|
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
|
|
|
4
4
|
declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
|
|
5
5
|
declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
|
|
6
6
|
declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
|
|
7
|
-
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "selector_visible", "selector_count_at_least", "selector_text_order", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "selector_visible", "selector_count_at_least", "selector_text_order", "frame_text_visible", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
8
8
|
declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "fill", "set_input_value", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
|
@@ -139,6 +139,7 @@ interface RiddleProofProfileViewportEvidence {
|
|
|
139
139
|
count: number;
|
|
140
140
|
visible_count: number;
|
|
141
141
|
}>;
|
|
142
|
+
frames?: Record<string, Record<string, JsonValue>>;
|
|
142
143
|
text_sequences?: Record<string, Record<string, JsonValue>>;
|
|
143
144
|
text_matches?: Record<string, boolean>;
|
|
144
145
|
route_inventory?: Record<string, JsonValue>;
|
package/dist/profile.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
|
|
|
4
4
|
declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
|
|
5
5
|
declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
|
|
6
6
|
declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
|
|
7
|
-
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "selector_visible", "selector_count_at_least", "selector_text_order", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "selector_visible", "selector_count_at_least", "selector_text_order", "frame_text_visible", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
8
8
|
declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "fill", "set_input_value", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
|
@@ -139,6 +139,7 @@ interface RiddleProofProfileViewportEvidence {
|
|
|
139
139
|
count: number;
|
|
140
140
|
visible_count: number;
|
|
141
141
|
}>;
|
|
142
|
+
frames?: Record<string, Record<string, JsonValue>>;
|
|
142
143
|
text_sequences?: Record<string, Record<string, JsonValue>>;
|
|
143
144
|
text_matches?: Record<string, boolean>;
|
|
144
145
|
route_inventory?: Record<string, JsonValue>;
|
package/dist/profile.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
resolveRiddleProofProfileTimeoutSec,
|
|
20
20
|
slugifyRiddleProofProfileName,
|
|
21
21
|
summarizeRiddleProofProfileResult
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-ZB7AFRN3.js";
|
|
23
23
|
export {
|
|
24
24
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
25
25
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|