@riddledc/riddle-proof 0.7.159 → 0.7.161
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 +15 -5
- package/dist/{chunk-TBTMD5WV.js → chunk-ZE2U5EML.js} +274 -12
- package/dist/cli.cjs +326 -20
- package/dist/cli.js +53 -9
- package/dist/index.cjs +274 -12
- package/dist/index.js +1 -1
- package/dist/profile.cjs +274 -12
- package/dist/profile.d.cts +3 -1
- package/dist/profile.d.ts +3 -1
- 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
|
@@ -398,10 +398,11 @@ when body matching overrides sequence order.
|
|
|
398
398
|
appears only after a picker, tab, login stub, storage seed, form fill,
|
|
399
399
|
transport control, or other bounded interaction. Supported setup actions are
|
|
400
400
|
`click`, `drag`, `press`, `fill`, `set_input_value`, `set_range_value`,
|
|
401
|
-
`
|
|
402
|
-
`
|
|
403
|
-
`
|
|
404
|
-
`
|
|
401
|
+
`canvas_signature`, `assert_text_visible`, `assert_text_absent`,
|
|
402
|
+
`assert_selector_count`, `assert_window_value`, `assert_window_number`,
|
|
403
|
+
`local_storage`, `session_storage`, `clear_storage`, `clear_console`,
|
|
404
|
+
`screenshot`, `wait`, `wait_for_selector`, `wait_for_text`, `window_eval`,
|
|
405
|
+
`window_call`, and `window_call_until`;
|
|
405
406
|
a failed setup action is recorded as a failed `setup_actions_succeeded` check so
|
|
406
407
|
the profile cannot pass without reaching the intended state. Text-matched `click` actions prefer
|
|
407
408
|
visible matching elements, which keeps responsive layouts from selecting hidden
|
|
@@ -422,6 +423,13 @@ events, and records the requested value plus the browser's actual normalized
|
|
|
422
423
|
value, numeric value, `min`, `max`, and `step`. The action is intentionally
|
|
423
424
|
strict: if the target is not an `input[type="range"]`, setup fails with
|
|
424
425
|
`not_range_input` instead of silently treating the control like a text field.
|
|
426
|
+
Use `canvas_signature` for canvas-only proof surfaces. It requires `selector`,
|
|
427
|
+
reads the selected canvas with `toDataURL("image/png")`, records a sampled hash,
|
|
428
|
+
canvas dimensions, CSS dimensions, and data length, and can store the result
|
|
429
|
+
with `store_return_to` or `store_signature_to`. Add `compare_to` plus
|
|
430
|
+
`expect_changed: true` to assert that the current canvas signature differs from
|
|
431
|
+
a previously stored signature, for example menu -> active play or terminal ->
|
|
432
|
+
restart.
|
|
425
433
|
Use `drag` for pointer-driven controls such as canvas launch areas, sliders, or
|
|
426
434
|
drag-to-aim games. Provide `selector`, `from_x`, `from_y`, `to_x`, and `to_y`;
|
|
427
435
|
coordinates are element-relative pixels by default. Set `coordinate_mode:
|
|
@@ -489,7 +497,9 @@ sequences include `clicked_total` and `clicked_truncated`; the compact `clicked`
|
|
|
489
497
|
list keeps the first and last clicked targets so later route switches and reset
|
|
490
498
|
actions stay visible. Click actions with `click_count` greater than `1` are
|
|
491
499
|
included in clicked-target evidence and rolled up as `click_count_action_total`
|
|
492
|
-
and `click_count_value_total`.
|
|
500
|
+
and `click_count_value_total`. Setup receipt sampling favors both first and last
|
|
501
|
+
per-viewport receipts before filling remaining space, so late lifecycle phases
|
|
502
|
+
such as terminal or restart remain visible in compact summaries.
|
|
493
503
|
|
|
494
504
|
`target.timeout_sec` is optional. Use it for known-heavy profile targets so the
|
|
495
505
|
profile carries its own hosted Riddle worker budget; an explicit CLI `--timeout`
|
|
@@ -49,6 +49,7 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
|
|
|
49
49
|
"fill",
|
|
50
50
|
"set_input_value",
|
|
51
51
|
"set_range_value",
|
|
52
|
+
"canvas_signature",
|
|
52
53
|
"assert_text_visible",
|
|
53
54
|
"assert_text_absent",
|
|
54
55
|
"assert_selector_count",
|
|
@@ -550,6 +551,26 @@ function profileSetupRangeValueReceipts(results) {
|
|
|
550
551
|
reason: result.reason ?? result.error ?? null
|
|
551
552
|
}));
|
|
552
553
|
}
|
|
554
|
+
function profileSetupCanvasSignatureReceipts(results) {
|
|
555
|
+
return results.filter((result) => profileSetupResultAction(result) === "canvas_signature").map((result) => ({
|
|
556
|
+
ordinal: result.ordinal ?? null,
|
|
557
|
+
ok: result.ok !== false,
|
|
558
|
+
selector: result.selector ?? null,
|
|
559
|
+
frame_selector: result.frame_selector ?? null,
|
|
560
|
+
label: result.label ?? null,
|
|
561
|
+
hash: result.hash ?? null,
|
|
562
|
+
data_length: result.data_length ?? null,
|
|
563
|
+
width: result.width ?? null,
|
|
564
|
+
height: result.height ?? null,
|
|
565
|
+
css_width: result.css_width ?? null,
|
|
566
|
+
css_height: result.css_height ?? null,
|
|
567
|
+
compare_to: result.compare_to ?? null,
|
|
568
|
+
previous_hash: result.previous_hash ?? null,
|
|
569
|
+
changed: result.changed ?? null,
|
|
570
|
+
return_stored_to: result.return_stored_to ?? null,
|
|
571
|
+
reason: result.reason ?? result.error ?? null
|
|
572
|
+
}));
|
|
573
|
+
}
|
|
553
574
|
function sampleProfileSetupSummaryItems(items, limit) {
|
|
554
575
|
if (items.length <= limit) return items;
|
|
555
576
|
const firstCount = Math.floor(limit / 2);
|
|
@@ -594,6 +615,8 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewpo
|
|
|
594
615
|
const sampledWindowEvalReceipts = sampleProfileSetupSummaryItems(windowEvalReceipts, 8);
|
|
595
616
|
const rangeValueReceipts = profileSetupRangeValueReceipts(results);
|
|
596
617
|
const sampledRangeValueReceipts = sampleProfileSetupSummaryItems(rangeValueReceipts, 8);
|
|
618
|
+
const canvasSignatureReceipts = profileSetupCanvasSignatureReceipts(results);
|
|
619
|
+
const sampledCanvasSignatureReceipts = sampleProfileSetupSummaryItems(canvasSignatureReceipts, 8);
|
|
597
620
|
const clickedItems = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false).map((result) => {
|
|
598
621
|
const clickCount = typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : void 0;
|
|
599
622
|
return {
|
|
@@ -645,6 +668,9 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewpo
|
|
|
645
668
|
set_range_value_total: rangeValueReceipts.length,
|
|
646
669
|
set_range_value_truncated: rangeValueReceipts.length > sampledRangeValueReceipts.length,
|
|
647
670
|
set_range_value: sampledRangeValueReceipts,
|
|
671
|
+
canvas_signature_total: canvasSignatureReceipts.length,
|
|
672
|
+
canvas_signature_truncated: canvasSignatureReceipts.length > sampledCanvasSignatureReceipts.length,
|
|
673
|
+
canvas_signature: sampledCanvasSignatureReceipts,
|
|
648
674
|
clicked,
|
|
649
675
|
text_samples,
|
|
650
676
|
failed: failed.map((result) => ({
|
|
@@ -697,7 +723,7 @@ function isSupportedCheckType(value) {
|
|
|
697
723
|
}
|
|
698
724
|
function normalizeSetupActionType(value, index) {
|
|
699
725
|
const normalizedInput = String(value || "").trim().replace(/-/g, "_");
|
|
700
|
-
const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
|
|
726
|
+
const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "canvas_hash" || normalizedInput === "capture_canvas_hash" || normalizedInput === "capture_canvas_signature" || normalizedInput === "canvas_state_signature" ? "canvas_signature" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
|
|
701
727
|
if (RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.includes(normalized)) {
|
|
702
728
|
return normalized;
|
|
703
729
|
}
|
|
@@ -826,7 +852,7 @@ function normalizeSetupAction(input, index) {
|
|
|
826
852
|
if (frameIndex !== void 0 && (!Number.isInteger(frameIndex) || frameIndex < 0)) {
|
|
827
853
|
throw new Error(`target.setup_actions[${index}].frame_index must be a non-negative integer.`);
|
|
828
854
|
}
|
|
829
|
-
if ((type === "click" || type === "drag" || type === "fill" || type === "set_input_value" || type === "set_range_value" || type === "wait_for_selector" || type === "wait_for_text" || type === "assert_text_visible" || type === "assert_text_absent" || type === "assert_selector_count") && !selector) {
|
|
855
|
+
if ((type === "click" || type === "drag" || type === "fill" || type === "set_input_value" || type === "set_range_value" || type === "canvas_signature" || type === "wait_for_selector" || type === "wait_for_text" || type === "assert_text_visible" || type === "assert_text_absent" || type === "assert_selector_count") && !selector) {
|
|
830
856
|
throw new Error(`target.setup_actions[${index}] ${type} requires selector.`);
|
|
831
857
|
}
|
|
832
858
|
const fromX = numberValue(valueFromOwn(input, "from_x", "fromX", "start_x", "startX", "x1"));
|
|
@@ -920,7 +946,11 @@ function normalizeSetupAction(input, index) {
|
|
|
920
946
|
"assign_return_to",
|
|
921
947
|
"assignReturnTo",
|
|
922
948
|
"return_state_path",
|
|
923
|
-
"returnStatePath"
|
|
949
|
+
"returnStatePath",
|
|
950
|
+
"store_signature_to",
|
|
951
|
+
"storeSignatureTo",
|
|
952
|
+
"signature_path",
|
|
953
|
+
"signaturePath"
|
|
924
954
|
);
|
|
925
955
|
const captureReturn = input.capture_return === false || input.captureReturn === false || input.include_return === false || input.includeReturn === false || input.omit_return === true || input.omitReturn === true ? false : void 0;
|
|
926
956
|
const untilPath = stringFromOwn(input, "until_path", "untilPath", "until_state_path", "untilStatePath", "until_window_path", "untilWindowPath", "until");
|
|
@@ -972,6 +1002,8 @@ function normalizeSetupAction(input, index) {
|
|
|
972
1002
|
store_return_to: storeReturnTo,
|
|
973
1003
|
capture_return: captureReturn,
|
|
974
1004
|
return_summary_fields: normalizeReturnSummaryFields(input, index),
|
|
1005
|
+
compare_to: stringFromOwn(input, "compare_to", "compareTo", "previous_signature_path", "previousSignaturePath", "previous_path", "previousPath", "changed_from", "changedFrom"),
|
|
1006
|
+
expect_changed: booleanValue(valueFromOwn(input, "expect_changed", "expectChanged", "should_change", "shouldChange", "changed")),
|
|
975
1007
|
until_path: untilPath,
|
|
976
1008
|
until_expected_value: hasUntilExpectedValue ? toJsonValue(valueFromOwn(input, "until_expected_value", "untilExpectedValue", "until_expected", "untilExpected", "until_value", "untilValue", "expected_value", "expectedValue", "expected")) : void 0,
|
|
977
1009
|
max_calls: maxCalls,
|
|
@@ -4098,6 +4130,28 @@ function profileSetupRangeValueReceipts(results) {
|
|
|
4098
4130
|
reason: result.reason || result.error || null,
|
|
4099
4131
|
}));
|
|
4100
4132
|
}
|
|
4133
|
+
function profileSetupCanvasSignatureReceipts(results) {
|
|
4134
|
+
return (results || [])
|
|
4135
|
+
.filter((result) => result && profileSetupResultAction(result) === "canvas_signature")
|
|
4136
|
+
.map((result) => ({
|
|
4137
|
+
ordinal: result.ordinal ?? null,
|
|
4138
|
+
ok: result.ok !== false,
|
|
4139
|
+
selector: result.selector ?? null,
|
|
4140
|
+
frame_selector: result.frame_selector ?? null,
|
|
4141
|
+
label: result.label ?? null,
|
|
4142
|
+
hash: result.hash ?? null,
|
|
4143
|
+
data_length: result.data_length ?? null,
|
|
4144
|
+
width: result.width ?? null,
|
|
4145
|
+
height: result.height ?? null,
|
|
4146
|
+
css_width: result.css_width ?? null,
|
|
4147
|
+
css_height: result.css_height ?? null,
|
|
4148
|
+
compare_to: result.compare_to ?? null,
|
|
4149
|
+
previous_hash: result.previous_hash ?? null,
|
|
4150
|
+
changed: result.changed ?? null,
|
|
4151
|
+
return_stored_to: result.return_stored_to ?? null,
|
|
4152
|
+
reason: result.reason || result.error || null,
|
|
4153
|
+
}));
|
|
4154
|
+
}
|
|
4101
4155
|
function sampleProfileSetupSummaryItems(items, limit) {
|
|
4102
4156
|
if ((items || []).length <= limit) return items || [];
|
|
4103
4157
|
const firstCount = Math.floor(limit / 2);
|
|
@@ -4156,6 +4210,8 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountsByViewp
|
|
|
4156
4210
|
const sampledWindowEvalReceipts = sampleProfileSetupSummaryItems(windowEvalReceipts, 8);
|
|
4157
4211
|
const rangeValueReceipts = profileSetupRangeValueReceipts(results);
|
|
4158
4212
|
const sampledRangeValueReceipts = sampleProfileSetupSummaryItems(rangeValueReceipts, 8);
|
|
4213
|
+
const canvasSignatureReceipts = profileSetupCanvasSignatureReceipts(results);
|
|
4214
|
+
const sampledCanvasSignatureReceipts = sampleProfileSetupSummaryItems(canvasSignatureReceipts, 8);
|
|
4159
4215
|
const clickedItems = results
|
|
4160
4216
|
.filter((result) => result && profileSetupResultAction(result) === "click" && result.ok !== false)
|
|
4161
4217
|
.map((result) => {
|
|
@@ -4217,6 +4273,9 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountsByViewp
|
|
|
4217
4273
|
set_range_value_total: rangeValueReceipts.length,
|
|
4218
4274
|
set_range_value_truncated: rangeValueReceipts.length > sampledRangeValueReceipts.length,
|
|
4219
4275
|
set_range_value: sampledRangeValueReceipts,
|
|
4276
|
+
canvas_signature_total: canvasSignatureReceipts.length,
|
|
4277
|
+
canvas_signature_truncated: canvasSignatureReceipts.length > sampledCanvasSignatureReceipts.length,
|
|
4278
|
+
canvas_signature: sampledCanvasSignatureReceipts,
|
|
4220
4279
|
clicked,
|
|
4221
4280
|
text_samples: textSamples,
|
|
4222
4281
|
failed: failed.map((result) => ({
|
|
@@ -5691,6 +5750,54 @@ async function executeSetupAction(action, ordinal, viewport) {
|
|
|
5691
5750
|
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
5692
5751
|
const rect = element.getBoundingClientRect();
|
|
5693
5752
|
const pointerId = payload.pointerType === "touch" ? 11 : 12;
|
|
5753
|
+
const capturedPointers = new Set();
|
|
5754
|
+
const originalOwnSetPointerCapture = Object.getOwnPropertyDescriptor(element, "setPointerCapture");
|
|
5755
|
+
const originalOwnReleasePointerCapture = Object.getOwnPropertyDescriptor(element, "releasePointerCapture");
|
|
5756
|
+
const originalOwnHasPointerCapture = Object.getOwnPropertyDescriptor(element, "hasPointerCapture");
|
|
5757
|
+
const originalSetPointerCapture = typeof element.setPointerCapture === "function" ? element.setPointerCapture.bind(element) : undefined;
|
|
5758
|
+
const originalReleasePointerCapture = typeof element.releasePointerCapture === "function" ? element.releasePointerCapture.bind(element) : undefined;
|
|
5759
|
+
const originalHasPointerCapture = typeof element.hasPointerCapture === "function" ? element.hasPointerCapture.bind(element) : undefined;
|
|
5760
|
+
const restorePointerCapture = () => {
|
|
5761
|
+
if (originalOwnSetPointerCapture) Object.defineProperty(element, "setPointerCapture", originalOwnSetPointerCapture);
|
|
5762
|
+
else delete (element as any).setPointerCapture;
|
|
5763
|
+
if (originalOwnReleasePointerCapture) Object.defineProperty(element, "releasePointerCapture", originalOwnReleasePointerCapture);
|
|
5764
|
+
else delete (element as any).releasePointerCapture;
|
|
5765
|
+
if (originalOwnHasPointerCapture) Object.defineProperty(element, "hasPointerCapture", originalOwnHasPointerCapture);
|
|
5766
|
+
else delete (element as any).hasPointerCapture;
|
|
5767
|
+
};
|
|
5768
|
+
Object.defineProperty(element, "setPointerCapture", {
|
|
5769
|
+
configurable: true,
|
|
5770
|
+
value: (activePointerId) => {
|
|
5771
|
+
capturedPointers.add(activePointerId);
|
|
5772
|
+
try {
|
|
5773
|
+
return originalSetPointerCapture?.(activePointerId);
|
|
5774
|
+
} catch {
|
|
5775
|
+
return undefined;
|
|
5776
|
+
}
|
|
5777
|
+
},
|
|
5778
|
+
});
|
|
5779
|
+
Object.defineProperty(element, "releasePointerCapture", {
|
|
5780
|
+
configurable: true,
|
|
5781
|
+
value: (activePointerId) => {
|
|
5782
|
+
capturedPointers.delete(activePointerId);
|
|
5783
|
+
try {
|
|
5784
|
+
return originalReleasePointerCapture?.(activePointerId);
|
|
5785
|
+
} catch {
|
|
5786
|
+
return undefined;
|
|
5787
|
+
}
|
|
5788
|
+
},
|
|
5789
|
+
});
|
|
5790
|
+
Object.defineProperty(element, "hasPointerCapture", {
|
|
5791
|
+
configurable: true,
|
|
5792
|
+
value: (activePointerId) => {
|
|
5793
|
+
if (capturedPointers.has(activePointerId)) return true;
|
|
5794
|
+
try {
|
|
5795
|
+
return Boolean(originalHasPointerCapture?.(activePointerId));
|
|
5796
|
+
} catch {
|
|
5797
|
+
return false;
|
|
5798
|
+
}
|
|
5799
|
+
},
|
|
5800
|
+
});
|
|
5694
5801
|
const point = (progress) => ({
|
|
5695
5802
|
clientX: rect.left + payload.start.x + (payload.end.x - payload.start.x) * progress,
|
|
5696
5803
|
clientY: rect.top + payload.start.y + (payload.end.y - payload.start.y) * progress,
|
|
@@ -5710,16 +5817,20 @@ async function executeSetupAction(action, ordinal, viewport) {
|
|
|
5710
5817
|
clientY: coords.clientY,
|
|
5711
5818
|
}));
|
|
5712
5819
|
};
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5820
|
+
try {
|
|
5821
|
+
dispatch("pointerover", 0);
|
|
5822
|
+
dispatch("pointerenter", 0);
|
|
5823
|
+
dispatch("pointerdown", 0);
|
|
5824
|
+
for (let step = 1; step <= payload.steps; step += 1) {
|
|
5825
|
+
dispatch("pointermove", step / payload.steps);
|
|
5826
|
+
if (payload.durationMs && payload.steps > 1) await wait(payload.durationMs / payload.steps);
|
|
5827
|
+
}
|
|
5828
|
+
dispatch("pointerup", 1);
|
|
5829
|
+
dispatch("pointerout", 1);
|
|
5830
|
+
dispatch("pointerleave", 1);
|
|
5831
|
+
} finally {
|
|
5832
|
+
restorePointerCapture();
|
|
5719
5833
|
}
|
|
5720
|
-
dispatch("pointerup", 1);
|
|
5721
|
-
dispatch("pointerout", 1);
|
|
5722
|
-
dispatch("pointerleave", 1);
|
|
5723
5834
|
}, {
|
|
5724
5835
|
pointerType,
|
|
5725
5836
|
start: localStart,
|
|
@@ -5759,6 +5870,7 @@ async function executeSetupAction(action, ordinal, viewport) {
|
|
|
5759
5870
|
to_x: toX,
|
|
5760
5871
|
to_y: toY,
|
|
5761
5872
|
pointer_type: pointerType,
|
|
5873
|
+
pointer_capture_polyfill: pointerType === "touch" || pointerType === "pen" ? true : undefined,
|
|
5762
5874
|
steps,
|
|
5763
5875
|
duration_ms: durationMs || undefined,
|
|
5764
5876
|
};
|
|
@@ -6232,6 +6344,156 @@ async function executeSetupAction(action, ordinal, viewport) {
|
|
|
6232
6344
|
reason: rangeResult && rangeResult.ok === true ? undefined : rangeResult?.reason || "range_value_not_set",
|
|
6233
6345
|
};
|
|
6234
6346
|
}
|
|
6347
|
+
if (type === "canvas_signature") {
|
|
6348
|
+
const scope = await setupActionScope(action, timeout);
|
|
6349
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
6350
|
+
const locator = scope.context.locator(action.selector);
|
|
6351
|
+
const count = await locator.count();
|
|
6352
|
+
if (!count) return { ...base, ...setupScopeEvidence(scope), reason: "selector_not_found", count };
|
|
6353
|
+
const targetIndex = Number.isInteger(action.index) ? action.index : 0;
|
|
6354
|
+
if (targetIndex < 0 || targetIndex >= count) return { ...base, ...setupScopeEvidence(scope), reason: "index_out_of_range", count, target_index: targetIndex };
|
|
6355
|
+
const target = locator.nth(targetIndex);
|
|
6356
|
+
await target.waitFor({ state: "visible", timeout });
|
|
6357
|
+
const storeReturnTo = String(action.store_return_to || action.storeReturnTo || action.save_return_to || action.saveReturnTo || action.store_signature_to || action.storeSignatureTo || action.signature_path || action.signaturePath || "").trim();
|
|
6358
|
+
const compareTo = String(action.compare_to || action.compareTo || action.previous_signature_path || action.previousSignaturePath || action.previous_path || action.previousPath || action.changed_from || action.changedFrom || "").trim();
|
|
6359
|
+
const expectChanged = action.expect_changed === true || action.expectChanged === true || action.should_change === true || action.shouldChange === true || action.changed === true
|
|
6360
|
+
? true
|
|
6361
|
+
: action.expect_changed === false || action.expectChanged === false || action.should_change === false || action.shouldChange === false || action.changed === false
|
|
6362
|
+
? false
|
|
6363
|
+
: undefined;
|
|
6364
|
+
const signatureResult = await target.evaluate((element, payload) => {
|
|
6365
|
+
const toJsonValue = (value) => {
|
|
6366
|
+
if (value === null || value === undefined) return null;
|
|
6367
|
+
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
6368
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
6369
|
+
if (Array.isArray(value)) return value.map(toJsonValue);
|
|
6370
|
+
if (typeof value === "object") {
|
|
6371
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
|
|
6372
|
+
}
|
|
6373
|
+
return String(value);
|
|
6374
|
+
};
|
|
6375
|
+
const pathParts = (path) => String(path || "").split(".").map((part) => part.trim()).filter(Boolean);
|
|
6376
|
+
const readWindowPath = (path) => {
|
|
6377
|
+
const parts = pathParts(path);
|
|
6378
|
+
if (parts[0] === "window") parts.shift();
|
|
6379
|
+
if (!parts.length) return { ok: false, reason: "missing_path" };
|
|
6380
|
+
let current = window;
|
|
6381
|
+
for (const part of parts) {
|
|
6382
|
+
if (current === null || current === undefined) return { ok: false, reason: "path_not_found", missing_part: part };
|
|
6383
|
+
current = current[part];
|
|
6384
|
+
if (current === undefined) return { ok: false, reason: "path_not_found", missing_part: part };
|
|
6385
|
+
}
|
|
6386
|
+
return { ok: true, value: toJsonValue(current) };
|
|
6387
|
+
};
|
|
6388
|
+
const storeWindowValue = (path, value) => {
|
|
6389
|
+
const parts = pathParts(path);
|
|
6390
|
+
if (parts[0] === "window") parts.shift();
|
|
6391
|
+
if (!parts.length) return { ok: false, reason: "missing_store_path" };
|
|
6392
|
+
let target = window;
|
|
6393
|
+
for (let index = 0; index < parts.length - 1; index += 1) {
|
|
6394
|
+
const part = parts[index];
|
|
6395
|
+
if (target[part] === null || typeof target[part] !== "object") target[part] = {};
|
|
6396
|
+
target = target[part];
|
|
6397
|
+
}
|
|
6398
|
+
target[parts[parts.length - 1]] = value;
|
|
6399
|
+
return { ok: true, path: parts.join(".") };
|
|
6400
|
+
};
|
|
6401
|
+
const hashText = (text) => {
|
|
6402
|
+
let hash = 2166136261;
|
|
6403
|
+
const step = Math.max(1, Math.floor((text.length || 1) / 4000));
|
|
6404
|
+
for (let index = 0; index < text.length; index += step) {
|
|
6405
|
+
hash ^= text.charCodeAt(index);
|
|
6406
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
6407
|
+
}
|
|
6408
|
+
return String(hash);
|
|
6409
|
+
};
|
|
6410
|
+
const tag = String(element && element.tagName ? element.tagName : "").toLowerCase();
|
|
6411
|
+
if (tag !== "canvas") return { ok: false, reason: "not_canvas_element", tag };
|
|
6412
|
+
const rect = element.getBoundingClientRect();
|
|
6413
|
+
let data = "";
|
|
6414
|
+
try {
|
|
6415
|
+
data = element.toDataURL("image/png");
|
|
6416
|
+
} catch (error) {
|
|
6417
|
+
return {
|
|
6418
|
+
ok: false,
|
|
6419
|
+
reason: "canvas_read_failed",
|
|
6420
|
+
error: String(error && error.message ? error.message : error).slice(0, 1000),
|
|
6421
|
+
width: element.width || 0,
|
|
6422
|
+
height: element.height || 0,
|
|
6423
|
+
css_width: Math.round(rect.width || 0),
|
|
6424
|
+
css_height: Math.round(rect.height || 0),
|
|
6425
|
+
};
|
|
6426
|
+
}
|
|
6427
|
+
const result = {
|
|
6428
|
+
ok: Boolean(element.width > 0 && element.height > 0 && data.length > 0),
|
|
6429
|
+
reason: element.width > 0 && element.height > 0 && data.length > 0 ? undefined : "empty_canvas_signature",
|
|
6430
|
+
hash: hashText(data),
|
|
6431
|
+
data_length: data.length,
|
|
6432
|
+
width: element.width || 0,
|
|
6433
|
+
height: element.height || 0,
|
|
6434
|
+
css_width: Math.round(rect.width || 0),
|
|
6435
|
+
css_height: Math.round(rect.height || 0),
|
|
6436
|
+
compare_to: payload.compareTo || undefined,
|
|
6437
|
+
previous_hash: null,
|
|
6438
|
+
changed: null,
|
|
6439
|
+
};
|
|
6440
|
+
if (payload.compareTo) {
|
|
6441
|
+
const previous = readWindowPath(payload.compareTo);
|
|
6442
|
+
if (!previous.ok) {
|
|
6443
|
+
result.ok = false;
|
|
6444
|
+
result.reason = previous.reason || "compare_path_not_found";
|
|
6445
|
+
result.missing_part = previous.missing_part || undefined;
|
|
6446
|
+
} else {
|
|
6447
|
+
const previousValue = previous.value;
|
|
6448
|
+
const previousHash = previousValue && typeof previousValue === "object" && !Array.isArray(previousValue)
|
|
6449
|
+
? previousValue.hash || previousValue.signature || previousValue.canvas_hash || null
|
|
6450
|
+
: typeof previousValue === "string"
|
|
6451
|
+
? previousValue
|
|
6452
|
+
: null;
|
|
6453
|
+
result.previous_hash = previousHash === null || previousHash === undefined ? null : String(previousHash);
|
|
6454
|
+
result.changed = result.previous_hash === null ? null : result.previous_hash !== result.hash;
|
|
6455
|
+
if (payload.expectChanged === true && result.changed !== true) {
|
|
6456
|
+
result.ok = false;
|
|
6457
|
+
result.reason = "canvas_signature_unchanged";
|
|
6458
|
+
} else if (payload.expectChanged === false && result.changed !== false) {
|
|
6459
|
+
result.ok = false;
|
|
6460
|
+
result.reason = "canvas_signature_changed";
|
|
6461
|
+
}
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
if (payload.storeReturnTo) {
|
|
6465
|
+
const stored = storeWindowValue(payload.storeReturnTo, result);
|
|
6466
|
+
if (!stored.ok) {
|
|
6467
|
+
return { ...result, ok: false, reason: "signature_store_failed", store_reason: stored.reason };
|
|
6468
|
+
}
|
|
6469
|
+
return { ...result, return_stored_to: stored.path };
|
|
6470
|
+
}
|
|
6471
|
+
return result;
|
|
6472
|
+
}, { compareTo, expectChanged, storeReturnTo });
|
|
6473
|
+
return {
|
|
6474
|
+
...base,
|
|
6475
|
+
...setupScopeEvidence(scope),
|
|
6476
|
+
ok: signatureResult && signatureResult.ok === true,
|
|
6477
|
+
count,
|
|
6478
|
+
target_index: targetIndex,
|
|
6479
|
+
label: action.label || action.name || undefined,
|
|
6480
|
+
hash: signatureResult?.hash,
|
|
6481
|
+
data_length: signatureResult?.data_length,
|
|
6482
|
+
width: signatureResult?.width,
|
|
6483
|
+
height: signatureResult?.height,
|
|
6484
|
+
css_width: signatureResult?.css_width,
|
|
6485
|
+
css_height: signatureResult?.css_height,
|
|
6486
|
+
compare_to: signatureResult?.compare_to || compareTo || undefined,
|
|
6487
|
+
previous_hash: signatureResult?.previous_hash,
|
|
6488
|
+
changed: signatureResult?.changed,
|
|
6489
|
+
return_stored_to: signatureResult?.return_stored_to || storeReturnTo || undefined,
|
|
6490
|
+
missing_part: signatureResult?.missing_part || undefined,
|
|
6491
|
+
store_reason: signatureResult?.store_reason || undefined,
|
|
6492
|
+
tag: signatureResult?.tag,
|
|
6493
|
+
reason: signatureResult && signatureResult.ok === true ? undefined : signatureResult?.reason || "canvas_signature_failed",
|
|
6494
|
+
error: signatureResult?.error || undefined,
|
|
6495
|
+
};
|
|
6496
|
+
}
|
|
6235
6497
|
if (type === "assert_selector_count") {
|
|
6236
6498
|
const scope = await setupActionScope(action, timeout);
|
|
6237
6499
|
if (!scope.ok) return setupScopeFailure(base, scope);
|