@riddledc/riddle-proof 0.7.40 → 0.7.42
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 +21 -1
- package/dist/{chunk-RAVOICNN.js → chunk-MT2OZCCP.js} +81 -4
- package/dist/cli.cjs +81 -4
- package/dist/cli.js +1 -1
- package/dist/index.cjs +81 -4
- package/dist/index.js +1 -1
- package/dist/profile.cjs +81 -4
- package/dist/profile.d.cts +3 -1
- package/dist/profile.d.ts +3 -1
- package/dist/profile.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -145,6 +145,8 @@ or as a stronger proof base before a change loop.
|
|
|
145
145
|
"checks": [
|
|
146
146
|
{ "type": "route_loaded", "expected_path": "/pricing" },
|
|
147
147
|
{ "type": "selector_visible", "selector": "[data-testid='pricing-cards']" },
|
|
148
|
+
{ "type": "selector_absent", "selector": "[data-testid='loading-spinner']" },
|
|
149
|
+
{ "type": "selector_count_equals", "selector": "[data-testid='pricing-card']", "expected_count": 3 },
|
|
148
150
|
{ "type": "text_visible", "text": "Start building" },
|
|
149
151
|
{ "type": "no_mobile_horizontal_overflow" },
|
|
150
152
|
{ "type": "no_fatal_console_errors" }
|
|
@@ -210,7 +212,10 @@ When `responses` is present, `network_mocks_succeeded` requires each configured
|
|
|
210
212
|
response to be hit at least once by default and records `hit_index`,
|
|
211
213
|
`response_index`, and `response_label` for each request. Set
|
|
212
214
|
`required_hit_count` / `min_hits` or `required: false` when a different
|
|
213
|
-
contract is intentional.
|
|
215
|
+
contract is intentional. Set `repeat_responses: true` when the sequence should
|
|
216
|
+
cycle instead of reusing the final response, for example to repeat a fail-then-
|
|
217
|
+
success pair across multiple viewports. Repeated sequences also record
|
|
218
|
+
`sequence_cycle: true` after the first cycle.
|
|
214
219
|
|
|
215
220
|
`target.setup_actions` is optional. Use it when the meaningful proof surface
|
|
216
221
|
appears only after a picker, tab, login stub, storage seed, form fill,
|
|
@@ -249,6 +254,21 @@ only unallowed `error` / `assert` console events and page errors fail the check.
|
|
|
249
254
|
Use `allowed_page_error_patterns`, `allowed_console_texts`, or
|
|
250
255
|
`allowed_page_error_texts` for narrower matching when needed.
|
|
251
256
|
|
|
257
|
+
Use `selector_absent` when a forbidden element must not render, and
|
|
258
|
+
`selector_count_equals` / `selector_count_equal` / `selector_count_eq` when a
|
|
259
|
+
profile needs an exact DOM count rather than a lower bound:
|
|
260
|
+
|
|
261
|
+
```json
|
|
262
|
+
[
|
|
263
|
+
{ "type": "selector_absent", "selector": ".game-player-root iframe" },
|
|
264
|
+
{ "type": "selector_count_equals", "selector": ".pricing-card", "expected_count": 3 }
|
|
265
|
+
]
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
These checks are useful for audit/no-diff profiles where the product should
|
|
269
|
+
show a fallback state and avoid rendering stale loaders, duplicate rows, or
|
|
270
|
+
missing-resource iframes.
|
|
271
|
+
|
|
252
272
|
Use `selector_text_order` when a table, list, or card group must show visible
|
|
253
273
|
items in a specific order after setup actions such as sorting or filtering:
|
|
254
274
|
|
|
@@ -13,7 +13,11 @@ var RIDDLE_PROOF_PROFILE_STATUSES = [
|
|
|
13
13
|
var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
14
14
|
"route_loaded",
|
|
15
15
|
"selector_visible",
|
|
16
|
+
"selector_absent",
|
|
16
17
|
"selector_count_at_least",
|
|
18
|
+
"selector_count_equals",
|
|
19
|
+
"selector_count_equal",
|
|
20
|
+
"selector_count_eq",
|
|
17
21
|
"selector_text_order",
|
|
18
22
|
"frame_text_visible",
|
|
19
23
|
"frame_no_horizontal_overflow",
|
|
@@ -261,6 +265,7 @@ function normalizeNetworkMock(input, index) {
|
|
|
261
265
|
url,
|
|
262
266
|
method: stringValue(input.method)?.toUpperCase(),
|
|
263
267
|
responses,
|
|
268
|
+
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
264
269
|
required_hit_count: requiredHitCount,
|
|
265
270
|
required: input.required === false ? false : true
|
|
266
271
|
};
|
|
@@ -352,7 +357,7 @@ function normalizeCheck(input, index) {
|
|
|
352
357
|
if (!isSupportedCheckType(type)) {
|
|
353
358
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
354
359
|
}
|
|
355
|
-
if ((type === "selector_visible" || type === "selector_count_at_least") && !stringValue(input.selector)) {
|
|
360
|
+
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)) {
|
|
356
361
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
357
362
|
}
|
|
358
363
|
if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
|
|
@@ -367,6 +372,10 @@ function normalizeCheck(input, index) {
|
|
|
367
372
|
if (type === "selector_count_at_least" && numberValue(input.min_count) === void 0) {
|
|
368
373
|
throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
|
|
369
374
|
}
|
|
375
|
+
const expectedCount = numberValue(input.expected_count) ?? numberValue(input.expectedCount) ?? numberValue(input.count);
|
|
376
|
+
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
|
|
377
|
+
throw new Error(`checks[${index}] ${type} requires expected_count.`);
|
|
378
|
+
}
|
|
370
379
|
const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
|
|
371
380
|
if (type === "selector_text_order") {
|
|
372
381
|
if (!stringValue(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
|
|
@@ -398,6 +407,7 @@ function normalizeCheck(input, index) {
|
|
|
398
407
|
allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
|
|
399
408
|
allowed_page_error_patterns: normalizeStringList(input.allowed_page_error_patterns ?? input.allowedPageErrorPatterns ?? input.allow_page_error_patterns ?? input.allowPageErrorPatterns, `checks[${index}] allowed_page_error_patterns`),
|
|
400
409
|
min_count: numberValue(input.min_count),
|
|
410
|
+
expected_count: expectedCount,
|
|
401
411
|
max_overflow_px: numberValue(input.max_overflow_px),
|
|
402
412
|
timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
|
|
403
413
|
run_direct_routes: input.run_direct_routes === false || input.runDirectRoutes === false ? false : true,
|
|
@@ -665,6 +675,20 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
665
675
|
message: failed.length ? `Selector ${key} was not visible in ${failed.length} viewport(s).` : void 0
|
|
666
676
|
};
|
|
667
677
|
}
|
|
678
|
+
if (check.type === "selector_absent") {
|
|
679
|
+
const key = selectorKey(check);
|
|
680
|
+
const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) > 0);
|
|
681
|
+
return {
|
|
682
|
+
type: check.type,
|
|
683
|
+
label: checkLabel(check),
|
|
684
|
+
status: failed.length ? "failed" : "passed",
|
|
685
|
+
evidence: {
|
|
686
|
+
selector: key,
|
|
687
|
+
counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
|
|
688
|
+
},
|
|
689
|
+
message: failed.length ? `Selector ${key} was present in ${failed.length} viewport(s).` : void 0
|
|
690
|
+
};
|
|
691
|
+
}
|
|
668
692
|
if (check.type === "selector_count_at_least") {
|
|
669
693
|
const key = selectorKey(check);
|
|
670
694
|
const minCount = check.min_count ?? 1;
|
|
@@ -681,6 +705,22 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
681
705
|
message: failed.length ? `Selector ${key} count was below ${minCount} in ${failed.length} viewport(s).` : void 0
|
|
682
706
|
};
|
|
683
707
|
}
|
|
708
|
+
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
709
|
+
const key = selectorKey(check);
|
|
710
|
+
const expectedCount = check.expected_count ?? 0;
|
|
711
|
+
const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) !== expectedCount);
|
|
712
|
+
return {
|
|
713
|
+
type: check.type,
|
|
714
|
+
label: checkLabel(check),
|
|
715
|
+
status: failed.length ? "failed" : "passed",
|
|
716
|
+
evidence: {
|
|
717
|
+
selector: key,
|
|
718
|
+
expected_count: expectedCount,
|
|
719
|
+
counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
|
|
720
|
+
},
|
|
721
|
+
message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
|
|
722
|
+
};
|
|
723
|
+
}
|
|
684
724
|
if (check.type === "selector_text_order") {
|
|
685
725
|
const key = selectorKey(check);
|
|
686
726
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -1417,6 +1457,18 @@ function assessProfile(profile, evidence) {
|
|
|
1417
1457
|
});
|
|
1418
1458
|
continue;
|
|
1419
1459
|
}
|
|
1460
|
+
if (check.type === "selector_absent") {
|
|
1461
|
+
const selector = check.selector || "";
|
|
1462
|
+
const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
1463
|
+
checks.push({
|
|
1464
|
+
type: check.type,
|
|
1465
|
+
label: check.label || check.type,
|
|
1466
|
+
status: failed.length ? "failed" : "passed",
|
|
1467
|
+
evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1468
|
+
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
1469
|
+
});
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1420
1472
|
if (check.type === "selector_count_at_least") {
|
|
1421
1473
|
const selector = check.selector || "";
|
|
1422
1474
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
@@ -1430,6 +1482,19 @@ function assessProfile(profile, evidence) {
|
|
|
1430
1482
|
});
|
|
1431
1483
|
continue;
|
|
1432
1484
|
}
|
|
1485
|
+
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
1486
|
+
const selector = check.selector || "";
|
|
1487
|
+
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
1488
|
+
const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
1489
|
+
checks.push({
|
|
1490
|
+
type: check.type,
|
|
1491
|
+
label: check.label || check.type,
|
|
1492
|
+
status: failed.length ? "failed" : "passed",
|
|
1493
|
+
evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1494
|
+
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
1495
|
+
});
|
|
1496
|
+
continue;
|
|
1497
|
+
}
|
|
1433
1498
|
if (check.type === "selector_text_order") {
|
|
1434
1499
|
const selector = check.selector || "";
|
|
1435
1500
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -1748,7 +1813,9 @@ async function registerNetworkMocks(mocks) {
|
|
|
1748
1813
|
const responses = Array.isArray(mock.responses) ? mock.responses : [];
|
|
1749
1814
|
const hitIndex = hitCount;
|
|
1750
1815
|
hitCount += 1;
|
|
1751
|
-
const responseIndex = responses.length
|
|
1816
|
+
const responseIndex = responses.length
|
|
1817
|
+
? (mock.repeat_responses ? hitIndex % responses.length : Math.min(hitIndex, responses.length - 1))
|
|
1818
|
+
: null;
|
|
1752
1819
|
const response = responseIndex === null ? mock : responses[responseIndex];
|
|
1753
1820
|
const headers = { ...(response.headers || mock.headers || {}) };
|
|
1754
1821
|
let body = response.body || "";
|
|
@@ -1763,7 +1830,8 @@ async function registerNetworkMocks(mocks) {
|
|
|
1763
1830
|
response_label: response.label || null,
|
|
1764
1831
|
hit_index: hitIndex,
|
|
1765
1832
|
response_index: responseIndex,
|
|
1766
|
-
sequence_reused: responseIndex !== null && hitIndex >= responses.length,
|
|
1833
|
+
sequence_reused: responseIndex !== null && !mock.repeat_responses && hitIndex >= responses.length,
|
|
1834
|
+
sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
|
|
1767
1835
|
url: request.url(),
|
|
1768
1836
|
method,
|
|
1769
1837
|
status: response.status || mock.status || 200,
|
|
@@ -2443,7 +2511,16 @@ async function captureViewport(viewport) {
|
|
|
2443
2511
|
const text_sequences = {};
|
|
2444
2512
|
const text_matches = {};
|
|
2445
2513
|
for (const check of profile.checks || []) {
|
|
2446
|
-
if (
|
|
2514
|
+
if (
|
|
2515
|
+
(
|
|
2516
|
+
check.type === "selector_visible"
|
|
2517
|
+
|| check.type === "selector_absent"
|
|
2518
|
+
|| check.type === "selector_count_at_least"
|
|
2519
|
+
|| check.type === "selector_count_equals"
|
|
2520
|
+
|| check.type === "selector_count_equal"
|
|
2521
|
+
|| check.type === "selector_count_eq"
|
|
2522
|
+
) && check.selector
|
|
2523
|
+
) {
|
|
2447
2524
|
selectors[check.selector] = await selectorStats(check.selector);
|
|
2448
2525
|
}
|
|
2449
2526
|
if (check.type === "selector_text_order" && check.selector) {
|
package/dist/cli.cjs
CHANGED
|
@@ -6886,7 +6886,11 @@ var RIDDLE_PROOF_PROFILE_STATUSES = [
|
|
|
6886
6886
|
var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
6887
6887
|
"route_loaded",
|
|
6888
6888
|
"selector_visible",
|
|
6889
|
+
"selector_absent",
|
|
6889
6890
|
"selector_count_at_least",
|
|
6891
|
+
"selector_count_equals",
|
|
6892
|
+
"selector_count_equal",
|
|
6893
|
+
"selector_count_eq",
|
|
6890
6894
|
"selector_text_order",
|
|
6891
6895
|
"frame_text_visible",
|
|
6892
6896
|
"frame_no_horizontal_overflow",
|
|
@@ -7134,6 +7138,7 @@ function normalizeNetworkMock(input, index) {
|
|
|
7134
7138
|
url,
|
|
7135
7139
|
method: stringValue2(input.method)?.toUpperCase(),
|
|
7136
7140
|
responses,
|
|
7141
|
+
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
7137
7142
|
required_hit_count: requiredHitCount,
|
|
7138
7143
|
required: input.required === false ? false : true
|
|
7139
7144
|
};
|
|
@@ -7225,7 +7230,7 @@ function normalizeCheck(input, index) {
|
|
|
7225
7230
|
if (!isSupportedCheckType(type)) {
|
|
7226
7231
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
7227
7232
|
}
|
|
7228
|
-
if ((type === "selector_visible" || type === "selector_count_at_least") && !stringValue2(input.selector)) {
|
|
7233
|
+
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)) {
|
|
7229
7234
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
7230
7235
|
}
|
|
7231
7236
|
if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue2(input.selector)) {
|
|
@@ -7240,6 +7245,10 @@ function normalizeCheck(input, index) {
|
|
|
7240
7245
|
if (type === "selector_count_at_least" && numberValue(input.min_count) === void 0) {
|
|
7241
7246
|
throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
|
|
7242
7247
|
}
|
|
7248
|
+
const expectedCount = numberValue(input.expected_count) ?? numberValue(input.expectedCount) ?? numberValue(input.count);
|
|
7249
|
+
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
|
|
7250
|
+
throw new Error(`checks[${index}] ${type} requires expected_count.`);
|
|
7251
|
+
}
|
|
7243
7252
|
const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
|
|
7244
7253
|
if (type === "selector_text_order") {
|
|
7245
7254
|
if (!stringValue2(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
|
|
@@ -7271,6 +7280,7 @@ function normalizeCheck(input, index) {
|
|
|
7271
7280
|
allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
|
|
7272
7281
|
allowed_page_error_patterns: normalizeStringList(input.allowed_page_error_patterns ?? input.allowedPageErrorPatterns ?? input.allow_page_error_patterns ?? input.allowPageErrorPatterns, `checks[${index}] allowed_page_error_patterns`),
|
|
7273
7282
|
min_count: numberValue(input.min_count),
|
|
7283
|
+
expected_count: expectedCount,
|
|
7274
7284
|
max_overflow_px: numberValue(input.max_overflow_px),
|
|
7275
7285
|
timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
|
|
7276
7286
|
run_direct_routes: input.run_direct_routes === false || input.runDirectRoutes === false ? false : true,
|
|
@@ -7538,6 +7548,20 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
7538
7548
|
message: failed.length ? `Selector ${key} was not visible in ${failed.length} viewport(s).` : void 0
|
|
7539
7549
|
};
|
|
7540
7550
|
}
|
|
7551
|
+
if (check.type === "selector_absent") {
|
|
7552
|
+
const key = selectorKey(check);
|
|
7553
|
+
const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) > 0);
|
|
7554
|
+
return {
|
|
7555
|
+
type: check.type,
|
|
7556
|
+
label: checkLabel(check),
|
|
7557
|
+
status: failed.length ? "failed" : "passed",
|
|
7558
|
+
evidence: {
|
|
7559
|
+
selector: key,
|
|
7560
|
+
counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
|
|
7561
|
+
},
|
|
7562
|
+
message: failed.length ? `Selector ${key} was present in ${failed.length} viewport(s).` : void 0
|
|
7563
|
+
};
|
|
7564
|
+
}
|
|
7541
7565
|
if (check.type === "selector_count_at_least") {
|
|
7542
7566
|
const key = selectorKey(check);
|
|
7543
7567
|
const minCount = check.min_count ?? 1;
|
|
@@ -7554,6 +7578,22 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
7554
7578
|
message: failed.length ? `Selector ${key} count was below ${minCount} in ${failed.length} viewport(s).` : void 0
|
|
7555
7579
|
};
|
|
7556
7580
|
}
|
|
7581
|
+
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
7582
|
+
const key = selectorKey(check);
|
|
7583
|
+
const expectedCount = check.expected_count ?? 0;
|
|
7584
|
+
const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) !== expectedCount);
|
|
7585
|
+
return {
|
|
7586
|
+
type: check.type,
|
|
7587
|
+
label: checkLabel(check),
|
|
7588
|
+
status: failed.length ? "failed" : "passed",
|
|
7589
|
+
evidence: {
|
|
7590
|
+
selector: key,
|
|
7591
|
+
expected_count: expectedCount,
|
|
7592
|
+
counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
|
|
7593
|
+
},
|
|
7594
|
+
message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
|
|
7595
|
+
};
|
|
7596
|
+
}
|
|
7557
7597
|
if (check.type === "selector_text_order") {
|
|
7558
7598
|
const key = selectorKey(check);
|
|
7559
7599
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -8274,6 +8314,18 @@ function assessProfile(profile, evidence) {
|
|
|
8274
8314
|
});
|
|
8275
8315
|
continue;
|
|
8276
8316
|
}
|
|
8317
|
+
if (check.type === "selector_absent") {
|
|
8318
|
+
const selector = check.selector || "";
|
|
8319
|
+
const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
8320
|
+
checks.push({
|
|
8321
|
+
type: check.type,
|
|
8322
|
+
label: check.label || check.type,
|
|
8323
|
+
status: failed.length ? "failed" : "passed",
|
|
8324
|
+
evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
8325
|
+
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
8326
|
+
});
|
|
8327
|
+
continue;
|
|
8328
|
+
}
|
|
8277
8329
|
if (check.type === "selector_count_at_least") {
|
|
8278
8330
|
const selector = check.selector || "";
|
|
8279
8331
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
@@ -8287,6 +8339,19 @@ function assessProfile(profile, evidence) {
|
|
|
8287
8339
|
});
|
|
8288
8340
|
continue;
|
|
8289
8341
|
}
|
|
8342
|
+
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
8343
|
+
const selector = check.selector || "";
|
|
8344
|
+
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
8345
|
+
const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
8346
|
+
checks.push({
|
|
8347
|
+
type: check.type,
|
|
8348
|
+
label: check.label || check.type,
|
|
8349
|
+
status: failed.length ? "failed" : "passed",
|
|
8350
|
+
evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
8351
|
+
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
8352
|
+
});
|
|
8353
|
+
continue;
|
|
8354
|
+
}
|
|
8290
8355
|
if (check.type === "selector_text_order") {
|
|
8291
8356
|
const selector = check.selector || "";
|
|
8292
8357
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -8605,7 +8670,9 @@ async function registerNetworkMocks(mocks) {
|
|
|
8605
8670
|
const responses = Array.isArray(mock.responses) ? mock.responses : [];
|
|
8606
8671
|
const hitIndex = hitCount;
|
|
8607
8672
|
hitCount += 1;
|
|
8608
|
-
const responseIndex = responses.length
|
|
8673
|
+
const responseIndex = responses.length
|
|
8674
|
+
? (mock.repeat_responses ? hitIndex % responses.length : Math.min(hitIndex, responses.length - 1))
|
|
8675
|
+
: null;
|
|
8609
8676
|
const response = responseIndex === null ? mock : responses[responseIndex];
|
|
8610
8677
|
const headers = { ...(response.headers || mock.headers || {}) };
|
|
8611
8678
|
let body = response.body || "";
|
|
@@ -8620,7 +8687,8 @@ async function registerNetworkMocks(mocks) {
|
|
|
8620
8687
|
response_label: response.label || null,
|
|
8621
8688
|
hit_index: hitIndex,
|
|
8622
8689
|
response_index: responseIndex,
|
|
8623
|
-
sequence_reused: responseIndex !== null && hitIndex >= responses.length,
|
|
8690
|
+
sequence_reused: responseIndex !== null && !mock.repeat_responses && hitIndex >= responses.length,
|
|
8691
|
+
sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
|
|
8624
8692
|
url: request.url(),
|
|
8625
8693
|
method,
|
|
8626
8694
|
status: response.status || mock.status || 200,
|
|
@@ -9300,7 +9368,16 @@ async function captureViewport(viewport) {
|
|
|
9300
9368
|
const text_sequences = {};
|
|
9301
9369
|
const text_matches = {};
|
|
9302
9370
|
for (const check of profile.checks || []) {
|
|
9303
|
-
if (
|
|
9371
|
+
if (
|
|
9372
|
+
(
|
|
9373
|
+
check.type === "selector_visible"
|
|
9374
|
+
|| check.type === "selector_absent"
|
|
9375
|
+
|| check.type === "selector_count_at_least"
|
|
9376
|
+
|| check.type === "selector_count_equals"
|
|
9377
|
+
|| check.type === "selector_count_equal"
|
|
9378
|
+
|| check.type === "selector_count_eq"
|
|
9379
|
+
) && check.selector
|
|
9380
|
+
) {
|
|
9304
9381
|
selectors[check.selector] = await selectorStats(check.selector);
|
|
9305
9382
|
}
|
|
9306
9383
|
if (check.type === "selector_text_order" && check.selector) {
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -8727,7 +8727,11 @@ var RIDDLE_PROOF_PROFILE_STATUSES = [
|
|
|
8727
8727
|
var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
8728
8728
|
"route_loaded",
|
|
8729
8729
|
"selector_visible",
|
|
8730
|
+
"selector_absent",
|
|
8730
8731
|
"selector_count_at_least",
|
|
8732
|
+
"selector_count_equals",
|
|
8733
|
+
"selector_count_equal",
|
|
8734
|
+
"selector_count_eq",
|
|
8731
8735
|
"selector_text_order",
|
|
8732
8736
|
"frame_text_visible",
|
|
8733
8737
|
"frame_no_horizontal_overflow",
|
|
@@ -8975,6 +8979,7 @@ function normalizeNetworkMock(input, index) {
|
|
|
8975
8979
|
url,
|
|
8976
8980
|
method: stringValue5(input.method)?.toUpperCase(),
|
|
8977
8981
|
responses,
|
|
8982
|
+
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
8978
8983
|
required_hit_count: requiredHitCount,
|
|
8979
8984
|
required: input.required === false ? false : true
|
|
8980
8985
|
};
|
|
@@ -9066,7 +9071,7 @@ function normalizeCheck(input, index) {
|
|
|
9066
9071
|
if (!isSupportedCheckType(type)) {
|
|
9067
9072
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
9068
9073
|
}
|
|
9069
|
-
if ((type === "selector_visible" || type === "selector_count_at_least") && !stringValue5(input.selector)) {
|
|
9074
|
+
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)) {
|
|
9070
9075
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
9071
9076
|
}
|
|
9072
9077
|
if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue5(input.selector)) {
|
|
@@ -9081,6 +9086,10 @@ function normalizeCheck(input, index) {
|
|
|
9081
9086
|
if (type === "selector_count_at_least" && numberValue3(input.min_count) === void 0) {
|
|
9082
9087
|
throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
|
|
9083
9088
|
}
|
|
9089
|
+
const expectedCount = numberValue3(input.expected_count) ?? numberValue3(input.expectedCount) ?? numberValue3(input.count);
|
|
9090
|
+
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
|
|
9091
|
+
throw new Error(`checks[${index}] ${type} requires expected_count.`);
|
|
9092
|
+
}
|
|
9084
9093
|
const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
|
|
9085
9094
|
if (type === "selector_text_order") {
|
|
9086
9095
|
if (!stringValue5(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
|
|
@@ -9112,6 +9121,7 @@ function normalizeCheck(input, index) {
|
|
|
9112
9121
|
allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
|
|
9113
9122
|
allowed_page_error_patterns: normalizeStringList(input.allowed_page_error_patterns ?? input.allowedPageErrorPatterns ?? input.allow_page_error_patterns ?? input.allowPageErrorPatterns, `checks[${index}] allowed_page_error_patterns`),
|
|
9114
9123
|
min_count: numberValue3(input.min_count),
|
|
9124
|
+
expected_count: expectedCount,
|
|
9115
9125
|
max_overflow_px: numberValue3(input.max_overflow_px),
|
|
9116
9126
|
timeout_ms: numberValue3(input.timeout_ms) ?? numberValue3(input.timeoutMs),
|
|
9117
9127
|
run_direct_routes: input.run_direct_routes === false || input.runDirectRoutes === false ? false : true,
|
|
@@ -9379,6 +9389,20 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
9379
9389
|
message: failed.length ? `Selector ${key} was not visible in ${failed.length} viewport(s).` : void 0
|
|
9380
9390
|
};
|
|
9381
9391
|
}
|
|
9392
|
+
if (check.type === "selector_absent") {
|
|
9393
|
+
const key = selectorKey(check);
|
|
9394
|
+
const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) > 0);
|
|
9395
|
+
return {
|
|
9396
|
+
type: check.type,
|
|
9397
|
+
label: checkLabel(check),
|
|
9398
|
+
status: failed.length ? "failed" : "passed",
|
|
9399
|
+
evidence: {
|
|
9400
|
+
selector: key,
|
|
9401
|
+
counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
|
|
9402
|
+
},
|
|
9403
|
+
message: failed.length ? `Selector ${key} was present in ${failed.length} viewport(s).` : void 0
|
|
9404
|
+
};
|
|
9405
|
+
}
|
|
9382
9406
|
if (check.type === "selector_count_at_least") {
|
|
9383
9407
|
const key = selectorKey(check);
|
|
9384
9408
|
const minCount = check.min_count ?? 1;
|
|
@@ -9395,6 +9419,22 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
9395
9419
|
message: failed.length ? `Selector ${key} count was below ${minCount} in ${failed.length} viewport(s).` : void 0
|
|
9396
9420
|
};
|
|
9397
9421
|
}
|
|
9422
|
+
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
9423
|
+
const key = selectorKey(check);
|
|
9424
|
+
const expectedCount = check.expected_count ?? 0;
|
|
9425
|
+
const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) !== expectedCount);
|
|
9426
|
+
return {
|
|
9427
|
+
type: check.type,
|
|
9428
|
+
label: checkLabel(check),
|
|
9429
|
+
status: failed.length ? "failed" : "passed",
|
|
9430
|
+
evidence: {
|
|
9431
|
+
selector: key,
|
|
9432
|
+
expected_count: expectedCount,
|
|
9433
|
+
counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
|
|
9434
|
+
},
|
|
9435
|
+
message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
|
|
9436
|
+
};
|
|
9437
|
+
}
|
|
9398
9438
|
if (check.type === "selector_text_order") {
|
|
9399
9439
|
const key = selectorKey(check);
|
|
9400
9440
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -10131,6 +10171,18 @@ function assessProfile(profile, evidence) {
|
|
|
10131
10171
|
});
|
|
10132
10172
|
continue;
|
|
10133
10173
|
}
|
|
10174
|
+
if (check.type === "selector_absent") {
|
|
10175
|
+
const selector = check.selector || "";
|
|
10176
|
+
const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
10177
|
+
checks.push({
|
|
10178
|
+
type: check.type,
|
|
10179
|
+
label: check.label || check.type,
|
|
10180
|
+
status: failed.length ? "failed" : "passed",
|
|
10181
|
+
evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
10182
|
+
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
10183
|
+
});
|
|
10184
|
+
continue;
|
|
10185
|
+
}
|
|
10134
10186
|
if (check.type === "selector_count_at_least") {
|
|
10135
10187
|
const selector = check.selector || "";
|
|
10136
10188
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
@@ -10144,6 +10196,19 @@ function assessProfile(profile, evidence) {
|
|
|
10144
10196
|
});
|
|
10145
10197
|
continue;
|
|
10146
10198
|
}
|
|
10199
|
+
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
10200
|
+
const selector = check.selector || "";
|
|
10201
|
+
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
10202
|
+
const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
10203
|
+
checks.push({
|
|
10204
|
+
type: check.type,
|
|
10205
|
+
label: check.label || check.type,
|
|
10206
|
+
status: failed.length ? "failed" : "passed",
|
|
10207
|
+
evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
10208
|
+
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
10209
|
+
});
|
|
10210
|
+
continue;
|
|
10211
|
+
}
|
|
10147
10212
|
if (check.type === "selector_text_order") {
|
|
10148
10213
|
const selector = check.selector || "";
|
|
10149
10214
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -10462,7 +10527,9 @@ async function registerNetworkMocks(mocks) {
|
|
|
10462
10527
|
const responses = Array.isArray(mock.responses) ? mock.responses : [];
|
|
10463
10528
|
const hitIndex = hitCount;
|
|
10464
10529
|
hitCount += 1;
|
|
10465
|
-
const responseIndex = responses.length
|
|
10530
|
+
const responseIndex = responses.length
|
|
10531
|
+
? (mock.repeat_responses ? hitIndex % responses.length : Math.min(hitIndex, responses.length - 1))
|
|
10532
|
+
: null;
|
|
10466
10533
|
const response = responseIndex === null ? mock : responses[responseIndex];
|
|
10467
10534
|
const headers = { ...(response.headers || mock.headers || {}) };
|
|
10468
10535
|
let body = response.body || "";
|
|
@@ -10477,7 +10544,8 @@ async function registerNetworkMocks(mocks) {
|
|
|
10477
10544
|
response_label: response.label || null,
|
|
10478
10545
|
hit_index: hitIndex,
|
|
10479
10546
|
response_index: responseIndex,
|
|
10480
|
-
sequence_reused: responseIndex !== null && hitIndex >= responses.length,
|
|
10547
|
+
sequence_reused: responseIndex !== null && !mock.repeat_responses && hitIndex >= responses.length,
|
|
10548
|
+
sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
|
|
10481
10549
|
url: request.url(),
|
|
10482
10550
|
method,
|
|
10483
10551
|
status: response.status || mock.status || 200,
|
|
@@ -11157,7 +11225,16 @@ async function captureViewport(viewport) {
|
|
|
11157
11225
|
const text_sequences = {};
|
|
11158
11226
|
const text_matches = {};
|
|
11159
11227
|
for (const check of profile.checks || []) {
|
|
11160
|
-
if (
|
|
11228
|
+
if (
|
|
11229
|
+
(
|
|
11230
|
+
check.type === "selector_visible"
|
|
11231
|
+
|| check.type === "selector_absent"
|
|
11232
|
+
|| check.type === "selector_count_at_least"
|
|
11233
|
+
|| check.type === "selector_count_equals"
|
|
11234
|
+
|| check.type === "selector_count_equal"
|
|
11235
|
+
|| check.type === "selector_count_eq"
|
|
11236
|
+
) && check.selector
|
|
11237
|
+
) {
|
|
11161
11238
|
selectors[check.selector] = await selectorStats(check.selector);
|
|
11162
11239
|
}
|
|
11163
11240
|
if (check.type === "selector_text_order" && check.selector) {
|
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-MT2OZCCP.js";
|
|
62
62
|
import {
|
|
63
63
|
DEFAULT_RIDDLE_API_BASE_URL,
|
|
64
64
|
DEFAULT_RIDDLE_API_KEY_FILE,
|
package/dist/profile.cjs
CHANGED
|
@@ -56,7 +56,11 @@ var RIDDLE_PROOF_PROFILE_STATUSES = [
|
|
|
56
56
|
var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
57
57
|
"route_loaded",
|
|
58
58
|
"selector_visible",
|
|
59
|
+
"selector_absent",
|
|
59
60
|
"selector_count_at_least",
|
|
61
|
+
"selector_count_equals",
|
|
62
|
+
"selector_count_equal",
|
|
63
|
+
"selector_count_eq",
|
|
60
64
|
"selector_text_order",
|
|
61
65
|
"frame_text_visible",
|
|
62
66
|
"frame_no_horizontal_overflow",
|
|
@@ -304,6 +308,7 @@ function normalizeNetworkMock(input, index) {
|
|
|
304
308
|
url,
|
|
305
309
|
method: stringValue(input.method)?.toUpperCase(),
|
|
306
310
|
responses,
|
|
311
|
+
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
307
312
|
required_hit_count: requiredHitCount,
|
|
308
313
|
required: input.required === false ? false : true
|
|
309
314
|
};
|
|
@@ -395,7 +400,7 @@ function normalizeCheck(input, index) {
|
|
|
395
400
|
if (!isSupportedCheckType(type)) {
|
|
396
401
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
397
402
|
}
|
|
398
|
-
if ((type === "selector_visible" || type === "selector_count_at_least") && !stringValue(input.selector)) {
|
|
403
|
+
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)) {
|
|
399
404
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
400
405
|
}
|
|
401
406
|
if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
|
|
@@ -410,6 +415,10 @@ function normalizeCheck(input, index) {
|
|
|
410
415
|
if (type === "selector_count_at_least" && numberValue(input.min_count) === void 0) {
|
|
411
416
|
throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
|
|
412
417
|
}
|
|
418
|
+
const expectedCount = numberValue(input.expected_count) ?? numberValue(input.expectedCount) ?? numberValue(input.count);
|
|
419
|
+
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
|
|
420
|
+
throw new Error(`checks[${index}] ${type} requires expected_count.`);
|
|
421
|
+
}
|
|
413
422
|
const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
|
|
414
423
|
if (type === "selector_text_order") {
|
|
415
424
|
if (!stringValue(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
|
|
@@ -441,6 +450,7 @@ function normalizeCheck(input, index) {
|
|
|
441
450
|
allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
|
|
442
451
|
allowed_page_error_patterns: normalizeStringList(input.allowed_page_error_patterns ?? input.allowedPageErrorPatterns ?? input.allow_page_error_patterns ?? input.allowPageErrorPatterns, `checks[${index}] allowed_page_error_patterns`),
|
|
443
452
|
min_count: numberValue(input.min_count),
|
|
453
|
+
expected_count: expectedCount,
|
|
444
454
|
max_overflow_px: numberValue(input.max_overflow_px),
|
|
445
455
|
timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
|
|
446
456
|
run_direct_routes: input.run_direct_routes === false || input.runDirectRoutes === false ? false : true,
|
|
@@ -708,6 +718,20 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
708
718
|
message: failed.length ? `Selector ${key} was not visible in ${failed.length} viewport(s).` : void 0
|
|
709
719
|
};
|
|
710
720
|
}
|
|
721
|
+
if (check.type === "selector_absent") {
|
|
722
|
+
const key = selectorKey(check);
|
|
723
|
+
const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) > 0);
|
|
724
|
+
return {
|
|
725
|
+
type: check.type,
|
|
726
|
+
label: checkLabel(check),
|
|
727
|
+
status: failed.length ? "failed" : "passed",
|
|
728
|
+
evidence: {
|
|
729
|
+
selector: key,
|
|
730
|
+
counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
|
|
731
|
+
},
|
|
732
|
+
message: failed.length ? `Selector ${key} was present in ${failed.length} viewport(s).` : void 0
|
|
733
|
+
};
|
|
734
|
+
}
|
|
711
735
|
if (check.type === "selector_count_at_least") {
|
|
712
736
|
const key = selectorKey(check);
|
|
713
737
|
const minCount = check.min_count ?? 1;
|
|
@@ -724,6 +748,22 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
724
748
|
message: failed.length ? `Selector ${key} count was below ${minCount} in ${failed.length} viewport(s).` : void 0
|
|
725
749
|
};
|
|
726
750
|
}
|
|
751
|
+
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
752
|
+
const key = selectorKey(check);
|
|
753
|
+
const expectedCount = check.expected_count ?? 0;
|
|
754
|
+
const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) !== expectedCount);
|
|
755
|
+
return {
|
|
756
|
+
type: check.type,
|
|
757
|
+
label: checkLabel(check),
|
|
758
|
+
status: failed.length ? "failed" : "passed",
|
|
759
|
+
evidence: {
|
|
760
|
+
selector: key,
|
|
761
|
+
expected_count: expectedCount,
|
|
762
|
+
counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
|
|
763
|
+
},
|
|
764
|
+
message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
|
|
765
|
+
};
|
|
766
|
+
}
|
|
727
767
|
if (check.type === "selector_text_order") {
|
|
728
768
|
const key = selectorKey(check);
|
|
729
769
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -1460,6 +1500,18 @@ function assessProfile(profile, evidence) {
|
|
|
1460
1500
|
});
|
|
1461
1501
|
continue;
|
|
1462
1502
|
}
|
|
1503
|
+
if (check.type === "selector_absent") {
|
|
1504
|
+
const selector = check.selector || "";
|
|
1505
|
+
const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
|
|
1506
|
+
checks.push({
|
|
1507
|
+
type: check.type,
|
|
1508
|
+
label: check.label || check.type,
|
|
1509
|
+
status: failed.length ? "failed" : "passed",
|
|
1510
|
+
evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1511
|
+
message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
|
|
1512
|
+
});
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1463
1515
|
if (check.type === "selector_count_at_least") {
|
|
1464
1516
|
const selector = check.selector || "";
|
|
1465
1517
|
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
@@ -1473,6 +1525,19 @@ function assessProfile(profile, evidence) {
|
|
|
1473
1525
|
});
|
|
1474
1526
|
continue;
|
|
1475
1527
|
}
|
|
1528
|
+
if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
|
|
1529
|
+
const selector = check.selector || "";
|
|
1530
|
+
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
1531
|
+
const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
|
|
1532
|
+
checks.push({
|
|
1533
|
+
type: check.type,
|
|
1534
|
+
label: check.label || check.type,
|
|
1535
|
+
status: failed.length ? "failed" : "passed",
|
|
1536
|
+
evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
1537
|
+
message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
1538
|
+
});
|
|
1539
|
+
continue;
|
|
1540
|
+
}
|
|
1476
1541
|
if (check.type === "selector_text_order") {
|
|
1477
1542
|
const selector = check.selector || "";
|
|
1478
1543
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -1791,7 +1856,9 @@ async function registerNetworkMocks(mocks) {
|
|
|
1791
1856
|
const responses = Array.isArray(mock.responses) ? mock.responses : [];
|
|
1792
1857
|
const hitIndex = hitCount;
|
|
1793
1858
|
hitCount += 1;
|
|
1794
|
-
const responseIndex = responses.length
|
|
1859
|
+
const responseIndex = responses.length
|
|
1860
|
+
? (mock.repeat_responses ? hitIndex % responses.length : Math.min(hitIndex, responses.length - 1))
|
|
1861
|
+
: null;
|
|
1795
1862
|
const response = responseIndex === null ? mock : responses[responseIndex];
|
|
1796
1863
|
const headers = { ...(response.headers || mock.headers || {}) };
|
|
1797
1864
|
let body = response.body || "";
|
|
@@ -1806,7 +1873,8 @@ async function registerNetworkMocks(mocks) {
|
|
|
1806
1873
|
response_label: response.label || null,
|
|
1807
1874
|
hit_index: hitIndex,
|
|
1808
1875
|
response_index: responseIndex,
|
|
1809
|
-
sequence_reused: responseIndex !== null && hitIndex >= responses.length,
|
|
1876
|
+
sequence_reused: responseIndex !== null && !mock.repeat_responses && hitIndex >= responses.length,
|
|
1877
|
+
sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
|
|
1810
1878
|
url: request.url(),
|
|
1811
1879
|
method,
|
|
1812
1880
|
status: response.status || mock.status || 200,
|
|
@@ -2486,7 +2554,16 @@ async function captureViewport(viewport) {
|
|
|
2486
2554
|
const text_sequences = {};
|
|
2487
2555
|
const text_matches = {};
|
|
2488
2556
|
for (const check of profile.checks || []) {
|
|
2489
|
-
if (
|
|
2557
|
+
if (
|
|
2558
|
+
(
|
|
2559
|
+
check.type === "selector_visible"
|
|
2560
|
+
|| check.type === "selector_absent"
|
|
2561
|
+
|| check.type === "selector_count_at_least"
|
|
2562
|
+
|| check.type === "selector_count_equals"
|
|
2563
|
+
|| check.type === "selector_count_equal"
|
|
2564
|
+
|| check.type === "selector_count_eq"
|
|
2565
|
+
) && check.selector
|
|
2566
|
+
) {
|
|
2490
2567
|
selectors[check.selector] = await selectorStats(check.selector);
|
|
2491
2568
|
}
|
|
2492
2569
|
if (check.type === "selector_text_order" && check.selector) {
|
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", "selector_visible", "selector_count_at_least", "selector_text_order", "frame_text_visible", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_no_horizontal_overflow", "text_visible", "text_absent", "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", "fill", "set_input_value", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
|
@@ -47,6 +47,7 @@ interface RiddleProofProfileNetworkMock extends RiddleProofProfileNetworkMockRes
|
|
|
47
47
|
url: string;
|
|
48
48
|
method?: string;
|
|
49
49
|
responses?: RiddleProofProfileNetworkMockResponse[];
|
|
50
|
+
repeat_responses?: boolean;
|
|
50
51
|
required_hit_count?: number;
|
|
51
52
|
required?: boolean;
|
|
52
53
|
}
|
|
@@ -87,6 +88,7 @@ interface RiddleProofProfileCheck {
|
|
|
87
88
|
allowed_page_error_texts?: string[];
|
|
88
89
|
allowed_page_error_patterns?: string[];
|
|
89
90
|
min_count?: number;
|
|
91
|
+
expected_count?: number;
|
|
90
92
|
max_overflow_px?: number;
|
|
91
93
|
timeout_ms?: number;
|
|
92
94
|
run_direct_routes?: boolean;
|
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", "selector_visible", "selector_count_at_least", "selector_text_order", "frame_text_visible", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_no_horizontal_overflow", "text_visible", "text_absent", "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", "fill", "set_input_value", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
|
@@ -47,6 +47,7 @@ interface RiddleProofProfileNetworkMock extends RiddleProofProfileNetworkMockRes
|
|
|
47
47
|
url: string;
|
|
48
48
|
method?: string;
|
|
49
49
|
responses?: RiddleProofProfileNetworkMockResponse[];
|
|
50
|
+
repeat_responses?: boolean;
|
|
50
51
|
required_hit_count?: number;
|
|
51
52
|
required?: boolean;
|
|
52
53
|
}
|
|
@@ -87,6 +88,7 @@ interface RiddleProofProfileCheck {
|
|
|
87
88
|
allowed_page_error_texts?: string[];
|
|
88
89
|
allowed_page_error_patterns?: string[];
|
|
89
90
|
min_count?: number;
|
|
91
|
+
expected_count?: number;
|
|
90
92
|
max_overflow_px?: number;
|
|
91
93
|
timeout_ms?: number;
|
|
92
94
|
run_direct_routes?: boolean;
|
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-MT2OZCCP.js";
|
|
23
23
|
export {
|
|
24
24
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
25
25
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|