@riddledc/riddle-proof 0.7.128 → 0.7.129
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-TO3FVF5S.js → chunk-TDK6WFH3.js} +143 -6
- package/dist/cli.cjs +143 -6
- package/dist/cli.js +1 -1
- package/dist/index.cjs +143 -6
- package/dist/index.js +1 -1
- package/dist/profile.cjs +143 -6
- package/dist/profile.d.cts +1 -0
- package/dist/profile.d.ts +1 -0
- package/dist/profile.js +1 -1
- package/package.json +1 -1
|
@@ -1854,6 +1854,49 @@ function matchText(sample, check) {
|
|
|
1854
1854
|
}
|
|
1855
1855
|
return sample.includes(check.text || "");
|
|
1856
1856
|
}
|
|
1857
|
+
function compactTextEvidenceSample(value) {
|
|
1858
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
1859
|
+
}
|
|
1860
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
1861
|
+
if (index < 0) return void 0;
|
|
1862
|
+
const source = String(sample || "");
|
|
1863
|
+
const context = 120;
|
|
1864
|
+
const start = Math.max(0, index - context);
|
|
1865
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
1866
|
+
const prefix = start > 0 ? "..." : "";
|
|
1867
|
+
const suffix = end < source.length ? "..." : "";
|
|
1868
|
+
const compacted = compactTextEvidenceSample(`${prefix}${source.slice(start, end)}${suffix}`);
|
|
1869
|
+
return compacted ? compacted.slice(0, 240) : void 0;
|
|
1870
|
+
}
|
|
1871
|
+
function textMatchSamples(sample, check) {
|
|
1872
|
+
const source = String(sample || "");
|
|
1873
|
+
if (!source) return [];
|
|
1874
|
+
if (check.pattern) {
|
|
1875
|
+
try {
|
|
1876
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
1877
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
1878
|
+
const sampleText2 = match ? textSampleAroundMatch(source, match.index, match[0]?.length || 1) : void 0;
|
|
1879
|
+
return sampleText2 ? [sampleText2] : [];
|
|
1880
|
+
} catch {
|
|
1881
|
+
return [];
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
const text = check.text || "";
|
|
1885
|
+
if (!text) return [];
|
|
1886
|
+
const index = source.indexOf(text);
|
|
1887
|
+
const sampleText = textSampleAroundMatch(source, index, text.length);
|
|
1888
|
+
return sampleText ? [sampleText] : [];
|
|
1889
|
+
}
|
|
1890
|
+
function textCheckFailureSamples(viewport, check) {
|
|
1891
|
+
const key = textKey(check);
|
|
1892
|
+
const captured = viewport.text_match_samples?.[key] || [];
|
|
1893
|
+
const capturedSamples = captured.map((sample) => compactTextEvidenceSample(sample).slice(0, 240)).filter(Boolean);
|
|
1894
|
+
if (capturedSamples.length) return capturedSamples.slice(0, 3);
|
|
1895
|
+
const matchedSamples = textMatchSamples(viewport.body_text_sample || "", check);
|
|
1896
|
+
if (matchedSamples.length) return matchedSamples.slice(0, 3);
|
|
1897
|
+
const fallback = compactTextEvidenceSample(viewport.body_text_sample || "").slice(0, 240);
|
|
1898
|
+
return fallback ? [fallback] : [];
|
|
1899
|
+
}
|
|
1857
1900
|
function allowedMessageSample(input) {
|
|
1858
1901
|
if (!isRecord(input)) return String(input || "");
|
|
1859
1902
|
const parts = [
|
|
@@ -2298,10 +2341,17 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
2298
2341
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
2299
2342
|
const key = textKey(check);
|
|
2300
2343
|
const expectedVisible = check.type === "text_visible";
|
|
2301
|
-
const
|
|
2344
|
+
const results = viewports.map((viewport) => {
|
|
2302
2345
|
const fromEvidence = viewport.text_matches?.[key];
|
|
2303
|
-
|
|
2346
|
+
const matched = typeof fromEvidence === "boolean" ? fromEvidence : matchText(viewport.body_text_sample || "", check);
|
|
2347
|
+
const failedAgainstExpectation = matched !== expectedVisible;
|
|
2348
|
+
return {
|
|
2349
|
+
viewport: viewport.name,
|
|
2350
|
+
matched,
|
|
2351
|
+
samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : []
|
|
2352
|
+
};
|
|
2304
2353
|
});
|
|
2354
|
+
const matches = results.map((result) => result.matched);
|
|
2305
2355
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
2306
2356
|
return {
|
|
2307
2357
|
type: check.type,
|
|
@@ -2310,7 +2360,8 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
2310
2360
|
evidence: {
|
|
2311
2361
|
text: check.text || null,
|
|
2312
2362
|
pattern: check.pattern || null,
|
|
2313
|
-
matches
|
|
2363
|
+
matches,
|
|
2364
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
2314
2365
|
},
|
|
2315
2366
|
message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
|
|
2316
2367
|
};
|
|
@@ -2897,6 +2948,48 @@ function textMatches(sample, check) {
|
|
|
2897
2948
|
}
|
|
2898
2949
|
return String(sample || "").includes(check.text || "");
|
|
2899
2950
|
}
|
|
2951
|
+
function compactTextEvidenceSample(value) {
|
|
2952
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
2953
|
+
}
|
|
2954
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
2955
|
+
if (index < 0) return undefined;
|
|
2956
|
+
const source = String(sample || "");
|
|
2957
|
+
const context = 120;
|
|
2958
|
+
const start = Math.max(0, index - context);
|
|
2959
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
2960
|
+
const prefix = start > 0 ? "..." : "";
|
|
2961
|
+
const suffix = end < source.length ? "..." : "";
|
|
2962
|
+
const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
|
|
2963
|
+
return compacted ? compacted.slice(0, 240) : undefined;
|
|
2964
|
+
}
|
|
2965
|
+
function textMatchSamples(sample, check) {
|
|
2966
|
+
const source = String(sample || "");
|
|
2967
|
+
if (!source) return [];
|
|
2968
|
+
if (check.pattern) {
|
|
2969
|
+
try {
|
|
2970
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
2971
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
2972
|
+
const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
|
|
2973
|
+
return sampleText ? [sampleText] : [];
|
|
2974
|
+
} catch { return []; }
|
|
2975
|
+
}
|
|
2976
|
+
const text = check.text || "";
|
|
2977
|
+
if (!text) return [];
|
|
2978
|
+
const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
|
|
2979
|
+
return sampleText ? [sampleText] : [];
|
|
2980
|
+
}
|
|
2981
|
+
function textCheckFailureSamples(viewport, check) {
|
|
2982
|
+
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
2983
|
+
const captured = viewport && viewport.text_match_samples && Array.isArray(viewport.text_match_samples[key]) ? viewport.text_match_samples[key] : [];
|
|
2984
|
+
const capturedSamples = captured
|
|
2985
|
+
.map((sample) => compactTextEvidenceSample(sample).slice(0, 240))
|
|
2986
|
+
.filter(Boolean);
|
|
2987
|
+
if (capturedSamples.length) return capturedSamples.slice(0, 3);
|
|
2988
|
+
const matchedSamples = textMatchSamples(viewport && viewport.body_text_sample || "", check);
|
|
2989
|
+
if (matchedSamples.length) return matchedSamples.slice(0, 3);
|
|
2990
|
+
const fallback = compactTextEvidenceSample(viewport && viewport.body_text_sample || "").slice(0, 240);
|
|
2991
|
+
return fallback ? [fallback] : [];
|
|
2992
|
+
}
|
|
2900
2993
|
function allowedMessageSample(input) {
|
|
2901
2994
|
if (!input || typeof input !== "object" || Array.isArray(input)) return String(input || "");
|
|
2902
2995
|
const parts = [
|
|
@@ -3974,13 +4067,22 @@ function assessProfile(profile, evidence) {
|
|
|
3974
4067
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
3975
4068
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
3976
4069
|
const expectedVisible = check.type === "text_visible";
|
|
3977
|
-
const
|
|
4070
|
+
const results = checkViewports.map((viewport) => {
|
|
4071
|
+
const matched = viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check);
|
|
4072
|
+
const failedAgainstExpectation = matched !== expectedVisible;
|
|
4073
|
+
return {
|
|
4074
|
+
viewport: viewport.name,
|
|
4075
|
+
matched,
|
|
4076
|
+
samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : [],
|
|
4077
|
+
};
|
|
4078
|
+
});
|
|
4079
|
+
const matches = results.map((result) => result.matched);
|
|
3978
4080
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
3979
4081
|
checks.push({
|
|
3980
4082
|
type: check.type,
|
|
3981
4083
|
label: check.label || check.type,
|
|
3982
4084
|
status: failed ? "failed" : "passed",
|
|
3983
|
-
evidence: { text: check.text, pattern: check.pattern, matches },
|
|
4085
|
+
evidence: { text: check.text, pattern: check.pattern, matches, viewports: results },
|
|
3984
4086
|
message: failed ? "Text assertion failed in " + failed + " viewport(s)." : undefined,
|
|
3985
4087
|
});
|
|
3986
4088
|
continue;
|
|
@@ -4300,6 +4402,36 @@ function textMatches(sample, check) {
|
|
|
4300
4402
|
}
|
|
4301
4403
|
return String(sample || "").includes(check.text || "");
|
|
4302
4404
|
}
|
|
4405
|
+
function compactTextEvidenceSample(value) {
|
|
4406
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
4407
|
+
}
|
|
4408
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
4409
|
+
if (index < 0) return undefined;
|
|
4410
|
+
const source = String(sample || "");
|
|
4411
|
+
const context = 120;
|
|
4412
|
+
const start = Math.max(0, index - context);
|
|
4413
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
4414
|
+
const prefix = start > 0 ? "..." : "";
|
|
4415
|
+
const suffix = end < source.length ? "..." : "";
|
|
4416
|
+
const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
|
|
4417
|
+
return compacted ? compacted.slice(0, 240) : undefined;
|
|
4418
|
+
}
|
|
4419
|
+
function textMatchSamples(sample, check) {
|
|
4420
|
+
const source = String(sample || "");
|
|
4421
|
+
if (!source) return [];
|
|
4422
|
+
if (check.pattern) {
|
|
4423
|
+
try {
|
|
4424
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
4425
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
4426
|
+
const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
|
|
4427
|
+
return sampleText ? [sampleText] : [];
|
|
4428
|
+
} catch { return []; }
|
|
4429
|
+
}
|
|
4430
|
+
const text = check.text || "";
|
|
4431
|
+
if (!text) return [];
|
|
4432
|
+
const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
|
|
4433
|
+
return sampleText ? [sampleText] : [];
|
|
4434
|
+
}
|
|
4303
4435
|
function profileCheckAppliesToViewport(check, viewport) {
|
|
4304
4436
|
if (!Array.isArray(check.viewports) || !check.viewports.length) return true;
|
|
4305
4437
|
return Boolean(viewport && viewport.name && check.viewports.includes(viewport.name));
|
|
@@ -6251,6 +6383,7 @@ async function captureViewport(viewport) {
|
|
|
6251
6383
|
const frames = {};
|
|
6252
6384
|
const text_sequences = {};
|
|
6253
6385
|
const text_matches = {};
|
|
6386
|
+
const text_match_samples = {};
|
|
6254
6387
|
const http_statuses = {};
|
|
6255
6388
|
const link_statuses = {};
|
|
6256
6389
|
for (const check of profile.checks || []) {
|
|
@@ -6272,7 +6405,10 @@ async function captureViewport(viewport) {
|
|
|
6272
6405
|
text_sequences[check.selector] = await selectorTextSequence(check.selector);
|
|
6273
6406
|
}
|
|
6274
6407
|
if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
|
|
6275
|
-
|
|
6408
|
+
const key = textKey(check);
|
|
6409
|
+
const sample = dom.body_text || dom.body_text_sample || "";
|
|
6410
|
+
text_matches[key] = textMatches(sample, check);
|
|
6411
|
+
text_match_samples[key] = textMatchSamples(sample, check);
|
|
6276
6412
|
}
|
|
6277
6413
|
if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
|
|
6278
6414
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
@@ -6349,6 +6485,7 @@ async function captureViewport(viewport) {
|
|
|
6349
6485
|
frames,
|
|
6350
6486
|
text_sequences,
|
|
6351
6487
|
text_matches,
|
|
6488
|
+
text_match_samples,
|
|
6352
6489
|
http_statuses,
|
|
6353
6490
|
link_statuses,
|
|
6354
6491
|
route_inventory: routeInventory,
|
package/dist/cli.cjs
CHANGED
|
@@ -8791,6 +8791,49 @@ function matchText(sample, check) {
|
|
|
8791
8791
|
}
|
|
8792
8792
|
return sample.includes(check.text || "");
|
|
8793
8793
|
}
|
|
8794
|
+
function compactTextEvidenceSample(value) {
|
|
8795
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
8796
|
+
}
|
|
8797
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
8798
|
+
if (index < 0) return void 0;
|
|
8799
|
+
const source = String(sample || "");
|
|
8800
|
+
const context = 120;
|
|
8801
|
+
const start = Math.max(0, index - context);
|
|
8802
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
8803
|
+
const prefix = start > 0 ? "..." : "";
|
|
8804
|
+
const suffix = end < source.length ? "..." : "";
|
|
8805
|
+
const compacted = compactTextEvidenceSample(`${prefix}${source.slice(start, end)}${suffix}`);
|
|
8806
|
+
return compacted ? compacted.slice(0, 240) : void 0;
|
|
8807
|
+
}
|
|
8808
|
+
function textMatchSamples(sample, check) {
|
|
8809
|
+
const source = String(sample || "");
|
|
8810
|
+
if (!source) return [];
|
|
8811
|
+
if (check.pattern) {
|
|
8812
|
+
try {
|
|
8813
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
8814
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
8815
|
+
const sampleText2 = match ? textSampleAroundMatch(source, match.index, match[0]?.length || 1) : void 0;
|
|
8816
|
+
return sampleText2 ? [sampleText2] : [];
|
|
8817
|
+
} catch {
|
|
8818
|
+
return [];
|
|
8819
|
+
}
|
|
8820
|
+
}
|
|
8821
|
+
const text = check.text || "";
|
|
8822
|
+
if (!text) return [];
|
|
8823
|
+
const index = source.indexOf(text);
|
|
8824
|
+
const sampleText = textSampleAroundMatch(source, index, text.length);
|
|
8825
|
+
return sampleText ? [sampleText] : [];
|
|
8826
|
+
}
|
|
8827
|
+
function textCheckFailureSamples(viewport, check) {
|
|
8828
|
+
const key = textKey(check);
|
|
8829
|
+
const captured = viewport.text_match_samples?.[key] || [];
|
|
8830
|
+
const capturedSamples = captured.map((sample) => compactTextEvidenceSample(sample).slice(0, 240)).filter(Boolean);
|
|
8831
|
+
if (capturedSamples.length) return capturedSamples.slice(0, 3);
|
|
8832
|
+
const matchedSamples = textMatchSamples(viewport.body_text_sample || "", check);
|
|
8833
|
+
if (matchedSamples.length) return matchedSamples.slice(0, 3);
|
|
8834
|
+
const fallback = compactTextEvidenceSample(viewport.body_text_sample || "").slice(0, 240);
|
|
8835
|
+
return fallback ? [fallback] : [];
|
|
8836
|
+
}
|
|
8794
8837
|
function allowedMessageSample(input) {
|
|
8795
8838
|
if (!isRecord(input)) return String(input || "");
|
|
8796
8839
|
const parts = [
|
|
@@ -9235,10 +9278,17 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
9235
9278
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
9236
9279
|
const key = textKey(check);
|
|
9237
9280
|
const expectedVisible = check.type === "text_visible";
|
|
9238
|
-
const
|
|
9281
|
+
const results = viewports.map((viewport) => {
|
|
9239
9282
|
const fromEvidence = viewport.text_matches?.[key];
|
|
9240
|
-
|
|
9283
|
+
const matched = typeof fromEvidence === "boolean" ? fromEvidence : matchText(viewport.body_text_sample || "", check);
|
|
9284
|
+
const failedAgainstExpectation = matched !== expectedVisible;
|
|
9285
|
+
return {
|
|
9286
|
+
viewport: viewport.name,
|
|
9287
|
+
matched,
|
|
9288
|
+
samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : []
|
|
9289
|
+
};
|
|
9241
9290
|
});
|
|
9291
|
+
const matches = results.map((result) => result.matched);
|
|
9242
9292
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
9243
9293
|
return {
|
|
9244
9294
|
type: check.type,
|
|
@@ -9247,7 +9297,8 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
9247
9297
|
evidence: {
|
|
9248
9298
|
text: check.text || null,
|
|
9249
9299
|
pattern: check.pattern || null,
|
|
9250
|
-
matches
|
|
9300
|
+
matches,
|
|
9301
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
9251
9302
|
},
|
|
9252
9303
|
message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
|
|
9253
9304
|
};
|
|
@@ -9818,6 +9869,48 @@ function textMatches(sample, check) {
|
|
|
9818
9869
|
}
|
|
9819
9870
|
return String(sample || "").includes(check.text || "");
|
|
9820
9871
|
}
|
|
9872
|
+
function compactTextEvidenceSample(value) {
|
|
9873
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
9874
|
+
}
|
|
9875
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
9876
|
+
if (index < 0) return undefined;
|
|
9877
|
+
const source = String(sample || "");
|
|
9878
|
+
const context = 120;
|
|
9879
|
+
const start = Math.max(0, index - context);
|
|
9880
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
9881
|
+
const prefix = start > 0 ? "..." : "";
|
|
9882
|
+
const suffix = end < source.length ? "..." : "";
|
|
9883
|
+
const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
|
|
9884
|
+
return compacted ? compacted.slice(0, 240) : undefined;
|
|
9885
|
+
}
|
|
9886
|
+
function textMatchSamples(sample, check) {
|
|
9887
|
+
const source = String(sample || "");
|
|
9888
|
+
if (!source) return [];
|
|
9889
|
+
if (check.pattern) {
|
|
9890
|
+
try {
|
|
9891
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
9892
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
9893
|
+
const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
|
|
9894
|
+
return sampleText ? [sampleText] : [];
|
|
9895
|
+
} catch { return []; }
|
|
9896
|
+
}
|
|
9897
|
+
const text = check.text || "";
|
|
9898
|
+
if (!text) return [];
|
|
9899
|
+
const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
|
|
9900
|
+
return sampleText ? [sampleText] : [];
|
|
9901
|
+
}
|
|
9902
|
+
function textCheckFailureSamples(viewport, check) {
|
|
9903
|
+
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
9904
|
+
const captured = viewport && viewport.text_match_samples && Array.isArray(viewport.text_match_samples[key]) ? viewport.text_match_samples[key] : [];
|
|
9905
|
+
const capturedSamples = captured
|
|
9906
|
+
.map((sample) => compactTextEvidenceSample(sample).slice(0, 240))
|
|
9907
|
+
.filter(Boolean);
|
|
9908
|
+
if (capturedSamples.length) return capturedSamples.slice(0, 3);
|
|
9909
|
+
const matchedSamples = textMatchSamples(viewport && viewport.body_text_sample || "", check);
|
|
9910
|
+
if (matchedSamples.length) return matchedSamples.slice(0, 3);
|
|
9911
|
+
const fallback = compactTextEvidenceSample(viewport && viewport.body_text_sample || "").slice(0, 240);
|
|
9912
|
+
return fallback ? [fallback] : [];
|
|
9913
|
+
}
|
|
9821
9914
|
function allowedMessageSample(input) {
|
|
9822
9915
|
if (!input || typeof input !== "object" || Array.isArray(input)) return String(input || "");
|
|
9823
9916
|
const parts = [
|
|
@@ -10895,13 +10988,22 @@ function assessProfile(profile, evidence) {
|
|
|
10895
10988
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
10896
10989
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
10897
10990
|
const expectedVisible = check.type === "text_visible";
|
|
10898
|
-
const
|
|
10991
|
+
const results = checkViewports.map((viewport) => {
|
|
10992
|
+
const matched = viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check);
|
|
10993
|
+
const failedAgainstExpectation = matched !== expectedVisible;
|
|
10994
|
+
return {
|
|
10995
|
+
viewport: viewport.name,
|
|
10996
|
+
matched,
|
|
10997
|
+
samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : [],
|
|
10998
|
+
};
|
|
10999
|
+
});
|
|
11000
|
+
const matches = results.map((result) => result.matched);
|
|
10899
11001
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
10900
11002
|
checks.push({
|
|
10901
11003
|
type: check.type,
|
|
10902
11004
|
label: check.label || check.type,
|
|
10903
11005
|
status: failed ? "failed" : "passed",
|
|
10904
|
-
evidence: { text: check.text, pattern: check.pattern, matches },
|
|
11006
|
+
evidence: { text: check.text, pattern: check.pattern, matches, viewports: results },
|
|
10905
11007
|
message: failed ? "Text assertion failed in " + failed + " viewport(s)." : undefined,
|
|
10906
11008
|
});
|
|
10907
11009
|
continue;
|
|
@@ -11221,6 +11323,36 @@ function textMatches(sample, check) {
|
|
|
11221
11323
|
}
|
|
11222
11324
|
return String(sample || "").includes(check.text || "");
|
|
11223
11325
|
}
|
|
11326
|
+
function compactTextEvidenceSample(value) {
|
|
11327
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
11328
|
+
}
|
|
11329
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
11330
|
+
if (index < 0) return undefined;
|
|
11331
|
+
const source = String(sample || "");
|
|
11332
|
+
const context = 120;
|
|
11333
|
+
const start = Math.max(0, index - context);
|
|
11334
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
11335
|
+
const prefix = start > 0 ? "..." : "";
|
|
11336
|
+
const suffix = end < source.length ? "..." : "";
|
|
11337
|
+
const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
|
|
11338
|
+
return compacted ? compacted.slice(0, 240) : undefined;
|
|
11339
|
+
}
|
|
11340
|
+
function textMatchSamples(sample, check) {
|
|
11341
|
+
const source = String(sample || "");
|
|
11342
|
+
if (!source) return [];
|
|
11343
|
+
if (check.pattern) {
|
|
11344
|
+
try {
|
|
11345
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
11346
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
11347
|
+
const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
|
|
11348
|
+
return sampleText ? [sampleText] : [];
|
|
11349
|
+
} catch { return []; }
|
|
11350
|
+
}
|
|
11351
|
+
const text = check.text || "";
|
|
11352
|
+
if (!text) return [];
|
|
11353
|
+
const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
|
|
11354
|
+
return sampleText ? [sampleText] : [];
|
|
11355
|
+
}
|
|
11224
11356
|
function profileCheckAppliesToViewport(check, viewport) {
|
|
11225
11357
|
if (!Array.isArray(check.viewports) || !check.viewports.length) return true;
|
|
11226
11358
|
return Boolean(viewport && viewport.name && check.viewports.includes(viewport.name));
|
|
@@ -13172,6 +13304,7 @@ async function captureViewport(viewport) {
|
|
|
13172
13304
|
const frames = {};
|
|
13173
13305
|
const text_sequences = {};
|
|
13174
13306
|
const text_matches = {};
|
|
13307
|
+
const text_match_samples = {};
|
|
13175
13308
|
const http_statuses = {};
|
|
13176
13309
|
const link_statuses = {};
|
|
13177
13310
|
for (const check of profile.checks || []) {
|
|
@@ -13193,7 +13326,10 @@ async function captureViewport(viewport) {
|
|
|
13193
13326
|
text_sequences[check.selector] = await selectorTextSequence(check.selector);
|
|
13194
13327
|
}
|
|
13195
13328
|
if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
|
|
13196
|
-
|
|
13329
|
+
const key = textKey(check);
|
|
13330
|
+
const sample = dom.body_text || dom.body_text_sample || "";
|
|
13331
|
+
text_matches[key] = textMatches(sample, check);
|
|
13332
|
+
text_match_samples[key] = textMatchSamples(sample, check);
|
|
13197
13333
|
}
|
|
13198
13334
|
if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
|
|
13199
13335
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
@@ -13270,6 +13406,7 @@ async function captureViewport(viewport) {
|
|
|
13270
13406
|
frames,
|
|
13271
13407
|
text_sequences,
|
|
13272
13408
|
text_matches,
|
|
13409
|
+
text_match_samples,
|
|
13273
13410
|
http_statuses,
|
|
13274
13411
|
link_statuses,
|
|
13275
13412
|
route_inventory: routeInventory,
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -10587,6 +10587,49 @@ function matchText(sample, check) {
|
|
|
10587
10587
|
}
|
|
10588
10588
|
return sample.includes(check.text || "");
|
|
10589
10589
|
}
|
|
10590
|
+
function compactTextEvidenceSample(value) {
|
|
10591
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
10592
|
+
}
|
|
10593
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
10594
|
+
if (index < 0) return void 0;
|
|
10595
|
+
const source = String(sample || "");
|
|
10596
|
+
const context = 120;
|
|
10597
|
+
const start = Math.max(0, index - context);
|
|
10598
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
10599
|
+
const prefix = start > 0 ? "..." : "";
|
|
10600
|
+
const suffix = end < source.length ? "..." : "";
|
|
10601
|
+
const compacted = compactTextEvidenceSample(`${prefix}${source.slice(start, end)}${suffix}`);
|
|
10602
|
+
return compacted ? compacted.slice(0, 240) : void 0;
|
|
10603
|
+
}
|
|
10604
|
+
function textMatchSamples(sample, check) {
|
|
10605
|
+
const source = String(sample || "");
|
|
10606
|
+
if (!source) return [];
|
|
10607
|
+
if (check.pattern) {
|
|
10608
|
+
try {
|
|
10609
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
10610
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
10611
|
+
const sampleText2 = match ? textSampleAroundMatch(source, match.index, match[0]?.length || 1) : void 0;
|
|
10612
|
+
return sampleText2 ? [sampleText2] : [];
|
|
10613
|
+
} catch {
|
|
10614
|
+
return [];
|
|
10615
|
+
}
|
|
10616
|
+
}
|
|
10617
|
+
const text = check.text || "";
|
|
10618
|
+
if (!text) return [];
|
|
10619
|
+
const index = source.indexOf(text);
|
|
10620
|
+
const sampleText = textSampleAroundMatch(source, index, text.length);
|
|
10621
|
+
return sampleText ? [sampleText] : [];
|
|
10622
|
+
}
|
|
10623
|
+
function textCheckFailureSamples(viewport, check) {
|
|
10624
|
+
const key = textKey(check);
|
|
10625
|
+
const captured = viewport.text_match_samples?.[key] || [];
|
|
10626
|
+
const capturedSamples = captured.map((sample) => compactTextEvidenceSample(sample).slice(0, 240)).filter(Boolean);
|
|
10627
|
+
if (capturedSamples.length) return capturedSamples.slice(0, 3);
|
|
10628
|
+
const matchedSamples = textMatchSamples(viewport.body_text_sample || "", check);
|
|
10629
|
+
if (matchedSamples.length) return matchedSamples.slice(0, 3);
|
|
10630
|
+
const fallback = compactTextEvidenceSample(viewport.body_text_sample || "").slice(0, 240);
|
|
10631
|
+
return fallback ? [fallback] : [];
|
|
10632
|
+
}
|
|
10590
10633
|
function allowedMessageSample(input) {
|
|
10591
10634
|
if (!isRecord2(input)) return String(input || "");
|
|
10592
10635
|
const parts = [
|
|
@@ -11031,10 +11074,17 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
11031
11074
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
11032
11075
|
const key = textKey(check);
|
|
11033
11076
|
const expectedVisible = check.type === "text_visible";
|
|
11034
|
-
const
|
|
11077
|
+
const results = viewports.map((viewport) => {
|
|
11035
11078
|
const fromEvidence = viewport.text_matches?.[key];
|
|
11036
|
-
|
|
11079
|
+
const matched = typeof fromEvidence === "boolean" ? fromEvidence : matchText(viewport.body_text_sample || "", check);
|
|
11080
|
+
const failedAgainstExpectation = matched !== expectedVisible;
|
|
11081
|
+
return {
|
|
11082
|
+
viewport: viewport.name,
|
|
11083
|
+
matched,
|
|
11084
|
+
samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : []
|
|
11085
|
+
};
|
|
11037
11086
|
});
|
|
11087
|
+
const matches = results.map((result) => result.matched);
|
|
11038
11088
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
11039
11089
|
return {
|
|
11040
11090
|
type: check.type,
|
|
@@ -11043,7 +11093,8 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
11043
11093
|
evidence: {
|
|
11044
11094
|
text: check.text || null,
|
|
11045
11095
|
pattern: check.pattern || null,
|
|
11046
|
-
matches
|
|
11096
|
+
matches,
|
|
11097
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
11047
11098
|
},
|
|
11048
11099
|
message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
|
|
11049
11100
|
};
|
|
@@ -11630,6 +11681,48 @@ function textMatches(sample, check) {
|
|
|
11630
11681
|
}
|
|
11631
11682
|
return String(sample || "").includes(check.text || "");
|
|
11632
11683
|
}
|
|
11684
|
+
function compactTextEvidenceSample(value) {
|
|
11685
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
11686
|
+
}
|
|
11687
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
11688
|
+
if (index < 0) return undefined;
|
|
11689
|
+
const source = String(sample || "");
|
|
11690
|
+
const context = 120;
|
|
11691
|
+
const start = Math.max(0, index - context);
|
|
11692
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
11693
|
+
const prefix = start > 0 ? "..." : "";
|
|
11694
|
+
const suffix = end < source.length ? "..." : "";
|
|
11695
|
+
const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
|
|
11696
|
+
return compacted ? compacted.slice(0, 240) : undefined;
|
|
11697
|
+
}
|
|
11698
|
+
function textMatchSamples(sample, check) {
|
|
11699
|
+
const source = String(sample || "");
|
|
11700
|
+
if (!source) return [];
|
|
11701
|
+
if (check.pattern) {
|
|
11702
|
+
try {
|
|
11703
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
11704
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
11705
|
+
const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
|
|
11706
|
+
return sampleText ? [sampleText] : [];
|
|
11707
|
+
} catch { return []; }
|
|
11708
|
+
}
|
|
11709
|
+
const text = check.text || "";
|
|
11710
|
+
if (!text) return [];
|
|
11711
|
+
const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
|
|
11712
|
+
return sampleText ? [sampleText] : [];
|
|
11713
|
+
}
|
|
11714
|
+
function textCheckFailureSamples(viewport, check) {
|
|
11715
|
+
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
11716
|
+
const captured = viewport && viewport.text_match_samples && Array.isArray(viewport.text_match_samples[key]) ? viewport.text_match_samples[key] : [];
|
|
11717
|
+
const capturedSamples = captured
|
|
11718
|
+
.map((sample) => compactTextEvidenceSample(sample).slice(0, 240))
|
|
11719
|
+
.filter(Boolean);
|
|
11720
|
+
if (capturedSamples.length) return capturedSamples.slice(0, 3);
|
|
11721
|
+
const matchedSamples = textMatchSamples(viewport && viewport.body_text_sample || "", check);
|
|
11722
|
+
if (matchedSamples.length) return matchedSamples.slice(0, 3);
|
|
11723
|
+
const fallback = compactTextEvidenceSample(viewport && viewport.body_text_sample || "").slice(0, 240);
|
|
11724
|
+
return fallback ? [fallback] : [];
|
|
11725
|
+
}
|
|
11633
11726
|
function allowedMessageSample(input) {
|
|
11634
11727
|
if (!input || typeof input !== "object" || Array.isArray(input)) return String(input || "");
|
|
11635
11728
|
const parts = [
|
|
@@ -12707,13 +12800,22 @@ function assessProfile(profile, evidence) {
|
|
|
12707
12800
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
12708
12801
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
12709
12802
|
const expectedVisible = check.type === "text_visible";
|
|
12710
|
-
const
|
|
12803
|
+
const results = checkViewports.map((viewport) => {
|
|
12804
|
+
const matched = viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check);
|
|
12805
|
+
const failedAgainstExpectation = matched !== expectedVisible;
|
|
12806
|
+
return {
|
|
12807
|
+
viewport: viewport.name,
|
|
12808
|
+
matched,
|
|
12809
|
+
samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : [],
|
|
12810
|
+
};
|
|
12811
|
+
});
|
|
12812
|
+
const matches = results.map((result) => result.matched);
|
|
12711
12813
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
12712
12814
|
checks.push({
|
|
12713
12815
|
type: check.type,
|
|
12714
12816
|
label: check.label || check.type,
|
|
12715
12817
|
status: failed ? "failed" : "passed",
|
|
12716
|
-
evidence: { text: check.text, pattern: check.pattern, matches },
|
|
12818
|
+
evidence: { text: check.text, pattern: check.pattern, matches, viewports: results },
|
|
12717
12819
|
message: failed ? "Text assertion failed in " + failed + " viewport(s)." : undefined,
|
|
12718
12820
|
});
|
|
12719
12821
|
continue;
|
|
@@ -13033,6 +13135,36 @@ function textMatches(sample, check) {
|
|
|
13033
13135
|
}
|
|
13034
13136
|
return String(sample || "").includes(check.text || "");
|
|
13035
13137
|
}
|
|
13138
|
+
function compactTextEvidenceSample(value) {
|
|
13139
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
13140
|
+
}
|
|
13141
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
13142
|
+
if (index < 0) return undefined;
|
|
13143
|
+
const source = String(sample || "");
|
|
13144
|
+
const context = 120;
|
|
13145
|
+
const start = Math.max(0, index - context);
|
|
13146
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
13147
|
+
const prefix = start > 0 ? "..." : "";
|
|
13148
|
+
const suffix = end < source.length ? "..." : "";
|
|
13149
|
+
const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
|
|
13150
|
+
return compacted ? compacted.slice(0, 240) : undefined;
|
|
13151
|
+
}
|
|
13152
|
+
function textMatchSamples(sample, check) {
|
|
13153
|
+
const source = String(sample || "");
|
|
13154
|
+
if (!source) return [];
|
|
13155
|
+
if (check.pattern) {
|
|
13156
|
+
try {
|
|
13157
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
13158
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
13159
|
+
const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
|
|
13160
|
+
return sampleText ? [sampleText] : [];
|
|
13161
|
+
} catch { return []; }
|
|
13162
|
+
}
|
|
13163
|
+
const text = check.text || "";
|
|
13164
|
+
if (!text) return [];
|
|
13165
|
+
const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
|
|
13166
|
+
return sampleText ? [sampleText] : [];
|
|
13167
|
+
}
|
|
13036
13168
|
function profileCheckAppliesToViewport(check, viewport) {
|
|
13037
13169
|
if (!Array.isArray(check.viewports) || !check.viewports.length) return true;
|
|
13038
13170
|
return Boolean(viewport && viewport.name && check.viewports.includes(viewport.name));
|
|
@@ -14984,6 +15116,7 @@ async function captureViewport(viewport) {
|
|
|
14984
15116
|
const frames = {};
|
|
14985
15117
|
const text_sequences = {};
|
|
14986
15118
|
const text_matches = {};
|
|
15119
|
+
const text_match_samples = {};
|
|
14987
15120
|
const http_statuses = {};
|
|
14988
15121
|
const link_statuses = {};
|
|
14989
15122
|
for (const check of profile.checks || []) {
|
|
@@ -15005,7 +15138,10 @@ async function captureViewport(viewport) {
|
|
|
15005
15138
|
text_sequences[check.selector] = await selectorTextSequence(check.selector);
|
|
15006
15139
|
}
|
|
15007
15140
|
if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
|
|
15008
|
-
|
|
15141
|
+
const key = textKey(check);
|
|
15142
|
+
const sample = dom.body_text || dom.body_text_sample || "";
|
|
15143
|
+
text_matches[key] = textMatches(sample, check);
|
|
15144
|
+
text_match_samples[key] = textMatchSamples(sample, check);
|
|
15009
15145
|
}
|
|
15010
15146
|
if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
|
|
15011
15147
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
@@ -15082,6 +15218,7 @@ async function captureViewport(viewport) {
|
|
|
15082
15218
|
frames,
|
|
15083
15219
|
text_sequences,
|
|
15084
15220
|
text_matches,
|
|
15221
|
+
text_match_samples,
|
|
15085
15222
|
http_statuses,
|
|
15086
15223
|
link_statuses,
|
|
15087
15224
|
route_inventory: routeInventory,
|
package/dist/index.js
CHANGED
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
resolveRiddleProofProfileTimeoutSec,
|
|
63
63
|
slugifyRiddleProofProfileName,
|
|
64
64
|
summarizeRiddleProofProfileResult
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-TDK6WFH3.js";
|
|
66
66
|
import {
|
|
67
67
|
DEFAULT_RIDDLE_API_BASE_URL,
|
|
68
68
|
DEFAULT_RIDDLE_API_KEY_FILE,
|
package/dist/profile.cjs
CHANGED
|
@@ -1901,6 +1901,49 @@ function matchText(sample, check) {
|
|
|
1901
1901
|
}
|
|
1902
1902
|
return sample.includes(check.text || "");
|
|
1903
1903
|
}
|
|
1904
|
+
function compactTextEvidenceSample(value) {
|
|
1905
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
1906
|
+
}
|
|
1907
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
1908
|
+
if (index < 0) return void 0;
|
|
1909
|
+
const source = String(sample || "");
|
|
1910
|
+
const context = 120;
|
|
1911
|
+
const start = Math.max(0, index - context);
|
|
1912
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
1913
|
+
const prefix = start > 0 ? "..." : "";
|
|
1914
|
+
const suffix = end < source.length ? "..." : "";
|
|
1915
|
+
const compacted = compactTextEvidenceSample(`${prefix}${source.slice(start, end)}${suffix}`);
|
|
1916
|
+
return compacted ? compacted.slice(0, 240) : void 0;
|
|
1917
|
+
}
|
|
1918
|
+
function textMatchSamples(sample, check) {
|
|
1919
|
+
const source = String(sample || "");
|
|
1920
|
+
if (!source) return [];
|
|
1921
|
+
if (check.pattern) {
|
|
1922
|
+
try {
|
|
1923
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
1924
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
1925
|
+
const sampleText2 = match ? textSampleAroundMatch(source, match.index, match[0]?.length || 1) : void 0;
|
|
1926
|
+
return sampleText2 ? [sampleText2] : [];
|
|
1927
|
+
} catch {
|
|
1928
|
+
return [];
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
const text = check.text || "";
|
|
1932
|
+
if (!text) return [];
|
|
1933
|
+
const index = source.indexOf(text);
|
|
1934
|
+
const sampleText = textSampleAroundMatch(source, index, text.length);
|
|
1935
|
+
return sampleText ? [sampleText] : [];
|
|
1936
|
+
}
|
|
1937
|
+
function textCheckFailureSamples(viewport, check) {
|
|
1938
|
+
const key = textKey(check);
|
|
1939
|
+
const captured = viewport.text_match_samples?.[key] || [];
|
|
1940
|
+
const capturedSamples = captured.map((sample) => compactTextEvidenceSample(sample).slice(0, 240)).filter(Boolean);
|
|
1941
|
+
if (capturedSamples.length) return capturedSamples.slice(0, 3);
|
|
1942
|
+
const matchedSamples = textMatchSamples(viewport.body_text_sample || "", check);
|
|
1943
|
+
if (matchedSamples.length) return matchedSamples.slice(0, 3);
|
|
1944
|
+
const fallback = compactTextEvidenceSample(viewport.body_text_sample || "").slice(0, 240);
|
|
1945
|
+
return fallback ? [fallback] : [];
|
|
1946
|
+
}
|
|
1904
1947
|
function allowedMessageSample(input) {
|
|
1905
1948
|
if (!isRecord(input)) return String(input || "");
|
|
1906
1949
|
const parts = [
|
|
@@ -2345,10 +2388,17 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
2345
2388
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
2346
2389
|
const key = textKey(check);
|
|
2347
2390
|
const expectedVisible = check.type === "text_visible";
|
|
2348
|
-
const
|
|
2391
|
+
const results = viewports.map((viewport) => {
|
|
2349
2392
|
const fromEvidence = viewport.text_matches?.[key];
|
|
2350
|
-
|
|
2393
|
+
const matched = typeof fromEvidence === "boolean" ? fromEvidence : matchText(viewport.body_text_sample || "", check);
|
|
2394
|
+
const failedAgainstExpectation = matched !== expectedVisible;
|
|
2395
|
+
return {
|
|
2396
|
+
viewport: viewport.name,
|
|
2397
|
+
matched,
|
|
2398
|
+
samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : []
|
|
2399
|
+
};
|
|
2351
2400
|
});
|
|
2401
|
+
const matches = results.map((result) => result.matched);
|
|
2352
2402
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
2353
2403
|
return {
|
|
2354
2404
|
type: check.type,
|
|
@@ -2357,7 +2407,8 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
2357
2407
|
evidence: {
|
|
2358
2408
|
text: check.text || null,
|
|
2359
2409
|
pattern: check.pattern || null,
|
|
2360
|
-
matches
|
|
2410
|
+
matches,
|
|
2411
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
2361
2412
|
},
|
|
2362
2413
|
message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
|
|
2363
2414
|
};
|
|
@@ -2944,6 +2995,48 @@ function textMatches(sample, check) {
|
|
|
2944
2995
|
}
|
|
2945
2996
|
return String(sample || "").includes(check.text || "");
|
|
2946
2997
|
}
|
|
2998
|
+
function compactTextEvidenceSample(value) {
|
|
2999
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
3000
|
+
}
|
|
3001
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
3002
|
+
if (index < 0) return undefined;
|
|
3003
|
+
const source = String(sample || "");
|
|
3004
|
+
const context = 120;
|
|
3005
|
+
const start = Math.max(0, index - context);
|
|
3006
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
3007
|
+
const prefix = start > 0 ? "..." : "";
|
|
3008
|
+
const suffix = end < source.length ? "..." : "";
|
|
3009
|
+
const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
|
|
3010
|
+
return compacted ? compacted.slice(0, 240) : undefined;
|
|
3011
|
+
}
|
|
3012
|
+
function textMatchSamples(sample, check) {
|
|
3013
|
+
const source = String(sample || "");
|
|
3014
|
+
if (!source) return [];
|
|
3015
|
+
if (check.pattern) {
|
|
3016
|
+
try {
|
|
3017
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
3018
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
3019
|
+
const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
|
|
3020
|
+
return sampleText ? [sampleText] : [];
|
|
3021
|
+
} catch { return []; }
|
|
3022
|
+
}
|
|
3023
|
+
const text = check.text || "";
|
|
3024
|
+
if (!text) return [];
|
|
3025
|
+
const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
|
|
3026
|
+
return sampleText ? [sampleText] : [];
|
|
3027
|
+
}
|
|
3028
|
+
function textCheckFailureSamples(viewport, check) {
|
|
3029
|
+
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
3030
|
+
const captured = viewport && viewport.text_match_samples && Array.isArray(viewport.text_match_samples[key]) ? viewport.text_match_samples[key] : [];
|
|
3031
|
+
const capturedSamples = captured
|
|
3032
|
+
.map((sample) => compactTextEvidenceSample(sample).slice(0, 240))
|
|
3033
|
+
.filter(Boolean);
|
|
3034
|
+
if (capturedSamples.length) return capturedSamples.slice(0, 3);
|
|
3035
|
+
const matchedSamples = textMatchSamples(viewport && viewport.body_text_sample || "", check);
|
|
3036
|
+
if (matchedSamples.length) return matchedSamples.slice(0, 3);
|
|
3037
|
+
const fallback = compactTextEvidenceSample(viewport && viewport.body_text_sample || "").slice(0, 240);
|
|
3038
|
+
return fallback ? [fallback] : [];
|
|
3039
|
+
}
|
|
2947
3040
|
function allowedMessageSample(input) {
|
|
2948
3041
|
if (!input || typeof input !== "object" || Array.isArray(input)) return String(input || "");
|
|
2949
3042
|
const parts = [
|
|
@@ -4021,13 +4114,22 @@ function assessProfile(profile, evidence) {
|
|
|
4021
4114
|
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
4022
4115
|
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
4023
4116
|
const expectedVisible = check.type === "text_visible";
|
|
4024
|
-
const
|
|
4117
|
+
const results = checkViewports.map((viewport) => {
|
|
4118
|
+
const matched = viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check);
|
|
4119
|
+
const failedAgainstExpectation = matched !== expectedVisible;
|
|
4120
|
+
return {
|
|
4121
|
+
viewport: viewport.name,
|
|
4122
|
+
matched,
|
|
4123
|
+
samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : [],
|
|
4124
|
+
};
|
|
4125
|
+
});
|
|
4126
|
+
const matches = results.map((result) => result.matched);
|
|
4025
4127
|
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
4026
4128
|
checks.push({
|
|
4027
4129
|
type: check.type,
|
|
4028
4130
|
label: check.label || check.type,
|
|
4029
4131
|
status: failed ? "failed" : "passed",
|
|
4030
|
-
evidence: { text: check.text, pattern: check.pattern, matches },
|
|
4132
|
+
evidence: { text: check.text, pattern: check.pattern, matches, viewports: results },
|
|
4031
4133
|
message: failed ? "Text assertion failed in " + failed + " viewport(s)." : undefined,
|
|
4032
4134
|
});
|
|
4033
4135
|
continue;
|
|
@@ -4347,6 +4449,36 @@ function textMatches(sample, check) {
|
|
|
4347
4449
|
}
|
|
4348
4450
|
return String(sample || "").includes(check.text || "");
|
|
4349
4451
|
}
|
|
4452
|
+
function compactTextEvidenceSample(value) {
|
|
4453
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
4454
|
+
}
|
|
4455
|
+
function textSampleAroundMatch(sample, index, length) {
|
|
4456
|
+
if (index < 0) return undefined;
|
|
4457
|
+
const source = String(sample || "");
|
|
4458
|
+
const context = 120;
|
|
4459
|
+
const start = Math.max(0, index - context);
|
|
4460
|
+
const end = Math.min(source.length, index + Math.max(length, 1) + context);
|
|
4461
|
+
const prefix = start > 0 ? "..." : "";
|
|
4462
|
+
const suffix = end < source.length ? "..." : "";
|
|
4463
|
+
const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
|
|
4464
|
+
return compacted ? compacted.slice(0, 240) : undefined;
|
|
4465
|
+
}
|
|
4466
|
+
function textMatchSamples(sample, check) {
|
|
4467
|
+
const source = String(sample || "");
|
|
4468
|
+
if (!source) return [];
|
|
4469
|
+
if (check.pattern) {
|
|
4470
|
+
try {
|
|
4471
|
+
const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
|
|
4472
|
+
const match = new RegExp(check.pattern, flags).exec(source);
|
|
4473
|
+
const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
|
|
4474
|
+
return sampleText ? [sampleText] : [];
|
|
4475
|
+
} catch { return []; }
|
|
4476
|
+
}
|
|
4477
|
+
const text = check.text || "";
|
|
4478
|
+
if (!text) return [];
|
|
4479
|
+
const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
|
|
4480
|
+
return sampleText ? [sampleText] : [];
|
|
4481
|
+
}
|
|
4350
4482
|
function profileCheckAppliesToViewport(check, viewport) {
|
|
4351
4483
|
if (!Array.isArray(check.viewports) || !check.viewports.length) return true;
|
|
4352
4484
|
return Boolean(viewport && viewport.name && check.viewports.includes(viewport.name));
|
|
@@ -6298,6 +6430,7 @@ async function captureViewport(viewport) {
|
|
|
6298
6430
|
const frames = {};
|
|
6299
6431
|
const text_sequences = {};
|
|
6300
6432
|
const text_matches = {};
|
|
6433
|
+
const text_match_samples = {};
|
|
6301
6434
|
const http_statuses = {};
|
|
6302
6435
|
const link_statuses = {};
|
|
6303
6436
|
for (const check of profile.checks || []) {
|
|
@@ -6319,7 +6452,10 @@ async function captureViewport(viewport) {
|
|
|
6319
6452
|
text_sequences[check.selector] = await selectorTextSequence(check.selector);
|
|
6320
6453
|
}
|
|
6321
6454
|
if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
|
|
6322
|
-
|
|
6455
|
+
const key = textKey(check);
|
|
6456
|
+
const sample = dom.body_text || dom.body_text_sample || "";
|
|
6457
|
+
text_matches[key] = textMatches(sample, check);
|
|
6458
|
+
text_match_samples[key] = textMatchSamples(sample, check);
|
|
6323
6459
|
}
|
|
6324
6460
|
if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
|
|
6325
6461
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
@@ -6396,6 +6532,7 @@ async function captureViewport(viewport) {
|
|
|
6396
6532
|
frames,
|
|
6397
6533
|
text_sequences,
|
|
6398
6534
|
text_matches,
|
|
6535
|
+
text_match_samples,
|
|
6399
6536
|
http_statuses,
|
|
6400
6537
|
link_statuses,
|
|
6401
6538
|
route_inventory: routeInventory,
|
package/dist/profile.d.cts
CHANGED
|
@@ -307,6 +307,7 @@ interface RiddleProofProfileViewportEvidence {
|
|
|
307
307
|
frames?: Record<string, Record<string, JsonValue>>;
|
|
308
308
|
text_sequences?: Record<string, Record<string, JsonValue>>;
|
|
309
309
|
text_matches?: Record<string, boolean>;
|
|
310
|
+
text_match_samples?: Record<string, string[]>;
|
|
310
311
|
http_statuses?: Record<string, Record<string, JsonValue>>;
|
|
311
312
|
link_statuses?: Record<string, Record<string, JsonValue>>;
|
|
312
313
|
route_inventory?: Record<string, JsonValue>;
|
package/dist/profile.d.ts
CHANGED
|
@@ -307,6 +307,7 @@ interface RiddleProofProfileViewportEvidence {
|
|
|
307
307
|
frames?: Record<string, Record<string, JsonValue>>;
|
|
308
308
|
text_sequences?: Record<string, Record<string, JsonValue>>;
|
|
309
309
|
text_matches?: Record<string, boolean>;
|
|
310
|
+
text_match_samples?: Record<string, string[]>;
|
|
310
311
|
http_statuses?: Record<string, Record<string, JsonValue>>;
|
|
311
312
|
link_statuses?: Record<string, Record<string, JsonValue>>;
|
|
312
313
|
route_inventory?: Record<string, JsonValue>;
|
package/dist/profile.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
resolveRiddleProofProfileTimeoutSec,
|
|
24
24
|
slugifyRiddleProofProfileName,
|
|
25
25
|
summarizeRiddleProofProfileResult
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-TDK6WFH3.js";
|
|
27
27
|
export {
|
|
28
28
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
29
29
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|