@riddledc/riddle-proof 0.7.116 → 0.7.118
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
CHANGED
|
@@ -198,10 +198,37 @@ generic inline-script warning threshold. Use `--strict=true` when you
|
|
|
198
198
|
deliberately want Riddle's non-critical script-safety warnings to block the run.
|
|
199
199
|
Critical script-safety violations remain blocked by Riddle either way.
|
|
200
200
|
|
|
201
|
-
The package includes
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
The package includes generic starter profiles:
|
|
202
|
+
|
|
203
|
+
- `examples/profiles/page-content-basic.json` for route/content/layout smoke profiles.
|
|
204
|
+
- `examples/profiles/route-inventory-basic.json` for source-link and direct-route audits.
|
|
205
|
+
- `examples/profiles/handled-recovery-list-load.json` for malformed list-load recovery profiles.
|
|
206
|
+
- `examples/profiles/handled-recovery-action-malformed-success.json` for action recovery profiles where the request succeeds at HTTP level but returns an unusable body.
|
|
207
|
+
|
|
208
|
+
Copy one of those shapes into a repository profile directory and replace the
|
|
209
|
+
routes, selectors, mock URLs, and text checks with app-specific invariants.
|
|
210
|
+
|
|
211
|
+
For handled recovery profiles, prefer proving the whole boundary instead of
|
|
212
|
+
only checking that an error message appears. Mock one dependent endpoint into a
|
|
213
|
+
malformed or failed response, keep independent endpoints healthy, and assert
|
|
214
|
+
that the page still renders the independent evidence. Capture a setup
|
|
215
|
+
screenshot immediately after the recovery state appears, before high-risk
|
|
216
|
+
absence assertions, so failing runs keep durable visual evidence. Then reject
|
|
217
|
+
raw parser text such as `SyntaxError`, `Expected property name`, and
|
|
218
|
+
`[object Object]`; reject contradictory empty-state copy such as `No items yet`
|
|
219
|
+
when the list failed to load; and keep `no_fatal_console_errors` plus
|
|
220
|
+
`no_console_warnings` in the final checks. This pattern catches both visible
|
|
221
|
+
recovery-quality bugs and hidden browser-health debt without requiring a
|
|
222
|
+
separate CI or wrapper-specific path.
|
|
223
|
+
|
|
224
|
+
For handled action recovery profiles, assert the action itself as well as the
|
|
225
|
+
recovery UI. Capture the request body when the action payload matters, preserve
|
|
226
|
+
the surrounding page state, and reject the success modal, toast, or row that
|
|
227
|
+
would imply the action completed. A useful malformed-success profile returns a
|
|
228
|
+
successful HTTP status with an invalid JSON body, waits for one generic failure
|
|
229
|
+
message, captures a recovery screenshot, and keeps parser text plus browser
|
|
230
|
+
console/page errors out of the final proof. This catches action paths that look
|
|
231
|
+
recovered to a user but still poison the browser evidence stream.
|
|
205
232
|
|
|
206
233
|
Checks normally apply to every captured viewport. Add `viewports` (or
|
|
207
234
|
`viewport_names`) to a check when responsive UI intentionally exposes an
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "riddle-proof.profile.v1",
|
|
3
|
+
"name": "handled-recovery-action-malformed-success",
|
|
4
|
+
"target": {
|
|
5
|
+
"route": "/account",
|
|
6
|
+
"viewports": [
|
|
7
|
+
{ "name": "mobile", "width": 390, "height": 844 },
|
|
8
|
+
{ "name": "tablet", "width": 820, "height": 1180 },
|
|
9
|
+
{ "name": "desktop", "width": 1440, "height": 1000 }
|
|
10
|
+
],
|
|
11
|
+
"timeout_sec": 300,
|
|
12
|
+
"wait_ms": 800,
|
|
13
|
+
"network_mocks": [
|
|
14
|
+
{
|
|
15
|
+
"label": "account-summary",
|
|
16
|
+
"url": "**/api/account/summary",
|
|
17
|
+
"method": "GET",
|
|
18
|
+
"status": 200,
|
|
19
|
+
"content_type": "application/json",
|
|
20
|
+
"required_hit_count": 3,
|
|
21
|
+
"json": {
|
|
22
|
+
"available_time": "4h 0m",
|
|
23
|
+
"active_jobs": 2
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"label": "recent-jobs",
|
|
28
|
+
"url": "**/api/jobs?limit=10",
|
|
29
|
+
"method": "GET",
|
|
30
|
+
"status": 200,
|
|
31
|
+
"content_type": "application/json",
|
|
32
|
+
"required_hit_count": 3,
|
|
33
|
+
"json": {
|
|
34
|
+
"jobs": [
|
|
35
|
+
{
|
|
36
|
+
"id": "job_action_template_survives",
|
|
37
|
+
"status": "completed"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"label": "existing-api-keys",
|
|
44
|
+
"url": "**/api/api-keys",
|
|
45
|
+
"method": "GET",
|
|
46
|
+
"status": 200,
|
|
47
|
+
"content_type": "application/json",
|
|
48
|
+
"required_hit_count": 3,
|
|
49
|
+
"json": {
|
|
50
|
+
"keys": [
|
|
51
|
+
{
|
|
52
|
+
"id": "key_action_template_existing",
|
|
53
|
+
"name": "Existing action template key",
|
|
54
|
+
"last4": "0001",
|
|
55
|
+
"status": "active"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"label": "create-api-key-malformed-success",
|
|
62
|
+
"url": "**/api/api-keys",
|
|
63
|
+
"method": "POST",
|
|
64
|
+
"status": 200,
|
|
65
|
+
"content_type": "application/json",
|
|
66
|
+
"required_hit_count": 3,
|
|
67
|
+
"max_hit_count": 3,
|
|
68
|
+
"capture_request_body": true,
|
|
69
|
+
"request_body_contains": ["Action template malformed success key"],
|
|
70
|
+
"body": "{not valid create success json"
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"setup_actions": [
|
|
74
|
+
{ "type": "clear_storage", "storage": "both", "reload": true },
|
|
75
|
+
{ "type": "wait_for_selector", "selector": "[data-testid='account-page']", "timeout_ms": 30000 },
|
|
76
|
+
{ "type": "wait_for_text", "selector": "body", "text": "4h 0m", "timeout_ms": 30000 },
|
|
77
|
+
{ "type": "wait_for_text", "selector": "body", "text": "job_action_template_survives", "timeout_ms": 30000 },
|
|
78
|
+
{ "type": "wait_for_text", "selector": "body", "text": "Existing action template key", "timeout_ms": 30000 },
|
|
79
|
+
{ "type": "fill", "selector": "[data-testid='new-key-name']", "value": "Action template malformed success key" },
|
|
80
|
+
{ "type": "clear_console" },
|
|
81
|
+
{ "type": "screenshot", "label": "before-malformed-success-action" },
|
|
82
|
+
{ "type": "click", "selector": "[data-testid='create-key-button']", "text": "Create API key" },
|
|
83
|
+
{ "type": "wait_for_text", "selector": "body", "text": "Failed to create API key", "timeout_ms": 30000 },
|
|
84
|
+
{ "type": "assert_text_visible", "selector": "body", "text": "Existing action template key", "timeout_ms": 5000 },
|
|
85
|
+
{ "type": "assert_text_visible", "selector": "body", "text": "Active", "timeout_ms": 5000 },
|
|
86
|
+
{ "type": "assert_text_absent", "selector": "body", "text": "API Key Created!", "timeout_ms": 1000 },
|
|
87
|
+
{ "type": "assert_text_absent", "selector": "body", "text": "Expected property name", "timeout_ms": 1000 },
|
|
88
|
+
{ "type": "assert_text_absent", "selector": "body", "text": "SyntaxError", "timeout_ms": 1000 },
|
|
89
|
+
{ "type": "assert_text_absent", "selector": "body", "text": "[object Object]", "timeout_ms": 1000 },
|
|
90
|
+
{ "type": "screenshot", "label": "malformed-success-action-visible-recovery" }
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
"checks": [
|
|
94
|
+
{ "type": "route_loaded", "expected_path": "/account" },
|
|
95
|
+
{ "type": "selector_visible", "selector": "[data-testid='account-page']" },
|
|
96
|
+
{ "type": "selector_visible", "selector": "[data-testid='api-key-create-error']" },
|
|
97
|
+
{ "type": "text_visible", "text": "4h 0m" },
|
|
98
|
+
{ "type": "text_visible", "text": "2 active" },
|
|
99
|
+
{ "type": "text_visible", "text": "job_action_template_survives" },
|
|
100
|
+
{ "type": "text_visible", "text": "Existing action template key" },
|
|
101
|
+
{ "type": "text_visible", "text": "Failed to create API key" },
|
|
102
|
+
{ "type": "text_visible", "text": "Active" },
|
|
103
|
+
{ "type": "text_absent", "text": "API Key Created!" },
|
|
104
|
+
{ "type": "text_absent", "text": "Expected property name" },
|
|
105
|
+
{ "type": "text_absent", "text": "SyntaxError" },
|
|
106
|
+
{ "type": "text_absent", "text": "[object Object]" },
|
|
107
|
+
{ "type": "text_absent", "text": "Application error" },
|
|
108
|
+
{ "type": "selector_count_equals", "selector": "[data-testid='api-key-create-error']", "expected_count": 1 },
|
|
109
|
+
{ "type": "selector_count_equals", "selector": "[data-testid='recent-job-row']", "expected_count": 1 },
|
|
110
|
+
{ "type": "selector_count_equals", "selector": "[data-testid='api-key-row']", "expected_count": 1 },
|
|
111
|
+
{ "type": "selector_count_equals", "selector": "[data-testid='created-key-modal']", "expected_count": 0 },
|
|
112
|
+
{ "type": "no_horizontal_overflow", "max_overflow_px": 1 },
|
|
113
|
+
{ "type": "no_fatal_console_errors" },
|
|
114
|
+
{ "type": "no_console_warnings" }
|
|
115
|
+
],
|
|
116
|
+
"artifacts": ["screenshot", "console", "dom_summary", "proof_json"],
|
|
117
|
+
"baseline_policy": "invariant_only",
|
|
118
|
+
"failure_policy": {
|
|
119
|
+
"environment_blocked": "neutral",
|
|
120
|
+
"proof_insufficient": "fail",
|
|
121
|
+
"product_regression": "fail"
|
|
122
|
+
},
|
|
123
|
+
"metadata": {
|
|
124
|
+
"purpose": "Template for handled malformed action-success recovery: prove surrounding state still renders, the failed action shows one recovery message, success UI and raw parser text stay absent, the action request body is captured, and browser console evidence remains clean."
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "riddle-proof.profile.v1",
|
|
3
|
+
"name": "handled-recovery-list-load",
|
|
4
|
+
"target": {
|
|
5
|
+
"route": "/account",
|
|
6
|
+
"viewports": [
|
|
7
|
+
{ "name": "mobile", "width": 390, "height": 844 },
|
|
8
|
+
{ "name": "tablet", "width": 820, "height": 1180 },
|
|
9
|
+
{ "name": "desktop", "width": 1440, "height": 1000 }
|
|
10
|
+
],
|
|
11
|
+
"timeout_sec": 300,
|
|
12
|
+
"wait_ms": 800,
|
|
13
|
+
"network_mocks": [
|
|
14
|
+
{
|
|
15
|
+
"label": "account-summary",
|
|
16
|
+
"url": "**/api/account/summary",
|
|
17
|
+
"method": "GET",
|
|
18
|
+
"status": 200,
|
|
19
|
+
"content_type": "application/json",
|
|
20
|
+
"required_hit_count": 3,
|
|
21
|
+
"json": {
|
|
22
|
+
"available_time": "4h 0m",
|
|
23
|
+
"active_jobs": 2
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"label": "recent-jobs",
|
|
28
|
+
"url": "**/api/jobs?limit=10",
|
|
29
|
+
"method": "GET",
|
|
30
|
+
"status": 200,
|
|
31
|
+
"content_type": "application/json",
|
|
32
|
+
"required_hit_count": 3,
|
|
33
|
+
"json": {
|
|
34
|
+
"jobs": [
|
|
35
|
+
{
|
|
36
|
+
"id": "job_recovery_template_survives",
|
|
37
|
+
"status": "completed"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"label": "saved-items-malformed-load",
|
|
44
|
+
"url": "**/api/saved-items",
|
|
45
|
+
"method": "GET",
|
|
46
|
+
"status": 200,
|
|
47
|
+
"content_type": "application/json",
|
|
48
|
+
"required_hit_count": 3,
|
|
49
|
+
"body": "{not valid saved items json"
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"setup_actions": [
|
|
53
|
+
{ "type": "clear_storage", "storage": "both", "reload": true },
|
|
54
|
+
{ "type": "wait_for_selector", "selector": "[data-testid='account-page']", "timeout_ms": 30000 },
|
|
55
|
+
{ "type": "wait_for_text", "selector": "body", "text": "4h 0m", "timeout_ms": 30000 },
|
|
56
|
+
{ "type": "wait_for_text", "selector": "body", "text": "job_recovery_template_survives", "timeout_ms": 30000 },
|
|
57
|
+
{ "type": "wait_for_text", "selector": "body", "text": "Failed to load saved items", "timeout_ms": 30000 },
|
|
58
|
+
{ "type": "screenshot", "label": "saved-items-malformed-load-visible-recovery" },
|
|
59
|
+
{ "type": "assert_text_absent", "selector": "body", "text": "No saved items yet", "timeout_ms": 1000 },
|
|
60
|
+
{ "type": "assert_text_absent", "selector": "body", "text": "Expected property name", "timeout_ms": 1000 },
|
|
61
|
+
{ "type": "assert_text_absent", "selector": "body", "text": "SyntaxError", "timeout_ms": 1000 },
|
|
62
|
+
{ "type": "assert_text_absent", "selector": "body", "text": "[object Object]", "timeout_ms": 1000 }
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"checks": [
|
|
66
|
+
{ "type": "route_loaded", "expected_path": "/account" },
|
|
67
|
+
{ "type": "selector_visible", "selector": "[data-testid='account-page']" },
|
|
68
|
+
{ "type": "selector_visible", "selector": "[data-testid='saved-items-error']" },
|
|
69
|
+
{ "type": "text_visible", "text": "4h 0m" },
|
|
70
|
+
{ "type": "text_visible", "text": "2 active" },
|
|
71
|
+
{ "type": "text_visible", "text": "job_recovery_template_survives" },
|
|
72
|
+
{ "type": "text_visible", "text": "Failed to load saved items" },
|
|
73
|
+
{ "type": "text_absent", "text": "No saved items yet" },
|
|
74
|
+
{ "type": "text_absent", "text": "Expected property name" },
|
|
75
|
+
{ "type": "text_absent", "text": "SyntaxError" },
|
|
76
|
+
{ "type": "text_absent", "text": "[object Object]" },
|
|
77
|
+
{ "type": "text_absent", "text": "Application error" },
|
|
78
|
+
{ "type": "selector_count_equals", "selector": "[data-testid='saved-items-error']", "expected_count": 1 },
|
|
79
|
+
{ "type": "selector_count_equals", "selector": "[data-testid='recent-job-row']", "expected_count": 1 },
|
|
80
|
+
{ "type": "selector_count_equals", "selector": "[data-testid='saved-item-row']", "expected_count": 0 },
|
|
81
|
+
{ "type": "no_horizontal_overflow", "max_overflow_px": 1 },
|
|
82
|
+
{ "type": "no_fatal_console_errors" },
|
|
83
|
+
{ "type": "no_console_warnings" }
|
|
84
|
+
],
|
|
85
|
+
"artifacts": ["screenshot", "console", "dom_summary", "proof_json"],
|
|
86
|
+
"baseline_policy": "invariant_only",
|
|
87
|
+
"failure_policy": {
|
|
88
|
+
"environment_blocked": "neutral",
|
|
89
|
+
"proof_insufficient": "fail",
|
|
90
|
+
"product_regression": "fail"
|
|
91
|
+
},
|
|
92
|
+
"metadata": {
|
|
93
|
+
"purpose": "Template for handled malformed list-load recovery: prove independent page data still renders, the failed list shows one recovery message, contradictory empty-state copy and raw parser text stay absent, and browser console evidence remains clean."
|
|
94
|
+
}
|
|
95
|
+
}
|