@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/README.md +19 -11
- package/dist/{chunk-WSZ5AVKX.js → chunk-V2KNQTQD.js} +135 -36
- package/dist/cli.cjs +135 -36
- package/dist/cli.js +1 -1
- package/dist/index.cjs +135 -36
- package/dist/index.js +1 -1
- package/dist/profile.cjs +135 -36
- package/dist/profile.d.cts +7 -1
- package/dist/profile.d.ts +7 -1
- package/dist/profile.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -229,21 +229,29 @@ mock hits. Add `request_body_contains` / `request_body_patterns` or
|
|
|
229
229
|
is part of the contract, such as proving that a save request references the
|
|
230
230
|
current build ID returned by a prior mocked build response and not a stale one.
|
|
231
231
|
Body assertions use the full request body for matching and store only length
|
|
232
|
-
plus a compact sample in the proof evidence.
|
|
232
|
+
plus a compact sample in the proof evidence. For sequenced mocks, the same
|
|
233
|
+
request-body fields may also be placed on individual `responses[]` entries when
|
|
234
|
+
each step has a different request contract, such as a fail-then-success retry
|
|
235
|
+
where the second request must carry newer state.
|
|
233
236
|
|
|
234
237
|
`target.setup_actions` is optional. Use it when the meaningful proof surface
|
|
235
238
|
appears only after a picker, tab, login stub, storage seed, form fill,
|
|
236
239
|
transport control, or other bounded interaction. Supported setup actions are
|
|
237
|
-
`click`, `fill`, `set_input_value`, `
|
|
238
|
-
`
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
240
|
+
`click`, `fill`, `set_input_value`, `assert_text_visible`,
|
|
241
|
+
`assert_text_absent`, `assert_selector_count`, `local_storage`,
|
|
242
|
+
`session_storage`, `clear_storage`, `wait`, `wait_for_selector`, and
|
|
243
|
+
`wait_for_text`; a failed setup action is recorded as a failed
|
|
244
|
+
`setup_actions_succeeded` check so the profile cannot pass without reaching
|
|
245
|
+
the intended state. Text-matched `click` actions prefer visible matching
|
|
246
|
+
elements, which keeps responsive layouts from selecting hidden desktop or
|
|
247
|
+
mobile-only links. Use setup assertions when the pre-click or pre-navigation
|
|
248
|
+
state is part of the contract, for example a fresh row must be present, stale
|
|
249
|
+
copy must be absent, or exactly one source link must exist before clicking into
|
|
250
|
+
the final route. `assert_selector_count` accepts `expected_count`.
|
|
251
|
+
`local_storage` and `session_storage` accept a `key` plus string `value` or
|
|
252
|
+
JSON `json` / `value_json`, and can reload the page with `reload: true`.
|
|
253
|
+
`clear_storage` clears `local`, `session`, or `both` browser storage scopes,
|
|
254
|
+
defaults to `both`, and can also reload with `reload: true`.
|
|
247
255
|
|
|
248
256
|
`target.timeout_sec` is optional. Use it for known-heavy profile targets so the
|
|
249
257
|
profile carries its own hosted Riddle worker budget; an explicit CLI `--timeout`
|
|
@@ -32,6 +32,9 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
|
|
|
32
32
|
"click",
|
|
33
33
|
"fill",
|
|
34
34
|
"set_input_value",
|
|
35
|
+
"assert_text_visible",
|
|
36
|
+
"assert_text_absent",
|
|
37
|
+
"assert_selector_count",
|
|
35
38
|
"local_storage",
|
|
36
39
|
"session_storage",
|
|
37
40
|
"clear_storage",
|
|
@@ -205,12 +208,19 @@ function normalizeSetupAction(input, index) {
|
|
|
205
208
|
if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
|
|
206
209
|
const type = normalizeSetupActionType(stringValue(input.type), index);
|
|
207
210
|
const selector = stringValue(input.selector);
|
|
208
|
-
if ((type === "click" || type === "fill" || type === "set_input_value" || type === "wait_for_selector" || type === "wait_for_text") && !selector) {
|
|
211
|
+
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) {
|
|
209
212
|
throw new Error(`target.setup_actions[${index}] ${type} requires selector.`);
|
|
210
213
|
}
|
|
211
214
|
if (type === "wait_for_text" && !stringValue(input.text) && !stringValue(input.pattern)) {
|
|
212
215
|
throw new Error(`target.setup_actions[${index}] wait_for_text requires text or pattern.`);
|
|
213
216
|
}
|
|
217
|
+
if ((type === "assert_text_visible" || type === "assert_text_absent") && !stringValue(input.text) && !stringValue(input.pattern)) {
|
|
218
|
+
throw new Error(`target.setup_actions[${index}] ${type} requires text or pattern.`);
|
|
219
|
+
}
|
|
220
|
+
const expectedCount = numberValue(input.expected_count ?? input.expectedCount ?? input.count);
|
|
221
|
+
if (type === "assert_selector_count" && (expectedCount === void 0 || !Number.isInteger(expectedCount) || expectedCount < 0)) {
|
|
222
|
+
throw new Error(`target.setup_actions[${index}] ${type} requires non-negative integer expected_count.`);
|
|
223
|
+
}
|
|
214
224
|
const value = stringFromOwn(input, "value", "input_value", "inputValue");
|
|
215
225
|
const hasJsonValue = hasOwn(input, "value_json") || hasOwn(input, "valueJson") || hasOwn(input, "json");
|
|
216
226
|
if ((type === "fill" || type === "set_input_value") && value === void 0 && !hasJsonValue) {
|
|
@@ -233,6 +243,7 @@ function normalizeSetupAction(input, index) {
|
|
|
233
243
|
pattern: stringValue(input.pattern),
|
|
234
244
|
flags: stringValue(input.flags),
|
|
235
245
|
index: numberValue(input.index),
|
|
246
|
+
expected_count: expectedCount,
|
|
236
247
|
ms: numberValue(input.ms) ?? numberValue(input.wait_ms) ?? numberValue(input.waitMs),
|
|
237
248
|
timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
|
|
238
249
|
after_ms: numberValue(input.after_ms) ?? numberValue(input.afterMs),
|
|
@@ -253,24 +264,7 @@ function normalizeNetworkMock(input, index) {
|
|
|
253
264
|
const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
|
|
254
265
|
const responsesInput = input.responses ?? input.sequence;
|
|
255
266
|
const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
|
|
256
|
-
const
|
|
257
|
-
input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
|
|
258
|
-
`target.network_mocks[${index}].request_body_contains`
|
|
259
|
-
);
|
|
260
|
-
const requestBodyPatterns = normalizeStringList(
|
|
261
|
-
input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
|
|
262
|
-
`target.network_mocks[${index}].request_body_patterns`
|
|
263
|
-
);
|
|
264
|
-
validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
|
|
265
|
-
const requestBodyNotContains = normalizeStringList(
|
|
266
|
-
input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
|
|
267
|
-
`target.network_mocks[${index}].request_body_not_contains`
|
|
268
|
-
);
|
|
269
|
-
const requestBodyNotPatterns = normalizeStringList(
|
|
270
|
-
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,
|
|
271
|
-
`target.network_mocks[${index}].request_body_not_patterns`
|
|
272
|
-
);
|
|
273
|
-
validateRegexPatterns(requestBodyNotPatterns, `target.network_mocks[${index}].request_body_not_patterns`);
|
|
267
|
+
const requestBody = normalizeNetworkMockRequestBodyConstraints(input, `target.network_mocks[${index}]`);
|
|
274
268
|
const requiredHitCount = numberValue(
|
|
275
269
|
input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
|
|
276
270
|
);
|
|
@@ -286,6 +280,33 @@ function normalizeNetworkMock(input, index) {
|
|
|
286
280
|
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
287
281
|
required_hit_count: requiredHitCount,
|
|
288
282
|
required: input.required === false ? false : true,
|
|
283
|
+
capture_request_body: requestBody.capture_request_body,
|
|
284
|
+
request_body_contains: requestBody.request_body_contains,
|
|
285
|
+
request_body_patterns: requestBody.request_body_patterns,
|
|
286
|
+
request_body_not_contains: requestBody.request_body_not_contains,
|
|
287
|
+
request_body_not_patterns: requestBody.request_body_not_patterns
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function normalizeNetworkMockRequestBodyConstraints(input, label) {
|
|
291
|
+
const requestBodyContains = normalizeStringList(
|
|
292
|
+
input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
|
|
293
|
+
`${label}.request_body_contains`
|
|
294
|
+
);
|
|
295
|
+
const requestBodyPatterns = normalizeStringList(
|
|
296
|
+
input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
|
|
297
|
+
`${label}.request_body_patterns`
|
|
298
|
+
);
|
|
299
|
+
validateRegexPatterns(requestBodyPatterns, `${label}.request_body_patterns`);
|
|
300
|
+
const requestBodyNotContains = normalizeStringList(
|
|
301
|
+
input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
|
|
302
|
+
`${label}.request_body_not_contains`
|
|
303
|
+
);
|
|
304
|
+
const requestBodyNotPatterns = normalizeStringList(
|
|
305
|
+
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,
|
|
306
|
+
`${label}.request_body_not_patterns`
|
|
307
|
+
);
|
|
308
|
+
validateRegexPatterns(requestBodyNotPatterns, `${label}.request_body_not_patterns`);
|
|
309
|
+
return {
|
|
289
310
|
capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length) || Boolean(requestBodyNotContains?.length) || Boolean(requestBodyNotPatterns?.length),
|
|
290
311
|
request_body_contains: requestBodyContains,
|
|
291
312
|
request_body_patterns: requestBodyPatterns,
|
|
@@ -300,20 +321,33 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
|
300
321
|
}
|
|
301
322
|
const body = stringValue(input.body) ?? stringValue(input.body_text) ?? stringValue(input.bodyText) ?? defaults.body;
|
|
302
323
|
const hasJsonBody = Object.prototype.hasOwnProperty.call(input, "body_json") || Object.prototype.hasOwnProperty.call(input, "bodyJson") || Object.prototype.hasOwnProperty.call(input, "json");
|
|
324
|
+
const requestBody = normalizeNetworkMockRequestBodyConstraints(input, label);
|
|
303
325
|
return {
|
|
304
326
|
label: stringValue(input.label) || stringValue(input.name) || defaults.label,
|
|
305
327
|
status,
|
|
306
328
|
content_type: stringValue(input.content_type) || stringValue(input.contentType) || defaults.content_type,
|
|
307
329
|
headers: stringRecord(input.headers) || defaults.headers,
|
|
308
330
|
body,
|
|
309
|
-
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json
|
|
331
|
+
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json,
|
|
332
|
+
capture_request_body: requestBody.capture_request_body,
|
|
333
|
+
request_body_contains: requestBody.request_body_contains,
|
|
334
|
+
request_body_patterns: requestBody.request_body_patterns,
|
|
335
|
+
request_body_not_contains: requestBody.request_body_not_contains,
|
|
336
|
+
request_body_not_patterns: requestBody.request_body_not_patterns
|
|
310
337
|
};
|
|
311
338
|
}
|
|
312
339
|
function normalizeNetworkMockResponses(value, mockIndex, defaults) {
|
|
313
340
|
if (value === void 0) return void 0;
|
|
314
341
|
if (!Array.isArray(value)) throw new Error(`target.network_mocks[${mockIndex}].responses must be an array.`);
|
|
315
342
|
if (!value.length) throw new Error(`target.network_mocks[${mockIndex}].responses must not be empty.`);
|
|
316
|
-
const responseDefaults = {
|
|
343
|
+
const responseDefaults = {
|
|
344
|
+
label: void 0,
|
|
345
|
+
status: defaults.status,
|
|
346
|
+
content_type: defaults.content_type,
|
|
347
|
+
headers: defaults.headers,
|
|
348
|
+
body: defaults.body,
|
|
349
|
+
body_json: defaults.body_json
|
|
350
|
+
};
|
|
317
351
|
return value.map((response, responseIndex) => {
|
|
318
352
|
if (!isRecord(response)) {
|
|
319
353
|
throw new Error(`target.network_mocks[${mockIndex}].responses[${responseIndex}] must be an object.`);
|
|
@@ -1056,6 +1090,9 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
1056
1090
|
if (event.request_body_matches === false) {
|
|
1057
1091
|
failed.push({
|
|
1058
1092
|
label: event.label ?? null,
|
|
1093
|
+
response_label: event.response_label ?? null,
|
|
1094
|
+
hit_index: event.hit_index ?? null,
|
|
1095
|
+
response_index: event.response_index ?? null,
|
|
1059
1096
|
url: event.url ?? null,
|
|
1060
1097
|
method: event.method ?? null,
|
|
1061
1098
|
reason: "request_body_mismatch",
|
|
@@ -1439,6 +1476,9 @@ function assessProfile(profile, evidence) {
|
|
|
1439
1476
|
if (event && event.request_body_matches === false) {
|
|
1440
1477
|
failed.push({
|
|
1441
1478
|
label: event.label || null,
|
|
1479
|
+
response_label: event.response_label || null,
|
|
1480
|
+
hit_index: event.hit_index ?? null,
|
|
1481
|
+
response_index: event.response_index ?? null,
|
|
1442
1482
|
url: event.url || null,
|
|
1443
1483
|
method: event.method || null,
|
|
1444
1484
|
reason: "request_body_mismatch",
|
|
@@ -1903,20 +1943,21 @@ function compactNetworkMockRequestBody(value) {
|
|
|
1903
1943
|
function networkMockStringList(value) {
|
|
1904
1944
|
return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
|
|
1905
1945
|
}
|
|
1906
|
-
function networkMockShouldCaptureRequestBody(
|
|
1907
|
-
return
|
|
1908
|
-
|
|
1909
|
-
|| networkMockStringList(
|
|
1910
|
-
|| networkMockStringList(
|
|
1911
|
-
|| networkMockStringList(
|
|
1912
|
-
|| networkMockStringList(
|
|
1913
|
-
);
|
|
1946
|
+
function networkMockShouldCaptureRequestBody(...sources) {
|
|
1947
|
+
return sources.some((source) => source && (
|
|
1948
|
+
source.capture_request_body === true
|
|
1949
|
+
|| networkMockStringList(source.request_body_contains).length > 0
|
|
1950
|
+
|| networkMockStringList(source.request_body_patterns).length > 0
|
|
1951
|
+
|| networkMockStringList(source.request_body_not_contains).length > 0
|
|
1952
|
+
|| networkMockStringList(source.request_body_not_patterns).length > 0
|
|
1953
|
+
));
|
|
1914
1954
|
}
|
|
1915
|
-
function
|
|
1955
|
+
function networkMockRequestBodyFailuresForSource(body, source) {
|
|
1916
1956
|
const failures = [];
|
|
1957
|
+
if (!source) return failures;
|
|
1917
1958
|
const rawBody = String(body || "");
|
|
1918
1959
|
const compactBody = compactNetworkMockRequestBody(rawBody);
|
|
1919
|
-
for (const expected of networkMockStringList(
|
|
1960
|
+
for (const expected of networkMockStringList(source.request_body_contains)) {
|
|
1920
1961
|
if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
|
|
1921
1962
|
failures.push({
|
|
1922
1963
|
type: "request_body_missing_text",
|
|
@@ -1924,7 +1965,7 @@ function networkMockRequestBodyFailures(body, mock) {
|
|
|
1924
1965
|
});
|
|
1925
1966
|
}
|
|
1926
1967
|
}
|
|
1927
|
-
for (const pattern of networkMockStringList(
|
|
1968
|
+
for (const pattern of networkMockStringList(source.request_body_patterns)) {
|
|
1928
1969
|
try {
|
|
1929
1970
|
const regex = new RegExp(pattern);
|
|
1930
1971
|
if (!regex.test(rawBody) && !regex.test(compactBody)) {
|
|
@@ -1941,7 +1982,7 @@ function networkMockRequestBodyFailures(body, mock) {
|
|
|
1941
1982
|
});
|
|
1942
1983
|
}
|
|
1943
1984
|
}
|
|
1944
|
-
for (const forbidden of networkMockStringList(
|
|
1985
|
+
for (const forbidden of networkMockStringList(source.request_body_not_contains)) {
|
|
1945
1986
|
if (rawBody.includes(forbidden) || compactBody.includes(forbidden)) {
|
|
1946
1987
|
failures.push({
|
|
1947
1988
|
type: "request_body_forbidden_text",
|
|
@@ -1949,7 +1990,7 @@ function networkMockRequestBodyFailures(body, mock) {
|
|
|
1949
1990
|
});
|
|
1950
1991
|
}
|
|
1951
1992
|
}
|
|
1952
|
-
for (const pattern of networkMockStringList(
|
|
1993
|
+
for (const pattern of networkMockStringList(source.request_body_not_patterns)) {
|
|
1953
1994
|
try {
|
|
1954
1995
|
const regex = new RegExp(pattern);
|
|
1955
1996
|
if (regex.test(rawBody) || regex.test(compactBody)) {
|
|
@@ -1968,6 +2009,9 @@ function networkMockRequestBodyFailures(body, mock) {
|
|
|
1968
2009
|
}
|
|
1969
2010
|
return failures;
|
|
1970
2011
|
}
|
|
2012
|
+
function networkMockRequestBodyFailures(body, ...sources) {
|
|
2013
|
+
return sources.flatMap((source) => networkMockRequestBodyFailuresForSource(body, source));
|
|
2014
|
+
}
|
|
1971
2015
|
async function setupLocatorVisible(locator, index) {
|
|
1972
2016
|
return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
|
|
1973
2017
|
}
|
|
@@ -1996,9 +2040,10 @@ async function registerNetworkMocks(mocks) {
|
|
|
1996
2040
|
body = JSON.stringify(response.body_json);
|
|
1997
2041
|
contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
|
|
1998
2042
|
}
|
|
1999
|
-
const
|
|
2043
|
+
const responseBodyContract = responseIndex === null ? null : response;
|
|
2044
|
+
const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock, responseBodyContract);
|
|
2000
2045
|
const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
|
|
2001
|
-
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
|
|
2046
|
+
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock, responseBodyContract) : [];
|
|
2002
2047
|
const status = response.status || mock.status || 200;
|
|
2003
2048
|
const event = {
|
|
2004
2049
|
ok: true,
|
|
@@ -2116,6 +2161,19 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2116
2161
|
await locator.nth(targetIndex).fill(value, { timeout });
|
|
2117
2162
|
return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
|
|
2118
2163
|
}
|
|
2164
|
+
if (type === "assert_selector_count") {
|
|
2165
|
+
const locator = page.locator(action.selector);
|
|
2166
|
+
const expectedCount = setupNumber(action.expected_count, -1);
|
|
2167
|
+
if (!Number.isInteger(expectedCount) || expectedCount < 0) return { ...base, reason: "invalid_expected_count", expected_count: action.expected_count };
|
|
2168
|
+
const startedAt = Date.now();
|
|
2169
|
+
let count = 0;
|
|
2170
|
+
while (Date.now() - startedAt <= timeout) {
|
|
2171
|
+
count = await locator.count().catch(() => 0);
|
|
2172
|
+
if (count === expectedCount) return { ...base, ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2173
|
+
await page.waitForTimeout(100);
|
|
2174
|
+
}
|
|
2175
|
+
return { ...base, reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2176
|
+
}
|
|
2119
2177
|
if (type === "wait_for_text") {
|
|
2120
2178
|
const locator = page.locator(action.selector);
|
|
2121
2179
|
const startedAt = Date.now();
|
|
@@ -2133,6 +2191,47 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2133
2191
|
}
|
|
2134
2192
|
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2135
2193
|
}
|
|
2194
|
+
if (type === "assert_text_visible" || type === "assert_text_absent") {
|
|
2195
|
+
const locator = page.locator(action.selector);
|
|
2196
|
+
const startedAt = Date.now();
|
|
2197
|
+
let lastText = "";
|
|
2198
|
+
let matchedText = "";
|
|
2199
|
+
let hiddenMatch = false;
|
|
2200
|
+
while (Date.now() - startedAt <= timeout) {
|
|
2201
|
+
const count = await locator.count().catch(() => 0);
|
|
2202
|
+
let matched = false;
|
|
2203
|
+
matchedText = "";
|
|
2204
|
+
hiddenMatch = false;
|
|
2205
|
+
for (let index = 0; index < count; index += 1) {
|
|
2206
|
+
const text = await setupLocatorText(locator, index);
|
|
2207
|
+
lastText = text || lastText;
|
|
2208
|
+
if (setupTextMatches(text, action)) {
|
|
2209
|
+
matched = true;
|
|
2210
|
+
matchedText = text;
|
|
2211
|
+
if (type === "assert_text_visible") {
|
|
2212
|
+
const visible = await setupLocatorVisible(locator, index);
|
|
2213
|
+
if (visible) {
|
|
2214
|
+
return { ...base, ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
2215
|
+
}
|
|
2216
|
+
hiddenMatch = true;
|
|
2217
|
+
break;
|
|
2218
|
+
}
|
|
2219
|
+
break;
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
if (type === "assert_text_absent" && !matched) {
|
|
2223
|
+
return { ...base, ok: true, count, timeout_ms: timeout };
|
|
2224
|
+
}
|
|
2225
|
+
await page.waitForTimeout(100);
|
|
2226
|
+
}
|
|
2227
|
+
if (type === "assert_text_visible") {
|
|
2228
|
+
if (hiddenMatch) {
|
|
2229
|
+
return { ...base, reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
|
|
2230
|
+
}
|
|
2231
|
+
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2232
|
+
}
|
|
2233
|
+
return { ...base, reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
|
|
2234
|
+
}
|
|
2136
2235
|
return { ...base, reason: "unsupported_action" };
|
|
2137
2236
|
} catch (error) {
|
|
2138
2237
|
return { ...base, error: String(error && error.message ? error.message : error).slice(0, 1000) };
|
package/dist/cli.cjs
CHANGED
|
@@ -6905,6 +6905,9 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
|
|
|
6905
6905
|
"click",
|
|
6906
6906
|
"fill",
|
|
6907
6907
|
"set_input_value",
|
|
6908
|
+
"assert_text_visible",
|
|
6909
|
+
"assert_text_absent",
|
|
6910
|
+
"assert_selector_count",
|
|
6908
6911
|
"local_storage",
|
|
6909
6912
|
"session_storage",
|
|
6910
6913
|
"clear_storage",
|
|
@@ -7078,12 +7081,19 @@ function normalizeSetupAction(input, index) {
|
|
|
7078
7081
|
if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
|
|
7079
7082
|
const type = normalizeSetupActionType(stringValue2(input.type), index);
|
|
7080
7083
|
const selector = stringValue2(input.selector);
|
|
7081
|
-
if ((type === "click" || type === "fill" || type === "set_input_value" || type === "wait_for_selector" || type === "wait_for_text") && !selector) {
|
|
7084
|
+
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) {
|
|
7082
7085
|
throw new Error(`target.setup_actions[${index}] ${type} requires selector.`);
|
|
7083
7086
|
}
|
|
7084
7087
|
if (type === "wait_for_text" && !stringValue2(input.text) && !stringValue2(input.pattern)) {
|
|
7085
7088
|
throw new Error(`target.setup_actions[${index}] wait_for_text requires text or pattern.`);
|
|
7086
7089
|
}
|
|
7090
|
+
if ((type === "assert_text_visible" || type === "assert_text_absent") && !stringValue2(input.text) && !stringValue2(input.pattern)) {
|
|
7091
|
+
throw new Error(`target.setup_actions[${index}] ${type} requires text or pattern.`);
|
|
7092
|
+
}
|
|
7093
|
+
const expectedCount = numberValue(input.expected_count ?? input.expectedCount ?? input.count);
|
|
7094
|
+
if (type === "assert_selector_count" && (expectedCount === void 0 || !Number.isInteger(expectedCount) || expectedCount < 0)) {
|
|
7095
|
+
throw new Error(`target.setup_actions[${index}] ${type} requires non-negative integer expected_count.`);
|
|
7096
|
+
}
|
|
7087
7097
|
const value = stringFromOwn(input, "value", "input_value", "inputValue");
|
|
7088
7098
|
const hasJsonValue = hasOwn(input, "value_json") || hasOwn(input, "valueJson") || hasOwn(input, "json");
|
|
7089
7099
|
if ((type === "fill" || type === "set_input_value") && value === void 0 && !hasJsonValue) {
|
|
@@ -7106,6 +7116,7 @@ function normalizeSetupAction(input, index) {
|
|
|
7106
7116
|
pattern: stringValue2(input.pattern),
|
|
7107
7117
|
flags: stringValue2(input.flags),
|
|
7108
7118
|
index: numberValue(input.index),
|
|
7119
|
+
expected_count: expectedCount,
|
|
7109
7120
|
ms: numberValue(input.ms) ?? numberValue(input.wait_ms) ?? numberValue(input.waitMs),
|
|
7110
7121
|
timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
|
|
7111
7122
|
after_ms: numberValue(input.after_ms) ?? numberValue(input.afterMs),
|
|
@@ -7126,24 +7137,7 @@ function normalizeNetworkMock(input, index) {
|
|
|
7126
7137
|
const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
|
|
7127
7138
|
const responsesInput = input.responses ?? input.sequence;
|
|
7128
7139
|
const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
|
|
7129
|
-
const
|
|
7130
|
-
input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
|
|
7131
|
-
`target.network_mocks[${index}].request_body_contains`
|
|
7132
|
-
);
|
|
7133
|
-
const requestBodyPatterns = normalizeStringList(
|
|
7134
|
-
input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
|
|
7135
|
-
`target.network_mocks[${index}].request_body_patterns`
|
|
7136
|
-
);
|
|
7137
|
-
validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
|
|
7138
|
-
const requestBodyNotContains = normalizeStringList(
|
|
7139
|
-
input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
|
|
7140
|
-
`target.network_mocks[${index}].request_body_not_contains`
|
|
7141
|
-
);
|
|
7142
|
-
const requestBodyNotPatterns = normalizeStringList(
|
|
7143
|
-
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,
|
|
7144
|
-
`target.network_mocks[${index}].request_body_not_patterns`
|
|
7145
|
-
);
|
|
7146
|
-
validateRegexPatterns(requestBodyNotPatterns, `target.network_mocks[${index}].request_body_not_patterns`);
|
|
7140
|
+
const requestBody = normalizeNetworkMockRequestBodyConstraints(input, `target.network_mocks[${index}]`);
|
|
7147
7141
|
const requiredHitCount = numberValue(
|
|
7148
7142
|
input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
|
|
7149
7143
|
);
|
|
@@ -7159,6 +7153,33 @@ function normalizeNetworkMock(input, index) {
|
|
|
7159
7153
|
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
7160
7154
|
required_hit_count: requiredHitCount,
|
|
7161
7155
|
required: input.required === false ? false : true,
|
|
7156
|
+
capture_request_body: requestBody.capture_request_body,
|
|
7157
|
+
request_body_contains: requestBody.request_body_contains,
|
|
7158
|
+
request_body_patterns: requestBody.request_body_patterns,
|
|
7159
|
+
request_body_not_contains: requestBody.request_body_not_contains,
|
|
7160
|
+
request_body_not_patterns: requestBody.request_body_not_patterns
|
|
7161
|
+
};
|
|
7162
|
+
}
|
|
7163
|
+
function normalizeNetworkMockRequestBodyConstraints(input, label) {
|
|
7164
|
+
const requestBodyContains = normalizeStringList(
|
|
7165
|
+
input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
|
|
7166
|
+
`${label}.request_body_contains`
|
|
7167
|
+
);
|
|
7168
|
+
const requestBodyPatterns = normalizeStringList(
|
|
7169
|
+
input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
|
|
7170
|
+
`${label}.request_body_patterns`
|
|
7171
|
+
);
|
|
7172
|
+
validateRegexPatterns(requestBodyPatterns, `${label}.request_body_patterns`);
|
|
7173
|
+
const requestBodyNotContains = normalizeStringList(
|
|
7174
|
+
input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
|
|
7175
|
+
`${label}.request_body_not_contains`
|
|
7176
|
+
);
|
|
7177
|
+
const requestBodyNotPatterns = normalizeStringList(
|
|
7178
|
+
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,
|
|
7179
|
+
`${label}.request_body_not_patterns`
|
|
7180
|
+
);
|
|
7181
|
+
validateRegexPatterns(requestBodyNotPatterns, `${label}.request_body_not_patterns`);
|
|
7182
|
+
return {
|
|
7162
7183
|
capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length) || Boolean(requestBodyNotContains?.length) || Boolean(requestBodyNotPatterns?.length),
|
|
7163
7184
|
request_body_contains: requestBodyContains,
|
|
7164
7185
|
request_body_patterns: requestBodyPatterns,
|
|
@@ -7173,20 +7194,33 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
|
7173
7194
|
}
|
|
7174
7195
|
const body = stringValue2(input.body) ?? stringValue2(input.body_text) ?? stringValue2(input.bodyText) ?? defaults.body;
|
|
7175
7196
|
const hasJsonBody = Object.prototype.hasOwnProperty.call(input, "body_json") || Object.prototype.hasOwnProperty.call(input, "bodyJson") || Object.prototype.hasOwnProperty.call(input, "json");
|
|
7197
|
+
const requestBody = normalizeNetworkMockRequestBodyConstraints(input, label);
|
|
7176
7198
|
return {
|
|
7177
7199
|
label: stringValue2(input.label) || stringValue2(input.name) || defaults.label,
|
|
7178
7200
|
status,
|
|
7179
7201
|
content_type: stringValue2(input.content_type) || stringValue2(input.contentType) || defaults.content_type,
|
|
7180
7202
|
headers: stringRecord(input.headers) || defaults.headers,
|
|
7181
7203
|
body,
|
|
7182
|
-
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json
|
|
7204
|
+
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json,
|
|
7205
|
+
capture_request_body: requestBody.capture_request_body,
|
|
7206
|
+
request_body_contains: requestBody.request_body_contains,
|
|
7207
|
+
request_body_patterns: requestBody.request_body_patterns,
|
|
7208
|
+
request_body_not_contains: requestBody.request_body_not_contains,
|
|
7209
|
+
request_body_not_patterns: requestBody.request_body_not_patterns
|
|
7183
7210
|
};
|
|
7184
7211
|
}
|
|
7185
7212
|
function normalizeNetworkMockResponses(value, mockIndex, defaults) {
|
|
7186
7213
|
if (value === void 0) return void 0;
|
|
7187
7214
|
if (!Array.isArray(value)) throw new Error(`target.network_mocks[${mockIndex}].responses must be an array.`);
|
|
7188
7215
|
if (!value.length) throw new Error(`target.network_mocks[${mockIndex}].responses must not be empty.`);
|
|
7189
|
-
const responseDefaults = {
|
|
7216
|
+
const responseDefaults = {
|
|
7217
|
+
label: void 0,
|
|
7218
|
+
status: defaults.status,
|
|
7219
|
+
content_type: defaults.content_type,
|
|
7220
|
+
headers: defaults.headers,
|
|
7221
|
+
body: defaults.body,
|
|
7222
|
+
body_json: defaults.body_json
|
|
7223
|
+
};
|
|
7190
7224
|
return value.map((response, responseIndex) => {
|
|
7191
7225
|
if (!isRecord(response)) {
|
|
7192
7226
|
throw new Error(`target.network_mocks[${mockIndex}].responses[${responseIndex}] must be an object.`);
|
|
@@ -7929,6 +7963,9 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
7929
7963
|
if (event.request_body_matches === false) {
|
|
7930
7964
|
failed.push({
|
|
7931
7965
|
label: event.label ?? null,
|
|
7966
|
+
response_label: event.response_label ?? null,
|
|
7967
|
+
hit_index: event.hit_index ?? null,
|
|
7968
|
+
response_index: event.response_index ?? null,
|
|
7932
7969
|
url: event.url ?? null,
|
|
7933
7970
|
method: event.method ?? null,
|
|
7934
7971
|
reason: "request_body_mismatch",
|
|
@@ -8296,6 +8333,9 @@ function assessProfile(profile, evidence) {
|
|
|
8296
8333
|
if (event && event.request_body_matches === false) {
|
|
8297
8334
|
failed.push({
|
|
8298
8335
|
label: event.label || null,
|
|
8336
|
+
response_label: event.response_label || null,
|
|
8337
|
+
hit_index: event.hit_index ?? null,
|
|
8338
|
+
response_index: event.response_index ?? null,
|
|
8299
8339
|
url: event.url || null,
|
|
8300
8340
|
method: event.method || null,
|
|
8301
8341
|
reason: "request_body_mismatch",
|
|
@@ -8760,20 +8800,21 @@ function compactNetworkMockRequestBody(value) {
|
|
|
8760
8800
|
function networkMockStringList(value) {
|
|
8761
8801
|
return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
|
|
8762
8802
|
}
|
|
8763
|
-
function networkMockShouldCaptureRequestBody(
|
|
8764
|
-
return
|
|
8765
|
-
|
|
8766
|
-
|| networkMockStringList(
|
|
8767
|
-
|| networkMockStringList(
|
|
8768
|
-
|| networkMockStringList(
|
|
8769
|
-
|| networkMockStringList(
|
|
8770
|
-
);
|
|
8803
|
+
function networkMockShouldCaptureRequestBody(...sources) {
|
|
8804
|
+
return sources.some((source) => source && (
|
|
8805
|
+
source.capture_request_body === true
|
|
8806
|
+
|| networkMockStringList(source.request_body_contains).length > 0
|
|
8807
|
+
|| networkMockStringList(source.request_body_patterns).length > 0
|
|
8808
|
+
|| networkMockStringList(source.request_body_not_contains).length > 0
|
|
8809
|
+
|| networkMockStringList(source.request_body_not_patterns).length > 0
|
|
8810
|
+
));
|
|
8771
8811
|
}
|
|
8772
|
-
function
|
|
8812
|
+
function networkMockRequestBodyFailuresForSource(body, source) {
|
|
8773
8813
|
const failures = [];
|
|
8814
|
+
if (!source) return failures;
|
|
8774
8815
|
const rawBody = String(body || "");
|
|
8775
8816
|
const compactBody = compactNetworkMockRequestBody(rawBody);
|
|
8776
|
-
for (const expected of networkMockStringList(
|
|
8817
|
+
for (const expected of networkMockStringList(source.request_body_contains)) {
|
|
8777
8818
|
if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
|
|
8778
8819
|
failures.push({
|
|
8779
8820
|
type: "request_body_missing_text",
|
|
@@ -8781,7 +8822,7 @@ function networkMockRequestBodyFailures(body, mock) {
|
|
|
8781
8822
|
});
|
|
8782
8823
|
}
|
|
8783
8824
|
}
|
|
8784
|
-
for (const pattern of networkMockStringList(
|
|
8825
|
+
for (const pattern of networkMockStringList(source.request_body_patterns)) {
|
|
8785
8826
|
try {
|
|
8786
8827
|
const regex = new RegExp(pattern);
|
|
8787
8828
|
if (!regex.test(rawBody) && !regex.test(compactBody)) {
|
|
@@ -8798,7 +8839,7 @@ function networkMockRequestBodyFailures(body, mock) {
|
|
|
8798
8839
|
});
|
|
8799
8840
|
}
|
|
8800
8841
|
}
|
|
8801
|
-
for (const forbidden of networkMockStringList(
|
|
8842
|
+
for (const forbidden of networkMockStringList(source.request_body_not_contains)) {
|
|
8802
8843
|
if (rawBody.includes(forbidden) || compactBody.includes(forbidden)) {
|
|
8803
8844
|
failures.push({
|
|
8804
8845
|
type: "request_body_forbidden_text",
|
|
@@ -8806,7 +8847,7 @@ function networkMockRequestBodyFailures(body, mock) {
|
|
|
8806
8847
|
});
|
|
8807
8848
|
}
|
|
8808
8849
|
}
|
|
8809
|
-
for (const pattern of networkMockStringList(
|
|
8850
|
+
for (const pattern of networkMockStringList(source.request_body_not_patterns)) {
|
|
8810
8851
|
try {
|
|
8811
8852
|
const regex = new RegExp(pattern);
|
|
8812
8853
|
if (regex.test(rawBody) || regex.test(compactBody)) {
|
|
@@ -8825,6 +8866,9 @@ function networkMockRequestBodyFailures(body, mock) {
|
|
|
8825
8866
|
}
|
|
8826
8867
|
return failures;
|
|
8827
8868
|
}
|
|
8869
|
+
function networkMockRequestBodyFailures(body, ...sources) {
|
|
8870
|
+
return sources.flatMap((source) => networkMockRequestBodyFailuresForSource(body, source));
|
|
8871
|
+
}
|
|
8828
8872
|
async function setupLocatorVisible(locator, index) {
|
|
8829
8873
|
return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
|
|
8830
8874
|
}
|
|
@@ -8853,9 +8897,10 @@ async function registerNetworkMocks(mocks) {
|
|
|
8853
8897
|
body = JSON.stringify(response.body_json);
|
|
8854
8898
|
contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
|
|
8855
8899
|
}
|
|
8856
|
-
const
|
|
8900
|
+
const responseBodyContract = responseIndex === null ? null : response;
|
|
8901
|
+
const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock, responseBodyContract);
|
|
8857
8902
|
const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
|
|
8858
|
-
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
|
|
8903
|
+
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock, responseBodyContract) : [];
|
|
8859
8904
|
const status = response.status || mock.status || 200;
|
|
8860
8905
|
const event = {
|
|
8861
8906
|
ok: true,
|
|
@@ -8973,6 +9018,19 @@ async function executeSetupAction(action, ordinal) {
|
|
|
8973
9018
|
await locator.nth(targetIndex).fill(value, { timeout });
|
|
8974
9019
|
return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
|
|
8975
9020
|
}
|
|
9021
|
+
if (type === "assert_selector_count") {
|
|
9022
|
+
const locator = page.locator(action.selector);
|
|
9023
|
+
const expectedCount = setupNumber(action.expected_count, -1);
|
|
9024
|
+
if (!Number.isInteger(expectedCount) || expectedCount < 0) return { ...base, reason: "invalid_expected_count", expected_count: action.expected_count };
|
|
9025
|
+
const startedAt = Date.now();
|
|
9026
|
+
let count = 0;
|
|
9027
|
+
while (Date.now() - startedAt <= timeout) {
|
|
9028
|
+
count = await locator.count().catch(() => 0);
|
|
9029
|
+
if (count === expectedCount) return { ...base, ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
|
|
9030
|
+
await page.waitForTimeout(100);
|
|
9031
|
+
}
|
|
9032
|
+
return { ...base, reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
|
|
9033
|
+
}
|
|
8976
9034
|
if (type === "wait_for_text") {
|
|
8977
9035
|
const locator = page.locator(action.selector);
|
|
8978
9036
|
const startedAt = Date.now();
|
|
@@ -8990,6 +9048,47 @@ async function executeSetupAction(action, ordinal) {
|
|
|
8990
9048
|
}
|
|
8991
9049
|
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
8992
9050
|
}
|
|
9051
|
+
if (type === "assert_text_visible" || type === "assert_text_absent") {
|
|
9052
|
+
const locator = page.locator(action.selector);
|
|
9053
|
+
const startedAt = Date.now();
|
|
9054
|
+
let lastText = "";
|
|
9055
|
+
let matchedText = "";
|
|
9056
|
+
let hiddenMatch = false;
|
|
9057
|
+
while (Date.now() - startedAt <= timeout) {
|
|
9058
|
+
const count = await locator.count().catch(() => 0);
|
|
9059
|
+
let matched = false;
|
|
9060
|
+
matchedText = "";
|
|
9061
|
+
hiddenMatch = false;
|
|
9062
|
+
for (let index = 0; index < count; index += 1) {
|
|
9063
|
+
const text = await setupLocatorText(locator, index);
|
|
9064
|
+
lastText = text || lastText;
|
|
9065
|
+
if (setupTextMatches(text, action)) {
|
|
9066
|
+
matched = true;
|
|
9067
|
+
matchedText = text;
|
|
9068
|
+
if (type === "assert_text_visible") {
|
|
9069
|
+
const visible = await setupLocatorVisible(locator, index);
|
|
9070
|
+
if (visible) {
|
|
9071
|
+
return { ...base, ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
9072
|
+
}
|
|
9073
|
+
hiddenMatch = true;
|
|
9074
|
+
break;
|
|
9075
|
+
}
|
|
9076
|
+
break;
|
|
9077
|
+
}
|
|
9078
|
+
}
|
|
9079
|
+
if (type === "assert_text_absent" && !matched) {
|
|
9080
|
+
return { ...base, ok: true, count, timeout_ms: timeout };
|
|
9081
|
+
}
|
|
9082
|
+
await page.waitForTimeout(100);
|
|
9083
|
+
}
|
|
9084
|
+
if (type === "assert_text_visible") {
|
|
9085
|
+
if (hiddenMatch) {
|
|
9086
|
+
return { ...base, reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
|
|
9087
|
+
}
|
|
9088
|
+
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
9089
|
+
}
|
|
9090
|
+
return { ...base, reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
|
|
9091
|
+
}
|
|
8993
9092
|
return { ...base, reason: "unsupported_action" };
|
|
8994
9093
|
} catch (error) {
|
|
8995
9094
|
return { ...base, error: String(error && error.message ? error.message : error).slice(0, 1000) };
|
package/dist/cli.js
CHANGED