@riddledc/riddle-proof 0.7.58 → 0.7.59
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 +7 -0
- package/dist/{chunk-7ZBK2GQX.js → chunk-Q5VW6MAP.js} +155 -36
- package/dist/cli.cjs +155 -36
- package/dist/cli.js +1 -1
- package/dist/index.cjs +155 -36
- package/dist/index.js +1 -1
- package/dist/profile.cjs +155 -36
- package/dist/profile.d.cts +2 -0
- package/dist/profile.d.ts +2 -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
|
@@ -270,6 +270,13 @@ can include `repeat` / `repeat_count` / `times` from 1 to 100; each repetition
|
|
|
270
270
|
is recorded with `repeat_index` and `repeat_count`, and `after_ms` runs after
|
|
271
271
|
each repetition. Use it for bounded game proof helpers, retry controls, or other
|
|
272
272
|
workflows where one declarative action needs to advance the app several times.
|
|
273
|
+
Add `frame_selector` / `frameSelector` to a setup action when the interaction
|
|
274
|
+
target lives inside an embedded iframe, such as a community game player or
|
|
275
|
+
hosted preview surface. Selector-based actions, storage actions, window calls,
|
|
276
|
+
and setup assertions then execute in that frame context and record
|
|
277
|
+
`frame_selector`, `frame_index`, and `frame_count` in setup-action evidence.
|
|
278
|
+
Use `frame_index` / `frameIndex` when more than one matching iframe is present;
|
|
279
|
+
it defaults to the first frame.
|
|
273
280
|
|
|
274
281
|
`target.timeout_sec` is optional. Use it for known-heavy profile targets so the
|
|
275
282
|
profile carries its own hosted Riddle worker budget; an explicit CLI `--timeout`
|
|
@@ -236,6 +236,11 @@ function normalizeSetupAction(input, index) {
|
|
|
236
236
|
if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
|
|
237
237
|
const type = normalizeSetupActionType(stringValue(input.type), index);
|
|
238
238
|
const selector = stringValue(input.selector);
|
|
239
|
+
const frameSelector = stringFromOwn(input, "frame_selector", "frameSelector", "iframe_selector", "iframeSelector");
|
|
240
|
+
const frameIndex = numberValue(valueFromOwn(input, "frame_index", "frameIndex", "iframe_index", "iframeIndex"));
|
|
241
|
+
if (frameIndex !== void 0 && (!Number.isInteger(frameIndex) || frameIndex < 0)) {
|
|
242
|
+
throw new Error(`target.setup_actions[${index}].frame_index must be a non-negative integer.`);
|
|
243
|
+
}
|
|
239
244
|
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) {
|
|
240
245
|
throw new Error(`target.setup_actions[${index}] ${type} requires selector.`);
|
|
241
246
|
}
|
|
@@ -288,6 +293,8 @@ function normalizeSetupAction(input, index) {
|
|
|
288
293
|
return {
|
|
289
294
|
type,
|
|
290
295
|
selector,
|
|
296
|
+
frame_selector: frameSelector,
|
|
297
|
+
frame_index: frameIndex,
|
|
291
298
|
force: type === "click" && (input.force === true || input.force_click === true || input.forceClick === true),
|
|
292
299
|
key,
|
|
293
300
|
value,
|
|
@@ -1174,6 +1181,8 @@ function assessSetupActionsFromEvidence(profile, evidence) {
|
|
|
1174
1181
|
viewport: viewport.name,
|
|
1175
1182
|
action: result.action ?? result.type ?? null,
|
|
1176
1183
|
selector: result.selector ?? null,
|
|
1184
|
+
frame_selector: result.frame_selector ?? null,
|
|
1185
|
+
frame_index: result.frame_index ?? null,
|
|
1177
1186
|
reason: result.reason ?? result.error ?? null
|
|
1178
1187
|
});
|
|
1179
1188
|
}
|
|
@@ -1686,10 +1695,12 @@ function assessProfile(profile, evidence) {
|
|
|
1686
1695
|
if (result && result.ok === false) {
|
|
1687
1696
|
failed.push({
|
|
1688
1697
|
viewport: viewport.name,
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1698
|
+
action: result.action || result.type || null,
|
|
1699
|
+
selector: result.selector || null,
|
|
1700
|
+
frame_selector: result.frame_selector || null,
|
|
1701
|
+
frame_index: result.frame_index ?? null,
|
|
1702
|
+
reason: result.reason || result.error || null,
|
|
1703
|
+
});
|
|
1693
1704
|
}
|
|
1694
1705
|
}
|
|
1695
1706
|
if (results.length < actionCount && results.every((result) => !result || result.ok !== false)) {
|
|
@@ -2152,8 +2163,8 @@ function setupJsonValue(value) {
|
|
|
2152
2163
|
function setupValuesEqual(left, right) {
|
|
2153
2164
|
return JSON.stringify(setupJsonValue(left)) === JSON.stringify(setupJsonValue(right));
|
|
2154
2165
|
}
|
|
2155
|
-
async function setupReadWindowValue(path) {
|
|
2156
|
-
return await
|
|
2166
|
+
async function setupReadWindowValue(context, path) {
|
|
2167
|
+
return await context.evaluate(({ path }) => {
|
|
2157
2168
|
const toJsonValue = (value) => {
|
|
2158
2169
|
if (value === null || value === undefined) return null;
|
|
2159
2170
|
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
@@ -2175,6 +2186,80 @@ async function setupReadWindowValue(path) {
|
|
|
2175
2186
|
return { ok: true, value: toJsonValue(current) };
|
|
2176
2187
|
}, { path });
|
|
2177
2188
|
}
|
|
2189
|
+
function setupFrameSelector(action) {
|
|
2190
|
+
return String(action?.frame_selector || action?.frameSelector || action?.iframe_selector || action?.iframeSelector || "").trim();
|
|
2191
|
+
}
|
|
2192
|
+
function setupFrameIndex(action) {
|
|
2193
|
+
const raw = action?.frame_index ?? action?.frameIndex ?? action?.iframe_index ?? action?.iframeIndex ?? 0;
|
|
2194
|
+
const number = Number(raw);
|
|
2195
|
+
return Number.isInteger(number) && number >= 0 ? number : 0;
|
|
2196
|
+
}
|
|
2197
|
+
function setupScopeEvidence(scope) {
|
|
2198
|
+
if (!scope || !scope.frame_selector) return {};
|
|
2199
|
+
return {
|
|
2200
|
+
frame_selector: scope.frame_selector,
|
|
2201
|
+
frame_index: scope.frame_index,
|
|
2202
|
+
frame_count: scope.frame_count,
|
|
2203
|
+
};
|
|
2204
|
+
}
|
|
2205
|
+
function setupScopeFailure(base, scope) {
|
|
2206
|
+
return {
|
|
2207
|
+
...base,
|
|
2208
|
+
...setupScopeEvidence(scope),
|
|
2209
|
+
reason: scope?.reason || "frame_scope_unavailable",
|
|
2210
|
+
error: scope?.error || undefined,
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
async function setupActionScope(action, timeout) {
|
|
2214
|
+
const frameSelector = setupFrameSelector(action);
|
|
2215
|
+
if (!frameSelector) return { ok: true, context: page };
|
|
2216
|
+
const frameIndex = setupFrameIndex(action);
|
|
2217
|
+
let frameCount = 0;
|
|
2218
|
+
let locator = null;
|
|
2219
|
+
try {
|
|
2220
|
+
await page.waitForSelector(frameSelector, { state: "attached", timeout });
|
|
2221
|
+
locator = page.locator(frameSelector);
|
|
2222
|
+
frameCount = await locator.count();
|
|
2223
|
+
} catch (error) {
|
|
2224
|
+
return {
|
|
2225
|
+
ok: false,
|
|
2226
|
+
reason: "frame_selector_not_found",
|
|
2227
|
+
frame_selector: frameSelector,
|
|
2228
|
+
frame_index: frameIndex,
|
|
2229
|
+
frame_count: frameCount,
|
|
2230
|
+
error: String(error && error.message ? error.message : error).slice(0, 1000),
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
if (!frameCount) {
|
|
2234
|
+
return { ok: false, reason: "frame_selector_not_found", frame_selector: frameSelector, frame_index: frameIndex, frame_count: frameCount };
|
|
2235
|
+
}
|
|
2236
|
+
if (frameIndex >= frameCount) {
|
|
2237
|
+
return { ok: false, reason: "frame_index_out_of_range", frame_selector: frameSelector, frame_index: frameIndex, frame_count: frameCount };
|
|
2238
|
+
}
|
|
2239
|
+
const handle = await locator.nth(frameIndex).elementHandle({ timeout }).catch((error) => ({ __riddle_error: error }));
|
|
2240
|
+
if (!handle || handle.__riddle_error) {
|
|
2241
|
+
return {
|
|
2242
|
+
ok: false,
|
|
2243
|
+
reason: "frame_element_unavailable",
|
|
2244
|
+
frame_selector: frameSelector,
|
|
2245
|
+
frame_index: frameIndex,
|
|
2246
|
+
frame_count: frameCount,
|
|
2247
|
+
error: handle?.__riddle_error ? String(handle.__riddle_error && handle.__riddle_error.message ? handle.__riddle_error.message : handle.__riddle_error).slice(0, 1000) : undefined,
|
|
2248
|
+
};
|
|
2249
|
+
}
|
|
2250
|
+
const frame = typeof handle.contentFrame === "function" ? await handle.contentFrame().catch((error) => ({ __riddle_error: error })) : null;
|
|
2251
|
+
if (!frame || frame.__riddle_error) {
|
|
2252
|
+
return {
|
|
2253
|
+
ok: false,
|
|
2254
|
+
reason: "content_frame_unavailable",
|
|
2255
|
+
frame_selector: frameSelector,
|
|
2256
|
+
frame_index: frameIndex,
|
|
2257
|
+
frame_count: frameCount,
|
|
2258
|
+
error: frame?.__riddle_error ? String(frame.__riddle_error && frame.__riddle_error.message ? frame.__riddle_error.message : frame.__riddle_error).slice(0, 1000) : undefined,
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
return { ok: true, context: frame, frame_selector: frameSelector, frame_index: frameIndex, frame_count: frameCount };
|
|
2262
|
+
}
|
|
2178
2263
|
async function setupLocatorText(locator, index) {
|
|
2179
2264
|
return await locator.nth(index).textContent({ timeout: 1000 }).catch(() => "");
|
|
2180
2265
|
}
|
|
@@ -2336,7 +2421,8 @@ async function registerNetworkMocks(mocks) {
|
|
|
2336
2421
|
}
|
|
2337
2422
|
async function executeSetupAction(action, ordinal) {
|
|
2338
2423
|
const type = setupActionType(action);
|
|
2339
|
-
const
|
|
2424
|
+
const frameSelector = setupFrameSelector(action);
|
|
2425
|
+
const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null };
|
|
2340
2426
|
const timeout = setupNumber(action.timeout_ms, 5000);
|
|
2341
2427
|
try {
|
|
2342
2428
|
if (type === "wait") {
|
|
@@ -2345,51 +2431,65 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2345
2431
|
return { ...base, ok: true, ms };
|
|
2346
2432
|
}
|
|
2347
2433
|
if (type === "wait_for_selector") {
|
|
2348
|
-
await
|
|
2349
|
-
return
|
|
2434
|
+
const scope = await setupActionScope(action, timeout);
|
|
2435
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2436
|
+
await scope.context.waitForSelector(action.selector, { state: "visible", timeout });
|
|
2437
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, timeout_ms: timeout };
|
|
2350
2438
|
}
|
|
2351
2439
|
if (type === "press") {
|
|
2352
2440
|
const key = String(action.key || "").trim();
|
|
2353
2441
|
if (!key) return { ...base, reason: "missing_key" };
|
|
2442
|
+
const scope = await setupActionScope(action, timeout);
|
|
2443
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2354
2444
|
if (!action.selector) {
|
|
2355
|
-
|
|
2356
|
-
|
|
2445
|
+
if (scope.frame_selector) {
|
|
2446
|
+
await scope.context.locator("body").press(key, { timeout });
|
|
2447
|
+
} else {
|
|
2448
|
+
await page.keyboard.press(key);
|
|
2449
|
+
}
|
|
2450
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, key };
|
|
2357
2451
|
}
|
|
2358
|
-
const locator =
|
|
2452
|
+
const locator = scope.context.locator(action.selector);
|
|
2359
2453
|
const count = await locator.count();
|
|
2360
2454
|
if (!count) return { ...base, reason: "selector_not_found", count, key };
|
|
2361
2455
|
const targetIndex = Number.isInteger(action.index) ? action.index : 0;
|
|
2362
2456
|
if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex, key };
|
|
2363
2457
|
await locator.nth(targetIndex).press(key, { timeout });
|
|
2364
|
-
return { ...base, ok: true, count, target_index: targetIndex, key };
|
|
2458
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, key };
|
|
2365
2459
|
}
|
|
2366
2460
|
if (type === "local_storage" || type === "session_storage") {
|
|
2367
2461
|
const value = setupActionValue(action);
|
|
2368
|
-
await
|
|
2462
|
+
const scope = await setupActionScope(action, timeout);
|
|
2463
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2464
|
+
await scope.context.evaluate(({ type, key, value }) => {
|
|
2369
2465
|
const storage = type === "session_storage" ? window.sessionStorage : window.localStorage;
|
|
2370
2466
|
storage.setItem(key, value);
|
|
2371
2467
|
}, { type, key: action.key, value });
|
|
2372
2468
|
if (action.reload === true) {
|
|
2373
2469
|
await page.reload({ waitUntil: "domcontentloaded", timeout: 45000 });
|
|
2374
2470
|
}
|
|
2375
|
-
return { ...base, ok: true, key: action.key, value_length: value.length, reload: action.reload === true };
|
|
2471
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, key: action.key, value_length: value.length, reload: action.reload === true };
|
|
2376
2472
|
}
|
|
2377
2473
|
if (type === "clear_storage") {
|
|
2378
2474
|
const storage = action.storage || "both";
|
|
2379
|
-
await
|
|
2475
|
+
const scope = await setupActionScope(action, timeout);
|
|
2476
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2477
|
+
await scope.context.evaluate(({ storage }) => {
|
|
2380
2478
|
if (storage === "local" || storage === "both") window.localStorage.clear();
|
|
2381
2479
|
if (storage === "session" || storage === "both") window.sessionStorage.clear();
|
|
2382
2480
|
}, { storage });
|
|
2383
2481
|
if (action.reload === true) {
|
|
2384
2482
|
await page.reload({ waitUntil: "domcontentloaded", timeout: 45000 });
|
|
2385
2483
|
}
|
|
2386
|
-
return { ...base, ok: true, storage, reload: action.reload === true };
|
|
2484
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, storage, reload: action.reload === true };
|
|
2387
2485
|
}
|
|
2388
2486
|
if (type === "window_call") {
|
|
2389
2487
|
const path = String(action.path || action.function_path || action.functionPath || "");
|
|
2390
2488
|
const args = Array.isArray(action.args) ? action.args : [];
|
|
2391
2489
|
if (!path) return { ...base, path, reason: "missing_path" };
|
|
2392
|
-
const
|
|
2490
|
+
const scope = await setupActionScope(action, timeout);
|
|
2491
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2492
|
+
const result = await scope.context.evaluate(async ({ path, args }) => {
|
|
2393
2493
|
const toJsonValue = (value) => {
|
|
2394
2494
|
if (value === null || value === undefined) return null;
|
|
2395
2495
|
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
@@ -2429,6 +2529,7 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2429
2529
|
const expectationMet = !hasExpectation || setupValuesEqual(result.returned, expected);
|
|
2430
2530
|
return {
|
|
2431
2531
|
...base,
|
|
2532
|
+
...setupScopeEvidence(scope),
|
|
2432
2533
|
ok: Boolean(result.ok && expectationMet),
|
|
2433
2534
|
path,
|
|
2434
2535
|
arg_count: args.length,
|
|
@@ -2459,13 +2560,16 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2459
2560
|
: action.expect;
|
|
2460
2561
|
if (!path) return { ...base, path, reason: "missing_path" };
|
|
2461
2562
|
if (!hasExpected) return { ...base, path, reason: "missing_expected_value" };
|
|
2563
|
+
const scope = await setupActionScope(action, timeout);
|
|
2564
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2462
2565
|
const startedAt = Date.now();
|
|
2463
2566
|
let result = null;
|
|
2464
2567
|
while (Date.now() - startedAt <= timeout) {
|
|
2465
|
-
result = await setupReadWindowValue(path);
|
|
2568
|
+
result = await setupReadWindowValue(scope.context, path);
|
|
2466
2569
|
if (result.ok && setupValuesEqual(result.value, expected)) {
|
|
2467
2570
|
return {
|
|
2468
2571
|
...base,
|
|
2572
|
+
...setupScopeEvidence(scope),
|
|
2469
2573
|
ok: true,
|
|
2470
2574
|
path,
|
|
2471
2575
|
value: setupJsonValue(result.value),
|
|
@@ -2477,6 +2581,7 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2477
2581
|
}
|
|
2478
2582
|
return {
|
|
2479
2583
|
...base,
|
|
2584
|
+
...setupScopeEvidence(scope),
|
|
2480
2585
|
path,
|
|
2481
2586
|
reason: result?.ok ? "unexpected_value" : result?.reason || "path_not_found",
|
|
2482
2587
|
missing_part: result?.missing_part || undefined,
|
|
@@ -2493,11 +2598,13 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2493
2598
|
const hasExpected = expected !== undefined;
|
|
2494
2599
|
if (!path) return { ...base, path, reason: "missing_path" };
|
|
2495
2600
|
if (!hasExpected && minValue === undefined && maxValue === undefined) return { ...base, path, reason: "missing_number_expectation" };
|
|
2601
|
+
const scope = await setupActionScope(action, timeout);
|
|
2602
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2496
2603
|
const startedAt = Date.now();
|
|
2497
2604
|
let result = null;
|
|
2498
2605
|
let lastReason = "path_not_found";
|
|
2499
2606
|
while (Date.now() - startedAt <= timeout) {
|
|
2500
|
-
result = await setupReadWindowValue(path);
|
|
2607
|
+
result = await setupReadWindowValue(scope.context, path);
|
|
2501
2608
|
if (result.ok) {
|
|
2502
2609
|
const actual = setupFiniteNumber(result.value);
|
|
2503
2610
|
if (actual === undefined) {
|
|
@@ -2511,6 +2618,7 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2511
2618
|
} else {
|
|
2512
2619
|
return {
|
|
2513
2620
|
...base,
|
|
2621
|
+
...setupScopeEvidence(scope),
|
|
2514
2622
|
ok: true,
|
|
2515
2623
|
path,
|
|
2516
2624
|
value: actual,
|
|
@@ -2527,6 +2635,7 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2527
2635
|
}
|
|
2528
2636
|
return {
|
|
2529
2637
|
...base,
|
|
2638
|
+
...setupScopeEvidence(scope),
|
|
2530
2639
|
path,
|
|
2531
2640
|
reason: lastReason,
|
|
2532
2641
|
missing_part: result?.missing_part || undefined,
|
|
@@ -2538,7 +2647,9 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2538
2647
|
};
|
|
2539
2648
|
}
|
|
2540
2649
|
if (type === "click") {
|
|
2541
|
-
const
|
|
2650
|
+
const scope = await setupActionScope(action, timeout);
|
|
2651
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2652
|
+
const locator = scope.context.locator(action.selector);
|
|
2542
2653
|
const count = await locator.count();
|
|
2543
2654
|
if (!count) return { ...base, reason: "selector_not_found", count };
|
|
2544
2655
|
let targetIndex = Number.isInteger(action.index) ? action.index : 0;
|
|
@@ -2570,33 +2681,39 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2570
2681
|
? { timeout, noWaitAfter: true, force: true }
|
|
2571
2682
|
: { timeout, noWaitAfter: true };
|
|
2572
2683
|
await locator.nth(targetIndex).click(clickOptions);
|
|
2573
|
-
return { ...base, ok: true, count, target_index: targetIndex, text: matchedText, force: action.force === true || undefined };
|
|
2684
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, text: matchedText, force: action.force === true || undefined };
|
|
2574
2685
|
}
|
|
2575
2686
|
if (type === "fill" || type === "set_input_value") {
|
|
2576
|
-
const
|
|
2687
|
+
const scope = await setupActionScope(action, timeout);
|
|
2688
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2689
|
+
const locator = scope.context.locator(action.selector);
|
|
2577
2690
|
const count = await locator.count();
|
|
2578
2691
|
if (!count) return { ...base, reason: "selector_not_found", count };
|
|
2579
2692
|
const targetIndex = Number.isInteger(action.index) ? action.index : 0;
|
|
2580
2693
|
if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex };
|
|
2581
2694
|
const value = setupActionValue(action);
|
|
2582
2695
|
await locator.nth(targetIndex).fill(value, { timeout });
|
|
2583
|
-
return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
|
|
2696
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, value_length: value.length };
|
|
2584
2697
|
}
|
|
2585
2698
|
if (type === "assert_selector_count") {
|
|
2586
|
-
const
|
|
2699
|
+
const scope = await setupActionScope(action, timeout);
|
|
2700
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2701
|
+
const locator = scope.context.locator(action.selector);
|
|
2587
2702
|
const expectedCount = setupNumber(action.expected_count, -1);
|
|
2588
2703
|
if (!Number.isInteger(expectedCount) || expectedCount < 0) return { ...base, reason: "invalid_expected_count", expected_count: action.expected_count };
|
|
2589
2704
|
const startedAt = Date.now();
|
|
2590
2705
|
let count = 0;
|
|
2591
2706
|
while (Date.now() - startedAt <= timeout) {
|
|
2592
2707
|
count = await locator.count().catch(() => 0);
|
|
2593
|
-
if (count === expectedCount) return { ...base, ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2708
|
+
if (count === expectedCount) return { ...base, ...setupScopeEvidence(scope), ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2594
2709
|
await page.waitForTimeout(100);
|
|
2595
2710
|
}
|
|
2596
|
-
return { ...base, reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2711
|
+
return { ...base, ...setupScopeEvidence(scope), reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2597
2712
|
}
|
|
2598
2713
|
if (type === "wait_for_text") {
|
|
2599
|
-
const
|
|
2714
|
+
const scope = await setupActionScope(action, timeout);
|
|
2715
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2716
|
+
const locator = scope.context.locator(action.selector);
|
|
2600
2717
|
const startedAt = Date.now();
|
|
2601
2718
|
let lastText = "";
|
|
2602
2719
|
while (Date.now() - startedAt <= timeout) {
|
|
@@ -2605,15 +2722,17 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2605
2722
|
const text = await setupLocatorText(locator, index);
|
|
2606
2723
|
lastText = text || lastText;
|
|
2607
2724
|
if (setupTextMatches(text, action)) {
|
|
2608
|
-
return { ...base, ok: true, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
2725
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
2609
2726
|
}
|
|
2610
2727
|
}
|
|
2611
2728
|
await page.waitForTimeout(100);
|
|
2612
2729
|
}
|
|
2613
|
-
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2730
|
+
return { ...base, ...setupScopeEvidence(scope), reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2614
2731
|
}
|
|
2615
2732
|
if (type === "assert_text_visible" || type === "assert_text_absent") {
|
|
2616
|
-
const
|
|
2733
|
+
const scope = await setupActionScope(action, timeout);
|
|
2734
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
2735
|
+
const locator = scope.context.locator(action.selector);
|
|
2617
2736
|
const startedAt = Date.now();
|
|
2618
2737
|
let lastText = "";
|
|
2619
2738
|
let matchedText = "";
|
|
@@ -2632,7 +2751,7 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2632
2751
|
if (type === "assert_text_visible") {
|
|
2633
2752
|
const visible = await setupLocatorVisible(locator, index);
|
|
2634
2753
|
if (visible) {
|
|
2635
|
-
return { ...base, ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
2754
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
2636
2755
|
}
|
|
2637
2756
|
hiddenMatch = true;
|
|
2638
2757
|
break;
|
|
@@ -2641,17 +2760,17 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2641
2760
|
}
|
|
2642
2761
|
}
|
|
2643
2762
|
if (type === "assert_text_absent" && !matched) {
|
|
2644
|
-
return { ...base, ok: true, count, timeout_ms: timeout };
|
|
2763
|
+
return { ...base, ...setupScopeEvidence(scope), ok: true, count, timeout_ms: timeout };
|
|
2645
2764
|
}
|
|
2646
2765
|
await page.waitForTimeout(100);
|
|
2647
2766
|
}
|
|
2648
2767
|
if (type === "assert_text_visible") {
|
|
2649
2768
|
if (hiddenMatch) {
|
|
2650
|
-
return { ...base, reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
|
|
2769
|
+
return { ...base, ...setupScopeEvidence(scope), reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
|
|
2651
2770
|
}
|
|
2652
|
-
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2771
|
+
return { ...base, ...setupScopeEvidence(scope), reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2653
2772
|
}
|
|
2654
|
-
return { ...base, reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
|
|
2773
|
+
return { ...base, ...setupScopeEvidence(scope), reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
|
|
2655
2774
|
}
|
|
2656
2775
|
return { ...base, reason: "unsupported_action" };
|
|
2657
2776
|
} catch (error) {
|