@riddledc/riddle-proof 0.7.8 → 0.7.10
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 +14 -1
- package/dist/{chunk-7NMAU4DP.js → chunk-A7RJZD4I.js} +263 -8
- package/dist/cli.cjs +262 -8
- package/dist/cli.js +1 -1
- package/dist/index.cjs +264 -8
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/profile.cjs +264 -8
- package/dist/profile.d.cts +17 -1
- package/dist/profile.d.ts +17 -1
- package/dist/profile.js +3 -1
- package/dist/proof-run-engine.d.cts +3 -3
- package/dist/proof-run-engine.d.ts +3 -3
- package/examples/profiles/page-content-basic.json +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,7 +108,11 @@ or as a stronger proof base before a change loop.
|
|
|
108
108
|
{ "name": "mobile", "width": 390, "height": 844 },
|
|
109
109
|
{ "name": "desktop", "width": 1440, "height": 1000 }
|
|
110
110
|
],
|
|
111
|
-
"auth": "none"
|
|
111
|
+
"auth": "none",
|
|
112
|
+
"setup_actions": [
|
|
113
|
+
{ "type": "click", "selector": "[data-testid='show-plans']" },
|
|
114
|
+
{ "type": "wait_for_text", "selector": "body", "text": "Start building" }
|
|
115
|
+
]
|
|
112
116
|
},
|
|
113
117
|
"checks": [
|
|
114
118
|
{ "type": "route_loaded", "expected_path": "/pricing" },
|
|
@@ -141,6 +145,15 @@ The package includes a generic starter profile at
|
|
|
141
145
|
profile directory and replace the selector/text checks with app-specific
|
|
142
146
|
invariants.
|
|
143
147
|
|
|
148
|
+
`target.setup_actions` is optional. Use it when the meaningful proof surface
|
|
149
|
+
appears only after a picker, tab, login stub, transport control, or other
|
|
150
|
+
bounded interaction. Supported setup actions are `click`, `wait`,
|
|
151
|
+
`wait_for_selector`, and `wait_for_text`; a failed setup action is recorded as
|
|
152
|
+
a failed `setup_actions_succeeded` check so the profile cannot pass without
|
|
153
|
+
reaching the intended state. Text-matched `click` actions prefer visible
|
|
154
|
+
matching elements, which keeps responsive layouts from selecting hidden desktop
|
|
155
|
+
or mobile-only links.
|
|
156
|
+
|
|
144
157
|
The result uses `riddle-proof.profile-result.v1` and separates product failures
|
|
145
158
|
from weak proof and environment blockers:
|
|
146
159
|
|
|
@@ -20,6 +20,12 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
20
20
|
"no_mobile_horizontal_overflow",
|
|
21
21
|
"no_fatal_console_errors"
|
|
22
22
|
];
|
|
23
|
+
var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
|
|
24
|
+
"click",
|
|
25
|
+
"wait",
|
|
26
|
+
"wait_for_selector",
|
|
27
|
+
"wait_for_text"
|
|
28
|
+
];
|
|
23
29
|
var DEFAULT_VIEWPORTS = [
|
|
24
30
|
{ name: "desktop", width: 1280, height: 800 }
|
|
25
31
|
];
|
|
@@ -72,6 +78,41 @@ function normalizeViewports(value) {
|
|
|
72
78
|
function isSupportedCheckType(value) {
|
|
73
79
|
return RIDDLE_PROOF_PROFILE_CHECK_TYPES.includes(value);
|
|
74
80
|
}
|
|
81
|
+
function normalizeSetupActionType(value, index) {
|
|
82
|
+
const normalized = String(value || "").trim().replace(/-/g, "_");
|
|
83
|
+
if (RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.includes(normalized)) {
|
|
84
|
+
return normalized;
|
|
85
|
+
}
|
|
86
|
+
throw new Error(`target.setup_actions[${index}].type ${value || "(missing)"} is not supported. Supported actions: ${RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.join(", ")}`);
|
|
87
|
+
}
|
|
88
|
+
function normalizeSetupAction(input, index) {
|
|
89
|
+
if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
|
|
90
|
+
const type = normalizeSetupActionType(stringValue(input.type), index);
|
|
91
|
+
const selector = stringValue(input.selector);
|
|
92
|
+
if ((type === "click" || type === "wait_for_selector" || type === "wait_for_text") && !selector) {
|
|
93
|
+
throw new Error(`target.setup_actions[${index}] ${type} requires selector.`);
|
|
94
|
+
}
|
|
95
|
+
if (type === "wait_for_text" && !stringValue(input.text) && !stringValue(input.pattern)) {
|
|
96
|
+
throw new Error(`target.setup_actions[${index}] wait_for_text requires text or pattern.`);
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
type,
|
|
100
|
+
selector,
|
|
101
|
+
text: stringValue(input.text),
|
|
102
|
+
pattern: stringValue(input.pattern),
|
|
103
|
+
flags: stringValue(input.flags),
|
|
104
|
+
index: numberValue(input.index),
|
|
105
|
+
ms: numberValue(input.ms) ?? numberValue(input.wait_ms) ?? numberValue(input.waitMs),
|
|
106
|
+
timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
|
|
107
|
+
after_ms: numberValue(input.after_ms) ?? numberValue(input.afterMs),
|
|
108
|
+
continue_on_failure: input.continue_on_failure === true || input.continueOnFailure === true
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function normalizeSetupActions(value) {
|
|
112
|
+
if (value === void 0) return void 0;
|
|
113
|
+
if (!Array.isArray(value)) throw new Error("target.setup_actions must be an array.");
|
|
114
|
+
return value.map(normalizeSetupAction);
|
|
115
|
+
}
|
|
75
116
|
function normalizeCheck(input, index) {
|
|
76
117
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
77
118
|
const type = stringValue(input.type);
|
|
@@ -139,7 +180,8 @@ function normalizeRiddleProofProfile(input, options = {}) {
|
|
|
139
180
|
viewports: options.viewports?.length ? options.viewports : normalizeViewports(targetInput.viewports),
|
|
140
181
|
auth: stringValue(targetInput.auth) || "none",
|
|
141
182
|
wait_for_selector: stringValue(targetInput.wait_for_selector) || stringValue(targetInput.waitForSelector),
|
|
142
|
-
wait_ms: numberValue(targetInput.wait_ms) ?? numberValue(targetInput.waitMs)
|
|
183
|
+
wait_ms: numberValue(targetInput.wait_ms) ?? numberValue(targetInput.waitMs),
|
|
184
|
+
setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions)
|
|
143
185
|
},
|
|
144
186
|
checks,
|
|
145
187
|
artifacts: Array.isArray(input.artifacts) ? input.artifacts.map((item) => String(item)).filter(Boolean) : ["screenshot", "console", "dom_summary", "proof_json"],
|
|
@@ -157,7 +199,13 @@ function resolveRiddleProofProfileTargetUrl(profile) {
|
|
|
157
199
|
throw new Error("profile target URL could not be resolved.");
|
|
158
200
|
}
|
|
159
201
|
function routeForViewport(viewport) {
|
|
160
|
-
|
|
202
|
+
if (viewport?.route) {
|
|
203
|
+
return {
|
|
204
|
+
...viewport.route,
|
|
205
|
+
matched: viewport.route.matched || routePathMatches(viewport.route.observed, viewport.route.expected_path)
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
161
209
|
requested: "",
|
|
162
210
|
observed: "",
|
|
163
211
|
matched: false,
|
|
@@ -183,8 +231,17 @@ function matchText(sample, check) {
|
|
|
183
231
|
}
|
|
184
232
|
return sample.includes(check.text || "");
|
|
185
233
|
}
|
|
234
|
+
function normalizeRoutePath(path) {
|
|
235
|
+
const value = path || "/";
|
|
236
|
+
if (value === "/") return "/";
|
|
237
|
+
return value.replace(/\/+$/, "") || "/";
|
|
238
|
+
}
|
|
239
|
+
function routePathMatches(observed, expected) {
|
|
240
|
+
return normalizeRoutePath(observed) === normalizeRoutePath(expected);
|
|
241
|
+
}
|
|
186
242
|
function successfulRoute(route) {
|
|
187
|
-
|
|
243
|
+
const matched = route.matched || routePathMatches(route.observed, route.expected_path);
|
|
244
|
+
return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
|
|
188
245
|
}
|
|
189
246
|
function assessCheckFromEvidence(check, evidence) {
|
|
190
247
|
const viewports = evidence.viewports || [];
|
|
@@ -202,7 +259,7 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
202
259
|
const failed = viewports.filter((viewport) => !successfulRoute({
|
|
203
260
|
...viewport.route,
|
|
204
261
|
expected_path: expectedPath,
|
|
205
|
-
matched: viewport.route.observed
|
|
262
|
+
matched: routePathMatches(viewport.route.observed, expectedPath) || viewport.route.matched
|
|
206
263
|
}));
|
|
207
264
|
return {
|
|
208
265
|
type: check.type,
|
|
@@ -312,6 +369,48 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
312
369
|
message: "Unsupported check type."
|
|
313
370
|
};
|
|
314
371
|
}
|
|
372
|
+
function assessSetupActionsFromEvidence(profile, evidence) {
|
|
373
|
+
if (!profile.target.setup_actions?.length) return void 0;
|
|
374
|
+
const actionCount = profile.target.setup_actions.length;
|
|
375
|
+
const failed = [];
|
|
376
|
+
const viewports = evidence.viewports || [];
|
|
377
|
+
for (const viewport of viewports) {
|
|
378
|
+
const results = viewport.setup_action_results || [];
|
|
379
|
+
for (const result of results) {
|
|
380
|
+
if (result.ok === false) {
|
|
381
|
+
failed.push({
|
|
382
|
+
viewport: viewport.name,
|
|
383
|
+
action: result.action ?? result.type ?? null,
|
|
384
|
+
selector: result.selector ?? null,
|
|
385
|
+
reason: result.reason ?? result.error ?? null
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (results.length < actionCount && results.every((result) => result.ok !== false)) {
|
|
390
|
+
failed.push({
|
|
391
|
+
viewport: viewport.name,
|
|
392
|
+
action: "setup_actions",
|
|
393
|
+
selector: null,
|
|
394
|
+
reason: `missing setup action results: ${results.length}/${actionCount}`
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
type: "setup_actions_succeeded",
|
|
400
|
+
label: "setup actions succeeded",
|
|
401
|
+
status: failed.length ? "failed" : "passed",
|
|
402
|
+
evidence: {
|
|
403
|
+
action_count: actionCount,
|
|
404
|
+
viewports: viewports.map((viewport) => ({
|
|
405
|
+
name: viewport.name,
|
|
406
|
+
ok: (viewport.setup_action_results || []).length >= actionCount && (viewport.setup_action_results || []).every((result) => result.ok !== false),
|
|
407
|
+
result_count: (viewport.setup_action_results || []).length
|
|
408
|
+
})),
|
|
409
|
+
failed
|
|
410
|
+
},
|
|
411
|
+
message: failed.length ? `Setup actions failed in ${failed.length} viewport action(s).` : void 0
|
|
412
|
+
};
|
|
413
|
+
}
|
|
315
414
|
function profileStatusFromEvidence(evidence, checks) {
|
|
316
415
|
if (!evidence) return "proof_insufficient";
|
|
317
416
|
const viewports = evidence.viewports || [];
|
|
@@ -323,7 +422,10 @@ function profileStatusFromEvidence(evidence, checks) {
|
|
|
323
422
|
}
|
|
324
423
|
function assessRiddleProofProfileEvidence(profile, evidence, options = {}) {
|
|
325
424
|
const capturedAt = evidence?.captured_at || (/* @__PURE__ */ new Date()).toISOString();
|
|
326
|
-
const checks = evidence ?
|
|
425
|
+
const checks = evidence ? [
|
|
426
|
+
assessSetupActionsFromEvidence(profile, evidence),
|
|
427
|
+
...profile.checks.map((check) => assessCheckFromEvidence(check, evidence))
|
|
428
|
+
].filter((check) => Boolean(check)) : [];
|
|
327
429
|
const status = profileStatusFromEvidence(evidence, checks);
|
|
328
430
|
const firstViewport = evidence?.viewports?.[0];
|
|
329
431
|
const screenshots = (evidence?.viewports || []).map((viewport) => viewport.screenshot_label).filter((label) => Boolean(label));
|
|
@@ -425,8 +527,16 @@ function createRiddleProofProfileInsufficientResult(input) {
|
|
|
425
527
|
}
|
|
426
528
|
function runtimeScriptAssessmentSource() {
|
|
427
529
|
return String.raw`
|
|
530
|
+
function normalizeRoutePath(path) {
|
|
531
|
+
const value = path || "/";
|
|
532
|
+
if (value === "/") return "/";
|
|
533
|
+
return value.replace(/\/+$/, "") || "/";
|
|
534
|
+
}
|
|
535
|
+
function routePathMatches(observed, expected) {
|
|
536
|
+
return normalizeRoutePath(observed) === normalizeRoutePath(expected);
|
|
537
|
+
}
|
|
428
538
|
function routeOk(route) {
|
|
429
|
-
return Boolean(route && route.matched && !route.error && (route.http_status == null || route.http_status < 400));
|
|
539
|
+
return Boolean(route && (route.matched || routePathMatches(route.observed, route.expected_path)) && !route.error && (route.http_status == null || route.http_status < 400));
|
|
430
540
|
}
|
|
431
541
|
function textMatches(sample, check) {
|
|
432
542
|
if (check.pattern) {
|
|
@@ -437,12 +547,53 @@ function textMatches(sample, check) {
|
|
|
437
547
|
function assessProfile(profile, evidence) {
|
|
438
548
|
const checks = [];
|
|
439
549
|
const viewports = evidence.viewports || [];
|
|
550
|
+
if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
|
|
551
|
+
const actionCount = profile.target.setup_actions.length;
|
|
552
|
+
const failed = [];
|
|
553
|
+
for (const viewport of viewports) {
|
|
554
|
+
const results = viewport.setup_action_results || [];
|
|
555
|
+
for (const result of results) {
|
|
556
|
+
if (result && result.ok === false) {
|
|
557
|
+
failed.push({
|
|
558
|
+
viewport: viewport.name,
|
|
559
|
+
action: result.action || result.type || null,
|
|
560
|
+
selector: result.selector || null,
|
|
561
|
+
reason: result.reason || result.error || null,
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (results.length < actionCount && results.every((result) => !result || result.ok !== false)) {
|
|
566
|
+
failed.push({
|
|
567
|
+
viewport: viewport.name,
|
|
568
|
+
action: "setup_actions",
|
|
569
|
+
selector: null,
|
|
570
|
+
reason: "missing setup action results: " + results.length + "/" + actionCount,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
checks.push({
|
|
575
|
+
type: "setup_actions_succeeded",
|
|
576
|
+
label: "setup actions succeeded",
|
|
577
|
+
status: failed.length ? "failed" : "passed",
|
|
578
|
+
evidence: {
|
|
579
|
+
action_count: actionCount,
|
|
580
|
+
viewports: viewports.map((viewport) => ({
|
|
581
|
+
name: viewport.name,
|
|
582
|
+
ok: (viewport.setup_action_results || []).length >= actionCount
|
|
583
|
+
&& (viewport.setup_action_results || []).every((result) => !result || result.ok !== false),
|
|
584
|
+
result_count: (viewport.setup_action_results || []).length,
|
|
585
|
+
})),
|
|
586
|
+
failed,
|
|
587
|
+
},
|
|
588
|
+
message: failed.length ? "Setup actions failed in " + failed.length + " viewport action(s)." : undefined,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
440
591
|
for (const check of profile.checks || []) {
|
|
441
592
|
if (check.type === "route_loaded") {
|
|
442
593
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
443
594
|
const failed = viewports.filter((viewport) => {
|
|
444
595
|
const route = { ...(viewport.route || {}), expected_path: expectedPath };
|
|
445
|
-
route.matched = route.observed
|
|
596
|
+
route.matched = routePathMatches(route.observed, expectedPath) || route.matched;
|
|
446
597
|
return !routeOk(route);
|
|
447
598
|
});
|
|
448
599
|
checks.push({
|
|
@@ -599,6 +750,105 @@ function textMatches(sample, check) {
|
|
|
599
750
|
}
|
|
600
751
|
return String(sample || "").includes(check.text || "");
|
|
601
752
|
}
|
|
753
|
+
function setupActionType(action) {
|
|
754
|
+
return String(action && action.type ? action.type : "").replace(/-/g, "_");
|
|
755
|
+
}
|
|
756
|
+
function setupNumber(value, fallback) {
|
|
757
|
+
const number = Number(value);
|
|
758
|
+
return Number.isFinite(number) && number >= 0 ? number : fallback;
|
|
759
|
+
}
|
|
760
|
+
function setupTextMatches(sample, action) {
|
|
761
|
+
if (action.pattern) {
|
|
762
|
+
try { return new RegExp(action.pattern, action.flags || "").test(sample || ""); } catch { return false; }
|
|
763
|
+
}
|
|
764
|
+
return String(sample || "").includes(action.text || "");
|
|
765
|
+
}
|
|
766
|
+
async function setupLocatorText(locator, index) {
|
|
767
|
+
return await locator.nth(index).textContent({ timeout: 1000 }).catch(() => "");
|
|
768
|
+
}
|
|
769
|
+
async function setupLocatorVisible(locator, index) {
|
|
770
|
+
return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
|
|
771
|
+
}
|
|
772
|
+
async function executeSetupAction(action, ordinal) {
|
|
773
|
+
const type = setupActionType(action);
|
|
774
|
+
const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null };
|
|
775
|
+
const timeout = setupNumber(action.timeout_ms, 5000);
|
|
776
|
+
try {
|
|
777
|
+
if (type === "wait") {
|
|
778
|
+
const ms = setupNumber(action.ms, 500);
|
|
779
|
+
await page.waitForTimeout(ms);
|
|
780
|
+
return { ...base, ok: true, ms };
|
|
781
|
+
}
|
|
782
|
+
if (type === "wait_for_selector") {
|
|
783
|
+
await page.waitForSelector(action.selector, { state: "visible", timeout });
|
|
784
|
+
return { ...base, ok: true, timeout_ms: timeout };
|
|
785
|
+
}
|
|
786
|
+
if (type === "click") {
|
|
787
|
+
const locator = page.locator(action.selector);
|
|
788
|
+
const count = await locator.count();
|
|
789
|
+
if (!count) return { ...base, reason: "selector_not_found", count };
|
|
790
|
+
let targetIndex = Number.isInteger(action.index) ? action.index : 0;
|
|
791
|
+
let matchedText = null;
|
|
792
|
+
let hiddenMatchIndex = -1;
|
|
793
|
+
let hiddenMatchedText = null;
|
|
794
|
+
if (action.text || action.pattern) {
|
|
795
|
+
targetIndex = -1;
|
|
796
|
+
for (let index = 0; index < count; index += 1) {
|
|
797
|
+
const text = await setupLocatorText(locator, index);
|
|
798
|
+
if (setupTextMatches(text, action)) {
|
|
799
|
+
const visible = await setupLocatorVisible(locator, index);
|
|
800
|
+
if (visible) {
|
|
801
|
+
targetIndex = index;
|
|
802
|
+
matchedText = text;
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
if (hiddenMatchIndex < 0) {
|
|
806
|
+
hiddenMatchIndex = index;
|
|
807
|
+
hiddenMatchedText = text;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (targetIndex < 0 && hiddenMatchIndex >= 0) return { ...base, reason: "matching_element_not_visible", count, target_index: hiddenMatchIndex, text: hiddenMatchedText };
|
|
812
|
+
if (targetIndex < 0) return { ...base, reason: "text_not_found", count };
|
|
813
|
+
}
|
|
814
|
+
if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex };
|
|
815
|
+
await locator.nth(targetIndex).click({ timeout });
|
|
816
|
+
return { ...base, ok: true, count, target_index: targetIndex, text: matchedText };
|
|
817
|
+
}
|
|
818
|
+
if (type === "wait_for_text") {
|
|
819
|
+
const locator = page.locator(action.selector);
|
|
820
|
+
const startedAt = Date.now();
|
|
821
|
+
let lastText = "";
|
|
822
|
+
while (Date.now() - startedAt <= timeout) {
|
|
823
|
+
const count = await locator.count().catch(() => 0);
|
|
824
|
+
for (let index = 0; index < count; index += 1) {
|
|
825
|
+
const text = await setupLocatorText(locator, index);
|
|
826
|
+
lastText = text || lastText;
|
|
827
|
+
if (setupTextMatches(text, action)) {
|
|
828
|
+
return { ...base, ok: true, text, target_index: index, timeout_ms: timeout };
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
await page.waitForTimeout(100);
|
|
832
|
+
}
|
|
833
|
+
return { ...base, reason: "text_not_found", text: lastText, timeout_ms: timeout };
|
|
834
|
+
}
|
|
835
|
+
return { ...base, reason: "unsupported_action" };
|
|
836
|
+
} catch (error) {
|
|
837
|
+
return { ...base, error: String(error && error.message ? error.message : error).slice(0, 1000) };
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
async function executeSetupActions(actions) {
|
|
841
|
+
const results = [];
|
|
842
|
+
for (let index = 0; index < (actions || []).length; index += 1) {
|
|
843
|
+
const action = actions[index] || {};
|
|
844
|
+
const result = await executeSetupAction(action, index);
|
|
845
|
+
results.push(result);
|
|
846
|
+
const afterMs = setupNumber(action.after_ms, 0);
|
|
847
|
+
if (afterMs) await page.waitForTimeout(afterMs);
|
|
848
|
+
if (result.ok === false && action.continue_on_failure !== true) break;
|
|
849
|
+
}
|
|
850
|
+
return results;
|
|
851
|
+
}
|
|
602
852
|
function expectedPathFor(check) {
|
|
603
853
|
return check.expected_path || new URL(targetUrl).pathname || "/";
|
|
604
854
|
}
|
|
@@ -633,6 +883,9 @@ async function captureViewport(viewport) {
|
|
|
633
883
|
if (!navigationError && profile.target.wait_ms) {
|
|
634
884
|
await page.waitForTimeout(profile.target.wait_ms);
|
|
635
885
|
}
|
|
886
|
+
const setupActionResults = (!navigationError && !waitError)
|
|
887
|
+
? await executeSetupActions(profile.target.setup_actions || [])
|
|
888
|
+
: [];
|
|
636
889
|
const dom = await page.evaluate(() => {
|
|
637
890
|
const body = document.body;
|
|
638
891
|
const documentElement = document.documentElement;
|
|
@@ -682,7 +935,7 @@ async function captureViewport(viewport) {
|
|
|
682
935
|
requested: targetUrl,
|
|
683
936
|
observed: dom.pathname,
|
|
684
937
|
expected_path: expectedPath,
|
|
685
|
-
matched: dom.pathname
|
|
938
|
+
matched: routePathMatches(dom.pathname, expectedPath),
|
|
686
939
|
http_status: httpStatus,
|
|
687
940
|
error: navigationError || waitError || undefined,
|
|
688
941
|
},
|
|
@@ -694,6 +947,7 @@ async function captureViewport(viewport) {
|
|
|
694
947
|
overflow_px: Math.max(0, (dom.scroll_width || 0) - (dom.client_width || viewport.width)),
|
|
695
948
|
selectors,
|
|
696
949
|
text_matches,
|
|
950
|
+
setup_action_results: setupActionResults,
|
|
697
951
|
screenshot_label: screenshotLabel,
|
|
698
952
|
navigation_error: navigationError,
|
|
699
953
|
wait_error: waitError,
|
|
@@ -793,6 +1047,7 @@ export {
|
|
|
793
1047
|
RIDDLE_PROOF_PROFILE_RESULT_VERSION,
|
|
794
1048
|
RIDDLE_PROOF_PROFILE_STATUSES,
|
|
795
1049
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
1050
|
+
RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES,
|
|
796
1051
|
slugifyRiddleProofProfileName,
|
|
797
1052
|
normalizeRiddleProofProfile,
|
|
798
1053
|
resolveRiddleProofProfileTargetUrl,
|