@riddledc/riddle-proof 0.7.46 → 0.7.48

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/index.cjs CHANGED
@@ -8746,6 +8746,9 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
8746
8746
  "click",
8747
8747
  "fill",
8748
8748
  "set_input_value",
8749
+ "assert_text_visible",
8750
+ "assert_text_absent",
8751
+ "assert_selector_count",
8749
8752
  "local_storage",
8750
8753
  "session_storage",
8751
8754
  "clear_storage",
@@ -8919,12 +8922,19 @@ function normalizeSetupAction(input, index) {
8919
8922
  if (!isRecord2(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
8920
8923
  const type = normalizeSetupActionType(stringValue5(input.type), index);
8921
8924
  const selector = stringValue5(input.selector);
8922
- if ((type === "click" || type === "fill" || type === "set_input_value" || type === "wait_for_selector" || type === "wait_for_text") && !selector) {
8925
+ if ((type === "click" || type === "fill" || type === "set_input_value" || type === "wait_for_selector" || type === "wait_for_text" || type === "assert_text_visible" || type === "assert_text_absent" || type === "assert_selector_count") && !selector) {
8923
8926
  throw new Error(`target.setup_actions[${index}] ${type} requires selector.`);
8924
8927
  }
8925
8928
  if (type === "wait_for_text" && !stringValue5(input.text) && !stringValue5(input.pattern)) {
8926
8929
  throw new Error(`target.setup_actions[${index}] wait_for_text requires text or pattern.`);
8927
8930
  }
8931
+ if ((type === "assert_text_visible" || type === "assert_text_absent") && !stringValue5(input.text) && !stringValue5(input.pattern)) {
8932
+ throw new Error(`target.setup_actions[${index}] ${type} requires text or pattern.`);
8933
+ }
8934
+ const expectedCount = numberValue3(input.expected_count ?? input.expectedCount ?? input.count);
8935
+ if (type === "assert_selector_count" && (expectedCount === void 0 || !Number.isInteger(expectedCount) || expectedCount < 0)) {
8936
+ throw new Error(`target.setup_actions[${index}] ${type} requires non-negative integer expected_count.`);
8937
+ }
8928
8938
  const value = stringFromOwn(input, "value", "input_value", "inputValue");
8929
8939
  const hasJsonValue = hasOwn(input, "value_json") || hasOwn(input, "valueJson") || hasOwn(input, "json");
8930
8940
  if ((type === "fill" || type === "set_input_value") && value === void 0 && !hasJsonValue) {
@@ -8947,6 +8957,7 @@ function normalizeSetupAction(input, index) {
8947
8957
  pattern: stringValue5(input.pattern),
8948
8958
  flags: stringValue5(input.flags),
8949
8959
  index: numberValue3(input.index),
8960
+ expected_count: expectedCount,
8950
8961
  ms: numberValue3(input.ms) ?? numberValue3(input.wait_ms) ?? numberValue3(input.waitMs),
8951
8962
  timeout_ms: numberValue3(input.timeout_ms) ?? numberValue3(input.timeoutMs),
8952
8963
  after_ms: numberValue3(input.after_ms) ?? numberValue3(input.afterMs),
@@ -8967,24 +8978,7 @@ function normalizeNetworkMock(input, index) {
8967
8978
  const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
8968
8979
  const responsesInput = input.responses ?? input.sequence;
8969
8980
  const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
8970
- const requestBodyContains = normalizeStringList(
8971
- input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
8972
- `target.network_mocks[${index}].request_body_contains`
8973
- );
8974
- const requestBodyPatterns = normalizeStringList(
8975
- input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
8976
- `target.network_mocks[${index}].request_body_patterns`
8977
- );
8978
- validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
8979
- const requestBodyNotContains = normalizeStringList(
8980
- input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
8981
- `target.network_mocks[${index}].request_body_not_contains`
8982
- );
8983
- const requestBodyNotPatterns = normalizeStringList(
8984
- input.request_body_not_patterns ?? input.requestBodyNotPatterns ?? input.request_body_forbidden_patterns ?? input.requestBodyForbiddenPatterns ?? input.body_not_patterns ?? input.bodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
8985
- `target.network_mocks[${index}].request_body_not_patterns`
8986
- );
8987
- validateRegexPatterns(requestBodyNotPatterns, `target.network_mocks[${index}].request_body_not_patterns`);
8981
+ const requestBody = normalizeNetworkMockRequestBodyConstraints(input, `target.network_mocks[${index}]`);
8988
8982
  const requiredHitCount = numberValue3(
8989
8983
  input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
8990
8984
  );
@@ -9000,6 +8994,33 @@ function normalizeNetworkMock(input, index) {
9000
8994
  repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
9001
8995
  required_hit_count: requiredHitCount,
9002
8996
  required: input.required === false ? false : true,
8997
+ capture_request_body: requestBody.capture_request_body,
8998
+ request_body_contains: requestBody.request_body_contains,
8999
+ request_body_patterns: requestBody.request_body_patterns,
9000
+ request_body_not_contains: requestBody.request_body_not_contains,
9001
+ request_body_not_patterns: requestBody.request_body_not_patterns
9002
+ };
9003
+ }
9004
+ function normalizeNetworkMockRequestBodyConstraints(input, label) {
9005
+ const requestBodyContains = normalizeStringList(
9006
+ input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
9007
+ `${label}.request_body_contains`
9008
+ );
9009
+ const requestBodyPatterns = normalizeStringList(
9010
+ input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
9011
+ `${label}.request_body_patterns`
9012
+ );
9013
+ validateRegexPatterns(requestBodyPatterns, `${label}.request_body_patterns`);
9014
+ const requestBodyNotContains = normalizeStringList(
9015
+ input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
9016
+ `${label}.request_body_not_contains`
9017
+ );
9018
+ const requestBodyNotPatterns = normalizeStringList(
9019
+ input.request_body_not_patterns ?? input.requestBodyNotPatterns ?? input.request_body_forbidden_patterns ?? input.requestBodyForbiddenPatterns ?? input.body_not_patterns ?? input.bodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
9020
+ `${label}.request_body_not_patterns`
9021
+ );
9022
+ validateRegexPatterns(requestBodyNotPatterns, `${label}.request_body_not_patterns`);
9023
+ return {
9003
9024
  capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length) || Boolean(requestBodyNotContains?.length) || Boolean(requestBodyNotPatterns?.length),
9004
9025
  request_body_contains: requestBodyContains,
9005
9026
  request_body_patterns: requestBodyPatterns,
@@ -9014,20 +9035,33 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
9014
9035
  }
9015
9036
  const body = stringValue5(input.body) ?? stringValue5(input.body_text) ?? stringValue5(input.bodyText) ?? defaults.body;
9016
9037
  const hasJsonBody = Object.prototype.hasOwnProperty.call(input, "body_json") || Object.prototype.hasOwnProperty.call(input, "bodyJson") || Object.prototype.hasOwnProperty.call(input, "json");
9038
+ const requestBody = normalizeNetworkMockRequestBodyConstraints(input, label);
9017
9039
  return {
9018
9040
  label: stringValue5(input.label) || stringValue5(input.name) || defaults.label,
9019
9041
  status,
9020
9042
  content_type: stringValue5(input.content_type) || stringValue5(input.contentType) || defaults.content_type,
9021
9043
  headers: stringRecord(input.headers) || defaults.headers,
9022
9044
  body,
9023
- body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json
9045
+ body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json,
9046
+ capture_request_body: requestBody.capture_request_body,
9047
+ request_body_contains: requestBody.request_body_contains,
9048
+ request_body_patterns: requestBody.request_body_patterns,
9049
+ request_body_not_contains: requestBody.request_body_not_contains,
9050
+ request_body_not_patterns: requestBody.request_body_not_patterns
9024
9051
  };
9025
9052
  }
9026
9053
  function normalizeNetworkMockResponses(value, mockIndex, defaults) {
9027
9054
  if (value === void 0) return void 0;
9028
9055
  if (!Array.isArray(value)) throw new Error(`target.network_mocks[${mockIndex}].responses must be an array.`);
9029
9056
  if (!value.length) throw new Error(`target.network_mocks[${mockIndex}].responses must not be empty.`);
9030
- const responseDefaults = { ...defaults, label: void 0 };
9057
+ const responseDefaults = {
9058
+ label: void 0,
9059
+ status: defaults.status,
9060
+ content_type: defaults.content_type,
9061
+ headers: defaults.headers,
9062
+ body: defaults.body,
9063
+ body_json: defaults.body_json
9064
+ };
9031
9065
  return value.map((response, responseIndex) => {
9032
9066
  if (!isRecord2(response)) {
9033
9067
  throw new Error(`target.network_mocks[${mockIndex}].responses[${responseIndex}] must be an object.`);
@@ -9770,6 +9804,9 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
9770
9804
  if (event.request_body_matches === false) {
9771
9805
  failed.push({
9772
9806
  label: event.label ?? null,
9807
+ response_label: event.response_label ?? null,
9808
+ hit_index: event.hit_index ?? null,
9809
+ response_index: event.response_index ?? null,
9773
9810
  url: event.url ?? null,
9774
9811
  method: event.method ?? null,
9775
9812
  reason: "request_body_mismatch",
@@ -10153,6 +10190,9 @@ function assessProfile(profile, evidence) {
10153
10190
  if (event && event.request_body_matches === false) {
10154
10191
  failed.push({
10155
10192
  label: event.label || null,
10193
+ response_label: event.response_label || null,
10194
+ hit_index: event.hit_index ?? null,
10195
+ response_index: event.response_index ?? null,
10156
10196
  url: event.url || null,
10157
10197
  method: event.method || null,
10158
10198
  reason: "request_body_mismatch",
@@ -10617,20 +10657,21 @@ function compactNetworkMockRequestBody(value) {
10617
10657
  function networkMockStringList(value) {
10618
10658
  return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
10619
10659
  }
10620
- function networkMockShouldCaptureRequestBody(mock) {
10621
- return mock && (
10622
- mock.capture_request_body === true
10623
- || networkMockStringList(mock.request_body_contains).length > 0
10624
- || networkMockStringList(mock.request_body_patterns).length > 0
10625
- || networkMockStringList(mock.request_body_not_contains).length > 0
10626
- || networkMockStringList(mock.request_body_not_patterns).length > 0
10627
- );
10660
+ function networkMockShouldCaptureRequestBody(...sources) {
10661
+ return sources.some((source) => source && (
10662
+ source.capture_request_body === true
10663
+ || networkMockStringList(source.request_body_contains).length > 0
10664
+ || networkMockStringList(source.request_body_patterns).length > 0
10665
+ || networkMockStringList(source.request_body_not_contains).length > 0
10666
+ || networkMockStringList(source.request_body_not_patterns).length > 0
10667
+ ));
10628
10668
  }
10629
- function networkMockRequestBodyFailures(body, mock) {
10669
+ function networkMockRequestBodyFailuresForSource(body, source) {
10630
10670
  const failures = [];
10671
+ if (!source) return failures;
10631
10672
  const rawBody = String(body || "");
10632
10673
  const compactBody = compactNetworkMockRequestBody(rawBody);
10633
- for (const expected of networkMockStringList(mock.request_body_contains)) {
10674
+ for (const expected of networkMockStringList(source.request_body_contains)) {
10634
10675
  if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
10635
10676
  failures.push({
10636
10677
  type: "request_body_missing_text",
@@ -10638,7 +10679,7 @@ function networkMockRequestBodyFailures(body, mock) {
10638
10679
  });
10639
10680
  }
10640
10681
  }
10641
- for (const pattern of networkMockStringList(mock.request_body_patterns)) {
10682
+ for (const pattern of networkMockStringList(source.request_body_patterns)) {
10642
10683
  try {
10643
10684
  const regex = new RegExp(pattern);
10644
10685
  if (!regex.test(rawBody) && !regex.test(compactBody)) {
@@ -10655,7 +10696,7 @@ function networkMockRequestBodyFailures(body, mock) {
10655
10696
  });
10656
10697
  }
10657
10698
  }
10658
- for (const forbidden of networkMockStringList(mock.request_body_not_contains)) {
10699
+ for (const forbidden of networkMockStringList(source.request_body_not_contains)) {
10659
10700
  if (rawBody.includes(forbidden) || compactBody.includes(forbidden)) {
10660
10701
  failures.push({
10661
10702
  type: "request_body_forbidden_text",
@@ -10663,7 +10704,7 @@ function networkMockRequestBodyFailures(body, mock) {
10663
10704
  });
10664
10705
  }
10665
10706
  }
10666
- for (const pattern of networkMockStringList(mock.request_body_not_patterns)) {
10707
+ for (const pattern of networkMockStringList(source.request_body_not_patterns)) {
10667
10708
  try {
10668
10709
  const regex = new RegExp(pattern);
10669
10710
  if (regex.test(rawBody) || regex.test(compactBody)) {
@@ -10682,6 +10723,9 @@ function networkMockRequestBodyFailures(body, mock) {
10682
10723
  }
10683
10724
  return failures;
10684
10725
  }
10726
+ function networkMockRequestBodyFailures(body, ...sources) {
10727
+ return sources.flatMap((source) => networkMockRequestBodyFailuresForSource(body, source));
10728
+ }
10685
10729
  async function setupLocatorVisible(locator, index) {
10686
10730
  return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
10687
10731
  }
@@ -10710,9 +10754,10 @@ async function registerNetworkMocks(mocks) {
10710
10754
  body = JSON.stringify(response.body_json);
10711
10755
  contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
10712
10756
  }
10713
- const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock);
10757
+ const responseBodyContract = responseIndex === null ? null : response;
10758
+ const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock, responseBodyContract);
10714
10759
  const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
10715
- const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
10760
+ const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock, responseBodyContract) : [];
10716
10761
  const status = response.status || mock.status || 200;
10717
10762
  const event = {
10718
10763
  ok: true,
@@ -10830,6 +10875,19 @@ async function executeSetupAction(action, ordinal) {
10830
10875
  await locator.nth(targetIndex).fill(value, { timeout });
10831
10876
  return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
10832
10877
  }
10878
+ if (type === "assert_selector_count") {
10879
+ const locator = page.locator(action.selector);
10880
+ const expectedCount = setupNumber(action.expected_count, -1);
10881
+ if (!Number.isInteger(expectedCount) || expectedCount < 0) return { ...base, reason: "invalid_expected_count", expected_count: action.expected_count };
10882
+ const startedAt = Date.now();
10883
+ let count = 0;
10884
+ while (Date.now() - startedAt <= timeout) {
10885
+ count = await locator.count().catch(() => 0);
10886
+ if (count === expectedCount) return { ...base, ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
10887
+ await page.waitForTimeout(100);
10888
+ }
10889
+ return { ...base, reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
10890
+ }
10833
10891
  if (type === "wait_for_text") {
10834
10892
  const locator = page.locator(action.selector);
10835
10893
  const startedAt = Date.now();
@@ -10847,6 +10905,47 @@ async function executeSetupAction(action, ordinal) {
10847
10905
  }
10848
10906
  return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
10849
10907
  }
10908
+ if (type === "assert_text_visible" || type === "assert_text_absent") {
10909
+ const locator = page.locator(action.selector);
10910
+ const startedAt = Date.now();
10911
+ let lastText = "";
10912
+ let matchedText = "";
10913
+ let hiddenMatch = false;
10914
+ while (Date.now() - startedAt <= timeout) {
10915
+ const count = await locator.count().catch(() => 0);
10916
+ let matched = false;
10917
+ matchedText = "";
10918
+ hiddenMatch = false;
10919
+ for (let index = 0; index < count; index += 1) {
10920
+ const text = await setupLocatorText(locator, index);
10921
+ lastText = text || lastText;
10922
+ if (setupTextMatches(text, action)) {
10923
+ matched = true;
10924
+ matchedText = text;
10925
+ if (type === "assert_text_visible") {
10926
+ const visible = await setupLocatorVisible(locator, index);
10927
+ if (visible) {
10928
+ return { ...base, ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
10929
+ }
10930
+ hiddenMatch = true;
10931
+ break;
10932
+ }
10933
+ break;
10934
+ }
10935
+ }
10936
+ if (type === "assert_text_absent" && !matched) {
10937
+ return { ...base, ok: true, count, timeout_ms: timeout };
10938
+ }
10939
+ await page.waitForTimeout(100);
10940
+ }
10941
+ if (type === "assert_text_visible") {
10942
+ if (hiddenMatch) {
10943
+ return { ...base, reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
10944
+ }
10945
+ return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
10946
+ }
10947
+ return { ...base, reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
10948
+ }
10850
10949
  return { ...base, reason: "unsupported_action" };
10851
10950
  } catch (error) {
10852
10951
  return { ...base, error: String(error && error.message ? error.message : error).slice(0, 1000) };
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  resolveRiddleProofProfileTimeoutSec,
59
59
  slugifyRiddleProofProfileName,
60
60
  summarizeRiddleProofProfileResult
61
- } from "./chunk-WSZ5AVKX.js";
61
+ } from "./chunk-V2KNQTQD.js";
62
62
  import {
63
63
  DEFAULT_RIDDLE_API_BASE_URL,
64
64
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -75,6 +75,9 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
75
75
  "click",
76
76
  "fill",
77
77
  "set_input_value",
78
+ "assert_text_visible",
79
+ "assert_text_absent",
80
+ "assert_selector_count",
78
81
  "local_storage",
79
82
  "session_storage",
80
83
  "clear_storage",
@@ -248,12 +251,19 @@ function normalizeSetupAction(input, index) {
248
251
  if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
249
252
  const type = normalizeSetupActionType(stringValue(input.type), index);
250
253
  const selector = stringValue(input.selector);
251
- if ((type === "click" || type === "fill" || type === "set_input_value" || type === "wait_for_selector" || type === "wait_for_text") && !selector) {
254
+ if ((type === "click" || type === "fill" || type === "set_input_value" || type === "wait_for_selector" || type === "wait_for_text" || type === "assert_text_visible" || type === "assert_text_absent" || type === "assert_selector_count") && !selector) {
252
255
  throw new Error(`target.setup_actions[${index}] ${type} requires selector.`);
253
256
  }
254
257
  if (type === "wait_for_text" && !stringValue(input.text) && !stringValue(input.pattern)) {
255
258
  throw new Error(`target.setup_actions[${index}] wait_for_text requires text or pattern.`);
256
259
  }
260
+ if ((type === "assert_text_visible" || type === "assert_text_absent") && !stringValue(input.text) && !stringValue(input.pattern)) {
261
+ throw new Error(`target.setup_actions[${index}] ${type} requires text or pattern.`);
262
+ }
263
+ const expectedCount = numberValue(input.expected_count ?? input.expectedCount ?? input.count);
264
+ if (type === "assert_selector_count" && (expectedCount === void 0 || !Number.isInteger(expectedCount) || expectedCount < 0)) {
265
+ throw new Error(`target.setup_actions[${index}] ${type} requires non-negative integer expected_count.`);
266
+ }
257
267
  const value = stringFromOwn(input, "value", "input_value", "inputValue");
258
268
  const hasJsonValue = hasOwn(input, "value_json") || hasOwn(input, "valueJson") || hasOwn(input, "json");
259
269
  if ((type === "fill" || type === "set_input_value") && value === void 0 && !hasJsonValue) {
@@ -276,6 +286,7 @@ function normalizeSetupAction(input, index) {
276
286
  pattern: stringValue(input.pattern),
277
287
  flags: stringValue(input.flags),
278
288
  index: numberValue(input.index),
289
+ expected_count: expectedCount,
279
290
  ms: numberValue(input.ms) ?? numberValue(input.wait_ms) ?? numberValue(input.waitMs),
280
291
  timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
281
292
  after_ms: numberValue(input.after_ms) ?? numberValue(input.afterMs),
@@ -296,24 +307,7 @@ function normalizeNetworkMock(input, index) {
296
307
  const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
297
308
  const responsesInput = input.responses ?? input.sequence;
298
309
  const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
299
- const requestBodyContains = normalizeStringList(
300
- input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
301
- `target.network_mocks[${index}].request_body_contains`
302
- );
303
- const requestBodyPatterns = normalizeStringList(
304
- input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
305
- `target.network_mocks[${index}].request_body_patterns`
306
- );
307
- validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
308
- const requestBodyNotContains = normalizeStringList(
309
- input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
310
- `target.network_mocks[${index}].request_body_not_contains`
311
- );
312
- const requestBodyNotPatterns = normalizeStringList(
313
- input.request_body_not_patterns ?? input.requestBodyNotPatterns ?? input.request_body_forbidden_patterns ?? input.requestBodyForbiddenPatterns ?? input.body_not_patterns ?? input.bodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
314
- `target.network_mocks[${index}].request_body_not_patterns`
315
- );
316
- validateRegexPatterns(requestBodyNotPatterns, `target.network_mocks[${index}].request_body_not_patterns`);
310
+ const requestBody = normalizeNetworkMockRequestBodyConstraints(input, `target.network_mocks[${index}]`);
317
311
  const requiredHitCount = numberValue(
318
312
  input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
319
313
  );
@@ -329,6 +323,33 @@ function normalizeNetworkMock(input, index) {
329
323
  repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
330
324
  required_hit_count: requiredHitCount,
331
325
  required: input.required === false ? false : true,
326
+ capture_request_body: requestBody.capture_request_body,
327
+ request_body_contains: requestBody.request_body_contains,
328
+ request_body_patterns: requestBody.request_body_patterns,
329
+ request_body_not_contains: requestBody.request_body_not_contains,
330
+ request_body_not_patterns: requestBody.request_body_not_patterns
331
+ };
332
+ }
333
+ function normalizeNetworkMockRequestBodyConstraints(input, label) {
334
+ const requestBodyContains = normalizeStringList(
335
+ input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
336
+ `${label}.request_body_contains`
337
+ );
338
+ const requestBodyPatterns = normalizeStringList(
339
+ input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
340
+ `${label}.request_body_patterns`
341
+ );
342
+ validateRegexPatterns(requestBodyPatterns, `${label}.request_body_patterns`);
343
+ const requestBodyNotContains = normalizeStringList(
344
+ input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
345
+ `${label}.request_body_not_contains`
346
+ );
347
+ const requestBodyNotPatterns = normalizeStringList(
348
+ input.request_body_not_patterns ?? input.requestBodyNotPatterns ?? input.request_body_forbidden_patterns ?? input.requestBodyForbiddenPatterns ?? input.body_not_patterns ?? input.bodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
349
+ `${label}.request_body_not_patterns`
350
+ );
351
+ validateRegexPatterns(requestBodyNotPatterns, `${label}.request_body_not_patterns`);
352
+ return {
332
353
  capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length) || Boolean(requestBodyNotContains?.length) || Boolean(requestBodyNotPatterns?.length),
333
354
  request_body_contains: requestBodyContains,
334
355
  request_body_patterns: requestBodyPatterns,
@@ -343,20 +364,33 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
343
364
  }
344
365
  const body = stringValue(input.body) ?? stringValue(input.body_text) ?? stringValue(input.bodyText) ?? defaults.body;
345
366
  const hasJsonBody = Object.prototype.hasOwnProperty.call(input, "body_json") || Object.prototype.hasOwnProperty.call(input, "bodyJson") || Object.prototype.hasOwnProperty.call(input, "json");
367
+ const requestBody = normalizeNetworkMockRequestBodyConstraints(input, label);
346
368
  return {
347
369
  label: stringValue(input.label) || stringValue(input.name) || defaults.label,
348
370
  status,
349
371
  content_type: stringValue(input.content_type) || stringValue(input.contentType) || defaults.content_type,
350
372
  headers: stringRecord(input.headers) || defaults.headers,
351
373
  body,
352
- body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json
374
+ body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json,
375
+ capture_request_body: requestBody.capture_request_body,
376
+ request_body_contains: requestBody.request_body_contains,
377
+ request_body_patterns: requestBody.request_body_patterns,
378
+ request_body_not_contains: requestBody.request_body_not_contains,
379
+ request_body_not_patterns: requestBody.request_body_not_patterns
353
380
  };
354
381
  }
355
382
  function normalizeNetworkMockResponses(value, mockIndex, defaults) {
356
383
  if (value === void 0) return void 0;
357
384
  if (!Array.isArray(value)) throw new Error(`target.network_mocks[${mockIndex}].responses must be an array.`);
358
385
  if (!value.length) throw new Error(`target.network_mocks[${mockIndex}].responses must not be empty.`);
359
- const responseDefaults = { ...defaults, label: void 0 };
386
+ const responseDefaults = {
387
+ label: void 0,
388
+ status: defaults.status,
389
+ content_type: defaults.content_type,
390
+ headers: defaults.headers,
391
+ body: defaults.body,
392
+ body_json: defaults.body_json
393
+ };
360
394
  return value.map((response, responseIndex) => {
361
395
  if (!isRecord(response)) {
362
396
  throw new Error(`target.network_mocks[${mockIndex}].responses[${responseIndex}] must be an object.`);
@@ -1099,6 +1133,9 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
1099
1133
  if (event.request_body_matches === false) {
1100
1134
  failed.push({
1101
1135
  label: event.label ?? null,
1136
+ response_label: event.response_label ?? null,
1137
+ hit_index: event.hit_index ?? null,
1138
+ response_index: event.response_index ?? null,
1102
1139
  url: event.url ?? null,
1103
1140
  method: event.method ?? null,
1104
1141
  reason: "request_body_mismatch",
@@ -1482,6 +1519,9 @@ function assessProfile(profile, evidence) {
1482
1519
  if (event && event.request_body_matches === false) {
1483
1520
  failed.push({
1484
1521
  label: event.label || null,
1522
+ response_label: event.response_label || null,
1523
+ hit_index: event.hit_index ?? null,
1524
+ response_index: event.response_index ?? null,
1485
1525
  url: event.url || null,
1486
1526
  method: event.method || null,
1487
1527
  reason: "request_body_mismatch",
@@ -1946,20 +1986,21 @@ function compactNetworkMockRequestBody(value) {
1946
1986
  function networkMockStringList(value) {
1947
1987
  return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
1948
1988
  }
1949
- function networkMockShouldCaptureRequestBody(mock) {
1950
- return mock && (
1951
- mock.capture_request_body === true
1952
- || networkMockStringList(mock.request_body_contains).length > 0
1953
- || networkMockStringList(mock.request_body_patterns).length > 0
1954
- || networkMockStringList(mock.request_body_not_contains).length > 0
1955
- || networkMockStringList(mock.request_body_not_patterns).length > 0
1956
- );
1989
+ function networkMockShouldCaptureRequestBody(...sources) {
1990
+ return sources.some((source) => source && (
1991
+ source.capture_request_body === true
1992
+ || networkMockStringList(source.request_body_contains).length > 0
1993
+ || networkMockStringList(source.request_body_patterns).length > 0
1994
+ || networkMockStringList(source.request_body_not_contains).length > 0
1995
+ || networkMockStringList(source.request_body_not_patterns).length > 0
1996
+ ));
1957
1997
  }
1958
- function networkMockRequestBodyFailures(body, mock) {
1998
+ function networkMockRequestBodyFailuresForSource(body, source) {
1959
1999
  const failures = [];
2000
+ if (!source) return failures;
1960
2001
  const rawBody = String(body || "");
1961
2002
  const compactBody = compactNetworkMockRequestBody(rawBody);
1962
- for (const expected of networkMockStringList(mock.request_body_contains)) {
2003
+ for (const expected of networkMockStringList(source.request_body_contains)) {
1963
2004
  if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
1964
2005
  failures.push({
1965
2006
  type: "request_body_missing_text",
@@ -1967,7 +2008,7 @@ function networkMockRequestBodyFailures(body, mock) {
1967
2008
  });
1968
2009
  }
1969
2010
  }
1970
- for (const pattern of networkMockStringList(mock.request_body_patterns)) {
2011
+ for (const pattern of networkMockStringList(source.request_body_patterns)) {
1971
2012
  try {
1972
2013
  const regex = new RegExp(pattern);
1973
2014
  if (!regex.test(rawBody) && !regex.test(compactBody)) {
@@ -1984,7 +2025,7 @@ function networkMockRequestBodyFailures(body, mock) {
1984
2025
  });
1985
2026
  }
1986
2027
  }
1987
- for (const forbidden of networkMockStringList(mock.request_body_not_contains)) {
2028
+ for (const forbidden of networkMockStringList(source.request_body_not_contains)) {
1988
2029
  if (rawBody.includes(forbidden) || compactBody.includes(forbidden)) {
1989
2030
  failures.push({
1990
2031
  type: "request_body_forbidden_text",
@@ -1992,7 +2033,7 @@ function networkMockRequestBodyFailures(body, mock) {
1992
2033
  });
1993
2034
  }
1994
2035
  }
1995
- for (const pattern of networkMockStringList(mock.request_body_not_patterns)) {
2036
+ for (const pattern of networkMockStringList(source.request_body_not_patterns)) {
1996
2037
  try {
1997
2038
  const regex = new RegExp(pattern);
1998
2039
  if (regex.test(rawBody) || regex.test(compactBody)) {
@@ -2011,6 +2052,9 @@ function networkMockRequestBodyFailures(body, mock) {
2011
2052
  }
2012
2053
  return failures;
2013
2054
  }
2055
+ function networkMockRequestBodyFailures(body, ...sources) {
2056
+ return sources.flatMap((source) => networkMockRequestBodyFailuresForSource(body, source));
2057
+ }
2014
2058
  async function setupLocatorVisible(locator, index) {
2015
2059
  return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
2016
2060
  }
@@ -2039,9 +2083,10 @@ async function registerNetworkMocks(mocks) {
2039
2083
  body = JSON.stringify(response.body_json);
2040
2084
  contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
2041
2085
  }
2042
- const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock);
2086
+ const responseBodyContract = responseIndex === null ? null : response;
2087
+ const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock, responseBodyContract);
2043
2088
  const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
2044
- const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
2089
+ const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock, responseBodyContract) : [];
2045
2090
  const status = response.status || mock.status || 200;
2046
2091
  const event = {
2047
2092
  ok: true,
@@ -2159,6 +2204,19 @@ async function executeSetupAction(action, ordinal) {
2159
2204
  await locator.nth(targetIndex).fill(value, { timeout });
2160
2205
  return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
2161
2206
  }
2207
+ if (type === "assert_selector_count") {
2208
+ const locator = page.locator(action.selector);
2209
+ const expectedCount = setupNumber(action.expected_count, -1);
2210
+ if (!Number.isInteger(expectedCount) || expectedCount < 0) return { ...base, reason: "invalid_expected_count", expected_count: action.expected_count };
2211
+ const startedAt = Date.now();
2212
+ let count = 0;
2213
+ while (Date.now() - startedAt <= timeout) {
2214
+ count = await locator.count().catch(() => 0);
2215
+ if (count === expectedCount) return { ...base, ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
2216
+ await page.waitForTimeout(100);
2217
+ }
2218
+ return { ...base, reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
2219
+ }
2162
2220
  if (type === "wait_for_text") {
2163
2221
  const locator = page.locator(action.selector);
2164
2222
  const startedAt = Date.now();
@@ -2176,6 +2234,47 @@ async function executeSetupAction(action, ordinal) {
2176
2234
  }
2177
2235
  return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
2178
2236
  }
2237
+ if (type === "assert_text_visible" || type === "assert_text_absent") {
2238
+ const locator = page.locator(action.selector);
2239
+ const startedAt = Date.now();
2240
+ let lastText = "";
2241
+ let matchedText = "";
2242
+ let hiddenMatch = false;
2243
+ while (Date.now() - startedAt <= timeout) {
2244
+ const count = await locator.count().catch(() => 0);
2245
+ let matched = false;
2246
+ matchedText = "";
2247
+ hiddenMatch = false;
2248
+ for (let index = 0; index < count; index += 1) {
2249
+ const text = await setupLocatorText(locator, index);
2250
+ lastText = text || lastText;
2251
+ if (setupTextMatches(text, action)) {
2252
+ matched = true;
2253
+ matchedText = text;
2254
+ if (type === "assert_text_visible") {
2255
+ const visible = await setupLocatorVisible(locator, index);
2256
+ if (visible) {
2257
+ return { ...base, ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
2258
+ }
2259
+ hiddenMatch = true;
2260
+ break;
2261
+ }
2262
+ break;
2263
+ }
2264
+ }
2265
+ if (type === "assert_text_absent" && !matched) {
2266
+ return { ...base, ok: true, count, timeout_ms: timeout };
2267
+ }
2268
+ await page.waitForTimeout(100);
2269
+ }
2270
+ if (type === "assert_text_visible") {
2271
+ if (hiddenMatch) {
2272
+ return { ...base, reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
2273
+ }
2274
+ return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
2275
+ }
2276
+ return { ...base, reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
2277
+ }
2179
2278
  return { ...base, reason: "unsupported_action" };
2180
2279
  } catch (error) {
2181
2280
  return { ...base, error: String(error && error.message ? error.message : error).slice(0, 1000) };
@@ -5,7 +5,7 @@ declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evide
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
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
- 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"];
8
+ declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "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];
11
11
  type RiddleProofProfileSetupActionType = typeof RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES[number];
@@ -27,6 +27,7 @@ interface RiddleProofProfileSetupAction {
27
27
  pattern?: string;
28
28
  flags?: string;
29
29
  index?: number;
30
+ expected_count?: number;
30
31
  ms?: number;
31
32
  timeout_ms?: number;
32
33
  after_ms?: number;
@@ -41,6 +42,11 @@ interface RiddleProofProfileNetworkMockResponse {
41
42
  headers?: Record<string, string>;
42
43
  body?: string;
43
44
  body_json?: JsonValue;
45
+ capture_request_body?: boolean;
46
+ request_body_contains?: string[];
47
+ request_body_patterns?: string[];
48
+ request_body_not_contains?: string[];
49
+ request_body_not_patterns?: string[];
44
50
  }
45
51
  interface RiddleProofProfileNetworkMock extends RiddleProofProfileNetworkMockResponse {
46
52
  label: string;