@riddledc/riddle-proof 0.7.31 → 0.7.33
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 +31 -0
- package/dist/{chunk-2NWVXT4Q.js → chunk-COUZXKGM.js} +436 -1
- package/dist/cli.cjs +443 -3
- package/dist/cli.js +8 -3
- package/dist/index.cjs +436 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/profile.cjs +436 -1
- package/dist/profile.d.cts +21 -2
- package/dist/profile.d.ts +21 -2
- package/dist/profile.js +1 -1
- package/examples/profiles/route-inventory-basic.json +29 -0
- package/package.json +1 -1
package/dist/profile.cjs
CHANGED
|
@@ -59,6 +59,7 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
59
59
|
"selector_count_at_least",
|
|
60
60
|
"text_visible",
|
|
61
61
|
"text_absent",
|
|
62
|
+
"route_inventory",
|
|
62
63
|
"no_horizontal_overflow",
|
|
63
64
|
"no_mobile_horizontal_overflow",
|
|
64
65
|
"no_fatal_console_errors"
|
|
@@ -308,6 +309,33 @@ function normalizeNetworkMocks(value) {
|
|
|
308
309
|
if (!Array.isArray(value)) throw new Error("target.network_mocks must be an array.");
|
|
309
310
|
return value.map(normalizeNetworkMock);
|
|
310
311
|
}
|
|
312
|
+
function normalizeRouteInventoryPath(value, label) {
|
|
313
|
+
const path = stringValue(value);
|
|
314
|
+
if (!path) throw new Error(`${label} requires path.`);
|
|
315
|
+
if (!path.startsWith("/")) throw new Error(`${label}.path must start with /.`);
|
|
316
|
+
return normalizeRoutePath(path);
|
|
317
|
+
}
|
|
318
|
+
function normalizeRouteInventoryRoute(input, index) {
|
|
319
|
+
if (typeof input === "string") return { path: normalizeRouteInventoryPath(input, `checks route_inventory expected_routes[${index}]`) };
|
|
320
|
+
if (!isRecord(input)) throw new Error(`checks route_inventory expected_routes[${index}] must be a string or object.`);
|
|
321
|
+
return {
|
|
322
|
+
name: stringValue(input.name),
|
|
323
|
+
path: normalizeRouteInventoryPath(input.path, `checks route_inventory expected_routes[${index}]`)
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function normalizeRouteInventoryRoutes(value, index) {
|
|
327
|
+
if (value === void 0) return void 0;
|
|
328
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
329
|
+
throw new Error(`checks[${index}] route_inventory expected_routes must be a non-empty array.`);
|
|
330
|
+
}
|
|
331
|
+
const routes = value.map(normalizeRouteInventoryRoute);
|
|
332
|
+
const seen = /* @__PURE__ */ new Set();
|
|
333
|
+
for (const route of routes) {
|
|
334
|
+
if (seen.has(route.path)) throw new Error(`checks[${index}] route_inventory expected_routes contains duplicate path ${route.path}.`);
|
|
335
|
+
seen.add(route.path);
|
|
336
|
+
}
|
|
337
|
+
return routes;
|
|
338
|
+
}
|
|
311
339
|
function normalizeCheck(input, index) {
|
|
312
340
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
313
341
|
const type = stringValue(input.type);
|
|
@@ -324,16 +352,34 @@ function normalizeCheck(input, index) {
|
|
|
324
352
|
if (type === "selector_count_at_least" && numberValue(input.min_count) === void 0) {
|
|
325
353
|
throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
|
|
326
354
|
}
|
|
355
|
+
const expectedRoutes = normalizeRouteInventoryRoutes(input.expected_routes ?? input.expectedRoutes, index);
|
|
356
|
+
if (type === "route_inventory" && !expectedRoutes?.length) {
|
|
357
|
+
throw new Error(`checks[${index}] route_inventory requires expected_routes.`);
|
|
358
|
+
}
|
|
327
359
|
return {
|
|
328
360
|
type,
|
|
329
361
|
label: stringValue(input.label),
|
|
330
362
|
expected_path: stringValue(input.expected_path),
|
|
363
|
+
expected_routes: expectedRoutes,
|
|
331
364
|
selector: stringValue(input.selector),
|
|
365
|
+
link_selector: stringValue(input.link_selector) || stringValue(input.linkSelector),
|
|
366
|
+
source_selector: stringValue(input.source_selector) || stringValue(input.sourceSelector),
|
|
367
|
+
route_path_prefix: stringValue(input.route_path_prefix) || stringValue(input.routePathPrefix),
|
|
368
|
+
route_ready_selector: stringValue(input.route_ready_selector) || stringValue(input.routeReadySelector),
|
|
369
|
+
route_ready_text: stringValue(input.route_ready_text) || stringValue(input.routeReadyText),
|
|
370
|
+
route_ready_pattern: stringValue(input.route_ready_pattern) || stringValue(input.routeReadyPattern),
|
|
371
|
+
route_ready_flags: stringValue(input.route_ready_flags) || stringValue(input.routeReadyFlags),
|
|
332
372
|
text: stringValue(input.text),
|
|
333
373
|
pattern: stringValue(input.pattern),
|
|
334
374
|
flags: stringValue(input.flags),
|
|
335
375
|
min_count: numberValue(input.min_count),
|
|
336
|
-
max_overflow_px: numberValue(input.max_overflow_px)
|
|
376
|
+
max_overflow_px: numberValue(input.max_overflow_px),
|
|
377
|
+
timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
|
|
378
|
+
run_direct_routes: input.run_direct_routes === false || input.runDirectRoutes === false ? false : true,
|
|
379
|
+
run_clickthroughs: input.run_clickthroughs === false || input.runClickthroughs === false ? false : true,
|
|
380
|
+
require_unique_routes: input.require_unique_routes === false || input.requireUniqueRoutes === false ? false : true,
|
|
381
|
+
allow_unexpected_routes: input.allow_unexpected_routes === true || input.allowUnexpectedRoutes === true,
|
|
382
|
+
save_route_screenshots: input.save_route_screenshots === true || input.saveRouteScreenshots === true
|
|
337
383
|
};
|
|
338
384
|
}
|
|
339
385
|
function normalizeFailurePolicy(input) {
|
|
@@ -560,6 +606,36 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
560
606
|
message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
|
|
561
607
|
};
|
|
562
608
|
}
|
|
609
|
+
if (check.type === "route_inventory") {
|
|
610
|
+
const inventories = viewports.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory })).filter((item) => isRecord(item.inventory));
|
|
611
|
+
if (!inventories.length) {
|
|
612
|
+
return {
|
|
613
|
+
type: check.type,
|
|
614
|
+
label: checkLabel(check),
|
|
615
|
+
status: "failed",
|
|
616
|
+
evidence: { expected_count: check.expected_routes?.length || 0 },
|
|
617
|
+
message: "No route inventory evidence was captured."
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
const failures = inventories.flatMap((item) => Array.isArray(item.inventory?.failures) ? item.inventory.failures.map((failure) => toJsonValue({ viewport: item.viewport, failure })) : []);
|
|
621
|
+
const first = inventories[0]?.inventory;
|
|
622
|
+
const directRoutes = Array.isArray(first?.direct_routes) ? first.direct_routes : [];
|
|
623
|
+
const clickthroughs = Array.isArray(first?.clickthroughs) ? first.clickthroughs : [];
|
|
624
|
+
return {
|
|
625
|
+
type: check.type,
|
|
626
|
+
label: checkLabel(check),
|
|
627
|
+
status: failures.length ? "failed" : "passed",
|
|
628
|
+
evidence: {
|
|
629
|
+
expected_count: check.expected_routes?.length || 0,
|
|
630
|
+
homepage_link_count: numberValue(first?.home_game_link_count) ?? null,
|
|
631
|
+
homepage_unique_link_count: numberValue(first?.home_unique_game_link_count) ?? null,
|
|
632
|
+
direct_route_count: directRoutes.length,
|
|
633
|
+
clickthrough_count: clickthroughs.length,
|
|
634
|
+
failures
|
|
635
|
+
},
|
|
636
|
+
message: failures.length ? `Route inventory failed with ${failures.length} issue(s).` : void 0
|
|
637
|
+
};
|
|
638
|
+
}
|
|
563
639
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
564
640
|
const maxOverflow = check.max_overflow_px ?? 4;
|
|
565
641
|
const applicable = check.type === "no_mobile_horizontal_overflow" ? viewports.filter((viewport) => viewport.width <= 820) : viewports;
|
|
@@ -1069,6 +1145,45 @@ function assessProfile(profile, evidence) {
|
|
|
1069
1145
|
});
|
|
1070
1146
|
continue;
|
|
1071
1147
|
}
|
|
1148
|
+
if (check.type === "route_inventory") {
|
|
1149
|
+
const inventories = viewports
|
|
1150
|
+
.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
|
|
1151
|
+
.filter((item) => item.inventory && typeof item.inventory === "object");
|
|
1152
|
+
if (!inventories.length) {
|
|
1153
|
+
checks.push({
|
|
1154
|
+
type: check.type,
|
|
1155
|
+
label: check.label || check.type,
|
|
1156
|
+
status: "failed",
|
|
1157
|
+
evidence: { expected_count: (check.expected_routes || []).length },
|
|
1158
|
+
message: "No route inventory evidence was captured.",
|
|
1159
|
+
});
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
const failures = [];
|
|
1163
|
+
for (const item of inventories) {
|
|
1164
|
+
for (const failure of Array.isArray(item.inventory.failures) ? item.inventory.failures : []) {
|
|
1165
|
+
failures.push({ viewport: item.viewport, failure });
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
const first = inventories[0].inventory || {};
|
|
1169
|
+
const directRoutes = Array.isArray(first.direct_routes) ? first.direct_routes : [];
|
|
1170
|
+
const clickthroughs = Array.isArray(first.clickthroughs) ? first.clickthroughs : [];
|
|
1171
|
+
checks.push({
|
|
1172
|
+
type: check.type,
|
|
1173
|
+
label: check.label || check.type,
|
|
1174
|
+
status: failures.length ? "failed" : "passed",
|
|
1175
|
+
evidence: {
|
|
1176
|
+
expected_count: (check.expected_routes || []).length,
|
|
1177
|
+
homepage_link_count: typeof first.home_game_link_count === "number" ? first.home_game_link_count : null,
|
|
1178
|
+
homepage_unique_link_count: typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : null,
|
|
1179
|
+
direct_route_count: directRoutes.length,
|
|
1180
|
+
clickthrough_count: clickthroughs.length,
|
|
1181
|
+
failures,
|
|
1182
|
+
},
|
|
1183
|
+
message: failures.length ? "Route inventory failed with " + failures.length + " issue(s)." : undefined,
|
|
1184
|
+
});
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1072
1187
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
1073
1188
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
1074
1189
|
const applicable = check.type === "no_mobile_horizontal_overflow" ? viewports.filter((viewport) => viewport.width <= 820) : viewports;
|
|
@@ -1380,6 +1495,290 @@ async function selectorStats(selector) {
|
|
|
1380
1495
|
return { count: elements.length, visible_count: elements.filter(isVisible).length };
|
|
1381
1496
|
}).catch((error) => ({ count: 0, visible_count: 0, error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
1382
1497
|
}
|
|
1498
|
+
function inventoryAppPathFromUrl(urlLike) {
|
|
1499
|
+
const url = new URL(String(urlLike), targetUrl);
|
|
1500
|
+
const mountPrefix = previewMountPrefix(new URL(targetUrl).pathname);
|
|
1501
|
+
let pathname = url.pathname || "/";
|
|
1502
|
+
if (mountPrefix && (pathname === mountPrefix || pathname.startsWith(mountPrefix + "/"))) {
|
|
1503
|
+
pathname = pathname.slice(mountPrefix.length) || "/";
|
|
1504
|
+
}
|
|
1505
|
+
return normalizeRoutePath(pathname);
|
|
1506
|
+
}
|
|
1507
|
+
function inventoryRouteUrl(expectedPath) {
|
|
1508
|
+
const base = new URL(targetUrl);
|
|
1509
|
+
const route = new URL(expectedPath, base.origin);
|
|
1510
|
+
const mountPrefix = previewMountPrefix(base.pathname);
|
|
1511
|
+
base.pathname = mountPrefix ? joinMountedRoutePath(mountPrefix, route.pathname) : route.pathname;
|
|
1512
|
+
base.search = route.search;
|
|
1513
|
+
base.hash = route.hash;
|
|
1514
|
+
return base.href;
|
|
1515
|
+
}
|
|
1516
|
+
function inventorySlugFromPath(path) {
|
|
1517
|
+
return String(path || "route").split("/").filter(Boolean).pop()?.replace(/[^a-z0-9]+/gi, "-").toLowerCase() || "route";
|
|
1518
|
+
}
|
|
1519
|
+
function inventoryCheckTimeout(check) {
|
|
1520
|
+
const timeout = Number(check && check.timeout_ms);
|
|
1521
|
+
return Number.isFinite(timeout) && timeout > 0 ? timeout : 45000;
|
|
1522
|
+
}
|
|
1523
|
+
async function collectInventoryHomeLinks(check) {
|
|
1524
|
+
const linkSelector = check.link_selector || "a[href]";
|
|
1525
|
+
const expectedPaths = (check.expected_routes || []).map((route) => route.path);
|
|
1526
|
+
const routePathPrefix = check.route_path_prefix || "";
|
|
1527
|
+
return await page.evaluate(({ linkSelector, expectedPaths, routePathPrefix, targetUrl }) => {
|
|
1528
|
+
const previewMountPrefix = (pathname) => {
|
|
1529
|
+
const value = pathname || "/";
|
|
1530
|
+
const apiPreview = value.match(/^(\/s\/[^/]+)(?:\/|$)/);
|
|
1531
|
+
if (apiPreview) return apiPreview[1];
|
|
1532
|
+
const internalPreview = value.match(/^(\/preview\/[^/]+\/[^/]+)(?:\/|$)/);
|
|
1533
|
+
return internalPreview ? internalPreview[1] : "";
|
|
1534
|
+
};
|
|
1535
|
+
const normalizeRoutePath = (path) => {
|
|
1536
|
+
const value = path || "/";
|
|
1537
|
+
if (value === "/") return "/";
|
|
1538
|
+
return value.replace(/\/+$/, "") || "/";
|
|
1539
|
+
};
|
|
1540
|
+
const appPathFromHref = (href) => {
|
|
1541
|
+
const url = new URL(href, location.href);
|
|
1542
|
+
const mountPrefix = previewMountPrefix(new URL(targetUrl).pathname);
|
|
1543
|
+
let pathname = url.pathname || "/";
|
|
1544
|
+
if (mountPrefix && (pathname === mountPrefix || pathname.startsWith(mountPrefix + "/"))) {
|
|
1545
|
+
pathname = pathname.slice(mountPrefix.length) || "/";
|
|
1546
|
+
}
|
|
1547
|
+
return normalizeRoutePath(pathname);
|
|
1548
|
+
};
|
|
1549
|
+
const expectedSet = new Set(expectedPaths);
|
|
1550
|
+
const links = Array.from(document.querySelectorAll(linkSelector)).map((anchor) => {
|
|
1551
|
+
const href = anchor.getAttribute("href") || "";
|
|
1552
|
+
const appPath = appPathFromHref(href || anchor.href || location.href);
|
|
1553
|
+
return {
|
|
1554
|
+
text: (anchor.textContent || "").replace(/\s+/g, " ").trim().slice(0, 240),
|
|
1555
|
+
href,
|
|
1556
|
+
pathname: new URL(href || anchor.href || location.href, location.href).pathname,
|
|
1557
|
+
app_path: appPath,
|
|
1558
|
+
};
|
|
1559
|
+
});
|
|
1560
|
+
return links.filter((link) => (
|
|
1561
|
+
routePathPrefix ? link.app_path.startsWith(routePathPrefix) : expectedSet.has(link.app_path)
|
|
1562
|
+
));
|
|
1563
|
+
}, { linkSelector, expectedPaths, routePathPrefix, targetUrl });
|
|
1564
|
+
}
|
|
1565
|
+
async function waitForInventoryRouteHealth(check, expectedPath) {
|
|
1566
|
+
const timeout = inventoryCheckTimeout(check);
|
|
1567
|
+
await page.waitForURL((url) => inventoryAppPathFromUrl(url.href) === normalizeRoutePath(expectedPath), { timeout: Math.min(timeout, 20000) });
|
|
1568
|
+
if (check.route_ready_selector) {
|
|
1569
|
+
await page.waitForSelector(check.route_ready_selector, { state: "visible", timeout });
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
await page.waitForFunction(({ expectedPath, sourceSelector, routeReadyText, routeReadyPattern, routeReadyFlags, targetUrl }) => {
|
|
1573
|
+
const previewMountPrefix = (pathname) => {
|
|
1574
|
+
const value = pathname || "/";
|
|
1575
|
+
const apiPreview = value.match(/^(\/s\/[^/]+)(?:\/|$)/);
|
|
1576
|
+
if (apiPreview) return apiPreview[1];
|
|
1577
|
+
const internalPreview = value.match(/^(\/preview\/[^/]+\/[^/]+)(?:\/|$)/);
|
|
1578
|
+
return internalPreview ? internalPreview[1] : "";
|
|
1579
|
+
};
|
|
1580
|
+
const normalizeRoutePath = (path) => {
|
|
1581
|
+
const value = path || "/";
|
|
1582
|
+
if (value === "/") return "/";
|
|
1583
|
+
return value.replace(/\/+$/, "") || "/";
|
|
1584
|
+
};
|
|
1585
|
+
const mountPrefix = previewMountPrefix(new URL(targetUrl).pathname);
|
|
1586
|
+
let pathname = location.pathname || "/";
|
|
1587
|
+
if (mountPrefix && (pathname === mountPrefix || pathname.startsWith(mountPrefix + "/"))) {
|
|
1588
|
+
pathname = pathname.slice(mountPrefix.length) || "/";
|
|
1589
|
+
}
|
|
1590
|
+
if (normalizeRoutePath(pathname) !== normalizeRoutePath(expectedPath)) return false;
|
|
1591
|
+
if (sourceSelector && document.querySelector(sourceSelector)) return false;
|
|
1592
|
+
const text = (document.body?.innerText || "").replace(/\s+/g, " ").trim();
|
|
1593
|
+
if (routeReadyPattern) {
|
|
1594
|
+
try {
|
|
1595
|
+
if (!new RegExp(routeReadyPattern, routeReadyFlags || "").test(text)) return false;
|
|
1596
|
+
} catch {
|
|
1597
|
+
return false;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
if (routeReadyText && !text.includes(routeReadyText)) return false;
|
|
1601
|
+
const loadingOnly = /^loading\.?$/i.test(text) || (/^loading/i.test(text) && text.length < 40);
|
|
1602
|
+
if (document.querySelectorAll("canvas").length > 0) return true;
|
|
1603
|
+
if (document.querySelectorAll("button").length > 0 && !loadingOnly) return true;
|
|
1604
|
+
return text.length > 30 && !loadingOnly && !/not found|cannot get/i.test(text);
|
|
1605
|
+
}, {
|
|
1606
|
+
expectedPath,
|
|
1607
|
+
sourceSelector: check.source_selector || "",
|
|
1608
|
+
routeReadyText: check.route_ready_text || "",
|
|
1609
|
+
routeReadyPattern: check.route_ready_pattern || "",
|
|
1610
|
+
routeReadyFlags: check.route_ready_flags || "",
|
|
1611
|
+
targetUrl,
|
|
1612
|
+
}, { timeout });
|
|
1613
|
+
}
|
|
1614
|
+
async function collectInventoryRouteSnapshot(expectedRoute, phase, check, error) {
|
|
1615
|
+
return await page.evaluate(({ expectedRoute, phase, sourceSelector, error, targetUrl }) => {
|
|
1616
|
+
const previewMountPrefix = (pathname) => {
|
|
1617
|
+
const value = pathname || "/";
|
|
1618
|
+
const apiPreview = value.match(/^(\/s\/[^/]+)(?:\/|$)/);
|
|
1619
|
+
if (apiPreview) return apiPreview[1];
|
|
1620
|
+
const internalPreview = value.match(/^(\/preview\/[^/]+\/[^/]+)(?:\/|$)/);
|
|
1621
|
+
return internalPreview ? internalPreview[1] : "";
|
|
1622
|
+
};
|
|
1623
|
+
const normalizeRoutePath = (path) => {
|
|
1624
|
+
const value = path || "/";
|
|
1625
|
+
if (value === "/") return "/";
|
|
1626
|
+
return value.replace(/\/+$/, "") || "/";
|
|
1627
|
+
};
|
|
1628
|
+
const mountPrefix = previewMountPrefix(new URL(targetUrl).pathname);
|
|
1629
|
+
let pathname = location.pathname || "/";
|
|
1630
|
+
if (mountPrefix && (pathname === mountPrefix || pathname.startsWith(mountPrefix + "/"))) {
|
|
1631
|
+
pathname = pathname.slice(mountPrefix.length) || "/";
|
|
1632
|
+
}
|
|
1633
|
+
const sourceCount = sourceSelector ? document.querySelectorAll(sourceSelector).length : 0;
|
|
1634
|
+
const text = (document.body?.innerText || "").replace(/\s+/g, " ").trim();
|
|
1635
|
+
return {
|
|
1636
|
+
phase,
|
|
1637
|
+
name: expectedRoute.name || null,
|
|
1638
|
+
path: expectedRoute.path,
|
|
1639
|
+
loaded: !error,
|
|
1640
|
+
error: error || null,
|
|
1641
|
+
actual_path: location.pathname,
|
|
1642
|
+
actual_app_path: normalizeRoutePath(pathname),
|
|
1643
|
+
source_selector: sourceSelector || null,
|
|
1644
|
+
source_count: sourceCount,
|
|
1645
|
+
source_visible: sourceCount > 0,
|
|
1646
|
+
title: document.title,
|
|
1647
|
+
body_text_sample: text.slice(0, 900),
|
|
1648
|
+
canvas_count: document.querySelectorAll("canvas").length,
|
|
1649
|
+
button_texts: Array.from(document.querySelectorAll("button")).slice(0, 12).map((button) => (
|
|
1650
|
+
(button.textContent || "").replace(/\s+/g, " ").trim()
|
|
1651
|
+
)),
|
|
1652
|
+
heading_texts: Array.from(document.querySelectorAll("h1,h2,h3")).slice(0, 8).map((heading) => (
|
|
1653
|
+
(heading.textContent || "").replace(/\s+/g, " ").trim()
|
|
1654
|
+
)),
|
|
1655
|
+
};
|
|
1656
|
+
}, { expectedRoute, phase, sourceSelector: check.source_selector || "", error: error || "", targetUrl });
|
|
1657
|
+
}
|
|
1658
|
+
async function findInventoryLinkIndex(check, expectedPath) {
|
|
1659
|
+
const locator = page.locator(check.link_selector || "a[href]");
|
|
1660
|
+
const count = await locator.count();
|
|
1661
|
+
for (let index = 0; index < count; index += 1) {
|
|
1662
|
+
const appPath = await locator.nth(index).evaluate((anchor, targetUrl) => {
|
|
1663
|
+
const previewMountPrefix = (pathname) => {
|
|
1664
|
+
const value = pathname || "/";
|
|
1665
|
+
const apiPreview = value.match(/^(\/s\/[^/]+)(?:\/|$)/);
|
|
1666
|
+
if (apiPreview) return apiPreview[1];
|
|
1667
|
+
const internalPreview = value.match(/^(\/preview\/[^/]+\/[^/]+)(?:\/|$)/);
|
|
1668
|
+
return internalPreview ? internalPreview[1] : "";
|
|
1669
|
+
};
|
|
1670
|
+
const normalizeRoutePath = (path) => {
|
|
1671
|
+
const value = path || "/";
|
|
1672
|
+
if (value === "/") return "/";
|
|
1673
|
+
return value.replace(/\/+$/, "") || "/";
|
|
1674
|
+
};
|
|
1675
|
+
const href = anchor.getAttribute("href") || anchor.href || location.href;
|
|
1676
|
+
const url = new URL(href, location.href);
|
|
1677
|
+
const mountPrefix = previewMountPrefix(new URL(targetUrl).pathname);
|
|
1678
|
+
let pathname = url.pathname || "/";
|
|
1679
|
+
if (mountPrefix && (pathname === mountPrefix || pathname.startsWith(mountPrefix + "/"))) {
|
|
1680
|
+
pathname = pathname.slice(mountPrefix.length) || "/";
|
|
1681
|
+
}
|
|
1682
|
+
return normalizeRoutePath(pathname);
|
|
1683
|
+
}, targetUrl).catch(() => "");
|
|
1684
|
+
if (appPath === normalizeRoutePath(expectedPath)) return index;
|
|
1685
|
+
}
|
|
1686
|
+
return -1;
|
|
1687
|
+
}
|
|
1688
|
+
async function collectRouteInventory(check, viewport) {
|
|
1689
|
+
const expectedRoutes = check.expected_routes || [];
|
|
1690
|
+
const expectedPaths = expectedRoutes.map((route) => normalizeRoutePath(route.path));
|
|
1691
|
+
const expectedSet = new Set(expectedPaths);
|
|
1692
|
+
const failures = [];
|
|
1693
|
+
const homeLinks = await collectInventoryHomeLinks(check);
|
|
1694
|
+
const homeLinkPaths = homeLinks.map((link) => link.app_path);
|
|
1695
|
+
const uniqueHomeLinkPaths = Array.from(new Set(homeLinkPaths));
|
|
1696
|
+
const duplicateHomeLinkPaths = homeLinkPaths.filter((path, index) => homeLinkPaths.indexOf(path) !== index);
|
|
1697
|
+
if (check.require_unique_routes !== false && duplicateHomeLinkPaths.length) {
|
|
1698
|
+
failures.push({ code: "duplicate_source_links", paths: Array.from(new Set(duplicateHomeLinkPaths)) });
|
|
1699
|
+
}
|
|
1700
|
+
for (const route of expectedRoutes) {
|
|
1701
|
+
if (!homeLinkPaths.includes(normalizeRoutePath(route.path))) {
|
|
1702
|
+
failures.push({ code: "expected_route_missing_from_source", name: route.name || null, path: route.path });
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
const unexpectedRoutes = uniqueHomeLinkPaths.filter((path) => !expectedSet.has(path));
|
|
1706
|
+
if (!check.allow_unexpected_routes && unexpectedRoutes.length) {
|
|
1707
|
+
failures.push({ code: "unexpected_source_links", paths: unexpectedRoutes });
|
|
1708
|
+
}
|
|
1709
|
+
if (!check.allow_unexpected_routes && uniqueHomeLinkPaths.length !== expectedRoutes.length) {
|
|
1710
|
+
failures.push({ code: "source_link_count_mismatch", expected: expectedRoutes.length, actual_unique: uniqueHomeLinkPaths.length, actual_total: homeLinkPaths.length });
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
const directRoutes = [];
|
|
1714
|
+
if (check.run_direct_routes !== false) {
|
|
1715
|
+
for (const expectedRoute of expectedRoutes) {
|
|
1716
|
+
let error = "";
|
|
1717
|
+
try {
|
|
1718
|
+
await page.goto(inventoryRouteUrl(expectedRoute.path), { waitUntil: "domcontentloaded", timeout: 90000 });
|
|
1719
|
+
await waitForInventoryRouteHealth(check, expectedRoute.path);
|
|
1720
|
+
} catch (caught) {
|
|
1721
|
+
error = String(caught && caught.message ? caught.message : caught).slice(0, 1000);
|
|
1722
|
+
}
|
|
1723
|
+
const snapshot = await collectInventoryRouteSnapshot(expectedRoute, "direct", check, error);
|
|
1724
|
+
directRoutes.push(snapshot);
|
|
1725
|
+
if (check.save_route_screenshots) {
|
|
1726
|
+
await saveScreenshot(profileSlug + "-direct-" + inventorySlugFromPath(expectedRoute.path)).catch(() => {});
|
|
1727
|
+
}
|
|
1728
|
+
if (error) failures.push({ code: "direct_route_unhealthy", name: expectedRoute.name || null, path: expectedRoute.path, error });
|
|
1729
|
+
else if (snapshot.actual_app_path !== normalizeRoutePath(expectedRoute.path)) failures.push({ code: "direct_route_wrong_path", name: expectedRoute.name || null, path: expectedRoute.path, actual_app_path: snapshot.actual_app_path });
|
|
1730
|
+
else if (check.source_selector && snapshot.source_visible) failures.push({ code: "direct_route_kept_source_surface", name: expectedRoute.name || null, path: expectedRoute.path, source_selector: check.source_selector });
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
const clickthroughs = [];
|
|
1735
|
+
if (check.run_clickthroughs !== false) {
|
|
1736
|
+
for (const expectedRoute of expectedRoutes) {
|
|
1737
|
+
const result = { name: expectedRoute.name || null, path: expectedRoute.path, clicked: false, snapshot: null, error: null };
|
|
1738
|
+
try {
|
|
1739
|
+
await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 90000 });
|
|
1740
|
+
if (profile.target.wait_for_selector) {
|
|
1741
|
+
await page.waitForSelector(profile.target.wait_for_selector, { state: "visible", timeout: 15000 }).catch(() => {});
|
|
1742
|
+
}
|
|
1743
|
+
const index = await findInventoryLinkIndex(check, expectedRoute.path);
|
|
1744
|
+
if (index < 0) {
|
|
1745
|
+
result.error = "source_link_not_found";
|
|
1746
|
+
failures.push({ code: "source_link_clickthrough_missing", name: expectedRoute.name || null, path: expectedRoute.path });
|
|
1747
|
+
} else {
|
|
1748
|
+
const locator = page.locator(check.link_selector || "a[href]").nth(index);
|
|
1749
|
+
await locator.scrollIntoViewIfNeeded();
|
|
1750
|
+
await locator.click({ timeout: inventoryCheckTimeout(check), noWaitAfter: true });
|
|
1751
|
+
result.clicked = true;
|
|
1752
|
+
await waitForInventoryRouteHealth(check, expectedRoute.path);
|
|
1753
|
+
}
|
|
1754
|
+
} catch (caught) {
|
|
1755
|
+
result.error = String(caught && caught.message ? caught.message : caught).slice(0, 1000);
|
|
1756
|
+
}
|
|
1757
|
+
result.snapshot = await collectInventoryRouteSnapshot(expectedRoute, "clickthrough", check, result.error || "");
|
|
1758
|
+
clickthroughs.push(result);
|
|
1759
|
+
if (check.save_route_screenshots) {
|
|
1760
|
+
await saveScreenshot(profileSlug + "-click-" + inventorySlugFromPath(expectedRoute.path)).catch(() => {});
|
|
1761
|
+
}
|
|
1762
|
+
if (result.error && result.error !== "source_link_not_found") failures.push({ code: "source_link_clickthrough_unhealthy", name: expectedRoute.name || null, path: expectedRoute.path, error: result.error });
|
|
1763
|
+
else if (result.snapshot.actual_app_path !== normalizeRoutePath(expectedRoute.path)) failures.push({ code: "source_link_clickthrough_wrong_path", name: expectedRoute.name || null, path: expectedRoute.path, actual_app_path: result.snapshot.actual_app_path });
|
|
1764
|
+
else if (check.source_selector && result.snapshot.source_visible) failures.push({ code: "source_link_clickthrough_kept_source_surface", name: expectedRoute.name || null, path: expectedRoute.path, source_selector: check.source_selector });
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
return {
|
|
1769
|
+
version: "riddle-proof.route-inventory.v1",
|
|
1770
|
+
viewport: viewport.name,
|
|
1771
|
+
expected_routes: expectedRoutes,
|
|
1772
|
+
link_selector: check.link_selector || "a[href]",
|
|
1773
|
+
source_selector: check.source_selector || null,
|
|
1774
|
+
home_game_link_count: homeLinkPaths.length,
|
|
1775
|
+
home_unique_game_link_count: uniqueHomeLinkPaths.length,
|
|
1776
|
+
home_links: homeLinks,
|
|
1777
|
+
direct_routes: directRoutes,
|
|
1778
|
+
clickthroughs,
|
|
1779
|
+
failures,
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1383
1782
|
async function captureViewport(viewport) {
|
|
1384
1783
|
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
|
1385
1784
|
let httpStatus = null;
|
|
@@ -1502,6 +1901,31 @@ async function captureViewport(viewport) {
|
|
|
1502
1901
|
} catch (error) {
|
|
1503
1902
|
pageErrors.push({ message: "saveScreenshot failed: " + String(error && error.message ? error.message : error).slice(0, 500) });
|
|
1504
1903
|
}
|
|
1904
|
+
let routeInventory;
|
|
1905
|
+
const routeInventoryCheck = (profile.checks || []).find((check) => check.type === "route_inventory");
|
|
1906
|
+
const firstViewportName = (profile.target.viewports || [])[0] && (profile.target.viewports || [])[0].name;
|
|
1907
|
+
if (routeInventoryCheck && (!firstViewportName || viewport.name === firstViewportName)) {
|
|
1908
|
+
try {
|
|
1909
|
+
routeInventory = await collectRouteInventory(routeInventoryCheck, viewport);
|
|
1910
|
+
} catch (error) {
|
|
1911
|
+
routeInventory = {
|
|
1912
|
+
version: "riddle-proof.route-inventory.v1",
|
|
1913
|
+
viewport: viewport.name,
|
|
1914
|
+
expected_routes: routeInventoryCheck.expected_routes || [],
|
|
1915
|
+
link_selector: routeInventoryCheck.link_selector || "a[href]",
|
|
1916
|
+
source_selector: routeInventoryCheck.source_selector || null,
|
|
1917
|
+
home_game_link_count: 0,
|
|
1918
|
+
home_unique_game_link_count: 0,
|
|
1919
|
+
home_links: [],
|
|
1920
|
+
direct_routes: [],
|
|
1921
|
+
clickthroughs: [],
|
|
1922
|
+
failures: [{
|
|
1923
|
+
code: "route_inventory_capture_failed",
|
|
1924
|
+
error: String(error && error.message ? error.message : error).slice(0, 1000),
|
|
1925
|
+
}],
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1505
1929
|
const expectedPath = profile.checks.find((check) => check.type === "route_loaded" && check.expected_path)?.expected_path || new URL(targetUrl).pathname || "/";
|
|
1506
1930
|
return {
|
|
1507
1931
|
name: viewport.name,
|
|
@@ -1526,6 +1950,7 @@ async function captureViewport(viewport) {
|
|
|
1526
1950
|
overflow_offenders: dom.overflow_offenders || [],
|
|
1527
1951
|
selectors,
|
|
1528
1952
|
text_matches,
|
|
1953
|
+
route_inventory: routeInventory,
|
|
1529
1954
|
setup_action_results: setupActionResults,
|
|
1530
1955
|
screenshot_label: screenshotLabel,
|
|
1531
1956
|
navigation_error: navigationError,
|
|
@@ -1558,6 +1983,16 @@ function buildProfileEvidence(currentViewports) {
|
|
|
1558
1983
|
overflow_px: currentViewports.map((viewport) => viewport.overflow_px),
|
|
1559
1984
|
bounds_overflow_px: currentViewports.map((viewport) => viewport.bounds_overflow_px),
|
|
1560
1985
|
overflow_offender_counts: currentViewports.map((viewport) => (viewport.overflow_offenders || []).length),
|
|
1986
|
+
route_inventory: currentViewports
|
|
1987
|
+
.filter((viewport) => viewport.route_inventory)
|
|
1988
|
+
.map((viewport) => ({
|
|
1989
|
+
viewport: viewport.name,
|
|
1990
|
+
expected_count: (viewport.route_inventory.expected_routes || []).length,
|
|
1991
|
+
home_unique_game_link_count: viewport.route_inventory.home_unique_game_link_count,
|
|
1992
|
+
direct_route_count: (viewport.route_inventory.direct_routes || []).length,
|
|
1993
|
+
clickthrough_count: (viewport.route_inventory.clickthroughs || []).length,
|
|
1994
|
+
failure_count: (viewport.route_inventory.failures || []).length,
|
|
1995
|
+
})),
|
|
1561
1996
|
network_mock_count: (profile.target.network_mocks || []).length,
|
|
1562
1997
|
network_mock_hit_count: networkMockEvents.filter((event) => event.ok !== false).length,
|
|
1563
1998
|
},
|
package/dist/profile.d.cts
CHANGED
|
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
|
|
|
4
4
|
declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
|
|
5
5
|
declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
|
|
6
6
|
declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
|
|
7
|
-
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "selector_visible", "selector_count_at_least", "text_visible", "text_absent", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "selector_visible", "selector_count_at_least", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
8
8
|
declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "fill", "set_input_value", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
|
@@ -45,6 +45,10 @@ interface RiddleProofProfileNetworkMock {
|
|
|
45
45
|
body_json?: JsonValue;
|
|
46
46
|
required?: boolean;
|
|
47
47
|
}
|
|
48
|
+
interface RiddleProofProfileRouteInventoryRoute {
|
|
49
|
+
name?: string;
|
|
50
|
+
path: string;
|
|
51
|
+
}
|
|
48
52
|
interface RiddleProofProfileTarget {
|
|
49
53
|
url?: string;
|
|
50
54
|
route?: string;
|
|
@@ -60,12 +64,26 @@ interface RiddleProofProfileCheck {
|
|
|
60
64
|
type: RiddleProofProfileCheckType;
|
|
61
65
|
label?: string;
|
|
62
66
|
expected_path?: string;
|
|
67
|
+
expected_routes?: RiddleProofProfileRouteInventoryRoute[];
|
|
63
68
|
selector?: string;
|
|
69
|
+
link_selector?: string;
|
|
70
|
+
source_selector?: string;
|
|
71
|
+
route_path_prefix?: string;
|
|
72
|
+
route_ready_selector?: string;
|
|
73
|
+
route_ready_text?: string;
|
|
74
|
+
route_ready_pattern?: string;
|
|
75
|
+
route_ready_flags?: string;
|
|
64
76
|
text?: string;
|
|
65
77
|
pattern?: string;
|
|
66
78
|
flags?: string;
|
|
67
79
|
min_count?: number;
|
|
68
80
|
max_overflow_px?: number;
|
|
81
|
+
timeout_ms?: number;
|
|
82
|
+
run_direct_routes?: boolean;
|
|
83
|
+
run_clickthroughs?: boolean;
|
|
84
|
+
require_unique_routes?: boolean;
|
|
85
|
+
allow_unexpected_routes?: boolean;
|
|
86
|
+
save_route_screenshots?: boolean;
|
|
69
87
|
}
|
|
70
88
|
interface RiddleProofProfile {
|
|
71
89
|
version: typeof RIDDLE_PROOF_PROFILE_VERSION;
|
|
@@ -120,6 +138,7 @@ interface RiddleProofProfileViewportEvidence {
|
|
|
120
138
|
visible_count: number;
|
|
121
139
|
}>;
|
|
122
140
|
text_matches?: Record<string, boolean>;
|
|
141
|
+
route_inventory?: Record<string, JsonValue>;
|
|
123
142
|
setup_action_results?: Array<Record<string, JsonValue>>;
|
|
124
143
|
screenshot_label?: string;
|
|
125
144
|
navigation_error?: string;
|
|
@@ -227,4 +246,4 @@ declare function buildRiddleProofProfileScript(profile: RiddleProofProfile): str
|
|
|
227
246
|
declare function collectRiddleProfileArtifactRefs(input: unknown): RiddleProofProfileArtifactRef[];
|
|
228
247
|
declare function extractRiddleProofProfileResult(input: unknown): RiddleProofProfileResult | undefined;
|
|
229
248
|
|
|
230
|
-
export { type NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, type RiddleProofProfile, type RiddleProofProfileArtifactRef, type RiddleProofProfileBaselinePolicy, type RiddleProofProfileBoundsOffender, type RiddleProofProfileCheck, type RiddleProofProfileCheckResult, type RiddleProofProfileCheckType, type RiddleProofProfileEvidence, type RiddleProofProfileFailureAction, type RiddleProofProfileNetworkMock, type RiddleProofProfileResult, type RiddleProofProfileRouteEvidence, type RiddleProofProfileRunner, type RiddleProofProfileSetupAction, type RiddleProofProfileSetupActionType, type RiddleProofProfileStatus, type RiddleProofProfileTarget, type RiddleProofProfileViewport, type RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult };
|
|
249
|
+
export { type NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, type RiddleProofProfile, type RiddleProofProfileArtifactRef, type RiddleProofProfileBaselinePolicy, type RiddleProofProfileBoundsOffender, type RiddleProofProfileCheck, type RiddleProofProfileCheckResult, type RiddleProofProfileCheckType, type RiddleProofProfileEvidence, type RiddleProofProfileFailureAction, type RiddleProofProfileNetworkMock, type RiddleProofProfileResult, type RiddleProofProfileRouteEvidence, type RiddleProofProfileRouteInventoryRoute, type RiddleProofProfileRunner, type RiddleProofProfileSetupAction, type RiddleProofProfileSetupActionType, type RiddleProofProfileStatus, type RiddleProofProfileTarget, type RiddleProofProfileViewport, type RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult };
|
package/dist/profile.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
|
|
|
4
4
|
declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
|
|
5
5
|
declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
|
|
6
6
|
declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
|
|
7
|
-
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "selector_visible", "selector_count_at_least", "text_visible", "text_absent", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "selector_visible", "selector_count_at_least", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
8
8
|
declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "fill", "set_input_value", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
|
@@ -45,6 +45,10 @@ interface RiddleProofProfileNetworkMock {
|
|
|
45
45
|
body_json?: JsonValue;
|
|
46
46
|
required?: boolean;
|
|
47
47
|
}
|
|
48
|
+
interface RiddleProofProfileRouteInventoryRoute {
|
|
49
|
+
name?: string;
|
|
50
|
+
path: string;
|
|
51
|
+
}
|
|
48
52
|
interface RiddleProofProfileTarget {
|
|
49
53
|
url?: string;
|
|
50
54
|
route?: string;
|
|
@@ -60,12 +64,26 @@ interface RiddleProofProfileCheck {
|
|
|
60
64
|
type: RiddleProofProfileCheckType;
|
|
61
65
|
label?: string;
|
|
62
66
|
expected_path?: string;
|
|
67
|
+
expected_routes?: RiddleProofProfileRouteInventoryRoute[];
|
|
63
68
|
selector?: string;
|
|
69
|
+
link_selector?: string;
|
|
70
|
+
source_selector?: string;
|
|
71
|
+
route_path_prefix?: string;
|
|
72
|
+
route_ready_selector?: string;
|
|
73
|
+
route_ready_text?: string;
|
|
74
|
+
route_ready_pattern?: string;
|
|
75
|
+
route_ready_flags?: string;
|
|
64
76
|
text?: string;
|
|
65
77
|
pattern?: string;
|
|
66
78
|
flags?: string;
|
|
67
79
|
min_count?: number;
|
|
68
80
|
max_overflow_px?: number;
|
|
81
|
+
timeout_ms?: number;
|
|
82
|
+
run_direct_routes?: boolean;
|
|
83
|
+
run_clickthroughs?: boolean;
|
|
84
|
+
require_unique_routes?: boolean;
|
|
85
|
+
allow_unexpected_routes?: boolean;
|
|
86
|
+
save_route_screenshots?: boolean;
|
|
69
87
|
}
|
|
70
88
|
interface RiddleProofProfile {
|
|
71
89
|
version: typeof RIDDLE_PROOF_PROFILE_VERSION;
|
|
@@ -120,6 +138,7 @@ interface RiddleProofProfileViewportEvidence {
|
|
|
120
138
|
visible_count: number;
|
|
121
139
|
}>;
|
|
122
140
|
text_matches?: Record<string, boolean>;
|
|
141
|
+
route_inventory?: Record<string, JsonValue>;
|
|
123
142
|
setup_action_results?: Array<Record<string, JsonValue>>;
|
|
124
143
|
screenshot_label?: string;
|
|
125
144
|
navigation_error?: string;
|
|
@@ -227,4 +246,4 @@ declare function buildRiddleProofProfileScript(profile: RiddleProofProfile): str
|
|
|
227
246
|
declare function collectRiddleProfileArtifactRefs(input: unknown): RiddleProofProfileArtifactRef[];
|
|
228
247
|
declare function extractRiddleProofProfileResult(input: unknown): RiddleProofProfileResult | undefined;
|
|
229
248
|
|
|
230
|
-
export { type NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, type RiddleProofProfile, type RiddleProofProfileArtifactRef, type RiddleProofProfileBaselinePolicy, type RiddleProofProfileBoundsOffender, type RiddleProofProfileCheck, type RiddleProofProfileCheckResult, type RiddleProofProfileCheckType, type RiddleProofProfileEvidence, type RiddleProofProfileFailureAction, type RiddleProofProfileNetworkMock, type RiddleProofProfileResult, type RiddleProofProfileRouteEvidence, type RiddleProofProfileRunner, type RiddleProofProfileSetupAction, type RiddleProofProfileSetupActionType, type RiddleProofProfileStatus, type RiddleProofProfileTarget, type RiddleProofProfileViewport, type RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult };
|
|
249
|
+
export { type NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, type RiddleProofProfile, type RiddleProofProfileArtifactRef, type RiddleProofProfileBaselinePolicy, type RiddleProofProfileBoundsOffender, type RiddleProofProfileCheck, type RiddleProofProfileCheckResult, type RiddleProofProfileCheckType, type RiddleProofProfileEvidence, type RiddleProofProfileFailureAction, type RiddleProofProfileNetworkMock, type RiddleProofProfileResult, type RiddleProofProfileRouteEvidence, type RiddleProofProfileRouteInventoryRoute, type RiddleProofProfileRunner, type RiddleProofProfileSetupAction, type RiddleProofProfileSetupActionType, type RiddleProofProfileStatus, type RiddleProofProfileTarget, type RiddleProofProfileViewport, type RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult };
|
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-COUZXKGM.js";
|
|
23
23
|
export {
|
|
24
24
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
25
25
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|