@riddledc/riddle-proof 0.7.43 → 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 +6 -0
- package/dist/{chunk-ZB5DSQF2.js → chunk-G673RKAC.js} +46 -20
- package/dist/cli.cjs +46 -20
- package/dist/cli.js +1 -1
- package/dist/index.cjs +46 -20
- package/dist/index.js +1 -1
- package/dist/profile.cjs +46 -20
- 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
|
|
@@ -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`),
|
|
@@ -640,15 +641,22 @@ function successfulRoute(route, targetUrl) {
|
|
|
640
641
|
const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
|
|
641
642
|
return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
|
|
642
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
|
+
}
|
|
643
649
|
function assessCheckFromEvidence(check, evidence) {
|
|
644
|
-
const viewports = evidence.viewports || [];
|
|
650
|
+
const viewports = viewportsForCheck(check, evidence.viewports || []);
|
|
645
651
|
if (!viewports.length) {
|
|
646
652
|
return {
|
|
647
653
|
type: check.type,
|
|
648
654
|
label: checkLabel(check),
|
|
649
655
|
status: "failed",
|
|
650
|
-
evidence: {
|
|
651
|
-
|
|
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."
|
|
652
660
|
};
|
|
653
661
|
}
|
|
654
662
|
if (check.type === "route_loaded") {
|
|
@@ -1253,6 +1261,11 @@ function frameTextSample(frame) {
|
|
|
1253
1261
|
frame && frame.text,
|
|
1254
1262
|
].map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
1255
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
|
+
}
|
|
1256
1269
|
function summarizeRouteInventory(viewport, inventory) {
|
|
1257
1270
|
const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
|
|
1258
1271
|
const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
|
|
@@ -1443,9 +1456,22 @@ function assessProfile(profile, evidence) {
|
|
|
1443
1456
|
});
|
|
1444
1457
|
}
|
|
1445
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
|
+
}
|
|
1446
1472
|
if (check.type === "route_loaded") {
|
|
1447
1473
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
1448
|
-
const failed =
|
|
1474
|
+
const failed = checkViewports.filter((viewport) => {
|
|
1449
1475
|
const route = { ...(viewport.route || {}), expected_path: expectedPath };
|
|
1450
1476
|
route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
|
|
1451
1477
|
return !routeOk(route, evidence.target_url);
|
|
@@ -1456,8 +1482,8 @@ function assessProfile(profile, evidence) {
|
|
|
1456
1482
|
status: failed.length ? "failed" : "passed",
|
|
1457
1483
|
evidence: {
|
|
1458
1484
|
expected_path: expectedPath,
|
|
1459
|
-
observed_paths:
|
|
1460
|
-
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),
|
|
1461
1487
|
},
|
|
1462
1488
|
message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
|
|
1463
1489
|
});
|
|
@@ -1465,24 +1491,24 @@ function assessProfile(profile, evidence) {
|
|
|
1465
1491
|
}
|
|
1466
1492
|
if (check.type === "selector_visible") {
|
|
1467
1493
|
const selector = check.selector || "";
|
|
1468
|
-
const failed =
|
|
1494
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
|
|
1469
1495
|
checks.push({
|
|
1470
1496
|
type: check.type,
|
|
1471
1497
|
label: check.label || check.type,
|
|
1472
1498
|
status: failed.length ? "failed" : "passed",
|
|
1473
|
-
evidence: { selector, visible_counts:
|
|
1499
|
+
evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
|
|
1474
1500
|
message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
|
|
1475
1501
|
});
|
|
1476
1502
|
continue;
|
|
1477
1503
|
}
|
|
1478
1504
|
if (check.type === "selector_absent") {
|
|
1479
1505
|
const selector = check.selector || "";
|
|
1480
|
-
const failed =
|
|
1506
|
+
const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
1481
1507
|
checks.push({
|
|
1482
1508
|
type: check.type,
|
|
1483
1509
|
label: check.label || check.type,
|
|
1484
1510
|
status: failed.length ? "failed" : "passed",
|
|
1485
|
-
evidence: { selector, counts:
|
|
1511
|
+
evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1486
1512
|
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
1487
1513
|
});
|
|
1488
1514
|
continue;
|
|
@@ -1490,12 +1516,12 @@ function assessProfile(profile, evidence) {
|
|
|
1490
1516
|
if (check.type === "selector_count_at_least") {
|
|
1491
1517
|
const selector = check.selector || "";
|
|
1492
1518
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
1493
|
-
const failed =
|
|
1519
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
|
|
1494
1520
|
checks.push({
|
|
1495
1521
|
type: check.type,
|
|
1496
1522
|
label: check.label || check.type,
|
|
1497
1523
|
status: failed.length ? "failed" : "passed",
|
|
1498
|
-
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) },
|
|
1499
1525
|
message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
1500
1526
|
});
|
|
1501
1527
|
continue;
|
|
@@ -1503,12 +1529,12 @@ function assessProfile(profile, evidence) {
|
|
|
1503
1529
|
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
1504
1530
|
const selector = check.selector || "";
|
|
1505
1531
|
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
1506
|
-
const failed =
|
|
1532
|
+
const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
1507
1533
|
checks.push({
|
|
1508
1534
|
type: check.type,
|
|
1509
1535
|
label: check.label || check.type,
|
|
1510
1536
|
status: failed.length ? "failed" : "passed",
|
|
1511
|
-
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) },
|
|
1512
1538
|
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
1513
1539
|
});
|
|
1514
1540
|
continue;
|
|
@@ -1516,7 +1542,7 @@ function assessProfile(profile, evidence) {
|
|
|
1516
1542
|
if (check.type === "selector_text_order") {
|
|
1517
1543
|
const selector = check.selector || "";
|
|
1518
1544
|
const expectedTexts = check.expected_texts || [];
|
|
1519
|
-
const results =
|
|
1545
|
+
const results = checkViewports.map((viewport) => {
|
|
1520
1546
|
const texts = textSequenceForCheck(viewport, check);
|
|
1521
1547
|
const match = textOrderMatch(texts, expectedTexts);
|
|
1522
1548
|
return {
|
|
@@ -1538,7 +1564,7 @@ function assessProfile(profile, evidence) {
|
|
|
1538
1564
|
}
|
|
1539
1565
|
if (check.type === "frame_text_visible") {
|
|
1540
1566
|
const selector = check.selector || "";
|
|
1541
|
-
const results =
|
|
1567
|
+
const results = checkViewports.map((viewport) => {
|
|
1542
1568
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
1543
1569
|
const matches = frames
|
|
1544
1570
|
.map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
|
|
@@ -1565,7 +1591,7 @@ function assessProfile(profile, evidence) {
|
|
|
1565
1591
|
if (check.type === "frame_no_horizontal_overflow") {
|
|
1566
1592
|
const selector = check.selector || "";
|
|
1567
1593
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
1568
|
-
const results =
|
|
1594
|
+
const results = checkViewports.map((viewport) => {
|
|
1569
1595
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
1570
1596
|
const frameOverflows = frames.map((frame, index) => ({
|
|
1571
1597
|
index,
|
|
@@ -1595,7 +1621,7 @@ function assessProfile(profile, evidence) {
|
|
|
1595
1621
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
1596
1622
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
1597
1623
|
const expectedVisible = check.type === "text_visible";
|
|
1598
|
-
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));
|
|
1599
1625
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
1600
1626
|
checks.push({
|
|
1601
1627
|
type: check.type,
|
|
@@ -1607,7 +1633,7 @@ function assessProfile(profile, evidence) {
|
|
|
1607
1633
|
continue;
|
|
1608
1634
|
}
|
|
1609
1635
|
if (check.type === "route_inventory") {
|
|
1610
|
-
const inventories =
|
|
1636
|
+
const inventories = checkViewports
|
|
1611
1637
|
.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
|
|
1612
1638
|
.filter((item) => item.inventory && typeof item.inventory === "object");
|
|
1613
1639
|
if (!inventories.length) {
|
|
@@ -1659,7 +1685,7 @@ function assessProfile(profile, evidence) {
|
|
|
1659
1685
|
}
|
|
1660
1686
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
1661
1687
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
1662
|
-
const applicable = check.type === "no_mobile_horizontal_overflow" ?
|
|
1688
|
+
const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
|
|
1663
1689
|
if (!applicable.length) {
|
|
1664
1690
|
checks.push({
|
|
1665
1691
|
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`),
|
|
@@ -7513,15 +7514,22 @@ function successfulRoute(route, targetUrl) {
|
|
|
7513
7514
|
const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
|
|
7514
7515
|
return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
|
|
7515
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
|
+
}
|
|
7516
7522
|
function assessCheckFromEvidence(check, evidence) {
|
|
7517
|
-
const viewports = evidence.viewports || [];
|
|
7523
|
+
const viewports = viewportsForCheck(check, evidence.viewports || []);
|
|
7518
7524
|
if (!viewports.length) {
|
|
7519
7525
|
return {
|
|
7520
7526
|
type: check.type,
|
|
7521
7527
|
label: checkLabel(check),
|
|
7522
7528
|
status: "failed",
|
|
7523
|
-
evidence: {
|
|
7524
|
-
|
|
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."
|
|
7525
7533
|
};
|
|
7526
7534
|
}
|
|
7527
7535
|
if (check.type === "route_loaded") {
|
|
@@ -8110,6 +8118,11 @@ function frameTextSample(frame) {
|
|
|
8110
8118
|
frame && frame.text,
|
|
8111
8119
|
].map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
8112
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
|
+
}
|
|
8113
8126
|
function summarizeRouteInventory(viewport, inventory) {
|
|
8114
8127
|
const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
|
|
8115
8128
|
const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
|
|
@@ -8300,9 +8313,22 @@ function assessProfile(profile, evidence) {
|
|
|
8300
8313
|
});
|
|
8301
8314
|
}
|
|
8302
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
|
+
}
|
|
8303
8329
|
if (check.type === "route_loaded") {
|
|
8304
8330
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
8305
|
-
const failed =
|
|
8331
|
+
const failed = checkViewports.filter((viewport) => {
|
|
8306
8332
|
const route = { ...(viewport.route || {}), expected_path: expectedPath };
|
|
8307
8333
|
route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
|
|
8308
8334
|
return !routeOk(route, evidence.target_url);
|
|
@@ -8313,8 +8339,8 @@ function assessProfile(profile, evidence) {
|
|
|
8313
8339
|
status: failed.length ? "failed" : "passed",
|
|
8314
8340
|
evidence: {
|
|
8315
8341
|
expected_path: expectedPath,
|
|
8316
|
-
observed_paths:
|
|
8317
|
-
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),
|
|
8318
8344
|
},
|
|
8319
8345
|
message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
|
|
8320
8346
|
});
|
|
@@ -8322,24 +8348,24 @@ function assessProfile(profile, evidence) {
|
|
|
8322
8348
|
}
|
|
8323
8349
|
if (check.type === "selector_visible") {
|
|
8324
8350
|
const selector = check.selector || "";
|
|
8325
|
-
const failed =
|
|
8351
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
|
|
8326
8352
|
checks.push({
|
|
8327
8353
|
type: check.type,
|
|
8328
8354
|
label: check.label || check.type,
|
|
8329
8355
|
status: failed.length ? "failed" : "passed",
|
|
8330
|
-
evidence: { selector, visible_counts:
|
|
8356
|
+
evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
|
|
8331
8357
|
message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
|
|
8332
8358
|
});
|
|
8333
8359
|
continue;
|
|
8334
8360
|
}
|
|
8335
8361
|
if (check.type === "selector_absent") {
|
|
8336
8362
|
const selector = check.selector || "";
|
|
8337
|
-
const failed =
|
|
8363
|
+
const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
8338
8364
|
checks.push({
|
|
8339
8365
|
type: check.type,
|
|
8340
8366
|
label: check.label || check.type,
|
|
8341
8367
|
status: failed.length ? "failed" : "passed",
|
|
8342
|
-
evidence: { selector, counts:
|
|
8368
|
+
evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
8343
8369
|
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
8344
8370
|
});
|
|
8345
8371
|
continue;
|
|
@@ -8347,12 +8373,12 @@ function assessProfile(profile, evidence) {
|
|
|
8347
8373
|
if (check.type === "selector_count_at_least") {
|
|
8348
8374
|
const selector = check.selector || "";
|
|
8349
8375
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
8350
|
-
const failed =
|
|
8376
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
|
|
8351
8377
|
checks.push({
|
|
8352
8378
|
type: check.type,
|
|
8353
8379
|
label: check.label || check.type,
|
|
8354
8380
|
status: failed.length ? "failed" : "passed",
|
|
8355
|
-
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) },
|
|
8356
8382
|
message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
8357
8383
|
});
|
|
8358
8384
|
continue;
|
|
@@ -8360,12 +8386,12 @@ function assessProfile(profile, evidence) {
|
|
|
8360
8386
|
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
8361
8387
|
const selector = check.selector || "";
|
|
8362
8388
|
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
8363
|
-
const failed =
|
|
8389
|
+
const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
8364
8390
|
checks.push({
|
|
8365
8391
|
type: check.type,
|
|
8366
8392
|
label: check.label || check.type,
|
|
8367
8393
|
status: failed.length ? "failed" : "passed",
|
|
8368
|
-
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) },
|
|
8369
8395
|
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
8370
8396
|
});
|
|
8371
8397
|
continue;
|
|
@@ -8373,7 +8399,7 @@ function assessProfile(profile, evidence) {
|
|
|
8373
8399
|
if (check.type === "selector_text_order") {
|
|
8374
8400
|
const selector = check.selector || "";
|
|
8375
8401
|
const expectedTexts = check.expected_texts || [];
|
|
8376
|
-
const results =
|
|
8402
|
+
const results = checkViewports.map((viewport) => {
|
|
8377
8403
|
const texts = textSequenceForCheck(viewport, check);
|
|
8378
8404
|
const match = textOrderMatch(texts, expectedTexts);
|
|
8379
8405
|
return {
|
|
@@ -8395,7 +8421,7 @@ function assessProfile(profile, evidence) {
|
|
|
8395
8421
|
}
|
|
8396
8422
|
if (check.type === "frame_text_visible") {
|
|
8397
8423
|
const selector = check.selector || "";
|
|
8398
|
-
const results =
|
|
8424
|
+
const results = checkViewports.map((viewport) => {
|
|
8399
8425
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
8400
8426
|
const matches = frames
|
|
8401
8427
|
.map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
|
|
@@ -8422,7 +8448,7 @@ function assessProfile(profile, evidence) {
|
|
|
8422
8448
|
if (check.type === "frame_no_horizontal_overflow") {
|
|
8423
8449
|
const selector = check.selector || "";
|
|
8424
8450
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
8425
|
-
const results =
|
|
8451
|
+
const results = checkViewports.map((viewport) => {
|
|
8426
8452
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
8427
8453
|
const frameOverflows = frames.map((frame, index) => ({
|
|
8428
8454
|
index,
|
|
@@ -8452,7 +8478,7 @@ function assessProfile(profile, evidence) {
|
|
|
8452
8478
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
8453
8479
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
8454
8480
|
const expectedVisible = check.type === "text_visible";
|
|
8455
|
-
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));
|
|
8456
8482
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
8457
8483
|
checks.push({
|
|
8458
8484
|
type: check.type,
|
|
@@ -8464,7 +8490,7 @@ function assessProfile(profile, evidence) {
|
|
|
8464
8490
|
continue;
|
|
8465
8491
|
}
|
|
8466
8492
|
if (check.type === "route_inventory") {
|
|
8467
|
-
const inventories =
|
|
8493
|
+
const inventories = checkViewports
|
|
8468
8494
|
.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
|
|
8469
8495
|
.filter((item) => item.inventory && typeof item.inventory === "object");
|
|
8470
8496
|
if (!inventories.length) {
|
|
@@ -8516,7 +8542,7 @@ function assessProfile(profile, evidence) {
|
|
|
8516
8542
|
}
|
|
8517
8543
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
8518
8544
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
8519
|
-
const applicable = check.type === "no_mobile_horizontal_overflow" ?
|
|
8545
|
+
const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
|
|
8520
8546
|
if (!applicable.length) {
|
|
8521
8547
|
checks.push({
|
|
8522
8548
|
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`),
|
|
@@ -9354,15 +9355,22 @@ function successfulRoute(route, targetUrl) {
|
|
|
9354
9355
|
const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
|
|
9355
9356
|
return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
|
|
9356
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
|
+
}
|
|
9357
9363
|
function assessCheckFromEvidence(check, evidence) {
|
|
9358
|
-
const viewports = evidence.viewports || [];
|
|
9364
|
+
const viewports = viewportsForCheck(check, evidence.viewports || []);
|
|
9359
9365
|
if (!viewports.length) {
|
|
9360
9366
|
return {
|
|
9361
9367
|
type: check.type,
|
|
9362
9368
|
label: checkLabel(check),
|
|
9363
9369
|
status: "failed",
|
|
9364
|
-
evidence: {
|
|
9365
|
-
|
|
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."
|
|
9366
9374
|
};
|
|
9367
9375
|
}
|
|
9368
9376
|
if (check.type === "route_loaded") {
|
|
@@ -9967,6 +9975,11 @@ function frameTextSample(frame) {
|
|
|
9967
9975
|
frame && frame.text,
|
|
9968
9976
|
].map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
9969
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
|
+
}
|
|
9970
9983
|
function summarizeRouteInventory(viewport, inventory) {
|
|
9971
9984
|
const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
|
|
9972
9985
|
const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
|
|
@@ -10157,9 +10170,22 @@ function assessProfile(profile, evidence) {
|
|
|
10157
10170
|
});
|
|
10158
10171
|
}
|
|
10159
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
|
+
}
|
|
10160
10186
|
if (check.type === "route_loaded") {
|
|
10161
10187
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
10162
|
-
const failed =
|
|
10188
|
+
const failed = checkViewports.filter((viewport) => {
|
|
10163
10189
|
const route = { ...(viewport.route || {}), expected_path: expectedPath };
|
|
10164
10190
|
route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
|
|
10165
10191
|
return !routeOk(route, evidence.target_url);
|
|
@@ -10170,8 +10196,8 @@ function assessProfile(profile, evidence) {
|
|
|
10170
10196
|
status: failed.length ? "failed" : "passed",
|
|
10171
10197
|
evidence: {
|
|
10172
10198
|
expected_path: expectedPath,
|
|
10173
|
-
observed_paths:
|
|
10174
|
-
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),
|
|
10175
10201
|
},
|
|
10176
10202
|
message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
|
|
10177
10203
|
});
|
|
@@ -10179,24 +10205,24 @@ function assessProfile(profile, evidence) {
|
|
|
10179
10205
|
}
|
|
10180
10206
|
if (check.type === "selector_visible") {
|
|
10181
10207
|
const selector = check.selector || "";
|
|
10182
|
-
const failed =
|
|
10208
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
|
|
10183
10209
|
checks.push({
|
|
10184
10210
|
type: check.type,
|
|
10185
10211
|
label: check.label || check.type,
|
|
10186
10212
|
status: failed.length ? "failed" : "passed",
|
|
10187
|
-
evidence: { selector, visible_counts:
|
|
10213
|
+
evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
|
|
10188
10214
|
message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
|
|
10189
10215
|
});
|
|
10190
10216
|
continue;
|
|
10191
10217
|
}
|
|
10192
10218
|
if (check.type === "selector_absent") {
|
|
10193
10219
|
const selector = check.selector || "";
|
|
10194
|
-
const failed =
|
|
10220
|
+
const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
10195
10221
|
checks.push({
|
|
10196
10222
|
type: check.type,
|
|
10197
10223
|
label: check.label || check.type,
|
|
10198
10224
|
status: failed.length ? "failed" : "passed",
|
|
10199
|
-
evidence: { selector, counts:
|
|
10225
|
+
evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
10200
10226
|
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
10201
10227
|
});
|
|
10202
10228
|
continue;
|
|
@@ -10204,12 +10230,12 @@ function assessProfile(profile, evidence) {
|
|
|
10204
10230
|
if (check.type === "selector_count_at_least") {
|
|
10205
10231
|
const selector = check.selector || "";
|
|
10206
10232
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
10207
|
-
const failed =
|
|
10233
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
|
|
10208
10234
|
checks.push({
|
|
10209
10235
|
type: check.type,
|
|
10210
10236
|
label: check.label || check.type,
|
|
10211
10237
|
status: failed.length ? "failed" : "passed",
|
|
10212
|
-
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) },
|
|
10213
10239
|
message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
10214
10240
|
});
|
|
10215
10241
|
continue;
|
|
@@ -10217,12 +10243,12 @@ function assessProfile(profile, evidence) {
|
|
|
10217
10243
|
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
10218
10244
|
const selector = check.selector || "";
|
|
10219
10245
|
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
10220
|
-
const failed =
|
|
10246
|
+
const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
10221
10247
|
checks.push({
|
|
10222
10248
|
type: check.type,
|
|
10223
10249
|
label: check.label || check.type,
|
|
10224
10250
|
status: failed.length ? "failed" : "passed",
|
|
10225
|
-
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) },
|
|
10226
10252
|
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
10227
10253
|
});
|
|
10228
10254
|
continue;
|
|
@@ -10230,7 +10256,7 @@ function assessProfile(profile, evidence) {
|
|
|
10230
10256
|
if (check.type === "selector_text_order") {
|
|
10231
10257
|
const selector = check.selector || "";
|
|
10232
10258
|
const expectedTexts = check.expected_texts || [];
|
|
10233
|
-
const results =
|
|
10259
|
+
const results = checkViewports.map((viewport) => {
|
|
10234
10260
|
const texts = textSequenceForCheck(viewport, check);
|
|
10235
10261
|
const match = textOrderMatch(texts, expectedTexts);
|
|
10236
10262
|
return {
|
|
@@ -10252,7 +10278,7 @@ function assessProfile(profile, evidence) {
|
|
|
10252
10278
|
}
|
|
10253
10279
|
if (check.type === "frame_text_visible") {
|
|
10254
10280
|
const selector = check.selector || "";
|
|
10255
|
-
const results =
|
|
10281
|
+
const results = checkViewports.map((viewport) => {
|
|
10256
10282
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
10257
10283
|
const matches = frames
|
|
10258
10284
|
.map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
|
|
@@ -10279,7 +10305,7 @@ function assessProfile(profile, evidence) {
|
|
|
10279
10305
|
if (check.type === "frame_no_horizontal_overflow") {
|
|
10280
10306
|
const selector = check.selector || "";
|
|
10281
10307
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
10282
|
-
const results =
|
|
10308
|
+
const results = checkViewports.map((viewport) => {
|
|
10283
10309
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
10284
10310
|
const frameOverflows = frames.map((frame, index) => ({
|
|
10285
10311
|
index,
|
|
@@ -10309,7 +10335,7 @@ function assessProfile(profile, evidence) {
|
|
|
10309
10335
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
10310
10336
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
10311
10337
|
const expectedVisible = check.type === "text_visible";
|
|
10312
|
-
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));
|
|
10313
10339
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
10314
10340
|
checks.push({
|
|
10315
10341
|
type: check.type,
|
|
@@ -10321,7 +10347,7 @@ function assessProfile(profile, evidence) {
|
|
|
10321
10347
|
continue;
|
|
10322
10348
|
}
|
|
10323
10349
|
if (check.type === "route_inventory") {
|
|
10324
|
-
const inventories =
|
|
10350
|
+
const inventories = checkViewports
|
|
10325
10351
|
.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
|
|
10326
10352
|
.filter((item) => item.inventory && typeof item.inventory === "object");
|
|
10327
10353
|
if (!inventories.length) {
|
|
@@ -10373,7 +10399,7 @@ function assessProfile(profile, evidence) {
|
|
|
10373
10399
|
}
|
|
10374
10400
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
10375
10401
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
10376
|
-
const applicable = check.type === "no_mobile_horizontal_overflow" ?
|
|
10402
|
+
const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
|
|
10377
10403
|
if (!applicable.length) {
|
|
10378
10404
|
checks.push({
|
|
10379
10405
|
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`),
|
|
@@ -683,15 +684,22 @@ function successfulRoute(route, targetUrl) {
|
|
|
683
684
|
const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
|
|
684
685
|
return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
|
|
685
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
|
+
}
|
|
686
692
|
function assessCheckFromEvidence(check, evidence) {
|
|
687
|
-
const viewports = evidence.viewports || [];
|
|
693
|
+
const viewports = viewportsForCheck(check, evidence.viewports || []);
|
|
688
694
|
if (!viewports.length) {
|
|
689
695
|
return {
|
|
690
696
|
type: check.type,
|
|
691
697
|
label: checkLabel(check),
|
|
692
698
|
status: "failed",
|
|
693
|
-
evidence: {
|
|
694
|
-
|
|
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."
|
|
695
703
|
};
|
|
696
704
|
}
|
|
697
705
|
if (check.type === "route_loaded") {
|
|
@@ -1296,6 +1304,11 @@ function frameTextSample(frame) {
|
|
|
1296
1304
|
frame && frame.text,
|
|
1297
1305
|
].map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
1298
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
|
+
}
|
|
1299
1312
|
function summarizeRouteInventory(viewport, inventory) {
|
|
1300
1313
|
const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
|
|
1301
1314
|
const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
|
|
@@ -1486,9 +1499,22 @@ function assessProfile(profile, evidence) {
|
|
|
1486
1499
|
});
|
|
1487
1500
|
}
|
|
1488
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
|
+
}
|
|
1489
1515
|
if (check.type === "route_loaded") {
|
|
1490
1516
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
1491
|
-
const failed =
|
|
1517
|
+
const failed = checkViewports.filter((viewport) => {
|
|
1492
1518
|
const route = { ...(viewport.route || {}), expected_path: expectedPath };
|
|
1493
1519
|
route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
|
|
1494
1520
|
return !routeOk(route, evidence.target_url);
|
|
@@ -1499,8 +1525,8 @@ function assessProfile(profile, evidence) {
|
|
|
1499
1525
|
status: failed.length ? "failed" : "passed",
|
|
1500
1526
|
evidence: {
|
|
1501
1527
|
expected_path: expectedPath,
|
|
1502
|
-
observed_paths:
|
|
1503
|
-
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),
|
|
1504
1530
|
},
|
|
1505
1531
|
message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
|
|
1506
1532
|
});
|
|
@@ -1508,24 +1534,24 @@ function assessProfile(profile, evidence) {
|
|
|
1508
1534
|
}
|
|
1509
1535
|
if (check.type === "selector_visible") {
|
|
1510
1536
|
const selector = check.selector || "";
|
|
1511
|
-
const failed =
|
|
1537
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
|
|
1512
1538
|
checks.push({
|
|
1513
1539
|
type: check.type,
|
|
1514
1540
|
label: check.label || check.type,
|
|
1515
1541
|
status: failed.length ? "failed" : "passed",
|
|
1516
|
-
evidence: { selector, visible_counts:
|
|
1542
|
+
evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
|
|
1517
1543
|
message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
|
|
1518
1544
|
});
|
|
1519
1545
|
continue;
|
|
1520
1546
|
}
|
|
1521
1547
|
if (check.type === "selector_absent") {
|
|
1522
1548
|
const selector = check.selector || "";
|
|
1523
|
-
const failed =
|
|
1549
|
+
const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
1524
1550
|
checks.push({
|
|
1525
1551
|
type: check.type,
|
|
1526
1552
|
label: check.label || check.type,
|
|
1527
1553
|
status: failed.length ? "failed" : "passed",
|
|
1528
|
-
evidence: { selector, counts:
|
|
1554
|
+
evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1529
1555
|
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
1530
1556
|
});
|
|
1531
1557
|
continue;
|
|
@@ -1533,12 +1559,12 @@ function assessProfile(profile, evidence) {
|
|
|
1533
1559
|
if (check.type === "selector_count_at_least") {
|
|
1534
1560
|
const selector = check.selector || "";
|
|
1535
1561
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
1536
|
-
const failed =
|
|
1562
|
+
const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
|
|
1537
1563
|
checks.push({
|
|
1538
1564
|
type: check.type,
|
|
1539
1565
|
label: check.label || check.type,
|
|
1540
1566
|
status: failed.length ? "failed" : "passed",
|
|
1541
|
-
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) },
|
|
1542
1568
|
message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
1543
1569
|
});
|
|
1544
1570
|
continue;
|
|
@@ -1546,12 +1572,12 @@ function assessProfile(profile, evidence) {
|
|
|
1546
1572
|
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
1547
1573
|
const selector = check.selector || "";
|
|
1548
1574
|
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
1549
|
-
const failed =
|
|
1575
|
+
const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
1550
1576
|
checks.push({
|
|
1551
1577
|
type: check.type,
|
|
1552
1578
|
label: check.label || check.type,
|
|
1553
1579
|
status: failed.length ? "failed" : "passed",
|
|
1554
|
-
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) },
|
|
1555
1581
|
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
1556
1582
|
});
|
|
1557
1583
|
continue;
|
|
@@ -1559,7 +1585,7 @@ function assessProfile(profile, evidence) {
|
|
|
1559
1585
|
if (check.type === "selector_text_order") {
|
|
1560
1586
|
const selector = check.selector || "";
|
|
1561
1587
|
const expectedTexts = check.expected_texts || [];
|
|
1562
|
-
const results =
|
|
1588
|
+
const results = checkViewports.map((viewport) => {
|
|
1563
1589
|
const texts = textSequenceForCheck(viewport, check);
|
|
1564
1590
|
const match = textOrderMatch(texts, expectedTexts);
|
|
1565
1591
|
return {
|
|
@@ -1581,7 +1607,7 @@ function assessProfile(profile, evidence) {
|
|
|
1581
1607
|
}
|
|
1582
1608
|
if (check.type === "frame_text_visible") {
|
|
1583
1609
|
const selector = check.selector || "";
|
|
1584
|
-
const results =
|
|
1610
|
+
const results = checkViewports.map((viewport) => {
|
|
1585
1611
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
1586
1612
|
const matches = frames
|
|
1587
1613
|
.map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
|
|
@@ -1608,7 +1634,7 @@ function assessProfile(profile, evidence) {
|
|
|
1608
1634
|
if (check.type === "frame_no_horizontal_overflow") {
|
|
1609
1635
|
const selector = check.selector || "";
|
|
1610
1636
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
1611
|
-
const results =
|
|
1637
|
+
const results = checkViewports.map((viewport) => {
|
|
1612
1638
|
const frames = frameEvidenceForSelector(viewport, selector);
|
|
1613
1639
|
const frameOverflows = frames.map((frame, index) => ({
|
|
1614
1640
|
index,
|
|
@@ -1638,7 +1664,7 @@ function assessProfile(profile, evidence) {
|
|
|
1638
1664
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
1639
1665
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
1640
1666
|
const expectedVisible = check.type === "text_visible";
|
|
1641
|
-
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));
|
|
1642
1668
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
1643
1669
|
checks.push({
|
|
1644
1670
|
type: check.type,
|
|
@@ -1650,7 +1676,7 @@ function assessProfile(profile, evidence) {
|
|
|
1650
1676
|
continue;
|
|
1651
1677
|
}
|
|
1652
1678
|
if (check.type === "route_inventory") {
|
|
1653
|
-
const inventories =
|
|
1679
|
+
const inventories = checkViewports
|
|
1654
1680
|
.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
|
|
1655
1681
|
.filter((item) => item.inventory && typeof item.inventory === "object");
|
|
1656
1682
|
if (!inventories.length) {
|
|
@@ -1702,7 +1728,7 @@ function assessProfile(profile, evidence) {
|
|
|
1702
1728
|
}
|
|
1703
1729
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
1704
1730
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
1705
|
-
const applicable = check.type === "no_mobile_horizontal_overflow" ?
|
|
1731
|
+
const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
|
|
1706
1732
|
if (!applicable.length) {
|
|
1707
1733
|
checks.push({
|
|
1708
1734
|
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;
|