@riddledc/riddle-proof 0.7.93 → 0.7.94
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 +17 -0
- package/dist/{chunk-QLBOFUON.js → chunk-FQ4QBY2N.js} +72 -8
- package/dist/cli.cjs +77 -8
- package/dist/cli.js +6 -1
- package/dist/index.cjs +72 -8
- package/dist/index.js +1 -1
- package/dist/profile.cjs +72 -8
- package/dist/profile.d.cts +1 -1
- package/dist/profile.d.ts +1 -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
|
@@ -163,6 +163,7 @@ or as a stronger proof base before a change loop.
|
|
|
163
163
|
{ "type": "selector_visible", "selector": "[data-testid='pricing-cards']" },
|
|
164
164
|
{ "type": "selector_absent", "selector": "[data-testid='loading-spinner']" },
|
|
165
165
|
{ "type": "selector_count_equals", "selector": "[data-testid='pricing-card']", "expected_count": 3 },
|
|
166
|
+
{ "type": "selector_text_visible", "selector": "[data-testid='pricing-cards']", "text": "Pro" },
|
|
166
167
|
{ "type": "text_visible", "text": "Start building" },
|
|
167
168
|
{ "type": "text_visible", "text": "Compare plans", "viewports": ["desktop"] },
|
|
168
169
|
{ "type": "no_mobile_horizontal_overflow" },
|
|
@@ -417,6 +418,22 @@ The check records the visible text sequence for the selector and passes when
|
|
|
417
418
|
the expected texts appear in that order as a subsequence. This is less brittle
|
|
418
419
|
than matching one large body-text regex when only row or card order matters.
|
|
419
420
|
|
|
421
|
+
Use `selector_text_visible` and `selector_text_absent` when the durable
|
|
422
|
+
assertion belongs to one panel, code sample, result area, or card group rather
|
|
423
|
+
than the whole page:
|
|
424
|
+
|
|
425
|
+
```json
|
|
426
|
+
{
|
|
427
|
+
"type": "selector_text_visible",
|
|
428
|
+
"selector": ".result-state",
|
|
429
|
+
"text": "\"sync\": false"
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
The check records visible text for the selector in each viewport and reports
|
|
434
|
+
matched counts plus short samples, which makes generated-command and evidence
|
|
435
|
+
card audits easier to diagnose than global `text_visible` checks.
|
|
436
|
+
|
|
420
437
|
Use `frame_text_visible` and `frame_no_horizontal_overflow` for embedded app,
|
|
421
438
|
game, or preview surfaces that render inside iframes:
|
|
422
439
|
|
|
@@ -20,6 +20,8 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
20
20
|
"selector_count_equals",
|
|
21
21
|
"selector_count_equal",
|
|
22
22
|
"selector_count_eq",
|
|
23
|
+
"selector_text_visible",
|
|
24
|
+
"selector_text_absent",
|
|
23
25
|
"selector_text_order",
|
|
24
26
|
"frame_text_visible",
|
|
25
27
|
"frame_url_equals",
|
|
@@ -733,7 +735,7 @@ function normalizeCheck(input, index) {
|
|
|
733
735
|
if (!isSupportedCheckType(type)) {
|
|
734
736
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
735
737
|
}
|
|
736
|
-
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && !stringValue(input.selector)) {
|
|
738
|
+
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue(input.selector)) {
|
|
737
739
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
738
740
|
}
|
|
739
741
|
if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
|
|
@@ -749,7 +751,7 @@ function normalizeCheck(input, index) {
|
|
|
749
751
|
if (type === "frame_url_matches" && !stringValue(input.pattern)) {
|
|
750
752
|
throw new Error(`checks[${index}] frame_url_matches requires pattern.`);
|
|
751
753
|
}
|
|
752
|
-
if ((type === "text_visible" || type === "text_absent") && !stringValue(input.text) && !stringValue(input.pattern)) {
|
|
754
|
+
if ((type === "text_visible" || type === "text_absent" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue(input.text) && !stringValue(input.pattern)) {
|
|
753
755
|
throw new Error(`checks[${index}] ${type} requires text or pattern.`);
|
|
754
756
|
}
|
|
755
757
|
if ((type === "url_search_param_equals" || type === "url_search_param_absent") && !stringValue(input.param) && !stringValue(input.search_param) && !stringValue(input.searchParam) && !stringValue(input.key)) {
|
|
@@ -1015,9 +1017,11 @@ function textSequenceForCheck(viewport, check) {
|
|
|
1015
1017
|
const key = selectorKey(check);
|
|
1016
1018
|
const sequence = viewport.text_sequences?.[key];
|
|
1017
1019
|
if (isRecord(sequence)) {
|
|
1020
|
+
const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
|
|
1021
|
+
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
1018
1022
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
1019
1023
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
1020
|
-
const candidates = visibleTexts.length ? visibleTexts : texts;
|
|
1024
|
+
const candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
1021
1025
|
return candidates.map((text) => String(text).replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
1022
1026
|
}
|
|
1023
1027
|
return [];
|
|
@@ -1351,6 +1355,35 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
1351
1355
|
message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
|
|
1352
1356
|
};
|
|
1353
1357
|
}
|
|
1358
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
1359
|
+
const key = selectorKey(check);
|
|
1360
|
+
const expectedVisible = check.type === "selector_text_visible";
|
|
1361
|
+
const results = viewports.map((viewport) => {
|
|
1362
|
+
const texts = textSequenceForCheck(viewport, check);
|
|
1363
|
+
const matches = texts.filter((text) => matchText(text, check));
|
|
1364
|
+
return {
|
|
1365
|
+
viewport: viewport.name,
|
|
1366
|
+
selector_count: viewport.selectors?.[key]?.count || 0,
|
|
1367
|
+
visible_count: viewport.selectors?.[key]?.visible_count || 0,
|
|
1368
|
+
matched_count: matches.length,
|
|
1369
|
+
matched: matches.length > 0,
|
|
1370
|
+
samples: matches.slice(0, 3).map((text) => text.slice(0, 240))
|
|
1371
|
+
};
|
|
1372
|
+
});
|
|
1373
|
+
const failed = results.filter((result) => result.matched !== expectedVisible).length;
|
|
1374
|
+
return {
|
|
1375
|
+
type: check.type,
|
|
1376
|
+
label: checkLabel(check),
|
|
1377
|
+
status: failed ? "failed" : "passed",
|
|
1378
|
+
evidence: {
|
|
1379
|
+
selector: key,
|
|
1380
|
+
text: check.text || null,
|
|
1381
|
+
pattern: check.pattern || null,
|
|
1382
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
1383
|
+
},
|
|
1384
|
+
message: failed ? `Selector ${key} text assertion failed in ${failed} viewport(s).` : void 0
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1354
1387
|
if (check.type === "selector_text_order") {
|
|
1355
1388
|
const key = selectorKey(check);
|
|
1356
1389
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -1361,7 +1394,7 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
1361
1394
|
viewport: viewport.name,
|
|
1362
1395
|
matched: match.matched,
|
|
1363
1396
|
matched_positions: match.positions,
|
|
1364
|
-
visible_texts: texts.slice(0, 20)
|
|
1397
|
+
visible_texts: texts.slice(0, 20).map((text) => text.slice(0, 240))
|
|
1365
1398
|
};
|
|
1366
1399
|
});
|
|
1367
1400
|
const failed = results.filter((result) => !result.matched).length;
|
|
@@ -2071,9 +2104,11 @@ function textSequenceForCheck(viewport, check) {
|
|
|
2071
2104
|
const key = check.selector || "";
|
|
2072
2105
|
const sequence = viewport.text_sequences && viewport.text_sequences[key];
|
|
2073
2106
|
if (sequence && typeof sequence === "object") {
|
|
2107
|
+
const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
|
|
2108
|
+
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
2074
2109
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
2075
2110
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
2076
|
-
const candidates = visibleTexts.length ? visibleTexts : texts;
|
|
2111
|
+
const candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
2077
2112
|
return candidates.map((text) => String(text || "").replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
2078
2113
|
}
|
|
2079
2114
|
return [];
|
|
@@ -2711,6 +2746,31 @@ function assessProfile(profile, evidence) {
|
|
|
2711
2746
|
});
|
|
2712
2747
|
continue;
|
|
2713
2748
|
}
|
|
2749
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
2750
|
+
const selector = check.selector || "";
|
|
2751
|
+
const expectedVisible = check.type === "selector_text_visible";
|
|
2752
|
+
const results = checkViewports.map((viewport) => {
|
|
2753
|
+
const texts = textSequenceForCheck(viewport, check);
|
|
2754
|
+
const matches = texts.filter((text) => textMatches(text, check));
|
|
2755
|
+
return {
|
|
2756
|
+
viewport: viewport.name,
|
|
2757
|
+
selector_count: viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0,
|
|
2758
|
+
visible_count: viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0,
|
|
2759
|
+
matched_count: matches.length,
|
|
2760
|
+
matched: matches.length > 0,
|
|
2761
|
+
samples: matches.slice(0, 3).map((text) => text.slice(0, 240)),
|
|
2762
|
+
};
|
|
2763
|
+
});
|
|
2764
|
+
const failed = results.filter((result) => result.matched !== expectedVisible).length;
|
|
2765
|
+
checks.push({
|
|
2766
|
+
type: check.type,
|
|
2767
|
+
label: check.label || check.type,
|
|
2768
|
+
status: failed ? "failed" : "passed",
|
|
2769
|
+
evidence: { selector, text: check.text || null, pattern: check.pattern || null, viewports: results },
|
|
2770
|
+
message: failed ? "Selector " + selector + " text assertion failed in " + failed + " viewport(s)." : undefined,
|
|
2771
|
+
});
|
|
2772
|
+
continue;
|
|
2773
|
+
}
|
|
2714
2774
|
if (check.type === "selector_text_order") {
|
|
2715
2775
|
const selector = check.selector || "";
|
|
2716
2776
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -2721,7 +2781,7 @@ function assessProfile(profile, evidence) {
|
|
|
2721
2781
|
viewport: viewport.name,
|
|
2722
2782
|
matched: match.matched,
|
|
2723
2783
|
matched_positions: match.positions,
|
|
2724
|
-
visible_texts: texts.slice(0, 20),
|
|
2784
|
+
visible_texts: texts.slice(0, 20).map((text) => text.slice(0, 240)),
|
|
2725
2785
|
};
|
|
2726
2786
|
});
|
|
2727
2787
|
const failed = results.filter((result) => !result.matched).length;
|
|
@@ -3925,6 +3985,7 @@ async function selectorStats(selector) {
|
|
|
3925
3985
|
async function selectorTextSequence(selector) {
|
|
3926
3986
|
return page.locator(selector).evaluateAll((elements) => {
|
|
3927
3987
|
const compact = (value) => String(value || "").replace(/\s+/g, " ").trim().slice(0, 240);
|
|
3988
|
+
const matchText = (value) => String(value || "").replace(/\s+/g, " ").trim().slice(0, 8000);
|
|
3928
3989
|
const isVisible = (element) => {
|
|
3929
3990
|
const style = window.getComputedStyle(element);
|
|
3930
3991
|
const rect = element.getBoundingClientRect();
|
|
@@ -3933,6 +3994,7 @@ async function selectorTextSequence(selector) {
|
|
|
3933
3994
|
const rows = elements.map((element, index) => ({
|
|
3934
3995
|
index,
|
|
3935
3996
|
text: compact(element.innerText || element.textContent || ""),
|
|
3997
|
+
match_text: matchText(element.innerText || element.textContent || ""),
|
|
3936
3998
|
visible: isVisible(element),
|
|
3937
3999
|
}));
|
|
3938
4000
|
return {
|
|
@@ -3940,8 +4002,10 @@ async function selectorTextSequence(selector) {
|
|
|
3940
4002
|
visible_count: rows.filter((row) => row.visible).length,
|
|
3941
4003
|
texts: rows.map((row) => row.text).filter(Boolean).slice(0, 40),
|
|
3942
4004
|
visible_texts: rows.filter((row) => row.visible).map((row) => row.text).filter(Boolean).slice(0, 40),
|
|
4005
|
+
match_texts: rows.map((row) => row.match_text).filter(Boolean).slice(0, 40),
|
|
4006
|
+
visible_match_texts: rows.filter((row) => row.visible).map((row) => row.match_text).filter(Boolean).slice(0, 40),
|
|
3943
4007
|
};
|
|
3944
|
-
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
4008
|
+
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], match_texts: [], visible_match_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
3945
4009
|
}
|
|
3946
4010
|
function linkProbeMaxLinks(check) {
|
|
3947
4011
|
const value = Number(check.max_links || check.maxLinks || check.limit || 100);
|
|
@@ -4677,7 +4741,7 @@ async function captureViewport(viewport) {
|
|
|
4677
4741
|
) {
|
|
4678
4742
|
selectors[check.selector] = await selectorStats(check.selector);
|
|
4679
4743
|
}
|
|
4680
|
-
if (check.type === "selector_text_order" && check.selector) {
|
|
4744
|
+
if ((check.type === "selector_text_order" || check.type === "selector_text_visible" || check.type === "selector_text_absent") && check.selector) {
|
|
4681
4745
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
4682
4746
|
text_sequences[check.selector] = await selectorTextSequence(check.selector);
|
|
4683
4747
|
}
|
package/dist/cli.cjs
CHANGED
|
@@ -6913,6 +6913,8 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
6913
6913
|
"selector_count_equals",
|
|
6914
6914
|
"selector_count_equal",
|
|
6915
6915
|
"selector_count_eq",
|
|
6916
|
+
"selector_text_visible",
|
|
6917
|
+
"selector_text_absent",
|
|
6916
6918
|
"selector_text_order",
|
|
6917
6919
|
"frame_text_visible",
|
|
6918
6920
|
"frame_url_equals",
|
|
@@ -7626,7 +7628,7 @@ function normalizeCheck(input, index) {
|
|
|
7626
7628
|
if (!isSupportedCheckType(type)) {
|
|
7627
7629
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
7628
7630
|
}
|
|
7629
|
-
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && !stringValue2(input.selector)) {
|
|
7631
|
+
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue2(input.selector)) {
|
|
7630
7632
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
7631
7633
|
}
|
|
7632
7634
|
if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue2(input.selector)) {
|
|
@@ -7642,7 +7644,7 @@ function normalizeCheck(input, index) {
|
|
|
7642
7644
|
if (type === "frame_url_matches" && !stringValue2(input.pattern)) {
|
|
7643
7645
|
throw new Error(`checks[${index}] frame_url_matches requires pattern.`);
|
|
7644
7646
|
}
|
|
7645
|
-
if ((type === "text_visible" || type === "text_absent") && !stringValue2(input.text) && !stringValue2(input.pattern)) {
|
|
7647
|
+
if ((type === "text_visible" || type === "text_absent" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue2(input.text) && !stringValue2(input.pattern)) {
|
|
7646
7648
|
throw new Error(`checks[${index}] ${type} requires text or pattern.`);
|
|
7647
7649
|
}
|
|
7648
7650
|
if ((type === "url_search_param_equals" || type === "url_search_param_absent") && !stringValue2(input.param) && !stringValue2(input.search_param) && !stringValue2(input.searchParam) && !stringValue2(input.key)) {
|
|
@@ -7908,9 +7910,11 @@ function textSequenceForCheck(viewport, check) {
|
|
|
7908
7910
|
const key = selectorKey(check);
|
|
7909
7911
|
const sequence = viewport.text_sequences?.[key];
|
|
7910
7912
|
if (isRecord(sequence)) {
|
|
7913
|
+
const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
|
|
7914
|
+
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
7911
7915
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
7912
7916
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
7913
|
-
const candidates = visibleTexts.length ? visibleTexts : texts;
|
|
7917
|
+
const candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
7914
7918
|
return candidates.map((text) => String(text).replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
7915
7919
|
}
|
|
7916
7920
|
return [];
|
|
@@ -8244,6 +8248,35 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
8244
8248
|
message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
|
|
8245
8249
|
};
|
|
8246
8250
|
}
|
|
8251
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
8252
|
+
const key = selectorKey(check);
|
|
8253
|
+
const expectedVisible = check.type === "selector_text_visible";
|
|
8254
|
+
const results = viewports.map((viewport) => {
|
|
8255
|
+
const texts = textSequenceForCheck(viewport, check);
|
|
8256
|
+
const matches = texts.filter((text) => matchText(text, check));
|
|
8257
|
+
return {
|
|
8258
|
+
viewport: viewport.name,
|
|
8259
|
+
selector_count: viewport.selectors?.[key]?.count || 0,
|
|
8260
|
+
visible_count: viewport.selectors?.[key]?.visible_count || 0,
|
|
8261
|
+
matched_count: matches.length,
|
|
8262
|
+
matched: matches.length > 0,
|
|
8263
|
+
samples: matches.slice(0, 3).map((text) => text.slice(0, 240))
|
|
8264
|
+
};
|
|
8265
|
+
});
|
|
8266
|
+
const failed = results.filter((result) => result.matched !== expectedVisible).length;
|
|
8267
|
+
return {
|
|
8268
|
+
type: check.type,
|
|
8269
|
+
label: checkLabel(check),
|
|
8270
|
+
status: failed ? "failed" : "passed",
|
|
8271
|
+
evidence: {
|
|
8272
|
+
selector: key,
|
|
8273
|
+
text: check.text || null,
|
|
8274
|
+
pattern: check.pattern || null,
|
|
8275
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
8276
|
+
},
|
|
8277
|
+
message: failed ? `Selector ${key} text assertion failed in ${failed} viewport(s).` : void 0
|
|
8278
|
+
};
|
|
8279
|
+
}
|
|
8247
8280
|
if (check.type === "selector_text_order") {
|
|
8248
8281
|
const key = selectorKey(check);
|
|
8249
8282
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -8254,7 +8287,7 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
8254
8287
|
viewport: viewport.name,
|
|
8255
8288
|
matched: match.matched,
|
|
8256
8289
|
matched_positions: match.positions,
|
|
8257
|
-
visible_texts: texts.slice(0, 20)
|
|
8290
|
+
visible_texts: texts.slice(0, 20).map((text) => text.slice(0, 240))
|
|
8258
8291
|
};
|
|
8259
8292
|
});
|
|
8260
8293
|
const failed = results.filter((result) => !result.matched).length;
|
|
@@ -8948,9 +8981,11 @@ function textSequenceForCheck(viewport, check) {
|
|
|
8948
8981
|
const key = check.selector || "";
|
|
8949
8982
|
const sequence = viewport.text_sequences && viewport.text_sequences[key];
|
|
8950
8983
|
if (sequence && typeof sequence === "object") {
|
|
8984
|
+
const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
|
|
8985
|
+
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
8951
8986
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
8952
8987
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
8953
|
-
const candidates = visibleTexts.length ? visibleTexts : texts;
|
|
8988
|
+
const candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
8954
8989
|
return candidates.map((text) => String(text || "").replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
8955
8990
|
}
|
|
8956
8991
|
return [];
|
|
@@ -9588,6 +9623,31 @@ function assessProfile(profile, evidence) {
|
|
|
9588
9623
|
});
|
|
9589
9624
|
continue;
|
|
9590
9625
|
}
|
|
9626
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
9627
|
+
const selector = check.selector || "";
|
|
9628
|
+
const expectedVisible = check.type === "selector_text_visible";
|
|
9629
|
+
const results = checkViewports.map((viewport) => {
|
|
9630
|
+
const texts = textSequenceForCheck(viewport, check);
|
|
9631
|
+
const matches = texts.filter((text) => textMatches(text, check));
|
|
9632
|
+
return {
|
|
9633
|
+
viewport: viewport.name,
|
|
9634
|
+
selector_count: viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0,
|
|
9635
|
+
visible_count: viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0,
|
|
9636
|
+
matched_count: matches.length,
|
|
9637
|
+
matched: matches.length > 0,
|
|
9638
|
+
samples: matches.slice(0, 3).map((text) => text.slice(0, 240)),
|
|
9639
|
+
};
|
|
9640
|
+
});
|
|
9641
|
+
const failed = results.filter((result) => result.matched !== expectedVisible).length;
|
|
9642
|
+
checks.push({
|
|
9643
|
+
type: check.type,
|
|
9644
|
+
label: check.label || check.type,
|
|
9645
|
+
status: failed ? "failed" : "passed",
|
|
9646
|
+
evidence: { selector, text: check.text || null, pattern: check.pattern || null, viewports: results },
|
|
9647
|
+
message: failed ? "Selector " + selector + " text assertion failed in " + failed + " viewport(s)." : undefined,
|
|
9648
|
+
});
|
|
9649
|
+
continue;
|
|
9650
|
+
}
|
|
9591
9651
|
if (check.type === "selector_text_order") {
|
|
9592
9652
|
const selector = check.selector || "";
|
|
9593
9653
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -9598,7 +9658,7 @@ function assessProfile(profile, evidence) {
|
|
|
9598
9658
|
viewport: viewport.name,
|
|
9599
9659
|
matched: match.matched,
|
|
9600
9660
|
matched_positions: match.positions,
|
|
9601
|
-
visible_texts: texts.slice(0, 20),
|
|
9661
|
+
visible_texts: texts.slice(0, 20).map((text) => text.slice(0, 240)),
|
|
9602
9662
|
};
|
|
9603
9663
|
});
|
|
9604
9664
|
const failed = results.filter((result) => !result.matched).length;
|
|
@@ -10802,6 +10862,7 @@ async function selectorStats(selector) {
|
|
|
10802
10862
|
async function selectorTextSequence(selector) {
|
|
10803
10863
|
return page.locator(selector).evaluateAll((elements) => {
|
|
10804
10864
|
const compact = (value) => String(value || "").replace(/\s+/g, " ").trim().slice(0, 240);
|
|
10865
|
+
const matchText = (value) => String(value || "").replace(/\s+/g, " ").trim().slice(0, 8000);
|
|
10805
10866
|
const isVisible = (element) => {
|
|
10806
10867
|
const style = window.getComputedStyle(element);
|
|
10807
10868
|
const rect = element.getBoundingClientRect();
|
|
@@ -10810,6 +10871,7 @@ async function selectorTextSequence(selector) {
|
|
|
10810
10871
|
const rows = elements.map((element, index) => ({
|
|
10811
10872
|
index,
|
|
10812
10873
|
text: compact(element.innerText || element.textContent || ""),
|
|
10874
|
+
match_text: matchText(element.innerText || element.textContent || ""),
|
|
10813
10875
|
visible: isVisible(element),
|
|
10814
10876
|
}));
|
|
10815
10877
|
return {
|
|
@@ -10817,8 +10879,10 @@ async function selectorTextSequence(selector) {
|
|
|
10817
10879
|
visible_count: rows.filter((row) => row.visible).length,
|
|
10818
10880
|
texts: rows.map((row) => row.text).filter(Boolean).slice(0, 40),
|
|
10819
10881
|
visible_texts: rows.filter((row) => row.visible).map((row) => row.text).filter(Boolean).slice(0, 40),
|
|
10882
|
+
match_texts: rows.map((row) => row.match_text).filter(Boolean).slice(0, 40),
|
|
10883
|
+
visible_match_texts: rows.filter((row) => row.visible).map((row) => row.match_text).filter(Boolean).slice(0, 40),
|
|
10820
10884
|
};
|
|
10821
|
-
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
10885
|
+
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], match_texts: [], visible_match_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
10822
10886
|
}
|
|
10823
10887
|
function linkProbeMaxLinks(check) {
|
|
10824
10888
|
const value = Number(check.max_links || check.maxLinks || check.limit || 100);
|
|
@@ -11554,7 +11618,7 @@ async function captureViewport(viewport) {
|
|
|
11554
11618
|
) {
|
|
11555
11619
|
selectors[check.selector] = await selectorStats(check.selector);
|
|
11556
11620
|
}
|
|
11557
|
-
if (check.type === "selector_text_order" && check.selector) {
|
|
11621
|
+
if ((check.type === "selector_text_order" || check.type === "selector_text_visible" || check.type === "selector_text_absent") && check.selector) {
|
|
11558
11622
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
11559
11623
|
text_sequences[check.selector] = await selectorTextSequence(check.selector);
|
|
11560
11624
|
}
|
|
@@ -12181,6 +12245,11 @@ function profileCheckMarkdownTarget(check) {
|
|
|
12181
12245
|
const expectedCount = cliFiniteNumber(evidence.expected_count);
|
|
12182
12246
|
return selector && expectedCount !== void 0 ? `${markdownInlineCode(selector)} = ${expectedCount}` : void 0;
|
|
12183
12247
|
}
|
|
12248
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
12249
|
+
const textTarget = profileCheckTextTarget(evidence);
|
|
12250
|
+
if (selector && textTarget) return `${markdownInlineCode(selector)} contains ${textTarget}`;
|
|
12251
|
+
return selector ? markdownInlineCode(selector) : textTarget;
|
|
12252
|
+
}
|
|
12184
12253
|
if (check.type === "selector_text_order") {
|
|
12185
12254
|
return selector ? `${markdownInlineCode(selector)} text order` : void 0;
|
|
12186
12255
|
}
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
profileStatusExitCode,
|
|
11
11
|
resolveRiddleProofProfileTargetUrl,
|
|
12
12
|
resolveRiddleProofProfileTimeoutSec
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-FQ4QBY2N.js";
|
|
14
14
|
import {
|
|
15
15
|
createRiddleApiClient,
|
|
16
16
|
parseRiddleViewport
|
|
@@ -389,6 +389,11 @@ function profileCheckMarkdownTarget(check) {
|
|
|
389
389
|
const expectedCount = cliFiniteNumber(evidence.expected_count);
|
|
390
390
|
return selector && expectedCount !== void 0 ? `${markdownInlineCode(selector)} = ${expectedCount}` : void 0;
|
|
391
391
|
}
|
|
392
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
393
|
+
const textTarget = profileCheckTextTarget(evidence);
|
|
394
|
+
if (selector && textTarget) return `${markdownInlineCode(selector)} contains ${textTarget}`;
|
|
395
|
+
return selector ? markdownInlineCode(selector) : textTarget;
|
|
396
|
+
}
|
|
392
397
|
if (check.type === "selector_text_order") {
|
|
393
398
|
return selector ? `${markdownInlineCode(selector)} text order` : void 0;
|
|
394
399
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -8749,6 +8749,8 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
8749
8749
|
"selector_count_equals",
|
|
8750
8750
|
"selector_count_equal",
|
|
8751
8751
|
"selector_count_eq",
|
|
8752
|
+
"selector_text_visible",
|
|
8753
|
+
"selector_text_absent",
|
|
8752
8754
|
"selector_text_order",
|
|
8753
8755
|
"frame_text_visible",
|
|
8754
8756
|
"frame_url_equals",
|
|
@@ -9462,7 +9464,7 @@ function normalizeCheck(input, index) {
|
|
|
9462
9464
|
if (!isSupportedCheckType(type)) {
|
|
9463
9465
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
9464
9466
|
}
|
|
9465
|
-
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && !stringValue5(input.selector)) {
|
|
9467
|
+
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue5(input.selector)) {
|
|
9466
9468
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
9467
9469
|
}
|
|
9468
9470
|
if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue5(input.selector)) {
|
|
@@ -9478,7 +9480,7 @@ function normalizeCheck(input, index) {
|
|
|
9478
9480
|
if (type === "frame_url_matches" && !stringValue5(input.pattern)) {
|
|
9479
9481
|
throw new Error(`checks[${index}] frame_url_matches requires pattern.`);
|
|
9480
9482
|
}
|
|
9481
|
-
if ((type === "text_visible" || type === "text_absent") && !stringValue5(input.text) && !stringValue5(input.pattern)) {
|
|
9483
|
+
if ((type === "text_visible" || type === "text_absent" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue5(input.text) && !stringValue5(input.pattern)) {
|
|
9482
9484
|
throw new Error(`checks[${index}] ${type} requires text or pattern.`);
|
|
9483
9485
|
}
|
|
9484
9486
|
if ((type === "url_search_param_equals" || type === "url_search_param_absent") && !stringValue5(input.param) && !stringValue5(input.search_param) && !stringValue5(input.searchParam) && !stringValue5(input.key)) {
|
|
@@ -9744,9 +9746,11 @@ function textSequenceForCheck(viewport, check) {
|
|
|
9744
9746
|
const key = selectorKey(check);
|
|
9745
9747
|
const sequence = viewport.text_sequences?.[key];
|
|
9746
9748
|
if (isRecord2(sequence)) {
|
|
9749
|
+
const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
|
|
9750
|
+
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
9747
9751
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
9748
9752
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
9749
|
-
const candidates = visibleTexts.length ? visibleTexts : texts;
|
|
9753
|
+
const candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
9750
9754
|
return candidates.map((text) => String(text).replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
9751
9755
|
}
|
|
9752
9756
|
return [];
|
|
@@ -10080,6 +10084,35 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
10080
10084
|
message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
|
|
10081
10085
|
};
|
|
10082
10086
|
}
|
|
10087
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
10088
|
+
const key = selectorKey(check);
|
|
10089
|
+
const expectedVisible = check.type === "selector_text_visible";
|
|
10090
|
+
const results = viewports.map((viewport) => {
|
|
10091
|
+
const texts = textSequenceForCheck(viewport, check);
|
|
10092
|
+
const matches = texts.filter((text) => matchText(text, check));
|
|
10093
|
+
return {
|
|
10094
|
+
viewport: viewport.name,
|
|
10095
|
+
selector_count: viewport.selectors?.[key]?.count || 0,
|
|
10096
|
+
visible_count: viewport.selectors?.[key]?.visible_count || 0,
|
|
10097
|
+
matched_count: matches.length,
|
|
10098
|
+
matched: matches.length > 0,
|
|
10099
|
+
samples: matches.slice(0, 3).map((text) => text.slice(0, 240))
|
|
10100
|
+
};
|
|
10101
|
+
});
|
|
10102
|
+
const failed = results.filter((result) => result.matched !== expectedVisible).length;
|
|
10103
|
+
return {
|
|
10104
|
+
type: check.type,
|
|
10105
|
+
label: checkLabel(check),
|
|
10106
|
+
status: failed ? "failed" : "passed",
|
|
10107
|
+
evidence: {
|
|
10108
|
+
selector: key,
|
|
10109
|
+
text: check.text || null,
|
|
10110
|
+
pattern: check.pattern || null,
|
|
10111
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
10112
|
+
},
|
|
10113
|
+
message: failed ? `Selector ${key} text assertion failed in ${failed} viewport(s).` : void 0
|
|
10114
|
+
};
|
|
10115
|
+
}
|
|
10083
10116
|
if (check.type === "selector_text_order") {
|
|
10084
10117
|
const key = selectorKey(check);
|
|
10085
10118
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -10090,7 +10123,7 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
10090
10123
|
viewport: viewport.name,
|
|
10091
10124
|
matched: match.matched,
|
|
10092
10125
|
matched_positions: match.positions,
|
|
10093
|
-
visible_texts: texts.slice(0, 20)
|
|
10126
|
+
visible_texts: texts.slice(0, 20).map((text) => text.slice(0, 240))
|
|
10094
10127
|
};
|
|
10095
10128
|
});
|
|
10096
10129
|
const failed = results.filter((result) => !result.matched).length;
|
|
@@ -10800,9 +10833,11 @@ function textSequenceForCheck(viewport, check) {
|
|
|
10800
10833
|
const key = check.selector || "";
|
|
10801
10834
|
const sequence = viewport.text_sequences && viewport.text_sequences[key];
|
|
10802
10835
|
if (sequence && typeof sequence === "object") {
|
|
10836
|
+
const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
|
|
10837
|
+
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
10803
10838
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
10804
10839
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
10805
|
-
const candidates = visibleTexts.length ? visibleTexts : texts;
|
|
10840
|
+
const candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
10806
10841
|
return candidates.map((text) => String(text || "").replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
10807
10842
|
}
|
|
10808
10843
|
return [];
|
|
@@ -11440,6 +11475,31 @@ function assessProfile(profile, evidence) {
|
|
|
11440
11475
|
});
|
|
11441
11476
|
continue;
|
|
11442
11477
|
}
|
|
11478
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
11479
|
+
const selector = check.selector || "";
|
|
11480
|
+
const expectedVisible = check.type === "selector_text_visible";
|
|
11481
|
+
const results = checkViewports.map((viewport) => {
|
|
11482
|
+
const texts = textSequenceForCheck(viewport, check);
|
|
11483
|
+
const matches = texts.filter((text) => textMatches(text, check));
|
|
11484
|
+
return {
|
|
11485
|
+
viewport: viewport.name,
|
|
11486
|
+
selector_count: viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0,
|
|
11487
|
+
visible_count: viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0,
|
|
11488
|
+
matched_count: matches.length,
|
|
11489
|
+
matched: matches.length > 0,
|
|
11490
|
+
samples: matches.slice(0, 3).map((text) => text.slice(0, 240)),
|
|
11491
|
+
};
|
|
11492
|
+
});
|
|
11493
|
+
const failed = results.filter((result) => result.matched !== expectedVisible).length;
|
|
11494
|
+
checks.push({
|
|
11495
|
+
type: check.type,
|
|
11496
|
+
label: check.label || check.type,
|
|
11497
|
+
status: failed ? "failed" : "passed",
|
|
11498
|
+
evidence: { selector, text: check.text || null, pattern: check.pattern || null, viewports: results },
|
|
11499
|
+
message: failed ? "Selector " + selector + " text assertion failed in " + failed + " viewport(s)." : undefined,
|
|
11500
|
+
});
|
|
11501
|
+
continue;
|
|
11502
|
+
}
|
|
11443
11503
|
if (check.type === "selector_text_order") {
|
|
11444
11504
|
const selector = check.selector || "";
|
|
11445
11505
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -11450,7 +11510,7 @@ function assessProfile(profile, evidence) {
|
|
|
11450
11510
|
viewport: viewport.name,
|
|
11451
11511
|
matched: match.matched,
|
|
11452
11512
|
matched_positions: match.positions,
|
|
11453
|
-
visible_texts: texts.slice(0, 20),
|
|
11513
|
+
visible_texts: texts.slice(0, 20).map((text) => text.slice(0, 240)),
|
|
11454
11514
|
};
|
|
11455
11515
|
});
|
|
11456
11516
|
const failed = results.filter((result) => !result.matched).length;
|
|
@@ -12654,6 +12714,7 @@ async function selectorStats(selector) {
|
|
|
12654
12714
|
async function selectorTextSequence(selector) {
|
|
12655
12715
|
return page.locator(selector).evaluateAll((elements) => {
|
|
12656
12716
|
const compact = (value) => String(value || "").replace(/\s+/g, " ").trim().slice(0, 240);
|
|
12717
|
+
const matchText = (value) => String(value || "").replace(/\s+/g, " ").trim().slice(0, 8000);
|
|
12657
12718
|
const isVisible = (element) => {
|
|
12658
12719
|
const style = window.getComputedStyle(element);
|
|
12659
12720
|
const rect = element.getBoundingClientRect();
|
|
@@ -12662,6 +12723,7 @@ async function selectorTextSequence(selector) {
|
|
|
12662
12723
|
const rows = elements.map((element, index) => ({
|
|
12663
12724
|
index,
|
|
12664
12725
|
text: compact(element.innerText || element.textContent || ""),
|
|
12726
|
+
match_text: matchText(element.innerText || element.textContent || ""),
|
|
12665
12727
|
visible: isVisible(element),
|
|
12666
12728
|
}));
|
|
12667
12729
|
return {
|
|
@@ -12669,8 +12731,10 @@ async function selectorTextSequence(selector) {
|
|
|
12669
12731
|
visible_count: rows.filter((row) => row.visible).length,
|
|
12670
12732
|
texts: rows.map((row) => row.text).filter(Boolean).slice(0, 40),
|
|
12671
12733
|
visible_texts: rows.filter((row) => row.visible).map((row) => row.text).filter(Boolean).slice(0, 40),
|
|
12734
|
+
match_texts: rows.map((row) => row.match_text).filter(Boolean).slice(0, 40),
|
|
12735
|
+
visible_match_texts: rows.filter((row) => row.visible).map((row) => row.match_text).filter(Boolean).slice(0, 40),
|
|
12672
12736
|
};
|
|
12673
|
-
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
12737
|
+
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], match_texts: [], visible_match_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
12674
12738
|
}
|
|
12675
12739
|
function linkProbeMaxLinks(check) {
|
|
12676
12740
|
const value = Number(check.max_links || check.maxLinks || check.limit || 100);
|
|
@@ -13406,7 +13470,7 @@ async function captureViewport(viewport) {
|
|
|
13406
13470
|
) {
|
|
13407
13471
|
selectors[check.selector] = await selectorStats(check.selector);
|
|
13408
13472
|
}
|
|
13409
|
-
if (check.type === "selector_text_order" && check.selector) {
|
|
13473
|
+
if ((check.type === "selector_text_order" || check.type === "selector_text_visible" || check.type === "selector_text_absent") && check.selector) {
|
|
13410
13474
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
13411
13475
|
text_sequences[check.selector] = await selectorTextSequence(check.selector);
|
|
13412
13476
|
}
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
resolveRiddleProofProfileTimeoutSec,
|
|
59
59
|
slugifyRiddleProofProfileName,
|
|
60
60
|
summarizeRiddleProofProfileResult
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-FQ4QBY2N.js";
|
|
62
62
|
import {
|
|
63
63
|
DEFAULT_RIDDLE_API_BASE_URL,
|
|
64
64
|
DEFAULT_RIDDLE_API_KEY_FILE,
|
package/dist/profile.cjs
CHANGED
|
@@ -63,6 +63,8 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
63
63
|
"selector_count_equals",
|
|
64
64
|
"selector_count_equal",
|
|
65
65
|
"selector_count_eq",
|
|
66
|
+
"selector_text_visible",
|
|
67
|
+
"selector_text_absent",
|
|
66
68
|
"selector_text_order",
|
|
67
69
|
"frame_text_visible",
|
|
68
70
|
"frame_url_equals",
|
|
@@ -776,7 +778,7 @@ function normalizeCheck(input, index) {
|
|
|
776
778
|
if (!isSupportedCheckType(type)) {
|
|
777
779
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
778
780
|
}
|
|
779
|
-
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && !stringValue(input.selector)) {
|
|
781
|
+
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue(input.selector)) {
|
|
780
782
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
781
783
|
}
|
|
782
784
|
if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
|
|
@@ -792,7 +794,7 @@ function normalizeCheck(input, index) {
|
|
|
792
794
|
if (type === "frame_url_matches" && !stringValue(input.pattern)) {
|
|
793
795
|
throw new Error(`checks[${index}] frame_url_matches requires pattern.`);
|
|
794
796
|
}
|
|
795
|
-
if ((type === "text_visible" || type === "text_absent") && !stringValue(input.text) && !stringValue(input.pattern)) {
|
|
797
|
+
if ((type === "text_visible" || type === "text_absent" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue(input.text) && !stringValue(input.pattern)) {
|
|
796
798
|
throw new Error(`checks[${index}] ${type} requires text or pattern.`);
|
|
797
799
|
}
|
|
798
800
|
if ((type === "url_search_param_equals" || type === "url_search_param_absent") && !stringValue(input.param) && !stringValue(input.search_param) && !stringValue(input.searchParam) && !stringValue(input.key)) {
|
|
@@ -1058,9 +1060,11 @@ function textSequenceForCheck(viewport, check) {
|
|
|
1058
1060
|
const key = selectorKey(check);
|
|
1059
1061
|
const sequence = viewport.text_sequences?.[key];
|
|
1060
1062
|
if (isRecord(sequence)) {
|
|
1063
|
+
const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
|
|
1064
|
+
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
1061
1065
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
1062
1066
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
1063
|
-
const candidates = visibleTexts.length ? visibleTexts : texts;
|
|
1067
|
+
const candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
1064
1068
|
return candidates.map((text) => String(text).replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
1065
1069
|
}
|
|
1066
1070
|
return [];
|
|
@@ -1394,6 +1398,35 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
1394
1398
|
message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
|
|
1395
1399
|
};
|
|
1396
1400
|
}
|
|
1401
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
1402
|
+
const key = selectorKey(check);
|
|
1403
|
+
const expectedVisible = check.type === "selector_text_visible";
|
|
1404
|
+
const results = viewports.map((viewport) => {
|
|
1405
|
+
const texts = textSequenceForCheck(viewport, check);
|
|
1406
|
+
const matches = texts.filter((text) => matchText(text, check));
|
|
1407
|
+
return {
|
|
1408
|
+
viewport: viewport.name,
|
|
1409
|
+
selector_count: viewport.selectors?.[key]?.count || 0,
|
|
1410
|
+
visible_count: viewport.selectors?.[key]?.visible_count || 0,
|
|
1411
|
+
matched_count: matches.length,
|
|
1412
|
+
matched: matches.length > 0,
|
|
1413
|
+
samples: matches.slice(0, 3).map((text) => text.slice(0, 240))
|
|
1414
|
+
};
|
|
1415
|
+
});
|
|
1416
|
+
const failed = results.filter((result) => result.matched !== expectedVisible).length;
|
|
1417
|
+
return {
|
|
1418
|
+
type: check.type,
|
|
1419
|
+
label: checkLabel(check),
|
|
1420
|
+
status: failed ? "failed" : "passed",
|
|
1421
|
+
evidence: {
|
|
1422
|
+
selector: key,
|
|
1423
|
+
text: check.text || null,
|
|
1424
|
+
pattern: check.pattern || null,
|
|
1425
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
1426
|
+
},
|
|
1427
|
+
message: failed ? `Selector ${key} text assertion failed in ${failed} viewport(s).` : void 0
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1397
1430
|
if (check.type === "selector_text_order") {
|
|
1398
1431
|
const key = selectorKey(check);
|
|
1399
1432
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -1404,7 +1437,7 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
1404
1437
|
viewport: viewport.name,
|
|
1405
1438
|
matched: match.matched,
|
|
1406
1439
|
matched_positions: match.positions,
|
|
1407
|
-
visible_texts: texts.slice(0, 20)
|
|
1440
|
+
visible_texts: texts.slice(0, 20).map((text) => text.slice(0, 240))
|
|
1408
1441
|
};
|
|
1409
1442
|
});
|
|
1410
1443
|
const failed = results.filter((result) => !result.matched).length;
|
|
@@ -2114,9 +2147,11 @@ function textSequenceForCheck(viewport, check) {
|
|
|
2114
2147
|
const key = check.selector || "";
|
|
2115
2148
|
const sequence = viewport.text_sequences && viewport.text_sequences[key];
|
|
2116
2149
|
if (sequence && typeof sequence === "object") {
|
|
2150
|
+
const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
|
|
2151
|
+
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
2117
2152
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
2118
2153
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
2119
|
-
const candidates = visibleTexts.length ? visibleTexts : texts;
|
|
2154
|
+
const candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
2120
2155
|
return candidates.map((text) => String(text || "").replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
2121
2156
|
}
|
|
2122
2157
|
return [];
|
|
@@ -2754,6 +2789,31 @@ function assessProfile(profile, evidence) {
|
|
|
2754
2789
|
});
|
|
2755
2790
|
continue;
|
|
2756
2791
|
}
|
|
2792
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
2793
|
+
const selector = check.selector || "";
|
|
2794
|
+
const expectedVisible = check.type === "selector_text_visible";
|
|
2795
|
+
const results = checkViewports.map((viewport) => {
|
|
2796
|
+
const texts = textSequenceForCheck(viewport, check);
|
|
2797
|
+
const matches = texts.filter((text) => textMatches(text, check));
|
|
2798
|
+
return {
|
|
2799
|
+
viewport: viewport.name,
|
|
2800
|
+
selector_count: viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0,
|
|
2801
|
+
visible_count: viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0,
|
|
2802
|
+
matched_count: matches.length,
|
|
2803
|
+
matched: matches.length > 0,
|
|
2804
|
+
samples: matches.slice(0, 3).map((text) => text.slice(0, 240)),
|
|
2805
|
+
};
|
|
2806
|
+
});
|
|
2807
|
+
const failed = results.filter((result) => result.matched !== expectedVisible).length;
|
|
2808
|
+
checks.push({
|
|
2809
|
+
type: check.type,
|
|
2810
|
+
label: check.label || check.type,
|
|
2811
|
+
status: failed ? "failed" : "passed",
|
|
2812
|
+
evidence: { selector, text: check.text || null, pattern: check.pattern || null, viewports: results },
|
|
2813
|
+
message: failed ? "Selector " + selector + " text assertion failed in " + failed + " viewport(s)." : undefined,
|
|
2814
|
+
});
|
|
2815
|
+
continue;
|
|
2816
|
+
}
|
|
2757
2817
|
if (check.type === "selector_text_order") {
|
|
2758
2818
|
const selector = check.selector || "";
|
|
2759
2819
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -2764,7 +2824,7 @@ function assessProfile(profile, evidence) {
|
|
|
2764
2824
|
viewport: viewport.name,
|
|
2765
2825
|
matched: match.matched,
|
|
2766
2826
|
matched_positions: match.positions,
|
|
2767
|
-
visible_texts: texts.slice(0, 20),
|
|
2827
|
+
visible_texts: texts.slice(0, 20).map((text) => text.slice(0, 240)),
|
|
2768
2828
|
};
|
|
2769
2829
|
});
|
|
2770
2830
|
const failed = results.filter((result) => !result.matched).length;
|
|
@@ -3968,6 +4028,7 @@ async function selectorStats(selector) {
|
|
|
3968
4028
|
async function selectorTextSequence(selector) {
|
|
3969
4029
|
return page.locator(selector).evaluateAll((elements) => {
|
|
3970
4030
|
const compact = (value) => String(value || "").replace(/\s+/g, " ").trim().slice(0, 240);
|
|
4031
|
+
const matchText = (value) => String(value || "").replace(/\s+/g, " ").trim().slice(0, 8000);
|
|
3971
4032
|
const isVisible = (element) => {
|
|
3972
4033
|
const style = window.getComputedStyle(element);
|
|
3973
4034
|
const rect = element.getBoundingClientRect();
|
|
@@ -3976,6 +4037,7 @@ async function selectorTextSequence(selector) {
|
|
|
3976
4037
|
const rows = elements.map((element, index) => ({
|
|
3977
4038
|
index,
|
|
3978
4039
|
text: compact(element.innerText || element.textContent || ""),
|
|
4040
|
+
match_text: matchText(element.innerText || element.textContent || ""),
|
|
3979
4041
|
visible: isVisible(element),
|
|
3980
4042
|
}));
|
|
3981
4043
|
return {
|
|
@@ -3983,8 +4045,10 @@ async function selectorTextSequence(selector) {
|
|
|
3983
4045
|
visible_count: rows.filter((row) => row.visible).length,
|
|
3984
4046
|
texts: rows.map((row) => row.text).filter(Boolean).slice(0, 40),
|
|
3985
4047
|
visible_texts: rows.filter((row) => row.visible).map((row) => row.text).filter(Boolean).slice(0, 40),
|
|
4048
|
+
match_texts: rows.map((row) => row.match_text).filter(Boolean).slice(0, 40),
|
|
4049
|
+
visible_match_texts: rows.filter((row) => row.visible).map((row) => row.match_text).filter(Boolean).slice(0, 40),
|
|
3986
4050
|
};
|
|
3987
|
-
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
4051
|
+
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], match_texts: [], visible_match_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
3988
4052
|
}
|
|
3989
4053
|
function linkProbeMaxLinks(check) {
|
|
3990
4054
|
const value = Number(check.max_links || check.maxLinks || check.limit || 100);
|
|
@@ -4720,7 +4784,7 @@ async function captureViewport(viewport) {
|
|
|
4720
4784
|
) {
|
|
4721
4785
|
selectors[check.selector] = await selectorStats(check.selector);
|
|
4722
4786
|
}
|
|
4723
|
-
if (check.type === "selector_text_order" && check.selector) {
|
|
4787
|
+
if ((check.type === "selector_text_order" || check.type === "selector_text_visible" || check.type === "selector_text_absent") && check.selector) {
|
|
4724
4788
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
4725
4789
|
text_sequences[check.selector] = await selectorTextSequence(check.selector);
|
|
4726
4790
|
}
|
package/dist/profile.d.cts
CHANGED
|
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
|
|
|
4
4
|
declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
|
|
5
5
|
declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
|
|
6
6
|
declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
|
|
7
|
-
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_visible", "selector_text_absent", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
8
8
|
declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "drag", "press", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_call"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
package/dist/profile.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
|
|
|
4
4
|
declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
|
|
5
5
|
declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
|
|
6
6
|
declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
|
|
7
|
-
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_visible", "selector_text_absent", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
8
8
|
declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "drag", "press", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_call"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
package/dist/profile.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
resolveRiddleProofProfileTimeoutSec,
|
|
20
20
|
slugifyRiddleProofProfileName,
|
|
21
21
|
summarizeRiddleProofProfileResult
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-FQ4QBY2N.js";
|
|
23
23
|
export {
|
|
24
24
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
25
25
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|
|
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
292
292
|
blocking?: boolean;
|
|
293
293
|
details?: Record<string, unknown>;
|
|
294
294
|
ok: boolean;
|
|
295
|
-
action: "
|
|
295
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
296
296
|
state_path: string;
|
|
297
297
|
stage: any;
|
|
298
298
|
summary: string;
|
|
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
382
382
|
continueWithStage?: WorkflowStage | null;
|
|
383
383
|
blocking?: boolean;
|
|
384
384
|
details?: Record<string, unknown>;
|
|
385
|
-
action: "
|
|
385
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
386
386
|
state_path: string;
|
|
387
387
|
stage: any;
|
|
388
388
|
checkpoint: string;
|
|
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
659
659
|
error?: undefined;
|
|
660
660
|
} | {
|
|
661
661
|
ok: boolean;
|
|
662
|
-
action: "
|
|
662
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
663
663
|
state_path: string;
|
|
664
664
|
stage: any;
|
|
665
665
|
summary: string;
|
|
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
292
292
|
blocking?: boolean;
|
|
293
293
|
details?: Record<string, unknown>;
|
|
294
294
|
ok: boolean;
|
|
295
|
-
action: "
|
|
295
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
296
296
|
state_path: string;
|
|
297
297
|
stage: any;
|
|
298
298
|
summary: string;
|
|
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
382
382
|
continueWithStage?: WorkflowStage | null;
|
|
383
383
|
blocking?: boolean;
|
|
384
384
|
details?: Record<string, unknown>;
|
|
385
|
-
action: "
|
|
385
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
386
386
|
state_path: string;
|
|
387
387
|
stage: any;
|
|
388
388
|
checkpoint: string;
|
|
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
659
659
|
error?: undefined;
|
|
660
660
|
} | {
|
|
661
661
|
ok: boolean;
|
|
662
|
-
action: "
|
|
662
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
663
663
|
state_path: string;
|
|
664
664
|
stage: any;
|
|
665
665
|
summary: string;
|