@riddledc/riddle-proof 0.7.42 → 0.7.44
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 +10 -1
- package/dist/{chunk-MT2OZCCP.js → chunk-G673RKAC.js} +76 -32
- package/dist/cli.cjs +76 -32
- package/dist/cli.js +1 -1
- package/dist/index.cjs +76 -32
- package/dist/index.js +1 -1
- package/dist/profile.cjs +76 -32
- package/dist/profile.d.cts +1 -0
- package/dist/profile.d.ts +1 -0
- package/dist/profile.js +1 -1
- package/dist/proof-run-engine.d.cts +3 -3
- package/dist/proof-run-engine.d.ts +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -148,6 +148,7 @@ or as a stronger proof base before a change loop.
|
|
|
148
148
|
{ "type": "selector_absent", "selector": "[data-testid='loading-spinner']" },
|
|
149
149
|
{ "type": "selector_count_equals", "selector": "[data-testid='pricing-card']", "expected_count": 3 },
|
|
150
150
|
{ "type": "text_visible", "text": "Start building" },
|
|
151
|
+
{ "type": "text_visible", "text": "Compare plans", "viewports": ["desktop"] },
|
|
151
152
|
{ "type": "no_mobile_horizontal_overflow" },
|
|
152
153
|
{ "type": "no_fatal_console_errors" }
|
|
153
154
|
],
|
|
@@ -179,6 +180,11 @@ The package includes a generic starter profile at
|
|
|
179
180
|
profile directory and replace the selector/text checks with app-specific
|
|
180
181
|
invariants.
|
|
181
182
|
|
|
183
|
+
Checks normally apply to every captured viewport. Add `viewports` (or
|
|
184
|
+
`viewport_names`) to a check when responsive UI intentionally exposes an
|
|
185
|
+
invariant only on named viewports, such as desktop-only helper copy while phone
|
|
186
|
+
layouts keep the same route, link, and overflow contracts.
|
|
187
|
+
|
|
182
188
|
`target.network_mocks` is optional. The Riddle runner registers these mocks
|
|
183
189
|
before navigation, records each hit, and adds an implicit
|
|
184
190
|
`network_mocks_succeeded` check when mocks are present. A mock supports
|
|
@@ -252,7 +258,10 @@ known browser console error, such as a mocked `503` that the app recovers from:
|
|
|
252
258
|
Allowed console events and page errors are still counted in check evidence, but
|
|
253
259
|
only unallowed `error` / `assert` console events and page errors fail the check.
|
|
254
260
|
Use `allowed_page_error_patterns`, `allowed_console_texts`, or
|
|
255
|
-
`allowed_page_error_texts` for narrower matching when needed.
|
|
261
|
+
`allowed_page_error_texts` for narrower matching when needed. Console allowlists
|
|
262
|
+
match both the console text and the event location URL, which is useful for
|
|
263
|
+
expected resource probes where the browser message is generic but the URL is
|
|
264
|
+
specific.
|
|
256
265
|
|
|
257
266
|
Use `selector_absent` when a forbidden element must not render, and
|
|
258
267
|
`selector_count_equals` / `selector_count_equal` / `selector_count_eq` when a
|
|
@@ -402,6 +402,7 @@ function normalizeCheck(input, index) {
|
|
|
402
402
|
text: stringValue(input.text),
|
|
403
403
|
pattern: stringValue(input.pattern),
|
|
404
404
|
flags: stringValue(input.flags),
|
|
405
|
+
viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `checks[${index}] viewports`),
|
|
405
406
|
allowed_console_texts: normalizeStringList(input.allowed_console_texts ?? input.allowedConsoleTexts ?? input.allow_console_texts ?? input.allowConsoleTexts, `checks[${index}] allowed_console_texts`),
|
|
406
407
|
allowed_console_patterns: normalizeStringList(input.allowed_console_patterns ?? input.allowedConsolePatterns ?? input.allow_console_patterns ?? input.allowConsolePatterns, `checks[${index}] allowed_console_patterns`),
|
|
407
408
|
allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
|
|
@@ -570,8 +571,17 @@ function matchText(sample, check) {
|
|
|
570
571
|
}
|
|
571
572
|
return sample.includes(check.text || "");
|
|
572
573
|
}
|
|
573
|
-
function
|
|
574
|
-
|
|
574
|
+
function allowedMessageSample(input) {
|
|
575
|
+
if (!isRecord(input)) return String(input || "");
|
|
576
|
+
const parts = [
|
|
577
|
+
input.text,
|
|
578
|
+
input.message,
|
|
579
|
+
isRecord(input.location) ? input.location.url : void 0
|
|
580
|
+
];
|
|
581
|
+
return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
582
|
+
}
|
|
583
|
+
function matchesAllowedMessage(input, texts, patterns) {
|
|
584
|
+
const sample = allowedMessageSample(input);
|
|
575
585
|
if (texts?.some((text) => sample.includes(text))) return true;
|
|
576
586
|
for (const pattern of patterns || []) {
|
|
577
587
|
try {
|
|
@@ -631,15 +641,22 @@ function successfulRoute(route, targetUrl) {
|
|
|
631
641
|
const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
|
|
632
642
|
return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
|
|
633
643
|
}
|
|
644
|
+
function viewportsForCheck(check, viewports) {
|
|
645
|
+
if (!check.viewports?.length) return viewports;
|
|
646
|
+
const names = new Set(check.viewports);
|
|
647
|
+
return viewports.filter((viewport) => names.has(viewport.name));
|
|
648
|
+
}
|
|
634
649
|
function assessCheckFromEvidence(check, evidence) {
|
|
635
|
-
const viewports = evidence.viewports || [];
|
|
650
|
+
const viewports = viewportsForCheck(check, evidence.viewports || []);
|
|
636
651
|
if (!viewports.length) {
|
|
637
652
|
return {
|
|
638
653
|
type: check.type,
|
|
639
654
|
label: checkLabel(check),
|
|
640
655
|
status: "failed",
|
|
641
|
-
evidence: {
|
|
642
|
-
|
|
656
|
+
evidence: {
|
|
657
|
+
expected_viewports: check.viewports || []
|
|
658
|
+
},
|
|
659
|
+
message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
|
|
643
660
|
};
|
|
644
661
|
}
|
|
645
662
|
if (check.type === "route_loaded") {
|
|
@@ -899,11 +916,11 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
899
916
|
}
|
|
900
917
|
if (check.type === "no_fatal_console_errors") {
|
|
901
918
|
const fatalConsoleEvents = (evidence.console?.events || []).filter((event) => event.type === "error" || event.type === "assert");
|
|
902
|
-
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event
|
|
903
|
-
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event
|
|
919
|
+
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
920
|
+
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
904
921
|
const pageErrors = evidence.page_errors || [];
|
|
905
|
-
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error
|
|
906
|
-
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error
|
|
922
|
+
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
923
|
+
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
907
924
|
const fatalCount = unallowedConsoleEvents.length + unallowedPageErrors.length;
|
|
908
925
|
return {
|
|
909
926
|
type: check.type,
|
|
@@ -1188,8 +1205,17 @@ function textMatches(sample, check) {
|
|
|
1188
1205
|
}
|
|
1189
1206
|
return String(sample || "").includes(check.text || "");
|
|
1190
1207
|
}
|
|
1191
|
-
function
|
|
1192
|
-
|
|
1208
|
+
function allowedMessageSample(input) {
|
|
1209
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return String(input || "");
|
|
1210
|
+
const parts = [
|
|
1211
|
+
input.text,
|
|
1212
|
+
input.message,
|
|
1213
|
+
input.location && typeof input.location === "object" ? input.location.url : undefined,
|
|
1214
|
+
];
|
|
1215
|
+
return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
1216
|
+
}
|
|
1217
|
+
function matchesAllowedMessage(input, texts, patterns) {
|
|
1218
|
+
const sample = allowedMessageSample(input);
|
|
1193
1219
|
if ((texts || []).some((text) => sample.includes(text))) return true;
|
|
1194
1220
|
for (const pattern of patterns || []) {
|
|
1195
1221
|
try {
|
|
@@ -1235,6 +1261,11 @@ function frameTextSample(frame) {
|
|
|
1235
1261
|
frame && frame.text,
|
|
1236
1262
|
].map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
1237
1263
|
}
|
|
1264
|
+
function viewportsForCheck(check, viewports) {
|
|
1265
|
+
if (!Array.isArray(check.viewports) || !check.viewports.length) return viewports;
|
|
1266
|
+
const names = new Set(check.viewports);
|
|
1267
|
+
return viewports.filter((viewport) => names.has(viewport.name));
|
|
1268
|
+
}
|
|
1238
1269
|
function summarizeRouteInventory(viewport, inventory) {
|
|
1239
1270
|
const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
|
|
1240
1271
|
const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
|
|
@@ -1425,9 +1456,22 @@ function assessProfile(profile, evidence) {
|
|
|
1425
1456
|
});
|
|
1426
1457
|
}
|
|
1427
1458
|
for (const check of profile.checks || []) {
|
|
1459
|
+
const checkViewports = viewportsForCheck(check, viewports);
|
|
1460
|
+
if (!checkViewports.length) {
|
|
1461
|
+
checks.push({
|
|
1462
|
+
type: check.type,
|
|
1463
|
+
label: check.label || check.type,
|
|
1464
|
+
status: "failed",
|
|
1465
|
+
evidence: { expected_viewports: check.viewports || [] },
|
|
1466
|
+
message: Array.isArray(check.viewports) && check.viewports.length
|
|
1467
|
+
? "No matching viewport evidence was captured for " + check.viewports.join(", ") + "."
|
|
1468
|
+
: "No viewport evidence was captured.",
|
|
1469
|
+
});
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1428
1472
|
if (check.type === "route_loaded") {
|
|
1429
1473
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
1430
|
-
const failed =
|
|
1474
|
+
const failed = checkViewports.filter((viewport) => {
|
|
1431
1475
|
const route = { ...(viewport.route || {}), expected_path: expectedPath };
|
|
1432
1476
|
route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
|
|
1433
1477
|
return !routeOk(route, evidence.target_url);
|
|
@@ -1438,8 +1482,8 @@ function assessProfile(profile, evidence) {
|
|
|
1438
1482
|
status: failed.length ? "failed" : "passed",
|
|
1439
1483
|
evidence: {
|
|
1440
1484
|
expected_path: expectedPath,
|
|
1441
|
-
observed_paths:
|
|
1442
|
-
http_statuses:
|
|
1485
|
+
observed_paths: checkViewports.map((viewport) => viewport.route && viewport.route.observed),
|
|
1486
|
+
http_statuses: checkViewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
|
|
1443
1487
|
},
|
|
1444
1488
|
message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
|
|
1445
1489
|
});
|
|
@@ -1447,24 +1491,24 @@ function assessProfile(profile, evidence) {
|
|
|
1447
1491
|
}
|
|
1448
1492
|
if (check.type === "selector_visible") {
|
|
1449
1493
|
const selector = check.selector || "";
|
|
1450
|
-
const failed =
|
|
1494
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
|
|
1451
1495
|
checks.push({
|
|
1452
1496
|
type: check.type,
|
|
1453
1497
|
label: check.label || check.type,
|
|
1454
1498
|
status: failed.length ? "failed" : "passed",
|
|
1455
|
-
evidence: { selector, visible_counts:
|
|
1499
|
+
evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
|
|
1456
1500
|
message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
|
|
1457
1501
|
});
|
|
1458
1502
|
continue;
|
|
1459
1503
|
}
|
|
1460
1504
|
if (check.type === "selector_absent") {
|
|
1461
1505
|
const selector = check.selector || "";
|
|
1462
|
-
const failed =
|
|
1506
|
+
const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
1463
1507
|
checks.push({
|
|
1464
1508
|
type: check.type,
|
|
1465
1509
|
label: check.label || check.type,
|
|
1466
1510
|
status: failed.length ? "failed" : "passed",
|
|
1467
|
-
evidence: { selector, counts:
|
|
1511
|
+
evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1468
1512
|
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
1469
1513
|
});
|
|
1470
1514
|
continue;
|
|
@@ -1472,12 +1516,12 @@ function assessProfile(profile, evidence) {
|
|
|
1472
1516
|
if (check.type === "selector_count_at_least") {
|
|
1473
1517
|
const selector = check.selector || "";
|
|
1474
1518
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
1475
|
-
const failed =
|
|
1519
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
|
|
1476
1520
|
checks.push({
|
|
1477
1521
|
type: check.type,
|
|
1478
1522
|
label: check.label || check.type,
|
|
1479
1523
|
status: failed.length ? "failed" : "passed",
|
|
1480
|
-
evidence: { selector, min_count: minCount, counts:
|
|
1524
|
+
evidence: { selector, min_count: minCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1481
1525
|
message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
1482
1526
|
});
|
|
1483
1527
|
continue;
|
|
@@ -1485,12 +1529,12 @@ function assessProfile(profile, evidence) {
|
|
|
1485
1529
|
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
1486
1530
|
const selector = check.selector || "";
|
|
1487
1531
|
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
1488
|
-
const failed =
|
|
1532
|
+
const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
1489
1533
|
checks.push({
|
|
1490
1534
|
type: check.type,
|
|
1491
1535
|
label: check.label || check.type,
|
|
1492
1536
|
status: failed.length ? "failed" : "passed",
|
|
1493
|
-
evidence: { selector, expected_count: expectedCount, counts:
|
|
1537
|
+
evidence: { selector, expected_count: expectedCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1494
1538
|
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
1495
1539
|
});
|
|
1496
1540
|
continue;
|
|
@@ -1498,7 +1542,7 @@ function assessProfile(profile, evidence) {
|
|
|
1498
1542
|
if (check.type === "selector_text_order") {
|
|
1499
1543
|
const selector = check.selector || "";
|
|
1500
1544
|
const expectedTexts = check.expected_texts || [];
|
|
1501
|
-
const results =
|
|
1545
|
+
const results = checkViewports.map((viewport) => {
|
|
1502
1546
|
const texts = textSequenceForCheck(viewport, check);
|
|
1503
1547
|
const match = textOrderMatch(texts, expectedTexts);
|
|
1504
1548
|
return {
|
|
@@ -1520,7 +1564,7 @@ function assessProfile(profile, evidence) {
|
|
|
1520
1564
|
}
|
|
1521
1565
|
if (check.type === "frame_text_visible") {
|
|
1522
1566
|
const selector = check.selector || "";
|
|
1523
|
-
const results =
|
|
1567
|
+
const results = checkViewports.map((viewport) => {
|
|
1524
1568
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
1525
1569
|
const matches = frames
|
|
1526
1570
|
.map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
|
|
@@ -1547,7 +1591,7 @@ function assessProfile(profile, evidence) {
|
|
|
1547
1591
|
if (check.type === "frame_no_horizontal_overflow") {
|
|
1548
1592
|
const selector = check.selector || "";
|
|
1549
1593
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
1550
|
-
const results =
|
|
1594
|
+
const results = checkViewports.map((viewport) => {
|
|
1551
1595
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
1552
1596
|
const frameOverflows = frames.map((frame, index) => ({
|
|
1553
1597
|
index,
|
|
@@ -1577,7 +1621,7 @@ function assessProfile(profile, evidence) {
|
|
|
1577
1621
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
1578
1622
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
1579
1623
|
const expectedVisible = check.type === "text_visible";
|
|
1580
|
-
const matches =
|
|
1624
|
+
const matches = checkViewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
|
|
1581
1625
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
1582
1626
|
checks.push({
|
|
1583
1627
|
type: check.type,
|
|
@@ -1589,7 +1633,7 @@ function assessProfile(profile, evidence) {
|
|
|
1589
1633
|
continue;
|
|
1590
1634
|
}
|
|
1591
1635
|
if (check.type === "route_inventory") {
|
|
1592
|
-
const inventories =
|
|
1636
|
+
const inventories = checkViewports
|
|
1593
1637
|
.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
|
|
1594
1638
|
.filter((item) => item.inventory && typeof item.inventory === "object");
|
|
1595
1639
|
if (!inventories.length) {
|
|
@@ -1641,7 +1685,7 @@ function assessProfile(profile, evidence) {
|
|
|
1641
1685
|
}
|
|
1642
1686
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
1643
1687
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
1644
|
-
const applicable = check.type === "no_mobile_horizontal_overflow" ?
|
|
1688
|
+
const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
|
|
1645
1689
|
if (!applicable.length) {
|
|
1646
1690
|
checks.push({
|
|
1647
1691
|
type: check.type,
|
|
@@ -1670,11 +1714,11 @@ function assessProfile(profile, evidence) {
|
|
|
1670
1714
|
}
|
|
1671
1715
|
if (check.type === "no_fatal_console_errors") {
|
|
1672
1716
|
const fatalConsoleEvents = ((evidence.console && evidence.console.events) || []).filter((event) => event && (event.type === "error" || event.type === "assert"));
|
|
1673
|
-
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event
|
|
1674
|
-
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event
|
|
1717
|
+
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
1718
|
+
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
1675
1719
|
const pageErrors = evidence.page_errors || [];
|
|
1676
|
-
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error
|
|
1677
|
-
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error
|
|
1720
|
+
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
1721
|
+
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
1678
1722
|
const fatalCount = unallowedConsoleEvents.length + unallowedPageErrors.length;
|
|
1679
1723
|
checks.push({
|
|
1680
1724
|
type: check.type,
|
package/dist/cli.cjs
CHANGED
|
@@ -7275,6 +7275,7 @@ function normalizeCheck(input, index) {
|
|
|
7275
7275
|
text: stringValue2(input.text),
|
|
7276
7276
|
pattern: stringValue2(input.pattern),
|
|
7277
7277
|
flags: stringValue2(input.flags),
|
|
7278
|
+
viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `checks[${index}] viewports`),
|
|
7278
7279
|
allowed_console_texts: normalizeStringList(input.allowed_console_texts ?? input.allowedConsoleTexts ?? input.allow_console_texts ?? input.allowConsoleTexts, `checks[${index}] allowed_console_texts`),
|
|
7279
7280
|
allowed_console_patterns: normalizeStringList(input.allowed_console_patterns ?? input.allowedConsolePatterns ?? input.allow_console_patterns ?? input.allowConsolePatterns, `checks[${index}] allowed_console_patterns`),
|
|
7280
7281
|
allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
|
|
@@ -7443,8 +7444,17 @@ function matchText(sample, check) {
|
|
|
7443
7444
|
}
|
|
7444
7445
|
return sample.includes(check.text || "");
|
|
7445
7446
|
}
|
|
7446
|
-
function
|
|
7447
|
-
|
|
7447
|
+
function allowedMessageSample(input) {
|
|
7448
|
+
if (!isRecord(input)) return String(input || "");
|
|
7449
|
+
const parts = [
|
|
7450
|
+
input.text,
|
|
7451
|
+
input.message,
|
|
7452
|
+
isRecord(input.location) ? input.location.url : void 0
|
|
7453
|
+
];
|
|
7454
|
+
return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
7455
|
+
}
|
|
7456
|
+
function matchesAllowedMessage(input, texts, patterns) {
|
|
7457
|
+
const sample = allowedMessageSample(input);
|
|
7448
7458
|
if (texts?.some((text) => sample.includes(text))) return true;
|
|
7449
7459
|
for (const pattern of patterns || []) {
|
|
7450
7460
|
try {
|
|
@@ -7504,15 +7514,22 @@ function successfulRoute(route, targetUrl) {
|
|
|
7504
7514
|
const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
|
|
7505
7515
|
return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
|
|
7506
7516
|
}
|
|
7517
|
+
function viewportsForCheck(check, viewports) {
|
|
7518
|
+
if (!check.viewports?.length) return viewports;
|
|
7519
|
+
const names = new Set(check.viewports);
|
|
7520
|
+
return viewports.filter((viewport) => names.has(viewport.name));
|
|
7521
|
+
}
|
|
7507
7522
|
function assessCheckFromEvidence(check, evidence) {
|
|
7508
|
-
const viewports = evidence.viewports || [];
|
|
7523
|
+
const viewports = viewportsForCheck(check, evidence.viewports || []);
|
|
7509
7524
|
if (!viewports.length) {
|
|
7510
7525
|
return {
|
|
7511
7526
|
type: check.type,
|
|
7512
7527
|
label: checkLabel(check),
|
|
7513
7528
|
status: "failed",
|
|
7514
|
-
evidence: {
|
|
7515
|
-
|
|
7529
|
+
evidence: {
|
|
7530
|
+
expected_viewports: check.viewports || []
|
|
7531
|
+
},
|
|
7532
|
+
message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
|
|
7516
7533
|
};
|
|
7517
7534
|
}
|
|
7518
7535
|
if (check.type === "route_loaded") {
|
|
@@ -7772,11 +7789,11 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
7772
7789
|
}
|
|
7773
7790
|
if (check.type === "no_fatal_console_errors") {
|
|
7774
7791
|
const fatalConsoleEvents = (evidence.console?.events || []).filter((event) => event.type === "error" || event.type === "assert");
|
|
7775
|
-
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event
|
|
7776
|
-
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event
|
|
7792
|
+
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
7793
|
+
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
7777
7794
|
const pageErrors = evidence.page_errors || [];
|
|
7778
|
-
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error
|
|
7779
|
-
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error
|
|
7795
|
+
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
7796
|
+
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
7780
7797
|
const fatalCount = unallowedConsoleEvents.length + unallowedPageErrors.length;
|
|
7781
7798
|
return {
|
|
7782
7799
|
type: check.type,
|
|
@@ -8045,8 +8062,17 @@ function textMatches(sample, check) {
|
|
|
8045
8062
|
}
|
|
8046
8063
|
return String(sample || "").includes(check.text || "");
|
|
8047
8064
|
}
|
|
8048
|
-
function
|
|
8049
|
-
|
|
8065
|
+
function allowedMessageSample(input) {
|
|
8066
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return String(input || "");
|
|
8067
|
+
const parts = [
|
|
8068
|
+
input.text,
|
|
8069
|
+
input.message,
|
|
8070
|
+
input.location && typeof input.location === "object" ? input.location.url : undefined,
|
|
8071
|
+
];
|
|
8072
|
+
return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
8073
|
+
}
|
|
8074
|
+
function matchesAllowedMessage(input, texts, patterns) {
|
|
8075
|
+
const sample = allowedMessageSample(input);
|
|
8050
8076
|
if ((texts || []).some((text) => sample.includes(text))) return true;
|
|
8051
8077
|
for (const pattern of patterns || []) {
|
|
8052
8078
|
try {
|
|
@@ -8092,6 +8118,11 @@ function frameTextSample(frame) {
|
|
|
8092
8118
|
frame && frame.text,
|
|
8093
8119
|
].map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
8094
8120
|
}
|
|
8121
|
+
function viewportsForCheck(check, viewports) {
|
|
8122
|
+
if (!Array.isArray(check.viewports) || !check.viewports.length) return viewports;
|
|
8123
|
+
const names = new Set(check.viewports);
|
|
8124
|
+
return viewports.filter((viewport) => names.has(viewport.name));
|
|
8125
|
+
}
|
|
8095
8126
|
function summarizeRouteInventory(viewport, inventory) {
|
|
8096
8127
|
const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
|
|
8097
8128
|
const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
|
|
@@ -8282,9 +8313,22 @@ function assessProfile(profile, evidence) {
|
|
|
8282
8313
|
});
|
|
8283
8314
|
}
|
|
8284
8315
|
for (const check of profile.checks || []) {
|
|
8316
|
+
const checkViewports = viewportsForCheck(check, viewports);
|
|
8317
|
+
if (!checkViewports.length) {
|
|
8318
|
+
checks.push({
|
|
8319
|
+
type: check.type,
|
|
8320
|
+
label: check.label || check.type,
|
|
8321
|
+
status: "failed",
|
|
8322
|
+
evidence: { expected_viewports: check.viewports || [] },
|
|
8323
|
+
message: Array.isArray(check.viewports) && check.viewports.length
|
|
8324
|
+
? "No matching viewport evidence was captured for " + check.viewports.join(", ") + "."
|
|
8325
|
+
: "No viewport evidence was captured.",
|
|
8326
|
+
});
|
|
8327
|
+
continue;
|
|
8328
|
+
}
|
|
8285
8329
|
if (check.type === "route_loaded") {
|
|
8286
8330
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
8287
|
-
const failed =
|
|
8331
|
+
const failed = checkViewports.filter((viewport) => {
|
|
8288
8332
|
const route = { ...(viewport.route || {}), expected_path: expectedPath };
|
|
8289
8333
|
route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
|
|
8290
8334
|
return !routeOk(route, evidence.target_url);
|
|
@@ -8295,8 +8339,8 @@ function assessProfile(profile, evidence) {
|
|
|
8295
8339
|
status: failed.length ? "failed" : "passed",
|
|
8296
8340
|
evidence: {
|
|
8297
8341
|
expected_path: expectedPath,
|
|
8298
|
-
observed_paths:
|
|
8299
|
-
http_statuses:
|
|
8342
|
+
observed_paths: checkViewports.map((viewport) => viewport.route && viewport.route.observed),
|
|
8343
|
+
http_statuses: checkViewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
|
|
8300
8344
|
},
|
|
8301
8345
|
message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
|
|
8302
8346
|
});
|
|
@@ -8304,24 +8348,24 @@ function assessProfile(profile, evidence) {
|
|
|
8304
8348
|
}
|
|
8305
8349
|
if (check.type === "selector_visible") {
|
|
8306
8350
|
const selector = check.selector || "";
|
|
8307
|
-
const failed =
|
|
8351
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
|
|
8308
8352
|
checks.push({
|
|
8309
8353
|
type: check.type,
|
|
8310
8354
|
label: check.label || check.type,
|
|
8311
8355
|
status: failed.length ? "failed" : "passed",
|
|
8312
|
-
evidence: { selector, visible_counts:
|
|
8356
|
+
evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
|
|
8313
8357
|
message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
|
|
8314
8358
|
});
|
|
8315
8359
|
continue;
|
|
8316
8360
|
}
|
|
8317
8361
|
if (check.type === "selector_absent") {
|
|
8318
8362
|
const selector = check.selector || "";
|
|
8319
|
-
const failed =
|
|
8363
|
+
const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
8320
8364
|
checks.push({
|
|
8321
8365
|
type: check.type,
|
|
8322
8366
|
label: check.label || check.type,
|
|
8323
8367
|
status: failed.length ? "failed" : "passed",
|
|
8324
|
-
evidence: { selector, counts:
|
|
8368
|
+
evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
8325
8369
|
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
8326
8370
|
});
|
|
8327
8371
|
continue;
|
|
@@ -8329,12 +8373,12 @@ function assessProfile(profile, evidence) {
|
|
|
8329
8373
|
if (check.type === "selector_count_at_least") {
|
|
8330
8374
|
const selector = check.selector || "";
|
|
8331
8375
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
8332
|
-
const failed =
|
|
8376
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
|
|
8333
8377
|
checks.push({
|
|
8334
8378
|
type: check.type,
|
|
8335
8379
|
label: check.label || check.type,
|
|
8336
8380
|
status: failed.length ? "failed" : "passed",
|
|
8337
|
-
evidence: { selector, min_count: minCount, counts:
|
|
8381
|
+
evidence: { selector, min_count: minCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
8338
8382
|
message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
8339
8383
|
});
|
|
8340
8384
|
continue;
|
|
@@ -8342,12 +8386,12 @@ function assessProfile(profile, evidence) {
|
|
|
8342
8386
|
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
8343
8387
|
const selector = check.selector || "";
|
|
8344
8388
|
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
8345
|
-
const failed =
|
|
8389
|
+
const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
8346
8390
|
checks.push({
|
|
8347
8391
|
type: check.type,
|
|
8348
8392
|
label: check.label || check.type,
|
|
8349
8393
|
status: failed.length ? "failed" : "passed",
|
|
8350
|
-
evidence: { selector, expected_count: expectedCount, counts:
|
|
8394
|
+
evidence: { selector, expected_count: expectedCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
8351
8395
|
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
8352
8396
|
});
|
|
8353
8397
|
continue;
|
|
@@ -8355,7 +8399,7 @@ function assessProfile(profile, evidence) {
|
|
|
8355
8399
|
if (check.type === "selector_text_order") {
|
|
8356
8400
|
const selector = check.selector || "";
|
|
8357
8401
|
const expectedTexts = check.expected_texts || [];
|
|
8358
|
-
const results =
|
|
8402
|
+
const results = checkViewports.map((viewport) => {
|
|
8359
8403
|
const texts = textSequenceForCheck(viewport, check);
|
|
8360
8404
|
const match = textOrderMatch(texts, expectedTexts);
|
|
8361
8405
|
return {
|
|
@@ -8377,7 +8421,7 @@ function assessProfile(profile, evidence) {
|
|
|
8377
8421
|
}
|
|
8378
8422
|
if (check.type === "frame_text_visible") {
|
|
8379
8423
|
const selector = check.selector || "";
|
|
8380
|
-
const results =
|
|
8424
|
+
const results = checkViewports.map((viewport) => {
|
|
8381
8425
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
8382
8426
|
const matches = frames
|
|
8383
8427
|
.map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
|
|
@@ -8404,7 +8448,7 @@ function assessProfile(profile, evidence) {
|
|
|
8404
8448
|
if (check.type === "frame_no_horizontal_overflow") {
|
|
8405
8449
|
const selector = check.selector || "";
|
|
8406
8450
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
8407
|
-
const results =
|
|
8451
|
+
const results = checkViewports.map((viewport) => {
|
|
8408
8452
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
8409
8453
|
const frameOverflows = frames.map((frame, index) => ({
|
|
8410
8454
|
index,
|
|
@@ -8434,7 +8478,7 @@ function assessProfile(profile, evidence) {
|
|
|
8434
8478
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
8435
8479
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
8436
8480
|
const expectedVisible = check.type === "text_visible";
|
|
8437
|
-
const matches =
|
|
8481
|
+
const matches = checkViewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
|
|
8438
8482
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
8439
8483
|
checks.push({
|
|
8440
8484
|
type: check.type,
|
|
@@ -8446,7 +8490,7 @@ function assessProfile(profile, evidence) {
|
|
|
8446
8490
|
continue;
|
|
8447
8491
|
}
|
|
8448
8492
|
if (check.type === "route_inventory") {
|
|
8449
|
-
const inventories =
|
|
8493
|
+
const inventories = checkViewports
|
|
8450
8494
|
.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
|
|
8451
8495
|
.filter((item) => item.inventory && typeof item.inventory === "object");
|
|
8452
8496
|
if (!inventories.length) {
|
|
@@ -8498,7 +8542,7 @@ function assessProfile(profile, evidence) {
|
|
|
8498
8542
|
}
|
|
8499
8543
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
8500
8544
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
8501
|
-
const applicable = check.type === "no_mobile_horizontal_overflow" ?
|
|
8545
|
+
const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
|
|
8502
8546
|
if (!applicable.length) {
|
|
8503
8547
|
checks.push({
|
|
8504
8548
|
type: check.type,
|
|
@@ -8527,11 +8571,11 @@ function assessProfile(profile, evidence) {
|
|
|
8527
8571
|
}
|
|
8528
8572
|
if (check.type === "no_fatal_console_errors") {
|
|
8529
8573
|
const fatalConsoleEvents = ((evidence.console && evidence.console.events) || []).filter((event) => event && (event.type === "error" || event.type === "assert"));
|
|
8530
|
-
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event
|
|
8531
|
-
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event
|
|
8574
|
+
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
8575
|
+
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
8532
8576
|
const pageErrors = evidence.page_errors || [];
|
|
8533
|
-
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error
|
|
8534
|
-
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error
|
|
8577
|
+
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
8578
|
+
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
8535
8579
|
const fatalCount = unallowedConsoleEvents.length + unallowedPageErrors.length;
|
|
8536
8580
|
checks.push({
|
|
8537
8581
|
type: check.type,
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -9116,6 +9116,7 @@ function normalizeCheck(input, index) {
|
|
|
9116
9116
|
text: stringValue5(input.text),
|
|
9117
9117
|
pattern: stringValue5(input.pattern),
|
|
9118
9118
|
flags: stringValue5(input.flags),
|
|
9119
|
+
viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `checks[${index}] viewports`),
|
|
9119
9120
|
allowed_console_texts: normalizeStringList(input.allowed_console_texts ?? input.allowedConsoleTexts ?? input.allow_console_texts ?? input.allowConsoleTexts, `checks[${index}] allowed_console_texts`),
|
|
9120
9121
|
allowed_console_patterns: normalizeStringList(input.allowed_console_patterns ?? input.allowedConsolePatterns ?? input.allow_console_patterns ?? input.allowConsolePatterns, `checks[${index}] allowed_console_patterns`),
|
|
9121
9122
|
allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
|
|
@@ -9284,8 +9285,17 @@ function matchText(sample, check) {
|
|
|
9284
9285
|
}
|
|
9285
9286
|
return sample.includes(check.text || "");
|
|
9286
9287
|
}
|
|
9287
|
-
function
|
|
9288
|
-
|
|
9288
|
+
function allowedMessageSample(input) {
|
|
9289
|
+
if (!isRecord2(input)) return String(input || "");
|
|
9290
|
+
const parts = [
|
|
9291
|
+
input.text,
|
|
9292
|
+
input.message,
|
|
9293
|
+
isRecord2(input.location) ? input.location.url : void 0
|
|
9294
|
+
];
|
|
9295
|
+
return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
9296
|
+
}
|
|
9297
|
+
function matchesAllowedMessage(input, texts, patterns) {
|
|
9298
|
+
const sample = allowedMessageSample(input);
|
|
9289
9299
|
if (texts?.some((text) => sample.includes(text))) return true;
|
|
9290
9300
|
for (const pattern of patterns || []) {
|
|
9291
9301
|
try {
|
|
@@ -9345,15 +9355,22 @@ function successfulRoute(route, targetUrl) {
|
|
|
9345
9355
|
const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
|
|
9346
9356
|
return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
|
|
9347
9357
|
}
|
|
9358
|
+
function viewportsForCheck(check, viewports) {
|
|
9359
|
+
if (!check.viewports?.length) return viewports;
|
|
9360
|
+
const names = new Set(check.viewports);
|
|
9361
|
+
return viewports.filter((viewport) => names.has(viewport.name));
|
|
9362
|
+
}
|
|
9348
9363
|
function assessCheckFromEvidence(check, evidence) {
|
|
9349
|
-
const viewports = evidence.viewports || [];
|
|
9364
|
+
const viewports = viewportsForCheck(check, evidence.viewports || []);
|
|
9350
9365
|
if (!viewports.length) {
|
|
9351
9366
|
return {
|
|
9352
9367
|
type: check.type,
|
|
9353
9368
|
label: checkLabel(check),
|
|
9354
9369
|
status: "failed",
|
|
9355
|
-
evidence: {
|
|
9356
|
-
|
|
9370
|
+
evidence: {
|
|
9371
|
+
expected_viewports: check.viewports || []
|
|
9372
|
+
},
|
|
9373
|
+
message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
|
|
9357
9374
|
};
|
|
9358
9375
|
}
|
|
9359
9376
|
if (check.type === "route_loaded") {
|
|
@@ -9613,11 +9630,11 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
9613
9630
|
}
|
|
9614
9631
|
if (check.type === "no_fatal_console_errors") {
|
|
9615
9632
|
const fatalConsoleEvents = (evidence.console?.events || []).filter((event) => event.type === "error" || event.type === "assert");
|
|
9616
|
-
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event
|
|
9617
|
-
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event
|
|
9633
|
+
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
9634
|
+
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
9618
9635
|
const pageErrors = evidence.page_errors || [];
|
|
9619
|
-
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error
|
|
9620
|
-
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error
|
|
9636
|
+
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
9637
|
+
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
9621
9638
|
const fatalCount = unallowedConsoleEvents.length + unallowedPageErrors.length;
|
|
9622
9639
|
return {
|
|
9623
9640
|
type: check.type,
|
|
@@ -9902,8 +9919,17 @@ function textMatches(sample, check) {
|
|
|
9902
9919
|
}
|
|
9903
9920
|
return String(sample || "").includes(check.text || "");
|
|
9904
9921
|
}
|
|
9905
|
-
function
|
|
9906
|
-
|
|
9922
|
+
function allowedMessageSample(input) {
|
|
9923
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return String(input || "");
|
|
9924
|
+
const parts = [
|
|
9925
|
+
input.text,
|
|
9926
|
+
input.message,
|
|
9927
|
+
input.location && typeof input.location === "object" ? input.location.url : undefined,
|
|
9928
|
+
];
|
|
9929
|
+
return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
9930
|
+
}
|
|
9931
|
+
function matchesAllowedMessage(input, texts, patterns) {
|
|
9932
|
+
const sample = allowedMessageSample(input);
|
|
9907
9933
|
if ((texts || []).some((text) => sample.includes(text))) return true;
|
|
9908
9934
|
for (const pattern of patterns || []) {
|
|
9909
9935
|
try {
|
|
@@ -9949,6 +9975,11 @@ function frameTextSample(frame) {
|
|
|
9949
9975
|
frame && frame.text,
|
|
9950
9976
|
].map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
9951
9977
|
}
|
|
9978
|
+
function viewportsForCheck(check, viewports) {
|
|
9979
|
+
if (!Array.isArray(check.viewports) || !check.viewports.length) return viewports;
|
|
9980
|
+
const names = new Set(check.viewports);
|
|
9981
|
+
return viewports.filter((viewport) => names.has(viewport.name));
|
|
9982
|
+
}
|
|
9952
9983
|
function summarizeRouteInventory(viewport, inventory) {
|
|
9953
9984
|
const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
|
|
9954
9985
|
const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
|
|
@@ -10139,9 +10170,22 @@ function assessProfile(profile, evidence) {
|
|
|
10139
10170
|
});
|
|
10140
10171
|
}
|
|
10141
10172
|
for (const check of profile.checks || []) {
|
|
10173
|
+
const checkViewports = viewportsForCheck(check, viewports);
|
|
10174
|
+
if (!checkViewports.length) {
|
|
10175
|
+
checks.push({
|
|
10176
|
+
type: check.type,
|
|
10177
|
+
label: check.label || check.type,
|
|
10178
|
+
status: "failed",
|
|
10179
|
+
evidence: { expected_viewports: check.viewports || [] },
|
|
10180
|
+
message: Array.isArray(check.viewports) && check.viewports.length
|
|
10181
|
+
? "No matching viewport evidence was captured for " + check.viewports.join(", ") + "."
|
|
10182
|
+
: "No viewport evidence was captured.",
|
|
10183
|
+
});
|
|
10184
|
+
continue;
|
|
10185
|
+
}
|
|
10142
10186
|
if (check.type === "route_loaded") {
|
|
10143
10187
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
10144
|
-
const failed =
|
|
10188
|
+
const failed = checkViewports.filter((viewport) => {
|
|
10145
10189
|
const route = { ...(viewport.route || {}), expected_path: expectedPath };
|
|
10146
10190
|
route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
|
|
10147
10191
|
return !routeOk(route, evidence.target_url);
|
|
@@ -10152,8 +10196,8 @@ function assessProfile(profile, evidence) {
|
|
|
10152
10196
|
status: failed.length ? "failed" : "passed",
|
|
10153
10197
|
evidence: {
|
|
10154
10198
|
expected_path: expectedPath,
|
|
10155
|
-
observed_paths:
|
|
10156
|
-
http_statuses:
|
|
10199
|
+
observed_paths: checkViewports.map((viewport) => viewport.route && viewport.route.observed),
|
|
10200
|
+
http_statuses: checkViewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
|
|
10157
10201
|
},
|
|
10158
10202
|
message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
|
|
10159
10203
|
});
|
|
@@ -10161,24 +10205,24 @@ function assessProfile(profile, evidence) {
|
|
|
10161
10205
|
}
|
|
10162
10206
|
if (check.type === "selector_visible") {
|
|
10163
10207
|
const selector = check.selector || "";
|
|
10164
|
-
const failed =
|
|
10208
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
|
|
10165
10209
|
checks.push({
|
|
10166
10210
|
type: check.type,
|
|
10167
10211
|
label: check.label || check.type,
|
|
10168
10212
|
status: failed.length ? "failed" : "passed",
|
|
10169
|
-
evidence: { selector, visible_counts:
|
|
10213
|
+
evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
|
|
10170
10214
|
message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
|
|
10171
10215
|
});
|
|
10172
10216
|
continue;
|
|
10173
10217
|
}
|
|
10174
10218
|
if (check.type === "selector_absent") {
|
|
10175
10219
|
const selector = check.selector || "";
|
|
10176
|
-
const failed =
|
|
10220
|
+
const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
10177
10221
|
checks.push({
|
|
10178
10222
|
type: check.type,
|
|
10179
10223
|
label: check.label || check.type,
|
|
10180
10224
|
status: failed.length ? "failed" : "passed",
|
|
10181
|
-
evidence: { selector, counts:
|
|
10225
|
+
evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
10182
10226
|
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
10183
10227
|
});
|
|
10184
10228
|
continue;
|
|
@@ -10186,12 +10230,12 @@ function assessProfile(profile, evidence) {
|
|
|
10186
10230
|
if (check.type === "selector_count_at_least") {
|
|
10187
10231
|
const selector = check.selector || "";
|
|
10188
10232
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
10189
|
-
const failed =
|
|
10233
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
|
|
10190
10234
|
checks.push({
|
|
10191
10235
|
type: check.type,
|
|
10192
10236
|
label: check.label || check.type,
|
|
10193
10237
|
status: failed.length ? "failed" : "passed",
|
|
10194
|
-
evidence: { selector, min_count: minCount, counts:
|
|
10238
|
+
evidence: { selector, min_count: minCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
10195
10239
|
message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
10196
10240
|
});
|
|
10197
10241
|
continue;
|
|
@@ -10199,12 +10243,12 @@ function assessProfile(profile, evidence) {
|
|
|
10199
10243
|
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
10200
10244
|
const selector = check.selector || "";
|
|
10201
10245
|
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
10202
|
-
const failed =
|
|
10246
|
+
const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
10203
10247
|
checks.push({
|
|
10204
10248
|
type: check.type,
|
|
10205
10249
|
label: check.label || check.type,
|
|
10206
10250
|
status: failed.length ? "failed" : "passed",
|
|
10207
|
-
evidence: { selector, expected_count: expectedCount, counts:
|
|
10251
|
+
evidence: { selector, expected_count: expectedCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
10208
10252
|
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
10209
10253
|
});
|
|
10210
10254
|
continue;
|
|
@@ -10212,7 +10256,7 @@ function assessProfile(profile, evidence) {
|
|
|
10212
10256
|
if (check.type === "selector_text_order") {
|
|
10213
10257
|
const selector = check.selector || "";
|
|
10214
10258
|
const expectedTexts = check.expected_texts || [];
|
|
10215
|
-
const results =
|
|
10259
|
+
const results = checkViewports.map((viewport) => {
|
|
10216
10260
|
const texts = textSequenceForCheck(viewport, check);
|
|
10217
10261
|
const match = textOrderMatch(texts, expectedTexts);
|
|
10218
10262
|
return {
|
|
@@ -10234,7 +10278,7 @@ function assessProfile(profile, evidence) {
|
|
|
10234
10278
|
}
|
|
10235
10279
|
if (check.type === "frame_text_visible") {
|
|
10236
10280
|
const selector = check.selector || "";
|
|
10237
|
-
const results =
|
|
10281
|
+
const results = checkViewports.map((viewport) => {
|
|
10238
10282
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
10239
10283
|
const matches = frames
|
|
10240
10284
|
.map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
|
|
@@ -10261,7 +10305,7 @@ function assessProfile(profile, evidence) {
|
|
|
10261
10305
|
if (check.type === "frame_no_horizontal_overflow") {
|
|
10262
10306
|
const selector = check.selector || "";
|
|
10263
10307
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
10264
|
-
const results =
|
|
10308
|
+
const results = checkViewports.map((viewport) => {
|
|
10265
10309
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
10266
10310
|
const frameOverflows = frames.map((frame, index) => ({
|
|
10267
10311
|
index,
|
|
@@ -10291,7 +10335,7 @@ function assessProfile(profile, evidence) {
|
|
|
10291
10335
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
10292
10336
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
10293
10337
|
const expectedVisible = check.type === "text_visible";
|
|
10294
|
-
const matches =
|
|
10338
|
+
const matches = checkViewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
|
|
10295
10339
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
10296
10340
|
checks.push({
|
|
10297
10341
|
type: check.type,
|
|
@@ -10303,7 +10347,7 @@ function assessProfile(profile, evidence) {
|
|
|
10303
10347
|
continue;
|
|
10304
10348
|
}
|
|
10305
10349
|
if (check.type === "route_inventory") {
|
|
10306
|
-
const inventories =
|
|
10350
|
+
const inventories = checkViewports
|
|
10307
10351
|
.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
|
|
10308
10352
|
.filter((item) => item.inventory && typeof item.inventory === "object");
|
|
10309
10353
|
if (!inventories.length) {
|
|
@@ -10355,7 +10399,7 @@ function assessProfile(profile, evidence) {
|
|
|
10355
10399
|
}
|
|
10356
10400
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
10357
10401
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
10358
|
-
const applicable = check.type === "no_mobile_horizontal_overflow" ?
|
|
10402
|
+
const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
|
|
10359
10403
|
if (!applicable.length) {
|
|
10360
10404
|
checks.push({
|
|
10361
10405
|
type: check.type,
|
|
@@ -10384,11 +10428,11 @@ function assessProfile(profile, evidence) {
|
|
|
10384
10428
|
}
|
|
10385
10429
|
if (check.type === "no_fatal_console_errors") {
|
|
10386
10430
|
const fatalConsoleEvents = ((evidence.console && evidence.console.events) || []).filter((event) => event && (event.type === "error" || event.type === "assert"));
|
|
10387
|
-
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event
|
|
10388
|
-
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event
|
|
10431
|
+
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
10432
|
+
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
10389
10433
|
const pageErrors = evidence.page_errors || [];
|
|
10390
|
-
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error
|
|
10391
|
-
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error
|
|
10434
|
+
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
10435
|
+
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
10392
10436
|
const fatalCount = unallowedConsoleEvents.length + unallowedPageErrors.length;
|
|
10393
10437
|
checks.push({
|
|
10394
10438
|
type: check.type,
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
resolveRiddleProofProfileTimeoutSec,
|
|
59
59
|
slugifyRiddleProofProfileName,
|
|
60
60
|
summarizeRiddleProofProfileResult
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-G673RKAC.js";
|
|
62
62
|
import {
|
|
63
63
|
DEFAULT_RIDDLE_API_BASE_URL,
|
|
64
64
|
DEFAULT_RIDDLE_API_KEY_FILE,
|
package/dist/profile.cjs
CHANGED
|
@@ -445,6 +445,7 @@ function normalizeCheck(input, index) {
|
|
|
445
445
|
text: stringValue(input.text),
|
|
446
446
|
pattern: stringValue(input.pattern),
|
|
447
447
|
flags: stringValue(input.flags),
|
|
448
|
+
viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `checks[${index}] viewports`),
|
|
448
449
|
allowed_console_texts: normalizeStringList(input.allowed_console_texts ?? input.allowedConsoleTexts ?? input.allow_console_texts ?? input.allowConsoleTexts, `checks[${index}] allowed_console_texts`),
|
|
449
450
|
allowed_console_patterns: normalizeStringList(input.allowed_console_patterns ?? input.allowedConsolePatterns ?? input.allow_console_patterns ?? input.allowConsolePatterns, `checks[${index}] allowed_console_patterns`),
|
|
450
451
|
allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
|
|
@@ -613,8 +614,17 @@ function matchText(sample, check) {
|
|
|
613
614
|
}
|
|
614
615
|
return sample.includes(check.text || "");
|
|
615
616
|
}
|
|
616
|
-
function
|
|
617
|
-
|
|
617
|
+
function allowedMessageSample(input) {
|
|
618
|
+
if (!isRecord(input)) return String(input || "");
|
|
619
|
+
const parts = [
|
|
620
|
+
input.text,
|
|
621
|
+
input.message,
|
|
622
|
+
isRecord(input.location) ? input.location.url : void 0
|
|
623
|
+
];
|
|
624
|
+
return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
625
|
+
}
|
|
626
|
+
function matchesAllowedMessage(input, texts, patterns) {
|
|
627
|
+
const sample = allowedMessageSample(input);
|
|
618
628
|
if (texts?.some((text) => sample.includes(text))) return true;
|
|
619
629
|
for (const pattern of patterns || []) {
|
|
620
630
|
try {
|
|
@@ -674,15 +684,22 @@ function successfulRoute(route, targetUrl) {
|
|
|
674
684
|
const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
|
|
675
685
|
return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
|
|
676
686
|
}
|
|
687
|
+
function viewportsForCheck(check, viewports) {
|
|
688
|
+
if (!check.viewports?.length) return viewports;
|
|
689
|
+
const names = new Set(check.viewports);
|
|
690
|
+
return viewports.filter((viewport) => names.has(viewport.name));
|
|
691
|
+
}
|
|
677
692
|
function assessCheckFromEvidence(check, evidence) {
|
|
678
|
-
const viewports = evidence.viewports || [];
|
|
693
|
+
const viewports = viewportsForCheck(check, evidence.viewports || []);
|
|
679
694
|
if (!viewports.length) {
|
|
680
695
|
return {
|
|
681
696
|
type: check.type,
|
|
682
697
|
label: checkLabel(check),
|
|
683
698
|
status: "failed",
|
|
684
|
-
evidence: {
|
|
685
|
-
|
|
699
|
+
evidence: {
|
|
700
|
+
expected_viewports: check.viewports || []
|
|
701
|
+
},
|
|
702
|
+
message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
|
|
686
703
|
};
|
|
687
704
|
}
|
|
688
705
|
if (check.type === "route_loaded") {
|
|
@@ -942,11 +959,11 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
942
959
|
}
|
|
943
960
|
if (check.type === "no_fatal_console_errors") {
|
|
944
961
|
const fatalConsoleEvents = (evidence.console?.events || []).filter((event) => event.type === "error" || event.type === "assert");
|
|
945
|
-
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event
|
|
946
|
-
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event
|
|
962
|
+
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
963
|
+
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
947
964
|
const pageErrors = evidence.page_errors || [];
|
|
948
|
-
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error
|
|
949
|
-
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error
|
|
965
|
+
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
966
|
+
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
950
967
|
const fatalCount = unallowedConsoleEvents.length + unallowedPageErrors.length;
|
|
951
968
|
return {
|
|
952
969
|
type: check.type,
|
|
@@ -1231,8 +1248,17 @@ function textMatches(sample, check) {
|
|
|
1231
1248
|
}
|
|
1232
1249
|
return String(sample || "").includes(check.text || "");
|
|
1233
1250
|
}
|
|
1234
|
-
function
|
|
1235
|
-
|
|
1251
|
+
function allowedMessageSample(input) {
|
|
1252
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return String(input || "");
|
|
1253
|
+
const parts = [
|
|
1254
|
+
input.text,
|
|
1255
|
+
input.message,
|
|
1256
|
+
input.location && typeof input.location === "object" ? input.location.url : undefined,
|
|
1257
|
+
];
|
|
1258
|
+
return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
1259
|
+
}
|
|
1260
|
+
function matchesAllowedMessage(input, texts, patterns) {
|
|
1261
|
+
const sample = allowedMessageSample(input);
|
|
1236
1262
|
if ((texts || []).some((text) => sample.includes(text))) return true;
|
|
1237
1263
|
for (const pattern of patterns || []) {
|
|
1238
1264
|
try {
|
|
@@ -1278,6 +1304,11 @@ function frameTextSample(frame) {
|
|
|
1278
1304
|
frame && frame.text,
|
|
1279
1305
|
].map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
1280
1306
|
}
|
|
1307
|
+
function viewportsForCheck(check, viewports) {
|
|
1308
|
+
if (!Array.isArray(check.viewports) || !check.viewports.length) return viewports;
|
|
1309
|
+
const names = new Set(check.viewports);
|
|
1310
|
+
return viewports.filter((viewport) => names.has(viewport.name));
|
|
1311
|
+
}
|
|
1281
1312
|
function summarizeRouteInventory(viewport, inventory) {
|
|
1282
1313
|
const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
|
|
1283
1314
|
const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
|
|
@@ -1468,9 +1499,22 @@ function assessProfile(profile, evidence) {
|
|
|
1468
1499
|
});
|
|
1469
1500
|
}
|
|
1470
1501
|
for (const check of profile.checks || []) {
|
|
1502
|
+
const checkViewports = viewportsForCheck(check, viewports);
|
|
1503
|
+
if (!checkViewports.length) {
|
|
1504
|
+
checks.push({
|
|
1505
|
+
type: check.type,
|
|
1506
|
+
label: check.label || check.type,
|
|
1507
|
+
status: "failed",
|
|
1508
|
+
evidence: { expected_viewports: check.viewports || [] },
|
|
1509
|
+
message: Array.isArray(check.viewports) && check.viewports.length
|
|
1510
|
+
? "No matching viewport evidence was captured for " + check.viewports.join(", ") + "."
|
|
1511
|
+
: "No viewport evidence was captured.",
|
|
1512
|
+
});
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1471
1515
|
if (check.type === "route_loaded") {
|
|
1472
1516
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
1473
|
-
const failed =
|
|
1517
|
+
const failed = checkViewports.filter((viewport) => {
|
|
1474
1518
|
const route = { ...(viewport.route || {}), expected_path: expectedPath };
|
|
1475
1519
|
route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
|
|
1476
1520
|
return !routeOk(route, evidence.target_url);
|
|
@@ -1481,8 +1525,8 @@ function assessProfile(profile, evidence) {
|
|
|
1481
1525
|
status: failed.length ? "failed" : "passed",
|
|
1482
1526
|
evidence: {
|
|
1483
1527
|
expected_path: expectedPath,
|
|
1484
|
-
observed_paths:
|
|
1485
|
-
http_statuses:
|
|
1528
|
+
observed_paths: checkViewports.map((viewport) => viewport.route && viewport.route.observed),
|
|
1529
|
+
http_statuses: checkViewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
|
|
1486
1530
|
},
|
|
1487
1531
|
message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
|
|
1488
1532
|
});
|
|
@@ -1490,24 +1534,24 @@ function assessProfile(profile, evidence) {
|
|
|
1490
1534
|
}
|
|
1491
1535
|
if (check.type === "selector_visible") {
|
|
1492
1536
|
const selector = check.selector || "";
|
|
1493
|
-
const failed =
|
|
1537
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
|
|
1494
1538
|
checks.push({
|
|
1495
1539
|
type: check.type,
|
|
1496
1540
|
label: check.label || check.type,
|
|
1497
1541
|
status: failed.length ? "failed" : "passed",
|
|
1498
|
-
evidence: { selector, visible_counts:
|
|
1542
|
+
evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
|
|
1499
1543
|
message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
|
|
1500
1544
|
});
|
|
1501
1545
|
continue;
|
|
1502
1546
|
}
|
|
1503
1547
|
if (check.type === "selector_absent") {
|
|
1504
1548
|
const selector = check.selector || "";
|
|
1505
|
-
const failed =
|
|
1549
|
+
const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
1506
1550
|
checks.push({
|
|
1507
1551
|
type: check.type,
|
|
1508
1552
|
label: check.label || check.type,
|
|
1509
1553
|
status: failed.length ? "failed" : "passed",
|
|
1510
|
-
evidence: { selector, counts:
|
|
1554
|
+
evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1511
1555
|
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
1512
1556
|
});
|
|
1513
1557
|
continue;
|
|
@@ -1515,12 +1559,12 @@ function assessProfile(profile, evidence) {
|
|
|
1515
1559
|
if (check.type === "selector_count_at_least") {
|
|
1516
1560
|
const selector = check.selector || "";
|
|
1517
1561
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
1518
|
-
const failed =
|
|
1562
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
|
|
1519
1563
|
checks.push({
|
|
1520
1564
|
type: check.type,
|
|
1521
1565
|
label: check.label || check.type,
|
|
1522
1566
|
status: failed.length ? "failed" : "passed",
|
|
1523
|
-
evidence: { selector, min_count: minCount, counts:
|
|
1567
|
+
evidence: { selector, min_count: minCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1524
1568
|
message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
1525
1569
|
});
|
|
1526
1570
|
continue;
|
|
@@ -1528,12 +1572,12 @@ function assessProfile(profile, evidence) {
|
|
|
1528
1572
|
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
1529
1573
|
const selector = check.selector || "";
|
|
1530
1574
|
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
1531
|
-
const failed =
|
|
1575
|
+
const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
1532
1576
|
checks.push({
|
|
1533
1577
|
type: check.type,
|
|
1534
1578
|
label: check.label || check.type,
|
|
1535
1579
|
status: failed.length ? "failed" : "passed",
|
|
1536
|
-
evidence: { selector, expected_count: expectedCount, counts:
|
|
1580
|
+
evidence: { selector, expected_count: expectedCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1537
1581
|
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
1538
1582
|
});
|
|
1539
1583
|
continue;
|
|
@@ -1541,7 +1585,7 @@ function assessProfile(profile, evidence) {
|
|
|
1541
1585
|
if (check.type === "selector_text_order") {
|
|
1542
1586
|
const selector = check.selector || "";
|
|
1543
1587
|
const expectedTexts = check.expected_texts || [];
|
|
1544
|
-
const results =
|
|
1588
|
+
const results = checkViewports.map((viewport) => {
|
|
1545
1589
|
const texts = textSequenceForCheck(viewport, check);
|
|
1546
1590
|
const match = textOrderMatch(texts, expectedTexts);
|
|
1547
1591
|
return {
|
|
@@ -1563,7 +1607,7 @@ function assessProfile(profile, evidence) {
|
|
|
1563
1607
|
}
|
|
1564
1608
|
if (check.type === "frame_text_visible") {
|
|
1565
1609
|
const selector = check.selector || "";
|
|
1566
|
-
const results =
|
|
1610
|
+
const results = checkViewports.map((viewport) => {
|
|
1567
1611
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
1568
1612
|
const matches = frames
|
|
1569
1613
|
.map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
|
|
@@ -1590,7 +1634,7 @@ function assessProfile(profile, evidence) {
|
|
|
1590
1634
|
if (check.type === "frame_no_horizontal_overflow") {
|
|
1591
1635
|
const selector = check.selector || "";
|
|
1592
1636
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
1593
|
-
const results =
|
|
1637
|
+
const results = checkViewports.map((viewport) => {
|
|
1594
1638
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
1595
1639
|
const frameOverflows = frames.map((frame, index) => ({
|
|
1596
1640
|
index,
|
|
@@ -1620,7 +1664,7 @@ function assessProfile(profile, evidence) {
|
|
|
1620
1664
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
1621
1665
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
1622
1666
|
const expectedVisible = check.type === "text_visible";
|
|
1623
|
-
const matches =
|
|
1667
|
+
const matches = checkViewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
|
|
1624
1668
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
1625
1669
|
checks.push({
|
|
1626
1670
|
type: check.type,
|
|
@@ -1632,7 +1676,7 @@ function assessProfile(profile, evidence) {
|
|
|
1632
1676
|
continue;
|
|
1633
1677
|
}
|
|
1634
1678
|
if (check.type === "route_inventory") {
|
|
1635
|
-
const inventories =
|
|
1679
|
+
const inventories = checkViewports
|
|
1636
1680
|
.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
|
|
1637
1681
|
.filter((item) => item.inventory && typeof item.inventory === "object");
|
|
1638
1682
|
if (!inventories.length) {
|
|
@@ -1684,7 +1728,7 @@ function assessProfile(profile, evidence) {
|
|
|
1684
1728
|
}
|
|
1685
1729
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
1686
1730
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
1687
|
-
const applicable = check.type === "no_mobile_horizontal_overflow" ?
|
|
1731
|
+
const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
|
|
1688
1732
|
if (!applicable.length) {
|
|
1689
1733
|
checks.push({
|
|
1690
1734
|
type: check.type,
|
|
@@ -1713,11 +1757,11 @@ function assessProfile(profile, evidence) {
|
|
|
1713
1757
|
}
|
|
1714
1758
|
if (check.type === "no_fatal_console_errors") {
|
|
1715
1759
|
const fatalConsoleEvents = ((evidence.console && evidence.console.events) || []).filter((event) => event && (event.type === "error" || event.type === "assert"));
|
|
1716
|
-
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event
|
|
1717
|
-
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event
|
|
1760
|
+
const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
1761
|
+
const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
|
|
1718
1762
|
const pageErrors = evidence.page_errors || [];
|
|
1719
|
-
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error
|
|
1720
|
-
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error
|
|
1763
|
+
const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
1764
|
+
const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
|
|
1721
1765
|
const fatalCount = unallowedConsoleEvents.length + unallowedPageErrors.length;
|
|
1722
1766
|
checks.push({
|
|
1723
1767
|
type: check.type,
|
package/dist/profile.d.cts
CHANGED
package/dist/profile.d.ts
CHANGED
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-G673RKAC.js";
|
|
23
23
|
export {
|
|
24
24
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
25
25
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|
|
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
292
292
|
blocking?: boolean;
|
|
293
293
|
details?: Record<string, unknown>;
|
|
294
294
|
ok: boolean;
|
|
295
|
-
action: "
|
|
295
|
+
action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
|
|
296
296
|
state_path: string;
|
|
297
297
|
stage: any;
|
|
298
298
|
summary: string;
|
|
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
382
382
|
continueWithStage?: WorkflowStage | null;
|
|
383
383
|
blocking?: boolean;
|
|
384
384
|
details?: Record<string, unknown>;
|
|
385
|
-
action: "
|
|
385
|
+
action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
|
|
386
386
|
state_path: string;
|
|
387
387
|
stage: any;
|
|
388
388
|
checkpoint: string;
|
|
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
659
659
|
error?: undefined;
|
|
660
660
|
} | {
|
|
661
661
|
ok: boolean;
|
|
662
|
-
action: "
|
|
662
|
+
action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
|
|
663
663
|
state_path: string;
|
|
664
664
|
stage: any;
|
|
665
665
|
summary: string;
|
|
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
292
292
|
blocking?: boolean;
|
|
293
293
|
details?: Record<string, unknown>;
|
|
294
294
|
ok: boolean;
|
|
295
|
-
action: "
|
|
295
|
+
action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
|
|
296
296
|
state_path: string;
|
|
297
297
|
stage: any;
|
|
298
298
|
summary: string;
|
|
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
382
382
|
continueWithStage?: WorkflowStage | null;
|
|
383
383
|
blocking?: boolean;
|
|
384
384
|
details?: Record<string, unknown>;
|
|
385
|
-
action: "
|
|
385
|
+
action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
|
|
386
386
|
state_path: string;
|
|
387
387
|
stage: any;
|
|
388
388
|
checkpoint: string;
|
|
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
659
659
|
error?: undefined;
|
|
660
660
|
} | {
|
|
661
661
|
ok: boolean;
|
|
662
|
-
action: "
|
|
662
|
+
action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
|
|
663
663
|
state_path: string;
|
|
664
664
|
stage: any;
|
|
665
665
|
summary: string;
|