@riddledc/riddle-proof 0.7.47 → 0.7.49
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 +15 -10
- package/dist/{chunk-NJDY7HYE.js → chunk-Q3X6UTPP.js} +82 -2
- package/dist/cli.cjs +82 -2
- package/dist/cli.js +1 -1
- package/dist/index.cjs +82 -2
- package/dist/index.js +1 -1
- package/dist/profile.cjs +82 -2
- package/dist/profile.d.cts +3 -1
- package/dist/profile.d.ts +3 -1
- package/dist/profile.js +1 -1
- package/dist/proof-run-engine.d.cts +3 -3
- package/dist/proof-run-engine.d.ts +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -237,16 +237,21 @@ where the second request must carry newer state.
|
|
|
237
237
|
`target.setup_actions` is optional. Use it when the meaningful proof surface
|
|
238
238
|
appears only after a picker, tab, login stub, storage seed, form fill,
|
|
239
239
|
transport control, or other bounded interaction. Supported setup actions are
|
|
240
|
-
`click`, `fill`, `set_input_value`, `
|
|
241
|
-
`
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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`.
|
|
250
255
|
|
|
251
256
|
`target.timeout_sec` is optional. Use it for known-heavy profile targets so the
|
|
252
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),
|
|
@@ -318,6 +329,7 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
|
318
329
|
headers: stringRecord(input.headers) || defaults.headers,
|
|
319
330
|
body,
|
|
320
331
|
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json,
|
|
332
|
+
delay_ms: normalizeNetworkMockDelay(input, label, defaults.delay_ms),
|
|
321
333
|
capture_request_body: requestBody.capture_request_body,
|
|
322
334
|
request_body_contains: requestBody.request_body_contains,
|
|
323
335
|
request_body_patterns: requestBody.request_body_patterns,
|
|
@@ -325,6 +337,16 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
|
325
337
|
request_body_not_patterns: requestBody.request_body_not_patterns
|
|
326
338
|
};
|
|
327
339
|
}
|
|
340
|
+
function normalizeNetworkMockDelay(input, label, defaultValue) {
|
|
341
|
+
const value = numberValue(
|
|
342
|
+
input.delay_ms ?? input.delayMs ?? input.wait_ms ?? input.waitMs ?? input.latency_ms ?? input.latencyMs
|
|
343
|
+
) ?? defaultValue;
|
|
344
|
+
if (value === void 0) return void 0;
|
|
345
|
+
if (!Number.isInteger(value) || value < 0 || value > 6e4) {
|
|
346
|
+
throw new Error(`${label}.delay_ms must be an integer from 0 to 60000.`);
|
|
347
|
+
}
|
|
348
|
+
return value;
|
|
349
|
+
}
|
|
328
350
|
function normalizeNetworkMockResponses(value, mockIndex, defaults) {
|
|
329
351
|
if (value === void 0) return void 0;
|
|
330
352
|
if (!Array.isArray(value)) throw new Error(`target.network_mocks[${mockIndex}].responses must be an array.`);
|
|
@@ -335,7 +357,8 @@ function normalizeNetworkMockResponses(value, mockIndex, defaults) {
|
|
|
335
357
|
content_type: defaults.content_type,
|
|
336
358
|
headers: defaults.headers,
|
|
337
359
|
body: defaults.body,
|
|
338
|
-
body_json: defaults.body_json
|
|
360
|
+
body_json: defaults.body_json,
|
|
361
|
+
delay_ms: defaults.delay_ms
|
|
339
362
|
};
|
|
340
363
|
return value.map((response, responseIndex) => {
|
|
341
364
|
if (!isRecord(response)) {
|
|
@@ -2034,6 +2057,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
2034
2057
|
const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
|
|
2035
2058
|
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock, responseBodyContract) : [];
|
|
2036
2059
|
const status = response.status || mock.status || 200;
|
|
2060
|
+
const delayMs = numberValue(response.delay_ms) || 0;
|
|
2037
2061
|
const event = {
|
|
2038
2062
|
ok: true,
|
|
2039
2063
|
label: mock.label,
|
|
@@ -2046,6 +2070,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
2046
2070
|
method,
|
|
2047
2071
|
status,
|
|
2048
2072
|
};
|
|
2073
|
+
if (delayMs) event.delay_ms = delayMs;
|
|
2049
2074
|
if (shouldCaptureRequestBody) {
|
|
2050
2075
|
event.request_body_matches = requestBodyFailures.length === 0;
|
|
2051
2076
|
event.request_body_failures = requestBodyFailures;
|
|
@@ -2053,6 +2078,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
2053
2078
|
event.request_body_sample = compactNetworkMockRequestBody(requestBody);
|
|
2054
2079
|
}
|
|
2055
2080
|
networkMockEvents.push(event);
|
|
2081
|
+
if (delayMs) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
2056
2082
|
await route.fulfill({
|
|
2057
2083
|
status,
|
|
2058
2084
|
headers,
|
|
@@ -2150,6 +2176,19 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2150
2176
|
await locator.nth(targetIndex).fill(value, { timeout });
|
|
2151
2177
|
return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
|
|
2152
2178
|
}
|
|
2179
|
+
if (type === "assert_selector_count") {
|
|
2180
|
+
const locator = page.locator(action.selector);
|
|
2181
|
+
const expectedCount = setupNumber(action.expected_count, -1);
|
|
2182
|
+
if (!Number.isInteger(expectedCount) || expectedCount < 0) return { ...base, reason: "invalid_expected_count", expected_count: action.expected_count };
|
|
2183
|
+
const startedAt = Date.now();
|
|
2184
|
+
let count = 0;
|
|
2185
|
+
while (Date.now() - startedAt <= timeout) {
|
|
2186
|
+
count = await locator.count().catch(() => 0);
|
|
2187
|
+
if (count === expectedCount) return { ...base, ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2188
|
+
await page.waitForTimeout(100);
|
|
2189
|
+
}
|
|
2190
|
+
return { ...base, reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2191
|
+
}
|
|
2153
2192
|
if (type === "wait_for_text") {
|
|
2154
2193
|
const locator = page.locator(action.selector);
|
|
2155
2194
|
const startedAt = Date.now();
|
|
@@ -2167,6 +2206,47 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2167
2206
|
}
|
|
2168
2207
|
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2169
2208
|
}
|
|
2209
|
+
if (type === "assert_text_visible" || type === "assert_text_absent") {
|
|
2210
|
+
const locator = page.locator(action.selector);
|
|
2211
|
+
const startedAt = Date.now();
|
|
2212
|
+
let lastText = "";
|
|
2213
|
+
let matchedText = "";
|
|
2214
|
+
let hiddenMatch = false;
|
|
2215
|
+
while (Date.now() - startedAt <= timeout) {
|
|
2216
|
+
const count = await locator.count().catch(() => 0);
|
|
2217
|
+
let matched = false;
|
|
2218
|
+
matchedText = "";
|
|
2219
|
+
hiddenMatch = false;
|
|
2220
|
+
for (let index = 0; index < count; index += 1) {
|
|
2221
|
+
const text = await setupLocatorText(locator, index);
|
|
2222
|
+
lastText = text || lastText;
|
|
2223
|
+
if (setupTextMatches(text, action)) {
|
|
2224
|
+
matched = true;
|
|
2225
|
+
matchedText = text;
|
|
2226
|
+
if (type === "assert_text_visible") {
|
|
2227
|
+
const visible = await setupLocatorVisible(locator, index);
|
|
2228
|
+
if (visible) {
|
|
2229
|
+
return { ...base, ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
2230
|
+
}
|
|
2231
|
+
hiddenMatch = true;
|
|
2232
|
+
break;
|
|
2233
|
+
}
|
|
2234
|
+
break;
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
if (type === "assert_text_absent" && !matched) {
|
|
2238
|
+
return { ...base, ok: true, count, timeout_ms: timeout };
|
|
2239
|
+
}
|
|
2240
|
+
await page.waitForTimeout(100);
|
|
2241
|
+
}
|
|
2242
|
+
if (type === "assert_text_visible") {
|
|
2243
|
+
if (hiddenMatch) {
|
|
2244
|
+
return { ...base, reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
|
|
2245
|
+
}
|
|
2246
|
+
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2247
|
+
}
|
|
2248
|
+
return { ...base, reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
|
|
2249
|
+
}
|
|
2170
2250
|
return { ...base, reason: "unsupported_action" };
|
|
2171
2251
|
} catch (error) {
|
|
2172
2252
|
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),
|
|
@@ -7191,6 +7202,7 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
|
7191
7202
|
headers: stringRecord(input.headers) || defaults.headers,
|
|
7192
7203
|
body,
|
|
7193
7204
|
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json,
|
|
7205
|
+
delay_ms: normalizeNetworkMockDelay(input, label, defaults.delay_ms),
|
|
7194
7206
|
capture_request_body: requestBody.capture_request_body,
|
|
7195
7207
|
request_body_contains: requestBody.request_body_contains,
|
|
7196
7208
|
request_body_patterns: requestBody.request_body_patterns,
|
|
@@ -7198,6 +7210,16 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
|
7198
7210
|
request_body_not_patterns: requestBody.request_body_not_patterns
|
|
7199
7211
|
};
|
|
7200
7212
|
}
|
|
7213
|
+
function normalizeNetworkMockDelay(input, label, defaultValue) {
|
|
7214
|
+
const value = numberValue(
|
|
7215
|
+
input.delay_ms ?? input.delayMs ?? input.wait_ms ?? input.waitMs ?? input.latency_ms ?? input.latencyMs
|
|
7216
|
+
) ?? defaultValue;
|
|
7217
|
+
if (value === void 0) return void 0;
|
|
7218
|
+
if (!Number.isInteger(value) || value < 0 || value > 6e4) {
|
|
7219
|
+
throw new Error(`${label}.delay_ms must be an integer from 0 to 60000.`);
|
|
7220
|
+
}
|
|
7221
|
+
return value;
|
|
7222
|
+
}
|
|
7201
7223
|
function normalizeNetworkMockResponses(value, mockIndex, defaults) {
|
|
7202
7224
|
if (value === void 0) return void 0;
|
|
7203
7225
|
if (!Array.isArray(value)) throw new Error(`target.network_mocks[${mockIndex}].responses must be an array.`);
|
|
@@ -7208,7 +7230,8 @@ function normalizeNetworkMockResponses(value, mockIndex, defaults) {
|
|
|
7208
7230
|
content_type: defaults.content_type,
|
|
7209
7231
|
headers: defaults.headers,
|
|
7210
7232
|
body: defaults.body,
|
|
7211
|
-
body_json: defaults.body_json
|
|
7233
|
+
body_json: defaults.body_json,
|
|
7234
|
+
delay_ms: defaults.delay_ms
|
|
7212
7235
|
};
|
|
7213
7236
|
return value.map((response, responseIndex) => {
|
|
7214
7237
|
if (!isRecord(response)) {
|
|
@@ -8891,6 +8914,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
8891
8914
|
const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
|
|
8892
8915
|
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock, responseBodyContract) : [];
|
|
8893
8916
|
const status = response.status || mock.status || 200;
|
|
8917
|
+
const delayMs = numberValue(response.delay_ms) || 0;
|
|
8894
8918
|
const event = {
|
|
8895
8919
|
ok: true,
|
|
8896
8920
|
label: mock.label,
|
|
@@ -8903,6 +8927,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
8903
8927
|
method,
|
|
8904
8928
|
status,
|
|
8905
8929
|
};
|
|
8930
|
+
if (delayMs) event.delay_ms = delayMs;
|
|
8906
8931
|
if (shouldCaptureRequestBody) {
|
|
8907
8932
|
event.request_body_matches = requestBodyFailures.length === 0;
|
|
8908
8933
|
event.request_body_failures = requestBodyFailures;
|
|
@@ -8910,6 +8935,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
8910
8935
|
event.request_body_sample = compactNetworkMockRequestBody(requestBody);
|
|
8911
8936
|
}
|
|
8912
8937
|
networkMockEvents.push(event);
|
|
8938
|
+
if (delayMs) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
8913
8939
|
await route.fulfill({
|
|
8914
8940
|
status,
|
|
8915
8941
|
headers,
|
|
@@ -9007,6 +9033,19 @@ async function executeSetupAction(action, ordinal) {
|
|
|
9007
9033
|
await locator.nth(targetIndex).fill(value, { timeout });
|
|
9008
9034
|
return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
|
|
9009
9035
|
}
|
|
9036
|
+
if (type === "assert_selector_count") {
|
|
9037
|
+
const locator = page.locator(action.selector);
|
|
9038
|
+
const expectedCount = setupNumber(action.expected_count, -1);
|
|
9039
|
+
if (!Number.isInteger(expectedCount) || expectedCount < 0) return { ...base, reason: "invalid_expected_count", expected_count: action.expected_count };
|
|
9040
|
+
const startedAt = Date.now();
|
|
9041
|
+
let count = 0;
|
|
9042
|
+
while (Date.now() - startedAt <= timeout) {
|
|
9043
|
+
count = await locator.count().catch(() => 0);
|
|
9044
|
+
if (count === expectedCount) return { ...base, ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
|
|
9045
|
+
await page.waitForTimeout(100);
|
|
9046
|
+
}
|
|
9047
|
+
return { ...base, reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
|
|
9048
|
+
}
|
|
9010
9049
|
if (type === "wait_for_text") {
|
|
9011
9050
|
const locator = page.locator(action.selector);
|
|
9012
9051
|
const startedAt = Date.now();
|
|
@@ -9024,6 +9063,47 @@ async function executeSetupAction(action, ordinal) {
|
|
|
9024
9063
|
}
|
|
9025
9064
|
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
9026
9065
|
}
|
|
9066
|
+
if (type === "assert_text_visible" || type === "assert_text_absent") {
|
|
9067
|
+
const locator = page.locator(action.selector);
|
|
9068
|
+
const startedAt = Date.now();
|
|
9069
|
+
let lastText = "";
|
|
9070
|
+
let matchedText = "";
|
|
9071
|
+
let hiddenMatch = false;
|
|
9072
|
+
while (Date.now() - startedAt <= timeout) {
|
|
9073
|
+
const count = await locator.count().catch(() => 0);
|
|
9074
|
+
let matched = false;
|
|
9075
|
+
matchedText = "";
|
|
9076
|
+
hiddenMatch = false;
|
|
9077
|
+
for (let index = 0; index < count; index += 1) {
|
|
9078
|
+
const text = await setupLocatorText(locator, index);
|
|
9079
|
+
lastText = text || lastText;
|
|
9080
|
+
if (setupTextMatches(text, action)) {
|
|
9081
|
+
matched = true;
|
|
9082
|
+
matchedText = text;
|
|
9083
|
+
if (type === "assert_text_visible") {
|
|
9084
|
+
const visible = await setupLocatorVisible(locator, index);
|
|
9085
|
+
if (visible) {
|
|
9086
|
+
return { ...base, ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
9087
|
+
}
|
|
9088
|
+
hiddenMatch = true;
|
|
9089
|
+
break;
|
|
9090
|
+
}
|
|
9091
|
+
break;
|
|
9092
|
+
}
|
|
9093
|
+
}
|
|
9094
|
+
if (type === "assert_text_absent" && !matched) {
|
|
9095
|
+
return { ...base, ok: true, count, timeout_ms: timeout };
|
|
9096
|
+
}
|
|
9097
|
+
await page.waitForTimeout(100);
|
|
9098
|
+
}
|
|
9099
|
+
if (type === "assert_text_visible") {
|
|
9100
|
+
if (hiddenMatch) {
|
|
9101
|
+
return { ...base, reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
|
|
9102
|
+
}
|
|
9103
|
+
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
9104
|
+
}
|
|
9105
|
+
return { ...base, reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
|
|
9106
|
+
}
|
|
9027
9107
|
return { ...base, reason: "unsupported_action" };
|
|
9028
9108
|
} catch (error) {
|
|
9029
9109
|
return { ...base, error: String(error && error.message ? error.message : error).slice(0, 1000) };
|
package/dist/cli.js
CHANGED
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),
|
|
@@ -9032,6 +9043,7 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
|
9032
9043
|
headers: stringRecord(input.headers) || defaults.headers,
|
|
9033
9044
|
body,
|
|
9034
9045
|
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json,
|
|
9046
|
+
delay_ms: normalizeNetworkMockDelay(input, label, defaults.delay_ms),
|
|
9035
9047
|
capture_request_body: requestBody.capture_request_body,
|
|
9036
9048
|
request_body_contains: requestBody.request_body_contains,
|
|
9037
9049
|
request_body_patterns: requestBody.request_body_patterns,
|
|
@@ -9039,6 +9051,16 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
|
9039
9051
|
request_body_not_patterns: requestBody.request_body_not_patterns
|
|
9040
9052
|
};
|
|
9041
9053
|
}
|
|
9054
|
+
function normalizeNetworkMockDelay(input, label, defaultValue) {
|
|
9055
|
+
const value = numberValue3(
|
|
9056
|
+
input.delay_ms ?? input.delayMs ?? input.wait_ms ?? input.waitMs ?? input.latency_ms ?? input.latencyMs
|
|
9057
|
+
) ?? defaultValue;
|
|
9058
|
+
if (value === void 0) return void 0;
|
|
9059
|
+
if (!Number.isInteger(value) || value < 0 || value > 6e4) {
|
|
9060
|
+
throw new Error(`${label}.delay_ms must be an integer from 0 to 60000.`);
|
|
9061
|
+
}
|
|
9062
|
+
return value;
|
|
9063
|
+
}
|
|
9042
9064
|
function normalizeNetworkMockResponses(value, mockIndex, defaults) {
|
|
9043
9065
|
if (value === void 0) return void 0;
|
|
9044
9066
|
if (!Array.isArray(value)) throw new Error(`target.network_mocks[${mockIndex}].responses must be an array.`);
|
|
@@ -9049,7 +9071,8 @@ function normalizeNetworkMockResponses(value, mockIndex, defaults) {
|
|
|
9049
9071
|
content_type: defaults.content_type,
|
|
9050
9072
|
headers: defaults.headers,
|
|
9051
9073
|
body: defaults.body,
|
|
9052
|
-
body_json: defaults.body_json
|
|
9074
|
+
body_json: defaults.body_json,
|
|
9075
|
+
delay_ms: defaults.delay_ms
|
|
9053
9076
|
};
|
|
9054
9077
|
return value.map((response, responseIndex) => {
|
|
9055
9078
|
if (!isRecord2(response)) {
|
|
@@ -10748,6 +10771,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
10748
10771
|
const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
|
|
10749
10772
|
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock, responseBodyContract) : [];
|
|
10750
10773
|
const status = response.status || mock.status || 200;
|
|
10774
|
+
const delayMs = numberValue(response.delay_ms) || 0;
|
|
10751
10775
|
const event = {
|
|
10752
10776
|
ok: true,
|
|
10753
10777
|
label: mock.label,
|
|
@@ -10760,6 +10784,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
10760
10784
|
method,
|
|
10761
10785
|
status,
|
|
10762
10786
|
};
|
|
10787
|
+
if (delayMs) event.delay_ms = delayMs;
|
|
10763
10788
|
if (shouldCaptureRequestBody) {
|
|
10764
10789
|
event.request_body_matches = requestBodyFailures.length === 0;
|
|
10765
10790
|
event.request_body_failures = requestBodyFailures;
|
|
@@ -10767,6 +10792,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
10767
10792
|
event.request_body_sample = compactNetworkMockRequestBody(requestBody);
|
|
10768
10793
|
}
|
|
10769
10794
|
networkMockEvents.push(event);
|
|
10795
|
+
if (delayMs) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
10770
10796
|
await route.fulfill({
|
|
10771
10797
|
status,
|
|
10772
10798
|
headers,
|
|
@@ -10864,6 +10890,19 @@ async function executeSetupAction(action, ordinal) {
|
|
|
10864
10890
|
await locator.nth(targetIndex).fill(value, { timeout });
|
|
10865
10891
|
return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
|
|
10866
10892
|
}
|
|
10893
|
+
if (type === "assert_selector_count") {
|
|
10894
|
+
const locator = page.locator(action.selector);
|
|
10895
|
+
const expectedCount = setupNumber(action.expected_count, -1);
|
|
10896
|
+
if (!Number.isInteger(expectedCount) || expectedCount < 0) return { ...base, reason: "invalid_expected_count", expected_count: action.expected_count };
|
|
10897
|
+
const startedAt = Date.now();
|
|
10898
|
+
let count = 0;
|
|
10899
|
+
while (Date.now() - startedAt <= timeout) {
|
|
10900
|
+
count = await locator.count().catch(() => 0);
|
|
10901
|
+
if (count === expectedCount) return { ...base, ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
|
|
10902
|
+
await page.waitForTimeout(100);
|
|
10903
|
+
}
|
|
10904
|
+
return { ...base, reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
|
|
10905
|
+
}
|
|
10867
10906
|
if (type === "wait_for_text") {
|
|
10868
10907
|
const locator = page.locator(action.selector);
|
|
10869
10908
|
const startedAt = Date.now();
|
|
@@ -10881,6 +10920,47 @@ async function executeSetupAction(action, ordinal) {
|
|
|
10881
10920
|
}
|
|
10882
10921
|
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
10883
10922
|
}
|
|
10923
|
+
if (type === "assert_text_visible" || type === "assert_text_absent") {
|
|
10924
|
+
const locator = page.locator(action.selector);
|
|
10925
|
+
const startedAt = Date.now();
|
|
10926
|
+
let lastText = "";
|
|
10927
|
+
let matchedText = "";
|
|
10928
|
+
let hiddenMatch = false;
|
|
10929
|
+
while (Date.now() - startedAt <= timeout) {
|
|
10930
|
+
const count = await locator.count().catch(() => 0);
|
|
10931
|
+
let matched = false;
|
|
10932
|
+
matchedText = "";
|
|
10933
|
+
hiddenMatch = false;
|
|
10934
|
+
for (let index = 0; index < count; index += 1) {
|
|
10935
|
+
const text = await setupLocatorText(locator, index);
|
|
10936
|
+
lastText = text || lastText;
|
|
10937
|
+
if (setupTextMatches(text, action)) {
|
|
10938
|
+
matched = true;
|
|
10939
|
+
matchedText = text;
|
|
10940
|
+
if (type === "assert_text_visible") {
|
|
10941
|
+
const visible = await setupLocatorVisible(locator, index);
|
|
10942
|
+
if (visible) {
|
|
10943
|
+
return { ...base, ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
10944
|
+
}
|
|
10945
|
+
hiddenMatch = true;
|
|
10946
|
+
break;
|
|
10947
|
+
}
|
|
10948
|
+
break;
|
|
10949
|
+
}
|
|
10950
|
+
}
|
|
10951
|
+
if (type === "assert_text_absent" && !matched) {
|
|
10952
|
+
return { ...base, ok: true, count, timeout_ms: timeout };
|
|
10953
|
+
}
|
|
10954
|
+
await page.waitForTimeout(100);
|
|
10955
|
+
}
|
|
10956
|
+
if (type === "assert_text_visible") {
|
|
10957
|
+
if (hiddenMatch) {
|
|
10958
|
+
return { ...base, reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
|
|
10959
|
+
}
|
|
10960
|
+
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
10961
|
+
}
|
|
10962
|
+
return { ...base, reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
|
|
10963
|
+
}
|
|
10884
10964
|
return { ...base, reason: "unsupported_action" };
|
|
10885
10965
|
} catch (error) {
|
|
10886
10966
|
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-
|
|
61
|
+
} from "./chunk-Q3X6UTPP.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),
|
|
@@ -361,6 +372,7 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
|
361
372
|
headers: stringRecord(input.headers) || defaults.headers,
|
|
362
373
|
body,
|
|
363
374
|
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json,
|
|
375
|
+
delay_ms: normalizeNetworkMockDelay(input, label, defaults.delay_ms),
|
|
364
376
|
capture_request_body: requestBody.capture_request_body,
|
|
365
377
|
request_body_contains: requestBody.request_body_contains,
|
|
366
378
|
request_body_patterns: requestBody.request_body_patterns,
|
|
@@ -368,6 +380,16 @@ function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
|
368
380
|
request_body_not_patterns: requestBody.request_body_not_patterns
|
|
369
381
|
};
|
|
370
382
|
}
|
|
383
|
+
function normalizeNetworkMockDelay(input, label, defaultValue) {
|
|
384
|
+
const value = numberValue(
|
|
385
|
+
input.delay_ms ?? input.delayMs ?? input.wait_ms ?? input.waitMs ?? input.latency_ms ?? input.latencyMs
|
|
386
|
+
) ?? defaultValue;
|
|
387
|
+
if (value === void 0) return void 0;
|
|
388
|
+
if (!Number.isInteger(value) || value < 0 || value > 6e4) {
|
|
389
|
+
throw new Error(`${label}.delay_ms must be an integer from 0 to 60000.`);
|
|
390
|
+
}
|
|
391
|
+
return value;
|
|
392
|
+
}
|
|
371
393
|
function normalizeNetworkMockResponses(value, mockIndex, defaults) {
|
|
372
394
|
if (value === void 0) return void 0;
|
|
373
395
|
if (!Array.isArray(value)) throw new Error(`target.network_mocks[${mockIndex}].responses must be an array.`);
|
|
@@ -378,7 +400,8 @@ function normalizeNetworkMockResponses(value, mockIndex, defaults) {
|
|
|
378
400
|
content_type: defaults.content_type,
|
|
379
401
|
headers: defaults.headers,
|
|
380
402
|
body: defaults.body,
|
|
381
|
-
body_json: defaults.body_json
|
|
403
|
+
body_json: defaults.body_json,
|
|
404
|
+
delay_ms: defaults.delay_ms
|
|
382
405
|
};
|
|
383
406
|
return value.map((response, responseIndex) => {
|
|
384
407
|
if (!isRecord(response)) {
|
|
@@ -2077,6 +2100,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
2077
2100
|
const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
|
|
2078
2101
|
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock, responseBodyContract) : [];
|
|
2079
2102
|
const status = response.status || mock.status || 200;
|
|
2103
|
+
const delayMs = numberValue(response.delay_ms) || 0;
|
|
2080
2104
|
const event = {
|
|
2081
2105
|
ok: true,
|
|
2082
2106
|
label: mock.label,
|
|
@@ -2089,6 +2113,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
2089
2113
|
method,
|
|
2090
2114
|
status,
|
|
2091
2115
|
};
|
|
2116
|
+
if (delayMs) event.delay_ms = delayMs;
|
|
2092
2117
|
if (shouldCaptureRequestBody) {
|
|
2093
2118
|
event.request_body_matches = requestBodyFailures.length === 0;
|
|
2094
2119
|
event.request_body_failures = requestBodyFailures;
|
|
@@ -2096,6 +2121,7 @@ async function registerNetworkMocks(mocks) {
|
|
|
2096
2121
|
event.request_body_sample = compactNetworkMockRequestBody(requestBody);
|
|
2097
2122
|
}
|
|
2098
2123
|
networkMockEvents.push(event);
|
|
2124
|
+
if (delayMs) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
2099
2125
|
await route.fulfill({
|
|
2100
2126
|
status,
|
|
2101
2127
|
headers,
|
|
@@ -2193,6 +2219,19 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2193
2219
|
await locator.nth(targetIndex).fill(value, { timeout });
|
|
2194
2220
|
return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
|
|
2195
2221
|
}
|
|
2222
|
+
if (type === "assert_selector_count") {
|
|
2223
|
+
const locator = page.locator(action.selector);
|
|
2224
|
+
const expectedCount = setupNumber(action.expected_count, -1);
|
|
2225
|
+
if (!Number.isInteger(expectedCount) || expectedCount < 0) return { ...base, reason: "invalid_expected_count", expected_count: action.expected_count };
|
|
2226
|
+
const startedAt = Date.now();
|
|
2227
|
+
let count = 0;
|
|
2228
|
+
while (Date.now() - startedAt <= timeout) {
|
|
2229
|
+
count = await locator.count().catch(() => 0);
|
|
2230
|
+
if (count === expectedCount) return { ...base, ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2231
|
+
await page.waitForTimeout(100);
|
|
2232
|
+
}
|
|
2233
|
+
return { ...base, reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
|
|
2234
|
+
}
|
|
2196
2235
|
if (type === "wait_for_text") {
|
|
2197
2236
|
const locator = page.locator(action.selector);
|
|
2198
2237
|
const startedAt = Date.now();
|
|
@@ -2210,6 +2249,47 @@ async function executeSetupAction(action, ordinal) {
|
|
|
2210
2249
|
}
|
|
2211
2250
|
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2212
2251
|
}
|
|
2252
|
+
if (type === "assert_text_visible" || type === "assert_text_absent") {
|
|
2253
|
+
const locator = page.locator(action.selector);
|
|
2254
|
+
const startedAt = Date.now();
|
|
2255
|
+
let lastText = "";
|
|
2256
|
+
let matchedText = "";
|
|
2257
|
+
let hiddenMatch = false;
|
|
2258
|
+
while (Date.now() - startedAt <= timeout) {
|
|
2259
|
+
const count = await locator.count().catch(() => 0);
|
|
2260
|
+
let matched = false;
|
|
2261
|
+
matchedText = "";
|
|
2262
|
+
hiddenMatch = false;
|
|
2263
|
+
for (let index = 0; index < count; index += 1) {
|
|
2264
|
+
const text = await setupLocatorText(locator, index);
|
|
2265
|
+
lastText = text || lastText;
|
|
2266
|
+
if (setupTextMatches(text, action)) {
|
|
2267
|
+
matched = true;
|
|
2268
|
+
matchedText = text;
|
|
2269
|
+
if (type === "assert_text_visible") {
|
|
2270
|
+
const visible = await setupLocatorVisible(locator, index);
|
|
2271
|
+
if (visible) {
|
|
2272
|
+
return { ...base, ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
|
|
2273
|
+
}
|
|
2274
|
+
hiddenMatch = true;
|
|
2275
|
+
break;
|
|
2276
|
+
}
|
|
2277
|
+
break;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
if (type === "assert_text_absent" && !matched) {
|
|
2281
|
+
return { ...base, ok: true, count, timeout_ms: timeout };
|
|
2282
|
+
}
|
|
2283
|
+
await page.waitForTimeout(100);
|
|
2284
|
+
}
|
|
2285
|
+
if (type === "assert_text_visible") {
|
|
2286
|
+
if (hiddenMatch) {
|
|
2287
|
+
return { ...base, reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
|
|
2288
|
+
}
|
|
2289
|
+
return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
|
|
2290
|
+
}
|
|
2291
|
+
return { ...base, reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
|
|
2292
|
+
}
|
|
2213
2293
|
return { ...base, reason: "unsupported_action" };
|
|
2214
2294
|
} catch (error) {
|
|
2215
2295
|
return { ...base, error: String(error && error.message ? error.message : error).slice(0, 1000) };
|
package/dist/profile.d.cts
CHANGED
|
@@ -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,7 @@ interface RiddleProofProfileNetworkMockResponse {
|
|
|
41
42
|
headers?: Record<string, string>;
|
|
42
43
|
body?: string;
|
|
43
44
|
body_json?: JsonValue;
|
|
45
|
+
delay_ms?: number;
|
|
44
46
|
capture_request_body?: boolean;
|
|
45
47
|
request_body_contains?: string[];
|
|
46
48
|
request_body_patterns?: string[];
|
package/dist/profile.d.ts
CHANGED
|
@@ -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,7 @@ interface RiddleProofProfileNetworkMockResponse {
|
|
|
41
42
|
headers?: Record<string, string>;
|
|
42
43
|
body?: string;
|
|
43
44
|
body_json?: JsonValue;
|
|
45
|
+
delay_ms?: number;
|
|
44
46
|
capture_request_body?: boolean;
|
|
45
47
|
request_body_contains?: string[];
|
|
46
48
|
request_body_patterns?: string[];
|
package/dist/profile.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
resolveRiddleProofProfileTimeoutSec,
|
|
20
20
|
slugifyRiddleProofProfileName,
|
|
21
21
|
summarizeRiddleProofProfileResult
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-Q3X6UTPP.js";
|
|
23
23
|
export {
|
|
24
24
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
25
25
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|
|
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
292
292
|
blocking?: boolean;
|
|
293
293
|
details?: Record<string, unknown>;
|
|
294
294
|
ok: boolean;
|
|
295
|
-
action: "
|
|
295
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
296
296
|
state_path: string;
|
|
297
297
|
stage: any;
|
|
298
298
|
summary: string;
|
|
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
382
382
|
continueWithStage?: WorkflowStage | null;
|
|
383
383
|
blocking?: boolean;
|
|
384
384
|
details?: Record<string, unknown>;
|
|
385
|
-
action: "
|
|
385
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
386
386
|
state_path: string;
|
|
387
387
|
stage: any;
|
|
388
388
|
checkpoint: string;
|
|
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
659
659
|
error?: undefined;
|
|
660
660
|
} | {
|
|
661
661
|
ok: boolean;
|
|
662
|
-
action: "
|
|
662
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
663
663
|
state_path: string;
|
|
664
664
|
stage: any;
|
|
665
665
|
summary: string;
|
|
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
292
292
|
blocking?: boolean;
|
|
293
293
|
details?: Record<string, unknown>;
|
|
294
294
|
ok: boolean;
|
|
295
|
-
action: "
|
|
295
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
296
296
|
state_path: string;
|
|
297
297
|
stage: any;
|
|
298
298
|
summary: string;
|
|
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
382
382
|
continueWithStage?: WorkflowStage | null;
|
|
383
383
|
blocking?: boolean;
|
|
384
384
|
details?: Record<string, unknown>;
|
|
385
|
-
action: "
|
|
385
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
386
386
|
state_path: string;
|
|
387
387
|
stage: any;
|
|
388
388
|
checkpoint: string;
|
|
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
659
659
|
error?: undefined;
|
|
660
660
|
} | {
|
|
661
661
|
ok: boolean;
|
|
662
|
-
action: "
|
|
662
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
663
663
|
state_path: string;
|
|
664
664
|
stage: any;
|
|
665
665
|
summary: string;
|