@riddledc/riddle-proof 0.7.158 → 0.7.160
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-YB3LNLZB.js → chunk-MV7UM4EV.js} +228 -5
- package/dist/cli.cjs +280 -13
- package/dist/cli.js +53 -9
- package/dist/index.cjs +228 -5
- package/dist/index.js +1 -1
- package/dist/profile.cjs +228 -5
- package/dist/profile.d.cts +3 -1
- package/dist/profile.d.ts +3 -1
- package/dist/profile.js +1 -1
- 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,
|
|
@@ -2094,7 +2126,14 @@ function textSequenceForCheck(viewport, check) {
|
|
|
2094
2126
|
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
2095
2127
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
2096
2128
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
2097
|
-
|
|
2129
|
+
let candidates;
|
|
2130
|
+
if (check.type === "selector_text_visible") {
|
|
2131
|
+
candidates = visibleMatchTexts.length ? visibleMatchTexts : visibleTexts;
|
|
2132
|
+
} else if (check.type === "selector_text_absent") {
|
|
2133
|
+
candidates = matchTexts.length ? matchTexts : texts;
|
|
2134
|
+
} else {
|
|
2135
|
+
candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
2136
|
+
}
|
|
2098
2137
|
return candidates.map((text) => String(text).replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
2099
2138
|
}
|
|
2100
2139
|
return [];
|
|
@@ -3474,7 +3513,14 @@ function textSequenceForCheck(viewport, check) {
|
|
|
3474
3513
|
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
3475
3514
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
3476
3515
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
3477
|
-
|
|
3516
|
+
let candidates;
|
|
3517
|
+
if (check.type === "selector_text_visible") {
|
|
3518
|
+
candidates = visibleMatchTexts.length ? visibleMatchTexts : visibleTexts;
|
|
3519
|
+
} else if (check.type === "selector_text_absent") {
|
|
3520
|
+
candidates = matchTexts.length ? matchTexts : texts;
|
|
3521
|
+
} else {
|
|
3522
|
+
candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
3523
|
+
}
|
|
3478
3524
|
return candidates.map((text) => String(text || "").replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
3479
3525
|
}
|
|
3480
3526
|
return [];
|
|
@@ -4084,6 +4130,28 @@ function profileSetupRangeValueReceipts(results) {
|
|
|
4084
4130
|
reason: result.reason || result.error || null,
|
|
4085
4131
|
}));
|
|
4086
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
|
+
}
|
|
4087
4155
|
function sampleProfileSetupSummaryItems(items, limit) {
|
|
4088
4156
|
if ((items || []).length <= limit) return items || [];
|
|
4089
4157
|
const firstCount = Math.floor(limit / 2);
|
|
@@ -4142,6 +4210,8 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountsByViewp
|
|
|
4142
4210
|
const sampledWindowEvalReceipts = sampleProfileSetupSummaryItems(windowEvalReceipts, 8);
|
|
4143
4211
|
const rangeValueReceipts = profileSetupRangeValueReceipts(results);
|
|
4144
4212
|
const sampledRangeValueReceipts = sampleProfileSetupSummaryItems(rangeValueReceipts, 8);
|
|
4213
|
+
const canvasSignatureReceipts = profileSetupCanvasSignatureReceipts(results);
|
|
4214
|
+
const sampledCanvasSignatureReceipts = sampleProfileSetupSummaryItems(canvasSignatureReceipts, 8);
|
|
4145
4215
|
const clickedItems = results
|
|
4146
4216
|
.filter((result) => result && profileSetupResultAction(result) === "click" && result.ok !== false)
|
|
4147
4217
|
.map((result) => {
|
|
@@ -4203,6 +4273,9 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountsByViewp
|
|
|
4203
4273
|
set_range_value_total: rangeValueReceipts.length,
|
|
4204
4274
|
set_range_value_truncated: rangeValueReceipts.length > sampledRangeValueReceipts.length,
|
|
4205
4275
|
set_range_value: sampledRangeValueReceipts,
|
|
4276
|
+
canvas_signature_total: canvasSignatureReceipts.length,
|
|
4277
|
+
canvas_signature_truncated: canvasSignatureReceipts.length > sampledCanvasSignatureReceipts.length,
|
|
4278
|
+
canvas_signature: sampledCanvasSignatureReceipts,
|
|
4206
4279
|
clicked,
|
|
4207
4280
|
text_samples: textSamples,
|
|
4208
4281
|
failed: failed.map((result) => ({
|
|
@@ -6218,6 +6291,156 @@ async function executeSetupAction(action, ordinal, viewport) {
|
|
|
6218
6291
|
reason: rangeResult && rangeResult.ok === true ? undefined : rangeResult?.reason || "range_value_not_set",
|
|
6219
6292
|
};
|
|
6220
6293
|
}
|
|
6294
|
+
if (type === "canvas_signature") {
|
|
6295
|
+
const scope = await setupActionScope(action, timeout);
|
|
6296
|
+
if (!scope.ok) return setupScopeFailure(base, scope);
|
|
6297
|
+
const locator = scope.context.locator(action.selector);
|
|
6298
|
+
const count = await locator.count();
|
|
6299
|
+
if (!count) return { ...base, ...setupScopeEvidence(scope), reason: "selector_not_found", count };
|
|
6300
|
+
const targetIndex = Number.isInteger(action.index) ? action.index : 0;
|
|
6301
|
+
if (targetIndex < 0 || targetIndex >= count) return { ...base, ...setupScopeEvidence(scope), reason: "index_out_of_range", count, target_index: targetIndex };
|
|
6302
|
+
const target = locator.nth(targetIndex);
|
|
6303
|
+
await target.waitFor({ state: "visible", timeout });
|
|
6304
|
+
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();
|
|
6305
|
+
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();
|
|
6306
|
+
const expectChanged = action.expect_changed === true || action.expectChanged === true || action.should_change === true || action.shouldChange === true || action.changed === true
|
|
6307
|
+
? true
|
|
6308
|
+
: action.expect_changed === false || action.expectChanged === false || action.should_change === false || action.shouldChange === false || action.changed === false
|
|
6309
|
+
? false
|
|
6310
|
+
: undefined;
|
|
6311
|
+
const signatureResult = await target.evaluate((element, payload) => {
|
|
6312
|
+
const toJsonValue = (value) => {
|
|
6313
|
+
if (value === null || value === undefined) return null;
|
|
6314
|
+
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
6315
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
6316
|
+
if (Array.isArray(value)) return value.map(toJsonValue);
|
|
6317
|
+
if (typeof value === "object") {
|
|
6318
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
|
|
6319
|
+
}
|
|
6320
|
+
return String(value);
|
|
6321
|
+
};
|
|
6322
|
+
const pathParts = (path) => String(path || "").split(".").map((part) => part.trim()).filter(Boolean);
|
|
6323
|
+
const readWindowPath = (path) => {
|
|
6324
|
+
const parts = pathParts(path);
|
|
6325
|
+
if (parts[0] === "window") parts.shift();
|
|
6326
|
+
if (!parts.length) return { ok: false, reason: "missing_path" };
|
|
6327
|
+
let current = window;
|
|
6328
|
+
for (const part of parts) {
|
|
6329
|
+
if (current === null || current === undefined) return { ok: false, reason: "path_not_found", missing_part: part };
|
|
6330
|
+
current = current[part];
|
|
6331
|
+
if (current === undefined) return { ok: false, reason: "path_not_found", missing_part: part };
|
|
6332
|
+
}
|
|
6333
|
+
return { ok: true, value: toJsonValue(current) };
|
|
6334
|
+
};
|
|
6335
|
+
const storeWindowValue = (path, value) => {
|
|
6336
|
+
const parts = pathParts(path);
|
|
6337
|
+
if (parts[0] === "window") parts.shift();
|
|
6338
|
+
if (!parts.length) return { ok: false, reason: "missing_store_path" };
|
|
6339
|
+
let target = window;
|
|
6340
|
+
for (let index = 0; index < parts.length - 1; index += 1) {
|
|
6341
|
+
const part = parts[index];
|
|
6342
|
+
if (target[part] === null || typeof target[part] !== "object") target[part] = {};
|
|
6343
|
+
target = target[part];
|
|
6344
|
+
}
|
|
6345
|
+
target[parts[parts.length - 1]] = value;
|
|
6346
|
+
return { ok: true, path: parts.join(".") };
|
|
6347
|
+
};
|
|
6348
|
+
const hashText = (text) => {
|
|
6349
|
+
let hash = 2166136261;
|
|
6350
|
+
const step = Math.max(1, Math.floor((text.length || 1) / 4000));
|
|
6351
|
+
for (let index = 0; index < text.length; index += step) {
|
|
6352
|
+
hash ^= text.charCodeAt(index);
|
|
6353
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
6354
|
+
}
|
|
6355
|
+
return String(hash);
|
|
6356
|
+
};
|
|
6357
|
+
const tag = String(element && element.tagName ? element.tagName : "").toLowerCase();
|
|
6358
|
+
if (tag !== "canvas") return { ok: false, reason: "not_canvas_element", tag };
|
|
6359
|
+
const rect = element.getBoundingClientRect();
|
|
6360
|
+
let data = "";
|
|
6361
|
+
try {
|
|
6362
|
+
data = element.toDataURL("image/png");
|
|
6363
|
+
} catch (error) {
|
|
6364
|
+
return {
|
|
6365
|
+
ok: false,
|
|
6366
|
+
reason: "canvas_read_failed",
|
|
6367
|
+
error: String(error && error.message ? error.message : error).slice(0, 1000),
|
|
6368
|
+
width: element.width || 0,
|
|
6369
|
+
height: element.height || 0,
|
|
6370
|
+
css_width: Math.round(rect.width || 0),
|
|
6371
|
+
css_height: Math.round(rect.height || 0),
|
|
6372
|
+
};
|
|
6373
|
+
}
|
|
6374
|
+
const result = {
|
|
6375
|
+
ok: Boolean(element.width > 0 && element.height > 0 && data.length > 0),
|
|
6376
|
+
reason: element.width > 0 && element.height > 0 && data.length > 0 ? undefined : "empty_canvas_signature",
|
|
6377
|
+
hash: hashText(data),
|
|
6378
|
+
data_length: data.length,
|
|
6379
|
+
width: element.width || 0,
|
|
6380
|
+
height: element.height || 0,
|
|
6381
|
+
css_width: Math.round(rect.width || 0),
|
|
6382
|
+
css_height: Math.round(rect.height || 0),
|
|
6383
|
+
compare_to: payload.compareTo || undefined,
|
|
6384
|
+
previous_hash: null,
|
|
6385
|
+
changed: null,
|
|
6386
|
+
};
|
|
6387
|
+
if (payload.compareTo) {
|
|
6388
|
+
const previous = readWindowPath(payload.compareTo);
|
|
6389
|
+
if (!previous.ok) {
|
|
6390
|
+
result.ok = false;
|
|
6391
|
+
result.reason = previous.reason || "compare_path_not_found";
|
|
6392
|
+
result.missing_part = previous.missing_part || undefined;
|
|
6393
|
+
} else {
|
|
6394
|
+
const previousValue = previous.value;
|
|
6395
|
+
const previousHash = previousValue && typeof previousValue === "object" && !Array.isArray(previousValue)
|
|
6396
|
+
? previousValue.hash || previousValue.signature || previousValue.canvas_hash || null
|
|
6397
|
+
: typeof previousValue === "string"
|
|
6398
|
+
? previousValue
|
|
6399
|
+
: null;
|
|
6400
|
+
result.previous_hash = previousHash === null || previousHash === undefined ? null : String(previousHash);
|
|
6401
|
+
result.changed = result.previous_hash === null ? null : result.previous_hash !== result.hash;
|
|
6402
|
+
if (payload.expectChanged === true && result.changed !== true) {
|
|
6403
|
+
result.ok = false;
|
|
6404
|
+
result.reason = "canvas_signature_unchanged";
|
|
6405
|
+
} else if (payload.expectChanged === false && result.changed !== false) {
|
|
6406
|
+
result.ok = false;
|
|
6407
|
+
result.reason = "canvas_signature_changed";
|
|
6408
|
+
}
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6411
|
+
if (payload.storeReturnTo) {
|
|
6412
|
+
const stored = storeWindowValue(payload.storeReturnTo, result);
|
|
6413
|
+
if (!stored.ok) {
|
|
6414
|
+
return { ...result, ok: false, reason: "signature_store_failed", store_reason: stored.reason };
|
|
6415
|
+
}
|
|
6416
|
+
return { ...result, return_stored_to: stored.path };
|
|
6417
|
+
}
|
|
6418
|
+
return result;
|
|
6419
|
+
}, { compareTo, expectChanged, storeReturnTo });
|
|
6420
|
+
return {
|
|
6421
|
+
...base,
|
|
6422
|
+
...setupScopeEvidence(scope),
|
|
6423
|
+
ok: signatureResult && signatureResult.ok === true,
|
|
6424
|
+
count,
|
|
6425
|
+
target_index: targetIndex,
|
|
6426
|
+
label: action.label || action.name || undefined,
|
|
6427
|
+
hash: signatureResult?.hash,
|
|
6428
|
+
data_length: signatureResult?.data_length,
|
|
6429
|
+
width: signatureResult?.width,
|
|
6430
|
+
height: signatureResult?.height,
|
|
6431
|
+
css_width: signatureResult?.css_width,
|
|
6432
|
+
css_height: signatureResult?.css_height,
|
|
6433
|
+
compare_to: signatureResult?.compare_to || compareTo || undefined,
|
|
6434
|
+
previous_hash: signatureResult?.previous_hash,
|
|
6435
|
+
changed: signatureResult?.changed,
|
|
6436
|
+
return_stored_to: signatureResult?.return_stored_to || storeReturnTo || undefined,
|
|
6437
|
+
missing_part: signatureResult?.missing_part || undefined,
|
|
6438
|
+
store_reason: signatureResult?.store_reason || undefined,
|
|
6439
|
+
tag: signatureResult?.tag,
|
|
6440
|
+
reason: signatureResult && signatureResult.ok === true ? undefined : signatureResult?.reason || "canvas_signature_failed",
|
|
6441
|
+
error: signatureResult?.error || undefined,
|
|
6442
|
+
};
|
|
6443
|
+
}
|
|
6221
6444
|
if (type === "assert_selector_count") {
|
|
6222
6445
|
const scope = await setupActionScope(action, timeout);
|
|
6223
6446
|
if (!scope.ok) return setupScopeFailure(base, scope);
|