@riddledc/riddle-proof 0.7.58 → 0.7.60
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 +17 -0
- package/dist/{chunk-7ZBK2GQX.js → chunk-FLQ4HHTT.js} +226 -38
- package/dist/cli.cjs +226 -38
- package/dist/cli.js +1 -1
- package/dist/index.cjs +226 -38
- package/dist/index.js +1 -1
- package/dist/profile.cjs +226 -38
- package/dist/profile.d.cts +4 -1
- package/dist/profile.d.ts +4 -1
- package/dist/profile.js +1 -1
- package/package.json +1 -1
package/dist/profile.cjs
CHANGED
|
@@ -65,6 +65,8 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
65
65
|
"selector_count_eq",
|
|
66
66
|
"selector_text_order",
|
|
67
67
|
"frame_text_visible",
|
|
68
|
+
"frame_url_equals",
|
|
69
|
+
"frame_url_matches",
|
|
68
70
|
"frame_no_horizontal_overflow",
|
|
69
71
|
"text_visible",
|
|
70
72
|
"text_absent",
|
|
@@ -279,6 +281,11 @@ function normalizeSetupAction(input, index) {
|
|
|
279
281
|
if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
|
|
280
282
|
const type = normalizeSetupActionType(stringValue(input.type), index);
|
|
281
283
|
const selector = stringValue(input.selector);
|
|
284
|
+
const frameSelector = stringFromOwn(input, "frame_selector", "frameSelector", "iframe_selector", "iframeSelector");
|
|
285
|
+
const frameIndex = numberValue(valueFromOwn(input, "frame_index", "frameIndex", "iframe_index", "iframeIndex"));
|
|
286
|
+
if (frameIndex !== void 0 && (!Number.isInteger(frameIndex) || frameIndex < 0)) {
|
|
287
|
+
throw new Error(`target.setup_actions[${index}].frame_index must be a non-negative integer.`);
|
|
288
|
+
}
|
|
282
289
|
if ((type === "click" || type === "fill" || type === "set_input_value" || type === "wait_for_selector" || type === "wait_for_text" || type === "assert_text_visible" || type === "assert_text_absent" || type === "assert_selector_count") && !selector) {
|
|
283
290
|
throw new Error(`target.setup_actions[${index}] ${type} requires selector.`);
|
|
284
291
|
}
|
|
@@ -331,6 +338,8 @@ function normalizeSetupAction(input, index) {
|
|
|
331
338
|
return {
|
|
332
339
|
type,
|
|
333
340
|
selector,
|
|
341
|
+
frame_selector: frameSelector,
|
|
342
|
+
frame_index: frameIndex,
|
|
334
343
|
force: type === "click" && (input.force === true || input.force_click === true || input.forceClick === true),
|
|
335
344
|
key,
|
|
336
345
|
value,
|
|
@@ -542,12 +551,19 @@ function normalizeCheck(input, index) {
|
|
|
542
551
|
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && !stringValue(input.selector)) {
|
|
543
552
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
544
553
|
}
|
|
545
|
-
if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
|
|
554
|
+
if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
|
|
546
555
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
547
556
|
}
|
|
548
557
|
if (type === "frame_text_visible" && !stringValue(input.text) && !stringValue(input.pattern)) {
|
|
549
558
|
throw new Error(`checks[${index}] frame_text_visible requires text or pattern.`);
|
|
550
559
|
}
|
|
560
|
+
const expectedUrl = stringFromOwn(input, "expected_url", "expectedUrl", "url", "expected_value", "expectedValue", "value");
|
|
561
|
+
if (type === "frame_url_equals" && expectedUrl === void 0) {
|
|
562
|
+
throw new Error(`checks[${index}] frame_url_equals requires expected_url.`);
|
|
563
|
+
}
|
|
564
|
+
if (type === "frame_url_matches" && !stringValue(input.pattern)) {
|
|
565
|
+
throw new Error(`checks[${index}] frame_url_matches requires pattern.`);
|
|
566
|
+
}
|
|
551
567
|
if ((type === "text_visible" || type === "text_absent") && !stringValue(input.text) && !stringValue(input.pattern)) {
|
|
552
568
|
throw new Error(`checks[${index}] ${type} requires text or pattern.`);
|
|
553
569
|
}
|
|
@@ -580,6 +596,7 @@ function normalizeCheck(input, index) {
|
|
|
580
596
|
expected_path: stringValue(input.expected_path),
|
|
581
597
|
param: stringValue(input.param) || stringValue(input.search_param) || stringValue(input.searchParam) || stringValue(input.key),
|
|
582
598
|
expected_value: expectedValue,
|
|
599
|
+
expected_url: expectedUrl,
|
|
583
600
|
expected_routes: expectedRoutes,
|
|
584
601
|
selector: stringValue(input.selector),
|
|
585
602
|
expected_texts: expectedTexts,
|
|
@@ -1047,6 +1064,35 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
1047
1064
|
message: failed ? `Frame selector ${key} did not contain expected text in ${failed} viewport(s).` : void 0
|
|
1048
1065
|
};
|
|
1049
1066
|
}
|
|
1067
|
+
if (check.type === "frame_url_equals" || check.type === "frame_url_matches") {
|
|
1068
|
+
const key = selectorKey(check);
|
|
1069
|
+
const expectedUrl = check.expected_url || check.expected_value || "";
|
|
1070
|
+
const results = viewports.map((viewport) => {
|
|
1071
|
+
const frames = frameEvidenceForSelector(viewport, key);
|
|
1072
|
+
const urls = frames.map((frame) => String(frame.url || "")).filter(Boolean);
|
|
1073
|
+
const matches = urls.filter((url) => check.type === "frame_url_equals" ? url === expectedUrl : matchText(url, check));
|
|
1074
|
+
return {
|
|
1075
|
+
viewport: viewport.name,
|
|
1076
|
+
frame_count: frames.length,
|
|
1077
|
+
matched_count: matches.length,
|
|
1078
|
+
matched: matches.length > 0,
|
|
1079
|
+
urls: urls.slice(0, 10)
|
|
1080
|
+
};
|
|
1081
|
+
});
|
|
1082
|
+
const failed = results.filter((result) => !result.matched).length;
|
|
1083
|
+
return {
|
|
1084
|
+
type: check.type,
|
|
1085
|
+
label: checkLabel(check),
|
|
1086
|
+
status: failed ? "failed" : "passed",
|
|
1087
|
+
evidence: {
|
|
1088
|
+
selector: key,
|
|
1089
|
+
expected_url: check.type === "frame_url_equals" ? expectedUrl : null,
|
|
1090
|
+
pattern: check.type === "frame_url_matches" ? check.pattern || null : null,
|
|
1091
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
1092
|
+
},
|
|
1093
|
+
message: failed ? `Frame selector ${key} URL assertion failed in ${failed} viewport(s).` : void 0
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1050
1096
|
if (check.type === "frame_no_horizontal_overflow") {
|
|
1051
1097
|
const key = selectorKey(check);
|
|
1052
1098
|
const maxOverflow = check.max_overflow_px ?? 4;
|
|
@@ -1217,6 +1263,8 @@ function assessSetupActionsFromEvidence(profile, evidence) {
|
|
|
1217
1263
|
viewport: viewport.name,
|
|
1218
1264
|
action: result.action ?? result.type ?? null,
|
|
1219
1265
|
selector: result.selector ?? null,
|
|
1266
|
+
frame_selector: result.frame_selector ?? null,
|
|
1267
|
+
frame_index: result.frame_index ?? null,
|
|
1220
1268
|
reason: result.reason ?? result.error ?? null
|
|
1221
1269
|
});
|
|
1222
1270
|
}
|
|
@@ -1729,10 +1777,12 @@ function assessProfile(profile, evidence) {
|
|
|
1729
1777
|
if (result && result.ok === false) {
|
|
1730
1778
|
failed.push({
|
|
1731
1779
|
viewport: viewport.name,
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1780
|
+
action: result.action || result.type || null,
|
|
1781
|
+
selector: result.selector || null,
|
|
1782
|
+
frame_selector: result.frame_selector || null,
|
|
1783
|
+
frame_index: result.frame_index ?? null,
|
|
1784
|
+
reason: result.reason || result.error || null,
|
|
1785
|
+
});
|
|
1736
1786
|
}
|
|
1737
1787
|
}
|
|
1738
1788
|
if (results.length < actionCount && results.every((result) => !result || result.ok !== false)) {
|
|
@@ -1934,6 +1984,36 @@ function assessProfile(profile, evidence) {
|
|
|
1934
1984
|
});
|
|
1935
1985
|
continue;
|
|
1936
1986
|
}
|
|
1987
|
+
if (check.type === "frame_url_equals" || check.type === "frame_url_matches") {
|
|
1988
|
+
const selector = check.selector || "";
|
|
1989
|
+
const expectedUrl = check.expected_url || check.expected_value || "";
|
|
1990
|
+
const results = checkViewports.map((viewport) => {
|
|
1991
|
+
const frames = frameEvidenceForSelector(viewport, selector);
|
|
1992
|
+
const urls = frames.map((frame) => String(frame.url || "")).filter(Boolean);
|
|
1993
|
+
const matches = urls.filter((url) => check.type === "frame_url_equals" ? url === expectedUrl : textMatches(url, check));
|
|
1994
|
+
return {
|
|
1995
|
+
viewport: viewport.name,
|
|
1996
|
+
frame_count: frames.length,
|
|
1997
|
+
matched_count: matches.length,
|
|
1998
|
+
matched: matches.length > 0,
|
|
1999
|
+
urls: urls.slice(0, 10),
|
|
2000
|
+
};
|
|
2001
|
+
});
|
|
2002
|
+
const failed = results.filter((result) => !result.matched).length;
|
|
2003
|
+
checks.push({
|
|
2004
|
+
type: check.type,
|
|
2005
|
+
label: check.label || check.type,
|
|
2006
|
+
status: failed ? "failed" : "passed",
|
|
2007
|
+
evidence: {
|
|
2008
|
+
selector,
|
|
2009
|
+
expected_url: check.type === "frame_url_equals" ? expectedUrl : null,
|
|
2010
|
+
pattern: check.type === "frame_url_matches" ? check.pattern || null : null,
|
|
2011
|
+
viewports: results,
|
|
2012
|
+
},
|
|
2013
|
+
message: failed ? "Frame selector " + selector + " URL assertion failed in " + failed + " viewport(s)." : undefined,
|
|
2014
|
+
});
|
|
2015
|
+
continue;
|
|
2016
|
+
}
|
|
1937
2017
|
if (check.type === "frame_no_horizontal_overflow") {
|
|
1938
2018
|
const selector = check.selector || "";
|
|
1939
2019
|
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
@@ -2195,8 +2275,8 @@ function setupJsonValue(value) {
|
|
|
2195
2275
|
function setupValuesEqual(left, right) {
|
|
2196
2276
|
return JSON.stringify(setupJsonValue(left)) === JSON.stringify(setupJsonValue(right));
|
|
2197
2277
|
}
|
|
2198
|
-
async function setupReadWindowValue(path) {
|
|
2199
|
-
return await
|
|
2278
|
+
async function setupReadWindowValue(context, path) {
|
|
2279
|
+
return await context.evaluate(({ path }) => {
|
|
2200
2280
|
const toJsonValue = (value) => {
|
|
2201
2281
|
if (value === null || value === undefined) return null;
|
|
2202
2282
|
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
@@ -2218,6 +2298,80 @@ async function setupReadWindowValue(path) {
|
|
|
2218
2298
|
return { ok: true, value: toJsonValue(current) };
|
|
2219
2299
|
}, { path });
|
|
2220
2300
|
}
|
|
2301
|
+
function setupFrameSelector(action) {
|
|
2302
|
+
return String(action?.frame_selector || action?.frameSelector || action?.iframe_selector || action?.iframeSelector || "").trim();
|
|
2303
|
+
}
|
|
2304
|
+
function setupFrameIndex(action) {
|
|
2305
|
+
const raw = action?.frame_index ?? action?.frameIndex ?? action?.iframe_index ?? action?.iframeIndex ?? 0;
|
|
2306
|
+
const number = Number(raw);
|
|
2307
|
+
return Number.isInteger(number) && number >= 0 ? number : 0;
|
|
2308
|
+
}
|
|
2309
|
+
function setupScopeEvidence(scope) {
|
|
2310
|
+
if (!scope || !scope.frame_selector) return {};
|
|
2311
|
+
return {
|
|
2312
|
+
frame_selector: scope.frame_selector,
|
|
2313
|
+
frame_index: scope.frame_index,
|
|
2314
|
+
frame_count: scope.frame_count,
|
|
2315
|
+
};
|
|
2316
|
+
}
|
|
2317
|
+
function setupScopeFailure(base, scope) {
|
|
2318
|
+
return {
|
|
2319
|
+
...base,
|
|
2320
|
+
...setupScopeEvidence(scope),
|
|
2321
|
+
reason: scope?.reason || "frame_scope_unavailable",
|
|
2322
|
+
error: scope?.error || undefined,
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
async function setupActionScope(action, timeout) {
|
|
2326
|
+
const frameSelector = setupFrameSelector(action);
|
|
2327
|
+
if (!frameSelector) return { ok: true, context: page };
|
|
2328
|
+
const frameIndex = setupFrameIndex(action);
|
|
2329
|
+
let frameCount = 0;
|
|
2330
|
+
let locator = null;
|
|
2331
|
+
try {
|
|
2332
|
+
await page.waitForSelector(frameSelector, { state: "attached", timeout });
|
|
2333
|
+
locator = page.locator(frameSelector);
|
|
2334
|
+
frameCount = await locator.count();
|
|
2335
|
+
} catch (error) {
|
|
2336
|
+
return {
|
|
2337
|
+
ok: false,
|
|
2338
|
+
reason: "frame_selector_not_found",
|
|
2339
|
+
frame_selector: frameSelector,
|
|
2340
|
+
frame_index: frameIndex,
|
|
2341
|
+
frame_count: frameCount,
|
|
2342
|
+
error: String(error && error.message ? error.message : error).slice(0, 1000),
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
if (!frameCount) {
|
|
2346
|
+
return { ok: false, reason: "frame_selector_not_found", frame_selector: frameSelector, frame_index: frameIndex, frame_count: frameCount };
|
|
2347
|
+
}
|
|
2348
|
+
if (frameIndex >= frameCount) {
|
|
2349
|
+
return { ok: false, reason: "frame_index_out_of_range", frame_selector: frameSelector, frame_index: frameIndex, frame_count: frameCount };
|
|
2350
|
+
}
|
|
2351
|
+
const handle = await locator.nth(frameIndex).elementHandle({ timeout }).catch((error) => ({ __riddle_error: error }));
|
|
2352
|
+
if (!handle || handle.__riddle_error) {
|
|
2353
|
+
return {
|
|
2354
|
+
ok: false,
|
|
2355
|
+
reason: "frame_element_unavailable",
|
|
2356
|
+
frame_selector: frameSelector,
|
|
2357
|
+
frame_index: frameIndex,
|
|
2358
|
+
frame_count: frameCount,
|
|
2359
|
+
error: handle?.__riddle_error ? String(handle.__riddle_error && handle.__riddle_error.message ? handle.__riddle_error.message : handle.__riddle_error).slice(0, 1000) : undefined,
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2362
|
+
const frame = typeof handle.contentFrame === "function" ? await handle.contentFrame().catch((error) => ({ __riddle_error: error })) : null;
|
|
2363
|
+
if (!frame || frame.__riddle_error) {
|
|
2364
|
+
return {
|
|
2365
|
+
ok: false,
|
|
2366
|
+
reason: "content_frame_unavailable",
|
|
2367
|
+
frame_selector: frameSelector,
|
|
2368
|
+
frame_index: frameIndex,
|
|
2369
|
+
frame_count: frameCount,
|
|
2370
|
+
error: frame?.__riddle_error ? String(frame.__riddle_error && frame.__riddle_error.message ? frame.__riddle_error.message : frame.__riddle_error).slice(0, 1000) : undefined,
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
return { ok: true, context: frame, frame_selector: frameSelector, frame_index: frameIndex, frame_count: frameCount };
|
|
2374
|
+
}
|
|
2221
2375
|
async function setupLocatorText(locator, index) {
|
|
2222
2376
|
return await locator.nth(index).textContent({ timeout: 1000 }).catch(() => "");
|
|
2223
2377
|
}
|
|
@@ -2379,7 +2533,8 @@ async function registerNetworkMocks(mocks) {
|
|
|
2379
2533
|
}
|
|
2380
2534
|
async function executeSetupAction(action, ordinal) {
|
|
2381
2535
|
const type = setupActionType(action);
|
|
2382
|
-
const
|
|
2536
|
+
const frameSelector = setupFrameSelector(action);
|
|
2537
|
+
const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null };
|
|
2383
2538
|
const timeout = setupNumber(action.timeout_ms, 5000);
|
|
2384
2539
|
try {
|
|
2385
2540
|
if (type === "wait") {
|
|
@@ -2388,51 +2543,65 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2388
2543
|
return { ...base, ok: true, ms };
|
|
2389
2544
|
}
|
|
2390
2545
|
if (type === "wait_for_selector") {
|
|
2391
|
-
await
|
|
2392
|
-
return
|
|
2546
|
+
const scope = await setupActionScope(action, timeout);
|
|
2547
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2548
|
+
await scope.context.waitForSelector(action.selector, { state: "visible", timeout });
|
|
2549
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, timeout_ms: timeout };
|
|
2393
2550
|
}
|
|
2394
2551
|
if (type === "press") {
|
|
2395
2552
|
const key = String(action.key || "").trim();
|
|
2396
2553
|
if (!key) return { ...base, reason: "missing_key" };
|
|
2554
|
+
const scope = await setupActionScope(action, timeout);
|
|
2555
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2397
2556
|
if (!action.selector) {
|
|
2398
|
-
|
|
2399
|
-
|
|
2557
|
+
if (scope.frame_selector) {
|
|
2558
|
+
await scope.context.locator("body").press(key, { timeout });
|
|
2559
|
+
} else {
|
|
2560
|
+
await page.keyboard.press(key);
|
|
2561
|
+
}
|
|
2562
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, key };
|
|
2400
2563
|
}
|
|
2401
|
-
const locator =
|
|
2564
|
+
const locator = scope.context.locator(action.selector);
|
|
2402
2565
|
const count = await locator.count();
|
|
2403
2566
|
if (!count) return { ...base, reason: "selector_not_found", count, key };
|
|
2404
2567
|
const targetIndex = Number.isInteger(action.index) ? action.index : 0;
|
|
2405
2568
|
if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex, key };
|
|
2406
2569
|
await locator.nth(targetIndex).press(key, { timeout });
|
|
2407
|
-
return { ...base, ok: true, count, target_index: targetIndex, key };
|
|
2570
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, key };
|
|
2408
2571
|
}
|
|
2409
2572
|
if (type === "local_storage" || type === "session_storage") {
|
|
2410
2573
|
const value = setupActionValue(action);
|
|
2411
|
-
await
|
|
2574
|
+
const scope = await setupActionScope(action, timeout);
|
|
2575
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2576
|
+
await scope.context.evaluate(({ type, key, value }) => {
|
|
2412
2577
|
const storage = type === "session_storage" ? window.sessionStorage : window.localStorage;
|
|
2413
2578
|
storage.setItem(key, value);
|
|
2414
2579
|
}, { type, key: action.key, value });
|
|
2415
2580
|
if (action.reload === true) {
|
|
2416
2581
|
await page.reload({ waitUntil: "domcontentloaded", timeout: 45000 });
|
|
2417
2582
|
}
|
|
2418
|
-
return { ...base, ok: true, key: action.key, value_length: value.length, reload: action.reload === true };
|
|
2583
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, key: action.key, value_length: value.length, reload: action.reload === true };
|
|
2419
2584
|
}
|
|
2420
2585
|
if (type === "clear_storage") {
|
|
2421
2586
|
const storage = action.storage || "both";
|
|
2422
|
-
await
|
|
2587
|
+
const scope = await setupActionScope(action, timeout);
|
|
2588
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2589
|
+
await scope.context.evaluate(({ storage }) => {
|
|
2423
2590
|
if (storage === "local" || storage === "both") window.localStorage.clear();
|
|
2424
2591
|
if (storage === "session" || storage === "both") window.sessionStorage.clear();
|
|
2425
2592
|
}, { storage });
|
|
2426
2593
|
if (action.reload === true) {
|
|
2427
2594
|
await page.reload({ waitUntil: "domcontentloaded", timeout: 45000 });
|
|
2428
2595
|
}
|
|
2429
|
-
return { ...base, ok: true, storage, reload: action.reload === true };
|
|
2596
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, storage, reload: action.reload === true };
|
|
2430
2597
|
}
|
|
2431
2598
|
if (type === "window_call") {
|
|
2432
2599
|
const path = String(action.path || action.function_path || action.functionPath || "");
|
|
2433
2600
|
const args = Array.isArray(action.args) ? action.args : [];
|
|
2434
2601
|
if (!path) return { ...base, path, reason: "missing_path" };
|
|
2435
|
-
const
|
|
2602
|
+
const scope = await setupActionScope(action, timeout);
|
|
2603
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2604
|
+
const result = await scope.context.evaluate(async ({ path, args }) => {
|
|
2436
2605
|
const toJsonValue = (value) => {
|
|
2437
2606
|
if (value === null || value === undefined) return null;
|
|
2438
2607
|
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
@@ -2472,6 +2641,7 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2472
2641
|
const expectationMet = !hasExpectation || setupValuesEqual(result.returned, expected);
|
|
2473
2642
|
return {
|
|
2474
2643
|
...base,
|
|
2644
|
+
...setupScopeEvidence(scope),
|
|
2475
2645
|
ok: Boolean(result.ok && expectationMet),
|
|
2476
2646
|
path,
|
|
2477
2647
|
arg_count: args.length,
|
|
@@ -2502,13 +2672,16 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2502
2672
|
: action.expect;
|
|
2503
2673
|
if (!path) return { ...base, path, reason: "missing_path" };
|
|
2504
2674
|
if (!hasExpected) return { ...base, path, reason: "missing_expected_value" };
|
|
2675
|
+
const scope = await setupActionScope(action, timeout);
|
|
2676
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2505
2677
|
const startedAt = Date.now();
|
|
2506
2678
|
let result = null;
|
|
2507
2679
|
while (Date.now() - startedAt <= timeout) {
|
|
2508
|
-
result = await setupReadWindowValue(path);
|
|
2680
|
+
result = await setupReadWindowValue(scope.context, path);
|
|
2509
2681
|
if (result.ok && setupValuesEqual(result.value, expected)) {
|
|
2510
2682
|
return {
|
|
2511
2683
|
...base,
|
|
2684
|
+
...setupScopeEvidence(scope),
|
|
2512
2685
|
ok: true,
|
|
2513
2686
|
path,
|
|
2514
2687
|
value: setupJsonValue(result.value),
|
|
@@ -2520,6 +2693,7 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2520
2693
|
}
|
|
2521
2694
|
return {
|
|
2522
2695
|
...base,
|
|
2696
|
+
...setupScopeEvidence(scope),
|
|
2523
2697
|
path,
|
|
2524
2698
|
reason: result?.ok ? "unexpected_value" : result?.reason || "path_not_found",
|
|
2525
2699
|
missing_part: result?.missing_part || undefined,
|
|
@@ -2536,11 +2710,13 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2536
2710
|
const hasExpected = expected !== undefined;
|
|
2537
2711
|
if (!path) return { ...base, path, reason: "missing_path" };
|
|
2538
2712
|
if (!hasExpected && minValue === undefined && maxValue === undefined) return { ...base, path, reason: "missing_number_expectation" };
|
|
2713
|
+
const scope = await setupActionScope(action, timeout);
|
|
2714
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2539
2715
|
const startedAt = Date.now();
|
|
2540
2716
|
let result = null;
|
|
2541
2717
|
let lastReason = "path_not_found";
|
|
2542
2718
|
while (Date.now() - startedAt <= timeout) {
|
|
2543
|
-
result = await setupReadWindowValue(path);
|
|
2719
|
+
result = await setupReadWindowValue(scope.context, path);
|
|
2544
2720
|
if (result.ok) {
|
|
2545
2721
|
const actual = setupFiniteNumber(result.value);
|
|
2546
2722
|
if (actual === undefined) {
|
|
@@ -2554,6 +2730,7 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2554
2730
|
} else {
|
|
2555
2731
|
return {
|
|
2556
2732
|
...base,
|
|
2733
|
+
...setupScopeEvidence(scope),
|
|
2557
2734
|
ok: true,
|
|
2558
2735
|
path,
|
|
2559
2736
|
value: actual,
|
|
@@ -2570,6 +2747,7 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2570
2747
|
}
|
|
2571
2748
|
return {
|
|
2572
2749
|
...base,
|
|
2750
|
+
...setupScopeEvidence(scope),
|
|
2573
2751
|
path,
|
|
2574
2752
|
reason: lastReason,
|
|
2575
2753
|
missing_part: result?.missing_part || undefined,
|
|
@@ -2581,7 +2759,9 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2581
2759
|
};
|
|
2582
2760
|
}
|
|
2583
2761
|
if (type === "click") {
|
|
2584
|
-
const
|
|
2762
|
+
const scope = await setupActionScope(action, timeout);
|
|
2763
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2764
|
+
const locator = scope.context.locator(action.selector);
|
|
2585
2765
|
const count = await locator.count();
|
|
2586
2766
|
if (!count) return { ...base, reason: "selector_not_found", count };
|
|
2587
2767
|
let targetIndex = Number.isInteger(action.index) ? action.index : 0;
|
|
@@ -2613,33 +2793,39 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2613
2793
|
? { timeout, noWaitAfter: true, force: true }
|
|
2614
2794
|
: { timeout, noWaitAfter: true };
|
|
2615
2795
|
await locator.nth(targetIndex).click(clickOptions);
|
|
2616
|
-
return { ...base, ok: true, count, target_index: targetIndex, text: matchedText, force: action.force === true || undefined };
|
|
2796
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, text: matchedText, force: action.force === true || undefined };
|
|
2617
2797
|
}
|
|
2618
2798
|
if (type === "fill" || type === "set_input_value") {
|
|
2619
|
-
const
|
|
2799
|
+
const scope = await setupActionScope(action, timeout);
|
|
2800
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2801
|
+
const locator = scope.context.locator(action.selector);
|
|
2620
2802
|
const count = await locator.count();
|
|
2621
2803
|
if (!count) return { ...base, reason: "selector_not_found", count };
|
|
2622
2804
|
const targetIndex = Number.isInteger(action.index) ? action.index : 0;
|
|
2623
2805
|
if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex };
|
|
2624
2806
|
const value = setupActionValue(action);
|
|
2625
2807
|
await locator.nth(targetIndex).fill(value, { timeout });
|
|
2626
|
-
return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
|
|
2808
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, value_length: value.length };
|
|
2627
2809
|
}
|
|
2628
2810
|
if (type === "assert_selector_count") {
|
|
2629
|
-
const
|
|
2811
|
+
const scope = await setupActionScope(action, timeout);
|
|
2812
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2813
|
+
const locator = scope.context.locator(action.selector);
|
|
2630
2814
|
const expectedCount = setupNumber(action.expected_count, -1);
|
|
2631
2815
|
if (!Number.isInteger(expectedCount) || expectedCount < 0) return { ...base, reason: "invalid_expected_count", expected_count: action.expected_count };
|
|
2632
2816
|
const startedAt = Date.now();
|
|
2633
2817
|
let count = 0;
|
|
2634
2818
|
while (Date.now() - startedAt <= timeout) {
|
|
2635
2819
|
count = await locator.count().catch(() => 0);
|
|
2636
|
-
if (count === expectedCount) return { ...base, ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2820
|
+
if (count === expectedCount) return { ...base, ...setupScopeEvidence(scope), ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2637
2821
|
await page.waitForTimeout(100);
|
|
2638
2822
|
}
|
|
2639
|
-
return { ...base, reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2823
|
+
return { ...base, ...setupScopeEvidence(scope), reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2640
2824
|
}
|
|
2641
2825
|
if (type === "wait_for_text") {
|
|
2642
|
-
const
|
|
2826
|
+
const scope = await setupActionScope(action, timeout);
|
|
2827
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2828
|
+
const locator = scope.context.locator(action.selector);
|
|
2643
2829
|
const startedAt = Date.now();
|
|
2644
2830
|
let lastText = "";
|
|
2645
2831
|
while (Date.now() - startedAt <= timeout) {
|
|
@@ -2648,15 +2834,17 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2648
2834
|
const text = await setupLocatorText(locator, index);
|
|
2649
2835
|
lastText = text || lastText;
|
|
2650
2836
|
if (setupTextMatches(text, action)) {
|
|
2651
|
-
return { ...base, ok: true, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
2837
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
2652
2838
|
}
|
|
2653
2839
|
}
|
|
2654
2840
|
await page.waitForTimeout(100);
|
|
2655
2841
|
}
|
|
2656
|
-
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2842
|
+
return { ...base, ...setupScopeEvidence(scope), reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2657
2843
|
}
|
|
2658
2844
|
if (type === "assert_text_visible" || type === "assert_text_absent") {
|
|
2659
|
-
const
|
|
2845
|
+
const scope = await setupActionScope(action, timeout);
|
|
2846
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2847
|
+
const locator = scope.context.locator(action.selector);
|
|
2660
2848
|
const startedAt = Date.now();
|
|
2661
2849
|
let lastText = "";
|
|
2662
2850
|
let matchedText = "";
|
|
@@ -2675,7 +2863,7 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2675
2863
|
if (type === "assert_text_visible") {
|
|
2676
2864
|
const visible = await setupLocatorVisible(locator, index);
|
|
2677
2865
|
if (visible) {
|
|
2678
|
-
return { ...base, ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
2866
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
2679
2867
|
}
|
|
2680
2868
|
hiddenMatch = true;
|
|
2681
2869
|
break;
|
|
@@ -2684,17 +2872,17 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2684
2872
|
}
|
|
2685
2873
|
}
|
|
2686
2874
|
if (type === "assert_text_absent" && !matched) {
|
|
2687
|
-
return { ...base, ok: true, count, timeout_ms: timeout };
|
|
2875
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, count, timeout_ms: timeout };
|
|
2688
2876
|
}
|
|
2689
2877
|
await page.waitForTimeout(100);
|
|
2690
2878
|
}
|
|
2691
2879
|
if (type === "assert_text_visible") {
|
|
2692
2880
|
if (hiddenMatch) {
|
|
2693
|
-
return { ...base, reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
|
|
2881
|
+
return { ...base, ...setupScopeEvidence(scope), reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
|
|
2694
2882
|
}
|
|
2695
|
-
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2883
|
+
return { ...base, ...setupScopeEvidence(scope), reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2696
2884
|
}
|
|
2697
|
-
return { ...base, reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
|
|
2885
|
+
return { ...base, ...setupScopeEvidence(scope), reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
|
|
2698
2886
|
}
|
|
2699
2887
|
return { ...base, reason: "unsupported_action" };
|
|
2700
2888
|
} catch (error) {
|
|
@@ -3297,7 +3485,7 @@ async function captureViewport(viewport) {
|
|
|
3297
3485
|
if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
|
|
3298
3486
|
text_matches[textKey(check)] = textMatches(dom.body_text || dom.body_text_sample || "", check);
|
|
3299
3487
|
}
|
|
3300
|
-
if ((check.type === "frame_text_visible" || check.type === "frame_no_horizontal_overflow") && check.selector) {
|
|
3488
|
+
if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
|
|
3301
3489
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
3302
3490
|
frames[check.selector] = frames[check.selector] || await frameEvidence(check.selector);
|
|
3303
3491
|
}
|
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", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
8
8
|
declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "press", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text", "window_call"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
|
@@ -20,6 +20,8 @@ interface RiddleProofProfileViewport {
|
|
|
20
20
|
interface RiddleProofProfileSetupAction {
|
|
21
21
|
type: RiddleProofProfileSetupActionType;
|
|
22
22
|
selector?: string;
|
|
23
|
+
frame_selector?: string;
|
|
24
|
+
frame_index?: number;
|
|
23
25
|
force?: boolean;
|
|
24
26
|
key?: string;
|
|
25
27
|
value?: string;
|
|
@@ -92,6 +94,7 @@ interface RiddleProofProfileCheck {
|
|
|
92
94
|
expected_path?: string;
|
|
93
95
|
param?: string;
|
|
94
96
|
expected_value?: string;
|
|
97
|
+
expected_url?: string;
|
|
95
98
|
expected_routes?: RiddleProofProfileRouteInventoryRoute[];
|
|
96
99
|
selector?: string;
|
|
97
100
|
expected_texts?: string[];
|
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", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
8
8
|
declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "press", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text", "window_call"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
|
@@ -20,6 +20,8 @@ interface RiddleProofProfileViewport {
|
|
|
20
20
|
interface RiddleProofProfileSetupAction {
|
|
21
21
|
type: RiddleProofProfileSetupActionType;
|
|
22
22
|
selector?: string;
|
|
23
|
+
frame_selector?: string;
|
|
24
|
+
frame_index?: number;
|
|
23
25
|
force?: boolean;
|
|
24
26
|
key?: string;
|
|
25
27
|
value?: string;
|
|
@@ -92,6 +94,7 @@ interface RiddleProofProfileCheck {
|
|
|
92
94
|
expected_path?: string;
|
|
93
95
|
param?: string;
|
|
94
96
|
expected_value?: string;
|
|
97
|
+
expected_url?: string;
|
|
95
98
|
expected_routes?: RiddleProofProfileRouteInventoryRoute[];
|
|
96
99
|
selector?: string;
|
|
97
100
|
expected_texts?: string[];
|
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-FLQ4HHTT.js";
|
|
23
23
|
export {
|
|
24
24
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
25
25
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|