@riddledc/riddle-proof 0.7.129 → 0.7.131

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.
@@ -607,6 +607,17 @@ function normalizeSetupActionCoordinateMode(value, index) {
607
607
  if (normalized === "ratio" || normalized === "relative" || normalized === "fraction") return "ratio";
608
608
  throw new Error(`target.setup_actions[${index}].coordinate_mode ${String(value)} is not supported. Supported coordinate modes: pixels, ratio.`);
609
609
  }
610
+ function normalizeSetupActionPointerType(value, type, index) {
611
+ if (value === void 0 || value === null || value === "") return void 0;
612
+ if (type !== "drag") {
613
+ throw new Error(`target.setup_actions[${index}].pointer_type is only supported for drag actions.`);
614
+ }
615
+ const normalized = String(value).trim().replace(/-/g, "_").toLowerCase();
616
+ if (normalized === "mouse") return "mouse";
617
+ if (normalized === "touch" || normalized === "finger") return "touch";
618
+ if (normalized === "pen" || normalized === "stylus") return "pen";
619
+ throw new Error(`target.setup_actions[${index}].pointer_type ${String(value)} is not supported. Supported pointer types: mouse, touch, pen.`);
620
+ }
610
621
  function normalizeSetupAction(input, index) {
611
622
  if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
612
623
  const type = normalizeSetupActionType(stringValue(input.type), index);
@@ -625,6 +636,7 @@ function normalizeSetupAction(input, index) {
625
636
  const toX = numberValue(valueFromOwn(input, "to_x", "toX", "end_x", "endX", "x2"));
626
637
  const toY = numberValue(valueFromOwn(input, "to_y", "toY", "end_y", "endY", "y2"));
627
638
  const coordinateMode = normalizeSetupActionCoordinateMode(valueFromOwn(input, "coordinate_mode", "coordinateMode", "coords", "units"), index);
639
+ const pointerType = normalizeSetupActionPointerType(valueFromOwn(input, "pointer_type", "pointerType", "input_type", "inputType"), type, index);
628
640
  if (type === "drag") {
629
641
  if (fromX === void 0 || fromY === void 0 || toX === void 0 || toY === void 0) {
630
642
  throw new Error(`target.setup_actions[${index}] drag requires from_x, from_y, to_x, and to_y.`);
@@ -709,6 +721,7 @@ function normalizeSetupAction(input, index) {
709
721
  force: type === "click" && (input.force === true || input.force_click === true || input.forceClick === true),
710
722
  click_count: normalizeSetupActionClickCount(input, type, index),
711
723
  coordinate_mode: coordinateMode,
724
+ pointer_type: pointerType,
712
725
  from_x: fromX,
713
726
  from_y: fromY,
714
727
  to_x: toX,
@@ -1887,6 +1900,27 @@ function textMatchSamples(sample, check) {
1887
1900
  const sampleText = textSampleAroundMatch(source, index, text.length);
1888
1901
  return sampleText ? [sampleText] : [];
1889
1902
  }
1903
+ function textCaseInsensitiveSamples(sample, check) {
1904
+ const source = String(sample || "");
1905
+ const text = check.pattern ? "" : check.text || "";
1906
+ if (!source || !text) return [];
1907
+ const index = source.toLowerCase().indexOf(text.toLowerCase());
1908
+ const sampleText = textSampleAroundMatch(source, index, text.length);
1909
+ return sampleText ? [sampleText] : [];
1910
+ }
1911
+ function textCaseInsensitiveSequenceSamples(texts, check) {
1912
+ const text = check.pattern ? "" : compactTextEvidenceSample(check.text || "");
1913
+ if (!text) return [];
1914
+ const expected = text.toLowerCase();
1915
+ return texts.map((candidate) => compactTextEvidenceSample(candidate)).filter((candidate) => candidate && candidate.toLowerCase().includes(expected)).slice(0, 3).map((candidate) => candidate.slice(0, 240));
1916
+ }
1917
+ function textCaseInsensitiveFailureSamples(viewport, check) {
1918
+ const key = textKey(check);
1919
+ const captured = viewport.text_case_insensitive_samples?.[key] || [];
1920
+ const capturedSamples = captured.map((sample) => compactTextEvidenceSample(sample).slice(0, 240)).filter(Boolean);
1921
+ if (capturedSamples.length) return capturedSamples.slice(0, 3);
1922
+ return textCaseInsensitiveSamples(viewport.body_text_sample || "", check).slice(0, 3);
1923
+ }
1890
1924
  function textCheckFailureSamples(viewport, check) {
1891
1925
  const key = textKey(check);
1892
1926
  const captured = viewport.text_match_samples?.[key] || [];
@@ -2199,13 +2233,15 @@ function assessCheckFromEvidence(check, evidence) {
2199
2233
  const matched = matches.length > 0;
2200
2234
  const failedAgainstExpectation = matched !== expectedVisible;
2201
2235
  const sampleTexts = matches.length ? matches : failedAgainstExpectation ? texts : [];
2236
+ const caseInsensitiveSamples = failedAgainstExpectation && expectedVisible && !matched ? textCaseInsensitiveSequenceSamples(texts, check) : [];
2202
2237
  return {
2203
2238
  viewport: viewport.name,
2204
2239
  selector_count: viewport.selectors?.[key]?.count || 0,
2205
2240
  visible_count: viewport.selectors?.[key]?.visible_count || 0,
2206
2241
  matched_count: matches.length,
2207
2242
  matched,
2208
- samples: sampleTexts.slice(0, 3).map((text) => text.slice(0, 240))
2243
+ samples: sampleTexts.slice(0, 3).map((text) => text.slice(0, 240)),
2244
+ case_insensitive_samples: caseInsensitiveSamples
2209
2245
  };
2210
2246
  });
2211
2247
  const failed = results.filter((result) => result.matched !== expectedVisible).length;
@@ -2348,7 +2384,8 @@ function assessCheckFromEvidence(check, evidence) {
2348
2384
  return {
2349
2385
  viewport: viewport.name,
2350
2386
  matched,
2351
- samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : []
2387
+ samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : [],
2388
+ case_insensitive_samples: failedAgainstExpectation && expectedVisible && !matched ? textCaseInsensitiveFailureSamples(viewport, check) : []
2352
2389
  };
2353
2390
  });
2354
2391
  const matches = results.map((result) => result.matched);
@@ -2978,6 +3015,32 @@ function textMatchSamples(sample, check) {
2978
3015
  const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
2979
3016
  return sampleText ? [sampleText] : [];
2980
3017
  }
3018
+ function textCaseInsensitiveSamples(sample, check) {
3019
+ const source = String(sample || "");
3020
+ const text = check.pattern ? "" : check.text || "";
3021
+ if (!source || !text) return [];
3022
+ const sampleText = textSampleAroundMatch(source, source.toLowerCase().indexOf(text.toLowerCase()), text.length);
3023
+ return sampleText ? [sampleText] : [];
3024
+ }
3025
+ function textCaseInsensitiveSequenceSamples(texts, check) {
3026
+ const text = check.pattern ? "" : compactTextEvidenceSample(check.text || "");
3027
+ if (!text) return [];
3028
+ const expected = text.toLowerCase();
3029
+ return (texts || [])
3030
+ .map((candidate) => compactTextEvidenceSample(candidate))
3031
+ .filter((candidate) => candidate && candidate.toLowerCase().includes(expected))
3032
+ .slice(0, 3)
3033
+ .map((candidate) => candidate.slice(0, 240));
3034
+ }
3035
+ function textCaseInsensitiveFailureSamples(viewport, check) {
3036
+ const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
3037
+ const captured = viewport && viewport.text_case_insensitive_samples && Array.isArray(viewport.text_case_insensitive_samples[key]) ? viewport.text_case_insensitive_samples[key] : [];
3038
+ const capturedSamples = captured
3039
+ .map((sample) => compactTextEvidenceSample(sample).slice(0, 240))
3040
+ .filter(Boolean);
3041
+ if (capturedSamples.length) return capturedSamples.slice(0, 3);
3042
+ return textCaseInsensitiveSamples(viewport && viewport.body_text_sample || "", check).slice(0, 3);
3043
+ }
2981
3044
  function textCheckFailureSamples(viewport, check) {
2982
3045
  const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
2983
3046
  const captured = viewport && viewport.text_match_samples && Array.isArray(viewport.text_match_samples[key]) ? viewport.text_match_samples[key] : [];
@@ -3936,6 +3999,9 @@ function assessProfile(profile, evidence) {
3936
3999
  const matched = matches.length > 0;
3937
4000
  const failedAgainstExpectation = matched !== expectedVisible;
3938
4001
  const sampleTexts = matches.length ? matches : failedAgainstExpectation ? texts : [];
4002
+ const caseInsensitiveSamples = failedAgainstExpectation && expectedVisible && !matched
4003
+ ? textCaseInsensitiveSequenceSamples(texts, check)
4004
+ : [];
3939
4005
  return {
3940
4006
  viewport: viewport.name,
3941
4007
  selector_count: viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0,
@@ -3943,6 +4009,7 @@ function assessProfile(profile, evidence) {
3943
4009
  matched_count: matches.length,
3944
4010
  matched,
3945
4011
  samples: sampleTexts.slice(0, 3).map((text) => text.slice(0, 240)),
4012
+ case_insensitive_samples: caseInsensitiveSamples,
3946
4013
  };
3947
4014
  });
3948
4015
  const failed = results.filter((result) => result.matched !== expectedVisible).length;
@@ -4074,6 +4141,9 @@ function assessProfile(profile, evidence) {
4074
4141
  viewport: viewport.name,
4075
4142
  matched,
4076
4143
  samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : [],
4144
+ case_insensitive_samples: failedAgainstExpectation && expectedVisible && !matched
4145
+ ? textCaseInsensitiveFailureSamples(viewport, check)
4146
+ : [],
4077
4147
  };
4078
4148
  });
4079
4149
  const matches = results.map((result) => result.matched);
@@ -4463,6 +4533,14 @@ function setupTextMatches(sample, action) {
4463
4533
  return rawSample.includes(expected)
4464
4534
  || normalizeSetupMatchText(rawSample).includes(normalizeSetupMatchText(expected));
4465
4535
  }
4536
+ function setupCaseInsensitiveTextSample(sample, action) {
4537
+ if (!action || action.pattern || !action.text) return "";
4538
+ const normalizedSample = normalizeSetupMatchText(sample);
4539
+ const normalizedExpected = normalizeSetupMatchText(action.text);
4540
+ if (!normalizedSample || !normalizedExpected) return "";
4541
+ if (!normalizedSample.toLowerCase().includes(normalizedExpected.toLowerCase())) return "";
4542
+ return compactSetupResultText(normalizedSample);
4543
+ }
4466
4544
  async function waitForAnyVisibleSelector(context, selector, timeout) {
4467
4545
  const deadline = Date.now() + setupNumber(timeout, 15000);
4468
4546
  let lastReason = "selector_not_found";
@@ -4909,23 +4987,75 @@ async function executeSetupAction(action, ordinal, viewport) {
4909
4987
  const requestedSteps = setupNumber(action.steps, 8);
4910
4988
  const steps = Math.min(100, Math.max(1, Math.floor(requestedSteps || 8)));
4911
4989
  const durationMs = setupNumber(action.duration_ms ?? action.durationMs, 0);
4912
- await page.mouse.move(start.x, start.y);
4913
- await page.mouse.down();
4914
- try {
4915
- if (durationMs && steps > 1) {
4916
- for (let step = 1; step <= steps; step += 1) {
4917
- const progress = step / steps;
4918
- await page.mouse.move(
4919
- start.x + (end.x - start.x) * progress,
4920
- start.y + (end.y - start.y) * progress,
4921
- );
4922
- await page.waitForTimeout(durationMs / steps);
4990
+ const pointerType = String(action.pointer_type || action.pointerType || "mouse").trim().toLowerCase();
4991
+ if (pointerType === "touch" || pointerType === "pen") {
4992
+ const localStart = {
4993
+ x: coordinate(fromX, box.width),
4994
+ y: coordinate(fromY, box.height),
4995
+ };
4996
+ const localEnd = {
4997
+ x: coordinate(toX, box.width),
4998
+ y: coordinate(toY, box.height),
4999
+ };
5000
+ await target.evaluate(async (element, payload) => {
5001
+ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5002
+ const rect = element.getBoundingClientRect();
5003
+ const pointerId = payload.pointerType === "touch" ? 11 : 12;
5004
+ const point = (progress) => ({
5005
+ clientX: rect.left + payload.start.x + (payload.end.x - payload.start.x) * progress,
5006
+ clientY: rect.top + payload.start.y + (payload.end.y - payload.start.y) * progress,
5007
+ });
5008
+ const dispatch = (type, progress) => {
5009
+ const coords = point(progress);
5010
+ element.dispatchEvent(new PointerEvent(type, {
5011
+ bubbles: true,
5012
+ cancelable: true,
5013
+ composed: true,
5014
+ pointerId,
5015
+ pointerType: payload.pointerType,
5016
+ isPrimary: true,
5017
+ buttons: type === "pointerup" ? 0 : 1,
5018
+ button: type === "pointerup" ? 0 : 0,
5019
+ clientX: coords.clientX,
5020
+ clientY: coords.clientY,
5021
+ }));
5022
+ };
5023
+ dispatch("pointerover", 0);
5024
+ dispatch("pointerenter", 0);
5025
+ dispatch("pointerdown", 0);
5026
+ for (let step = 1; step <= payload.steps; step += 1) {
5027
+ dispatch("pointermove", step / payload.steps);
5028
+ if (payload.durationMs && payload.steps > 1) await wait(payload.durationMs / payload.steps);
4923
5029
  }
4924
- } else {
4925
- await page.mouse.move(end.x, end.y, { steps });
5030
+ dispatch("pointerup", 1);
5031
+ dispatch("pointerout", 1);
5032
+ dispatch("pointerleave", 1);
5033
+ }, {
5034
+ pointerType,
5035
+ start: localStart,
5036
+ end: localEnd,
5037
+ steps,
5038
+ durationMs,
5039
+ });
5040
+ } else {
5041
+ await page.mouse.move(start.x, start.y);
5042
+ await page.mouse.down();
5043
+ try {
5044
+ if (durationMs && steps > 1) {
5045
+ for (let step = 1; step <= steps; step += 1) {
5046
+ const progress = step / steps;
5047
+ await page.mouse.move(
5048
+ start.x + (end.x - start.x) * progress,
5049
+ start.y + (end.y - start.y) * progress,
5050
+ );
5051
+ await page.waitForTimeout(durationMs / steps);
5052
+ }
5053
+ } else {
5054
+ await page.mouse.move(end.x, end.y, { steps });
5055
+ }
5056
+ } finally {
5057
+ await page.mouse.up().catch(() => {});
4926
5058
  }
4927
- } finally {
4928
- await page.mouse.up().catch(() => {});
4929
5059
  }
4930
5060
  return {
4931
5061
  ...base,
@@ -4938,6 +5068,7 @@ async function executeSetupAction(action, ordinal, viewport) {
4938
5068
  from_y: fromY,
4939
5069
  to_x: toX,
4940
5070
  to_y: toY,
5071
+ pointer_type: pointerType,
4941
5072
  steps,
4942
5073
  duration_ms: durationMs || undefined,
4943
5074
  };
@@ -5160,6 +5291,7 @@ async function executeSetupAction(action, ordinal, viewport) {
5160
5291
  if (!count) return { ...base, reason: "selector_not_found", count };
5161
5292
  let targetIndex = Number.isInteger(action.index) ? action.index : 0;
5162
5293
  let matchedText = null;
5294
+ let caseInsensitiveText = null;
5163
5295
  let hiddenMatchIndex = -1;
5164
5296
  let hiddenMatchedText = null;
5165
5297
  if (action.text || action.pattern) {
@@ -5177,10 +5309,12 @@ async function executeSetupAction(action, ordinal, viewport) {
5177
5309
  hiddenMatchIndex = index;
5178
5310
  hiddenMatchedText = compactSetupResultText(text);
5179
5311
  }
5312
+ } else if (!caseInsensitiveText) {
5313
+ caseInsensitiveText = setupCaseInsensitiveTextSample(text, action) || null;
5180
5314
  }
5181
5315
  }
5182
5316
  if (targetIndex < 0 && hiddenMatchIndex >= 0) return { ...base, reason: "matching_element_not_visible", count, target_index: hiddenMatchIndex, text: hiddenMatchedText };
5183
- if (targetIndex < 0) return { ...base, reason: "text_not_found", count };
5317
+ if (targetIndex < 0) return { ...base, reason: "text_not_found", count, case_insensitive_text: caseInsensitiveText || undefined };
5184
5318
  }
5185
5319
  if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex };
5186
5320
  const clickOptions = action.force === true
@@ -5224,6 +5358,7 @@ async function executeSetupAction(action, ordinal, viewport) {
5224
5358
  const locator = scope.context.locator(action.selector);
5225
5359
  const startedAt = Date.now();
5226
5360
  let lastText = "";
5361
+ let caseInsensitiveText = "";
5227
5362
  while (Date.now() - startedAt <= timeout) {
5228
5363
  const count = await locator.count().catch(() => 0);
5229
5364
  for (let index = 0; index < count; index += 1) {
@@ -5232,10 +5367,11 @@ async function executeSetupAction(action, ordinal, viewport) {
5232
5367
  if (setupTextMatches(text, action)) {
5233
5368
  return { ...base, ...setupScopeEvidence(scope), ok: true, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
5234
5369
  }
5370
+ caseInsensitiveText = caseInsensitiveText || setupCaseInsensitiveTextSample(text, action);
5235
5371
  }
5236
5372
  await page.waitForTimeout(100);
5237
5373
  }
5238
- return { ...base, ...setupScopeEvidence(scope), reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
5374
+ return { ...base, ...setupScopeEvidence(scope), reason: "text_not_found", text: compactSetupResultText(lastText), case_insensitive_text: caseInsensitiveText || undefined, timeout_ms: timeout };
5239
5375
  }
5240
5376
  if (type === "assert_text_visible" || type === "assert_text_absent") {
5241
5377
  const scope = await setupActionScope(action, timeout);
@@ -5244,6 +5380,7 @@ async function executeSetupAction(action, ordinal, viewport) {
5244
5380
  const startedAt = Date.now();
5245
5381
  let lastText = "";
5246
5382
  let matchedText = "";
5383
+ let caseInsensitiveText = "";
5247
5384
  let hiddenMatch = false;
5248
5385
  while (Date.now() - startedAt <= timeout) {
5249
5386
  const count = await locator.count().catch(() => 0);
@@ -5266,6 +5403,7 @@ async function executeSetupAction(action, ordinal, viewport) {
5266
5403
  }
5267
5404
  break;
5268
5405
  }
5406
+ caseInsensitiveText = caseInsensitiveText || setupCaseInsensitiveTextSample(text, action);
5269
5407
  }
5270
5408
  if (type === "assert_text_absent" && !matched) {
5271
5409
  return { ...base, ...setupScopeEvidence(scope), ok: true, count, timeout_ms: timeout };
@@ -5276,7 +5414,7 @@ async function executeSetupAction(action, ordinal, viewport) {
5276
5414
  if (hiddenMatch) {
5277
5415
  return { ...base, ...setupScopeEvidence(scope), reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
5278
5416
  }
5279
- return { ...base, ...setupScopeEvidence(scope), reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
5417
+ return { ...base, ...setupScopeEvidence(scope), reason: "text_not_found", text: compactSetupResultText(lastText), case_insensitive_text: caseInsensitiveText || undefined, timeout_ms: timeout };
5280
5418
  }
5281
5419
  return { ...base, ...setupScopeEvidence(scope), reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
5282
5420
  }
@@ -6384,6 +6522,7 @@ async function captureViewport(viewport) {
6384
6522
  const text_sequences = {};
6385
6523
  const text_matches = {};
6386
6524
  const text_match_samples = {};
6525
+ const text_case_insensitive_samples = {};
6387
6526
  const http_statuses = {};
6388
6527
  const link_statuses = {};
6389
6528
  for (const check of profile.checks || []) {
@@ -6409,6 +6548,7 @@ async function captureViewport(viewport) {
6409
6548
  const sample = dom.body_text || dom.body_text_sample || "";
6410
6549
  text_matches[key] = textMatches(sample, check);
6411
6550
  text_match_samples[key] = textMatchSamples(sample, check);
6551
+ text_case_insensitive_samples[key] = textCaseInsensitiveSamples(sample, check);
6412
6552
  }
6413
6553
  if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
6414
6554
  selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
@@ -6486,6 +6626,7 @@ async function captureViewport(viewport) {
6486
6626
  text_sequences,
6487
6627
  text_matches,
6488
6628
  text_match_samples,
6629
+ text_case_insensitive_samples,
6489
6630
  http_statuses,
6490
6631
  link_statuses,
6491
6632
  route_inventory: routeInventory,