@riddledc/riddle-proof 0.7.128 → 0.7.130
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/dist/{chunk-TO3FVF5S.js → chunk-ZJNM3YAB.js} +224 -21
- package/dist/cli.cjs +224 -21
- package/dist/cli.js +1 -1
- package/dist/index.cjs +224 -21
- package/dist/index.js +1 -1
- package/dist/profile.cjs +224 -21
- 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
|
@@ -607,6 +607,17 @@ function normalizeSetupActionCoordinateMode(value, index) {
|
|
|
607
607
|
if (normalized === "ratio" || normalized === "relative" || normalized === "fraction") return "ratio";
|
|
608
608
|
throw new Error(`target.setup_actions[${index}].coordinate_mode ${String(value)} is not supported. Supported coordinate modes: pixels, ratio.`);
|
|
609
609
|
}
|
|
610
|
+
function normalizeSetupActionPointerType(value, type, index) {
|
|
611
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
612
|
+
if (type !== "drag") {
|
|
613
|
+
throw new Error(`target.setup_actions[${index}].pointer_type is only supported for drag actions.`);
|
|
614
|
+
}
|
|
615
|
+
const normalized = String(value).trim().replace(/-/g, "_").toLowerCase();
|
|
616
|
+
if (normalized === "mouse") return "mouse";
|
|
617
|
+
if (normalized === "touch" || normalized === "finger") return "touch";
|
|
618
|
+
if (normalized === "pen" || normalized === "stylus") return "pen";
|
|
619
|
+
throw new Error(`target.setup_actions[${index}].pointer_type ${String(value)} is not supported. Supported pointer types: mouse, touch, pen.`);
|
|
620
|
+
}
|
|
610
621
|
function normalizeSetupAction(input, index) {
|
|
611
622
|
if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
|
|
612
623
|
const type = normalizeSetupActionType(stringValue(input.type), index);
|
|
@@ -625,6 +636,7 @@ function normalizeSetupAction(input, index) {
|
|
|
625
636
|
const toX = numberValue(valueFromOwn(input, "to_x", "toX", "end_x", "endX", "x2"));
|
|
626
637
|
const toY = numberValue(valueFromOwn(input, "to_y", "toY", "end_y", "endY", "y2"));
|
|
627
638
|
const coordinateMode = normalizeSetupActionCoordinateMode(valueFromOwn(input, "coordinate_mode", "coordinateMode", "coords", "units"), index);
|
|
639
|
+
const pointerType = normalizeSetupActionPointerType(valueFromOwn(input, "pointer_type", "pointerType", "input_type", "inputType"), type, index);
|
|
628
640
|
if (type === "drag") {
|
|
629
641
|
if (fromX === void 0 || fromY === void 0 || toX === void 0 || toY === void 0) {
|
|
630
642
|
throw new Error(`target.setup_actions[${index}] drag requires from_x, from_y, to_x, and to_y.`);
|
|
@@ -709,6 +721,7 @@ function normalizeSetupAction(input, index) {
|
|
|
709
721
|
force: type === "click" && (input.force === true || input.force_click === true || input.forceClick === true),
|
|
710
722
|
click_count: normalizeSetupActionClickCount(input, type, index),
|
|
711
723
|
coordinate_mode: coordinateMode,
|
|
724
|
+
pointer_type: pointerType,
|
|
712
725
|
from_x: fromX,
|
|
713
726
|
from_y: fromY,
|
|
714
727
|
to_x: toX,
|
|
@@ -1854,6 +1867,49 @@ function matchText(sample, check) {
|
|
|
1854
1867
|
}
|
|
1855
1868
|
return sample.includes(check.text || "");
|
|
1856
1869
|
}
|
|
1870
|
+
function compactTextEvidenceSample(value) {
|
|
1871
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
1872
|
+
}
|
|
1873
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
1874
|
+
if (index < 0) return void 0;
|
|
1875
|
+
const source = String(sample || "");
|
|
1876
|
+
const context = 120;
|
|
1877
|
+
const start = Math.max(0, index - context);
|
|
1878
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
1879
|
+
const prefix = start > 0 ? "..." : "";
|
|
1880
|
+
const suffix = end < source.length ? "..." : "";
|
|
1881
|
+
const compacted = compactTextEvidenceSample(`${prefix}${source.slice(start, end)}${suffix}`);
|
|
1882
|
+
return compacted ? compacted.slice(0, 240) : void 0;
|
|
1883
|
+
}
|
|
1884
|
+
function textMatchSamples(sample, check) {
|
|
1885
|
+
const source = String(sample || "");
|
|
1886
|
+
if (!source) return [];
|
|
1887
|
+
if (check.pattern) {
|
|
1888
|
+
try {
|
|
1889
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
1890
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
1891
|
+
const sampleText2 = match ? textSampleAroundMatch(source, match.index, match[0]?.length || 1) : void 0;
|
|
1892
|
+
return sampleText2 ? [sampleText2] : [];
|
|
1893
|
+
} catch {
|
|
1894
|
+
return [];
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
const text = check.text || "";
|
|
1898
|
+
if (!text) return [];
|
|
1899
|
+
const index = source.indexOf(text);
|
|
1900
|
+
const sampleText = textSampleAroundMatch(source, index, text.length);
|
|
1901
|
+
return sampleText ? [sampleText] : [];
|
|
1902
|
+
}
|
|
1903
|
+
function textCheckFailureSamples(viewport, check) {
|
|
1904
|
+
const key = textKey(check);
|
|
1905
|
+
const captured = viewport.text_match_samples?.[key] || [];
|
|
1906
|
+
const capturedSamples = captured.map((sample) => compactTextEvidenceSample(sample).slice(0, 240)).filter(Boolean);
|
|
1907
|
+
if (capturedSamples.length) return capturedSamples.slice(0, 3);
|
|
1908
|
+
const matchedSamples = textMatchSamples(viewport.body_text_sample || "", check);
|
|
1909
|
+
if (matchedSamples.length) return matchedSamples.slice(0, 3);
|
|
1910
|
+
const fallback = compactTextEvidenceSample(viewport.body_text_sample || "").slice(0, 240);
|
|
1911
|
+
return fallback ? [fallback] : [];
|
|
1912
|
+
}
|
|
1857
1913
|
function allowedMessageSample(input) {
|
|
1858
1914
|
if (!isRecord(input)) return String(input || "");
|
|
1859
1915
|
const parts = [
|
|
@@ -2298,10 +2354,17 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
2298
2354
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
2299
2355
|
const key = textKey(check);
|
|
2300
2356
|
const expectedVisible = check.type === "text_visible";
|
|
2301
|
-
const
|
|
2357
|
+
const results = viewports.map((viewport) => {
|
|
2302
2358
|
const fromEvidence = viewport.text_matches?.[key];
|
|
2303
|
-
|
|
2359
|
+
const matched = typeof fromEvidence === "boolean" ? fromEvidence : matchText(viewport.body_text_sample || "", check);
|
|
2360
|
+
const failedAgainstExpectation = matched !== expectedVisible;
|
|
2361
|
+
return {
|
|
2362
|
+
viewport: viewport.name,
|
|
2363
|
+
matched,
|
|
2364
|
+
samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : []
|
|
2365
|
+
};
|
|
2304
2366
|
});
|
|
2367
|
+
const matches = results.map((result) => result.matched);
|
|
2305
2368
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
2306
2369
|
return {
|
|
2307
2370
|
type: check.type,
|
|
@@ -2310,7 +2373,8 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
2310
2373
|
evidence: {
|
|
2311
2374
|
text: check.text || null,
|
|
2312
2375
|
pattern: check.pattern || null,
|
|
2313
|
-
matches
|
|
2376
|
+
matches,
|
|
2377
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
2314
2378
|
},
|
|
2315
2379
|
message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
|
|
2316
2380
|
};
|
|
@@ -2897,6 +2961,48 @@ function textMatches(sample, check) {
|
|
|
2897
2961
|
}
|
|
2898
2962
|
return String(sample || "").includes(check.text || "");
|
|
2899
2963
|
}
|
|
2964
|
+
function compactTextEvidenceSample(value) {
|
|
2965
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
2966
|
+
}
|
|
2967
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
2968
|
+
if (index < 0) return undefined;
|
|
2969
|
+
const source = String(sample || "");
|
|
2970
|
+
const context = 120;
|
|
2971
|
+
const start = Math.max(0, index - context);
|
|
2972
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
2973
|
+
const prefix = start > 0 ? "..." : "";
|
|
2974
|
+
const suffix = end < source.length ? "..." : "";
|
|
2975
|
+
const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
|
|
2976
|
+
return compacted ? compacted.slice(0, 240) : undefined;
|
|
2977
|
+
}
|
|
2978
|
+
function textMatchSamples(sample, check) {
|
|
2979
|
+
const source = String(sample || "");
|
|
2980
|
+
if (!source) return [];
|
|
2981
|
+
if (check.pattern) {
|
|
2982
|
+
try {
|
|
2983
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
2984
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
2985
|
+
const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
|
|
2986
|
+
return sampleText ? [sampleText] : [];
|
|
2987
|
+
} catch { return []; }
|
|
2988
|
+
}
|
|
2989
|
+
const text = check.text || "";
|
|
2990
|
+
if (!text) return [];
|
|
2991
|
+
const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
|
|
2992
|
+
return sampleText ? [sampleText] : [];
|
|
2993
|
+
}
|
|
2994
|
+
function textCheckFailureSamples(viewport, check) {
|
|
2995
|
+
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
2996
|
+
const captured = viewport && viewport.text_match_samples && Array.isArray(viewport.text_match_samples[key]) ? viewport.text_match_samples[key] : [];
|
|
2997
|
+
const capturedSamples = captured
|
|
2998
|
+
.map((sample) => compactTextEvidenceSample(sample).slice(0, 240))
|
|
2999
|
+
.filter(Boolean);
|
|
3000
|
+
if (capturedSamples.length) return capturedSamples.slice(0, 3);
|
|
3001
|
+
const matchedSamples = textMatchSamples(viewport && viewport.body_text_sample || "", check);
|
|
3002
|
+
if (matchedSamples.length) return matchedSamples.slice(0, 3);
|
|
3003
|
+
const fallback = compactTextEvidenceSample(viewport && viewport.body_text_sample || "").slice(0, 240);
|
|
3004
|
+
return fallback ? [fallback] : [];
|
|
3005
|
+
}
|
|
2900
3006
|
function allowedMessageSample(input) {
|
|
2901
3007
|
if (!input || typeof input !== "object" || Array.isArray(input)) return String(input || "");
|
|
2902
3008
|
const parts = [
|
|
@@ -3974,13 +4080,22 @@ function assessProfile(profile, evidence) {
|
|
|
3974
4080
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
3975
4081
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
3976
4082
|
const expectedVisible = check.type === "text_visible";
|
|
3977
|
-
const
|
|
4083
|
+
const results = checkViewports.map((viewport) => {
|
|
4084
|
+
const matched = viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check);
|
|
4085
|
+
const failedAgainstExpectation = matched !== expectedVisible;
|
|
4086
|
+
return {
|
|
4087
|
+
viewport: viewport.name,
|
|
4088
|
+
matched,
|
|
4089
|
+
samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : [],
|
|
4090
|
+
};
|
|
4091
|
+
});
|
|
4092
|
+
const matches = results.map((result) => result.matched);
|
|
3978
4093
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
3979
4094
|
checks.push({
|
|
3980
4095
|
type: check.type,
|
|
3981
4096
|
label: check.label || check.type,
|
|
3982
4097
|
status: failed ? "failed" : "passed",
|
|
3983
|
-
evidence: { text: check.text, pattern: check.pattern, matches },
|
|
4098
|
+
evidence: { text: check.text, pattern: check.pattern, matches, viewports: results },
|
|
3984
4099
|
message: failed ? "Text assertion failed in " + failed + " viewport(s)." : undefined,
|
|
3985
4100
|
});
|
|
3986
4101
|
continue;
|
|
@@ -4300,6 +4415,36 @@ function textMatches(sample, check) {
|
|
|
4300
4415
|
}
|
|
4301
4416
|
return String(sample || "").includes(check.text || "");
|
|
4302
4417
|
}
|
|
4418
|
+
function compactTextEvidenceSample(value) {
|
|
4419
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
4420
|
+
}
|
|
4421
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
4422
|
+
if (index < 0) return undefined;
|
|
4423
|
+
const source = String(sample || "");
|
|
4424
|
+
const context = 120;
|
|
4425
|
+
const start = Math.max(0, index - context);
|
|
4426
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
4427
|
+
const prefix = start > 0 ? "..." : "";
|
|
4428
|
+
const suffix = end < source.length ? "..." : "";
|
|
4429
|
+
const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
|
|
4430
|
+
return compacted ? compacted.slice(0, 240) : undefined;
|
|
4431
|
+
}
|
|
4432
|
+
function textMatchSamples(sample, check) {
|
|
4433
|
+
const source = String(sample || "");
|
|
4434
|
+
if (!source) return [];
|
|
4435
|
+
if (check.pattern) {
|
|
4436
|
+
try {
|
|
4437
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
4438
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
4439
|
+
const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
|
|
4440
|
+
return sampleText ? [sampleText] : [];
|
|
4441
|
+
} catch { return []; }
|
|
4442
|
+
}
|
|
4443
|
+
const text = check.text || "";
|
|
4444
|
+
if (!text) return [];
|
|
4445
|
+
const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
|
|
4446
|
+
return sampleText ? [sampleText] : [];
|
|
4447
|
+
}
|
|
4303
4448
|
function profileCheckAppliesToViewport(check, viewport) {
|
|
4304
4449
|
if (!Array.isArray(check.viewports) || !check.viewports.length) return true;
|
|
4305
4450
|
return Boolean(viewport && viewport.name && check.viewports.includes(viewport.name));
|
|
@@ -4777,23 +4922,75 @@ async function executeSetupAction(action, ordinal, viewport) {
|
|
|
4777
4922
|
const requestedSteps = setupNumber(action.steps, 8);
|
|
4778
4923
|
const steps = Math.min(100, Math.max(1, Math.floor(requestedSteps || 8)));
|
|
4779
4924
|
const durationMs = setupNumber(action.duration_ms ?? action.durationMs, 0);
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4925
|
+
const pointerType = String(action.pointer_type || action.pointerType || "mouse").trim().toLowerCase();
|
|
4926
|
+
if (pointerType === "touch" || pointerType === "pen") {
|
|
4927
|
+
const localStart = {
|
|
4928
|
+
x: coordinate(fromX, box.width),
|
|
4929
|
+
y: coordinate(fromY, box.height),
|
|
4930
|
+
};
|
|
4931
|
+
const localEnd = {
|
|
4932
|
+
x: coordinate(toX, box.width),
|
|
4933
|
+
y: coordinate(toY, box.height),
|
|
4934
|
+
};
|
|
4935
|
+
await target.evaluate(async (element, payload) => {
|
|
4936
|
+
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4937
|
+
const rect = element.getBoundingClientRect();
|
|
4938
|
+
const pointerId = payload.pointerType === "touch" ? 11 : 12;
|
|
4939
|
+
const point = (progress) => ({
|
|
4940
|
+
clientX: rect.left + payload.start.x + (payload.end.x - payload.start.x) * progress,
|
|
4941
|
+
clientY: rect.top + payload.start.y + (payload.end.y - payload.start.y) * progress,
|
|
4942
|
+
});
|
|
4943
|
+
const dispatch = (type, progress) => {
|
|
4944
|
+
const coords = point(progress);
|
|
4945
|
+
element.dispatchEvent(new PointerEvent(type, {
|
|
4946
|
+
bubbles: true,
|
|
4947
|
+
cancelable: true,
|
|
4948
|
+
composed: true,
|
|
4949
|
+
pointerId,
|
|
4950
|
+
pointerType: payload.pointerType,
|
|
4951
|
+
isPrimary: true,
|
|
4952
|
+
buttons: type === "pointerup" ? 0 : 1,
|
|
4953
|
+
button: type === "pointerup" ? 0 : 0,
|
|
4954
|
+
clientX: coords.clientX,
|
|
4955
|
+
clientY: coords.clientY,
|
|
4956
|
+
}));
|
|
4957
|
+
};
|
|
4958
|
+
dispatch("pointerover", 0);
|
|
4959
|
+
dispatch("pointerenter", 0);
|
|
4960
|
+
dispatch("pointerdown", 0);
|
|
4961
|
+
for (let step = 1; step <= payload.steps; step += 1) {
|
|
4962
|
+
dispatch("pointermove", step / payload.steps);
|
|
4963
|
+
if (payload.durationMs && payload.steps > 1) await wait(payload.durationMs / payload.steps);
|
|
4791
4964
|
}
|
|
4792
|
-
|
|
4793
|
-
|
|
4965
|
+
dispatch("pointerup", 1);
|
|
4966
|
+
dispatch("pointerout", 1);
|
|
4967
|
+
dispatch("pointerleave", 1);
|
|
4968
|
+
}, {
|
|
4969
|
+
pointerType,
|
|
4970
|
+
start: localStart,
|
|
4971
|
+
end: localEnd,
|
|
4972
|
+
steps,
|
|
4973
|
+
durationMs,
|
|
4974
|
+
});
|
|
4975
|
+
} else {
|
|
4976
|
+
await page.mouse.move(start.x, start.y);
|
|
4977
|
+
await page.mouse.down();
|
|
4978
|
+
try {
|
|
4979
|
+
if (durationMs && steps > 1) {
|
|
4980
|
+
for (let step = 1; step <= steps; step += 1) {
|
|
4981
|
+
const progress = step / steps;
|
|
4982
|
+
await page.mouse.move(
|
|
4983
|
+
start.x + (end.x - start.x) * progress,
|
|
4984
|
+
start.y + (end.y - start.y) * progress,
|
|
4985
|
+
);
|
|
4986
|
+
await page.waitForTimeout(durationMs / steps);
|
|
4987
|
+
}
|
|
4988
|
+
} else {
|
|
4989
|
+
await page.mouse.move(end.x, end.y, { steps });
|
|
4990
|
+
}
|
|
4991
|
+
} finally {
|
|
4992
|
+
await page.mouse.up().catch(() => {});
|
|
4794
4993
|
}
|
|
4795
|
-
} finally {
|
|
4796
|
-
await page.mouse.up().catch(() => {});
|
|
4797
4994
|
}
|
|
4798
4995
|
return {
|
|
4799
4996
|
...base,
|
|
@@ -4806,6 +5003,7 @@ async function executeSetupAction(action, ordinal, viewport) {
|
|
|
4806
5003
|
from_y: fromY,
|
|
4807
5004
|
to_x: toX,
|
|
4808
5005
|
to_y: toY,
|
|
5006
|
+
pointer_type: pointerType,
|
|
4809
5007
|
steps,
|
|
4810
5008
|
duration_ms: durationMs || undefined,
|
|
4811
5009
|
};
|
|
@@ -6251,6 +6449,7 @@ async function captureViewport(viewport) {
|
|
|
6251
6449
|
const frames = {};
|
|
6252
6450
|
const text_sequences = {};
|
|
6253
6451
|
const text_matches = {};
|
|
6452
|
+
const text_match_samples = {};
|
|
6254
6453
|
const http_statuses = {};
|
|
6255
6454
|
const link_statuses = {};
|
|
6256
6455
|
for (const check of profile.checks || []) {
|
|
@@ -6272,7 +6471,10 @@ async function captureViewport(viewport) {
|
|
|
6272
6471
|
text_sequences[check.selector] = await selectorTextSequence(check.selector);
|
|
6273
6472
|
}
|
|
6274
6473
|
if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
|
|
6275
|
-
|
|
6474
|
+
const key = textKey(check);
|
|
6475
|
+
const sample = dom.body_text || dom.body_text_sample || "";
|
|
6476
|
+
text_matches[key] = textMatches(sample, check);
|
|
6477
|
+
text_match_samples[key] = textMatchSamples(sample, check);
|
|
6276
6478
|
}
|
|
6277
6479
|
if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
|
|
6278
6480
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
@@ -6349,6 +6551,7 @@ async function captureViewport(viewport) {
|
|
|
6349
6551
|
frames,
|
|
6350
6552
|
text_sequences,
|
|
6351
6553
|
text_matches,
|
|
6554
|
+
text_match_samples,
|
|
6352
6555
|
http_statuses,
|
|
6353
6556
|
link_statuses,
|
|
6354
6557
|
route_inventory: routeInventory,
|