@matware/e2e-runner 1.1.1 → 1.3.0
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/.claude-plugin/marketplace.json +21 -0
- package/.claude-plugin/plugin.json +9 -0
- package/.mcp.json +9 -0
- package/.opencode/commands/create-test.md +63 -0
- package/.opencode/commands/run.md +50 -0
- package/.opencode/commands/verify-issue.md +62 -0
- package/.opencode/skills/e2e-testing/SKILL.md +181 -0
- package/.opencode/skills/e2e-testing/references/action-types.md +143 -0
- package/.opencode/skills/e2e-testing/references/auth-strategies.md +91 -0
- package/.opencode/skills/e2e-testing/references/graphql.md +59 -0
- package/.opencode/skills/e2e-testing/references/issue-verification.md +59 -0
- package/.opencode/skills/e2e-testing/references/multi-pool.md +60 -0
- package/.opencode/skills/e2e-testing/references/network-debugging.md +62 -0
- package/.opencode/skills/e2e-testing/references/test-json-format.md +163 -0
- package/.opencode/skills/e2e-testing/references/troubleshooting.md +224 -0
- package/.opencode/skills/e2e-testing/references/variables.md +41 -0
- package/.opencode/skills/e2e-testing/references/visual-verification.md +89 -0
- package/OPENCODE.md +166 -0
- package/README.md +990 -296
- package/agents/test-analyzer.md +81 -0
- package/agents/test-creator.md +155 -0
- package/agents/test-improver.md +177 -0
- package/bin/cli.js +602 -22
- package/commands/create-test.md +65 -0
- package/commands/run.md +49 -0
- package/commands/verify-issue.md +63 -0
- package/opencode.json +11 -0
- package/package.json +15 -2
- package/scripts/setup-opencode.sh +113 -0
- package/skills/e2e-testing/SKILL.md +173 -0
- package/skills/e2e-testing/references/action-types.md +143 -0
- package/skills/e2e-testing/references/auth-strategies.md +91 -0
- package/skills/e2e-testing/references/graphql.md +59 -0
- package/skills/e2e-testing/references/issue-verification.md +59 -0
- package/skills/e2e-testing/references/multi-pool.md +60 -0
- package/skills/e2e-testing/references/network-debugging.md +62 -0
- package/skills/e2e-testing/references/test-json-format.md +163 -0
- package/skills/e2e-testing/references/troubleshooting.md +224 -0
- package/skills/e2e-testing/references/variables.md +41 -0
- package/skills/e2e-testing/references/visual-verification.md +89 -0
- package/src/actions.js +597 -20
- package/src/ai-generate.js +142 -12
- package/src/config.js +171 -0
- package/src/dashboard.js +299 -17
- package/src/db.js +335 -13
- package/src/index.js +15 -8
- package/src/learner-markdown.js +177 -0
- package/src/learner-neo4j.js +255 -0
- package/src/learner-sqlite.js +658 -0
- package/src/learner.js +418 -0
- package/src/mcp-tools.js +1558 -50
- package/src/module-resolver.js +310 -0
- package/src/narrate.js +262 -0
- package/src/neo4j-pool.js +124 -0
- package/src/pool-manager.js +223 -0
- package/src/reporter.js +117 -3
- package/src/runner.js +274 -71
- package/src/sync/auth.js +354 -0
- package/src/sync/client.js +572 -0
- package/src/sync/hub-routes.js +816 -0
- package/src/sync/index.js +68 -0
- package/src/sync/middleware.js +347 -0
- package/src/sync/queue.js +209 -0
- package/src/sync/schema.js +540 -0
- package/src/verify.js +14 -9
- package/src/watch.js +384 -0
- package/templates/build-dashboard.js +69 -0
- package/templates/dashboard/js/api.js +60 -0
- package/templates/dashboard/js/init.js +13 -0
- package/templates/dashboard/js/keyboard.js +46 -0
- package/templates/dashboard/js/state.js +40 -0
- package/templates/dashboard/js/toast.js +41 -0
- package/templates/dashboard/js/utils.js +196 -0
- package/templates/dashboard/js/view-live.js +143 -0
- package/templates/dashboard/js/view-runs.js +572 -0
- package/templates/dashboard/js/view-tests.js +294 -0
- package/templates/dashboard/js/view-watch.js +242 -0
- package/templates/dashboard/js/websocket.js +110 -0
- package/templates/dashboard/styles/base.css +69 -0
- package/templates/dashboard/styles/components.css +110 -0
- package/templates/dashboard/styles/view-live.css +74 -0
- package/templates/dashboard/styles/view-runs.css +207 -0
- package/templates/dashboard/styles/view-tests.css +96 -0
- package/templates/dashboard/styles/view-watch.css +53 -0
- package/templates/dashboard/template.html +267 -0
- package/templates/dashboard.html +2171 -530
- package/templates/docker-compose-neo4j.yml +19 -0
- package/templates/e2e.config.js +3 -0
- package/templates/sample-test.json +0 -8
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Troubleshooting Guide
|
|
2
|
+
|
|
3
|
+
## Pool Connection Issues
|
|
4
|
+
|
|
5
|
+
### "Pool not reachable" / Connection refused
|
|
6
|
+
|
|
7
|
+
**Cause**: Chrome pool (browserless/chrome Docker container) is not running.
|
|
8
|
+
|
|
9
|
+
**Fix**:
|
|
10
|
+
```bash
|
|
11
|
+
npx e2e-runner pool start
|
|
12
|
+
npx e2e-runner pool status # verify it's running
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Pool management is CLI-only — `pool start` and `pool stop` are not available via MCP.
|
|
16
|
+
|
|
17
|
+
### "Pool at capacity" / Tests queuing
|
|
18
|
+
|
|
19
|
+
**Cause**: All Chrome sessions are occupied.
|
|
20
|
+
|
|
21
|
+
**Fix**: Increase capacity or reduce concurrency:
|
|
22
|
+
```bash
|
|
23
|
+
npx e2e-runner pool stop
|
|
24
|
+
npx e2e-runner pool start --max-sessions 10
|
|
25
|
+
```
|
|
26
|
+
Or reduce test concurrency: `--concurrency 2`
|
|
27
|
+
|
|
28
|
+
The runner checks `/pressure` before each connection and waits up to 60s for a free slot.
|
|
29
|
+
|
|
30
|
+
### Docker not running
|
|
31
|
+
|
|
32
|
+
**Cause**: Docker daemon is not started.
|
|
33
|
+
|
|
34
|
+
**Fix**: Start Docker Desktop or `sudo systemctl start docker`, then `npx e2e-runner pool start`.
|
|
35
|
+
|
|
36
|
+
## React / SPA Issues
|
|
37
|
+
|
|
38
|
+
### React inputs not updating state
|
|
39
|
+
|
|
40
|
+
**Symptom**: `type` action enters text but React state doesn't change (form validation fails, submit disabled).
|
|
41
|
+
|
|
42
|
+
**Fix**: Use `type_react` instead of `type` for React controlled inputs:
|
|
43
|
+
```json
|
|
44
|
+
{ "type": "type_react", "selector": "#email", "value": "user@test.com" }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`type_react` uses the native value setter and dispatches `input` + `change` events that React's synthetic event system recognizes.
|
|
48
|
+
|
|
49
|
+
### SPA navigation not completing
|
|
50
|
+
|
|
51
|
+
**Symptom**: `goto` hangs or times out on client-side route changes.
|
|
52
|
+
|
|
53
|
+
**Fix**: Use `navigate` instead of `goto` for SPA route changes:
|
|
54
|
+
```json
|
|
55
|
+
{ "type": "navigate", "value": "/new-page" }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`navigate` uses a 5s race timeout and won't block if `load` doesn't fire (common in SPAs).
|
|
59
|
+
|
|
60
|
+
### MUI autocomplete not opening
|
|
61
|
+
|
|
62
|
+
**Symptom**: Clicking or typing in an MUI Autocomplete doesn't open the dropdown.
|
|
63
|
+
|
|
64
|
+
**Fix**: Use `focus_autocomplete` to properly focus by label text:
|
|
65
|
+
```json
|
|
66
|
+
{ "type": "focus_autocomplete", "text": "Search by name" },
|
|
67
|
+
{ "type": "type_react", "selector": "#autocomplete-input", "value": "search term" },
|
|
68
|
+
{ "type": "click_option", "text": "Desired option" }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Flaky Tests
|
|
72
|
+
|
|
73
|
+
### Intermittent failures on dynamic content
|
|
74
|
+
|
|
75
|
+
**Symptom**: Tests pass sometimes, fail others. Usually timing-related.
|
|
76
|
+
|
|
77
|
+
**Fixes**:
|
|
78
|
+
1. Add explicit `wait` before assertions:
|
|
79
|
+
```json
|
|
80
|
+
{ "type": "wait", "selector": ".data-loaded" },
|
|
81
|
+
{ "type": "assert_text", "text": "Expected content" }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
2. Use action-level retries for known flaky selectors:
|
|
85
|
+
```json
|
|
86
|
+
{ "type": "click", "selector": "#dynamic-btn", "retries": 3 }
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
3. Use test-level retries:
|
|
90
|
+
```json
|
|
91
|
+
{ "name": "flaky-test", "retries": 2, "actions": [...] }
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
4. Check the learning system for patterns:
|
|
95
|
+
```
|
|
96
|
+
e2e_learnings("flaky") → identify consistently flaky tests
|
|
97
|
+
e2e_learnings("selectors") → find unstable selectors
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Tests interfering with each other
|
|
101
|
+
|
|
102
|
+
**Symptom**: Tests pass individually but fail when run together.
|
|
103
|
+
|
|
104
|
+
**Fix**: Mark tests that share mutable state as `serial`:
|
|
105
|
+
```json
|
|
106
|
+
{ "name": "create-item", "serial": true, "actions": [...] },
|
|
107
|
+
{ "name": "verify-item", "serial": true, "actions": [...] }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Timeout Issues
|
|
111
|
+
|
|
112
|
+
### Test timeout (default 60s)
|
|
113
|
+
|
|
114
|
+
**Fix**: Increase per-test or globally:
|
|
115
|
+
```json
|
|
116
|
+
{ "name": "slow-test", "timeout": 120000, "actions": [...] }
|
|
117
|
+
```
|
|
118
|
+
Or globally: `--test-timeout 120000`
|
|
119
|
+
|
|
120
|
+
### Action timeout (default 10s)
|
|
121
|
+
|
|
122
|
+
Each action's `waitForSelector` uses the default timeout. Override per-action:
|
|
123
|
+
```json
|
|
124
|
+
{ "type": "wait", "selector": ".slow-element", "timeout": 30000 }
|
|
125
|
+
```
|
|
126
|
+
Or globally: `--timeout 30000`
|
|
127
|
+
|
|
128
|
+
## Network Errors
|
|
129
|
+
|
|
130
|
+
### Tests passing but network requests failing
|
|
131
|
+
|
|
132
|
+
**Symptom**: Tests pass but `networkSummary` shows failed requests.
|
|
133
|
+
|
|
134
|
+
**Fix**: Enable strict mode to fail tests with network errors:
|
|
135
|
+
```
|
|
136
|
+
e2e_run({ all: true, failOnNetworkError: true })
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Or use `assert_no_network_errors` at specific points:
|
|
140
|
+
```json
|
|
141
|
+
{ "type": "goto", "value": "/api-heavy-page" },
|
|
142
|
+
{ "type": "wait", "selector": ".loaded" },
|
|
143
|
+
{ "type": "assert_no_network_errors" }
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Investigating specific failures
|
|
147
|
+
|
|
148
|
+
Use network log drill-down:
|
|
149
|
+
```
|
|
150
|
+
e2e_network_logs(runDbId, errorsOnly: true) → see all failed requests
|
|
151
|
+
e2e_network_logs(runDbId, urlPattern: "/api/users") → filter by URL
|
|
152
|
+
e2e_network_logs(runDbId, testName: "create-user", includeBodies: true) → full request/response
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Common Mistakes
|
|
156
|
+
|
|
157
|
+
### Using `beforeAll` for browser state
|
|
158
|
+
|
|
159
|
+
`beforeAll` runs on a separate page that closes before tests. Use `beforeEach` for state setup.
|
|
160
|
+
|
|
161
|
+
### Using `evaluate` for simple assertions
|
|
162
|
+
|
|
163
|
+
Prefer granular assertion actions over `evaluate` with inline JS:
|
|
164
|
+
```json
|
|
165
|
+
// Bad: verbose, error-prone
|
|
166
|
+
{ "type": "evaluate", "value": "if (!document.querySelector('h1').textContent.includes('Dashboard')) throw 'not found'" }
|
|
167
|
+
|
|
168
|
+
// Good: clear, auto-waits
|
|
169
|
+
{ "type": "assert_element_text", "selector": "h1", "text": "Dashboard" }
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Forgetting `cwd` in MCP calls
|
|
173
|
+
|
|
174
|
+
All MCP tools need `cwd` to resolve config files and test directories. Always pass the project root.
|
|
175
|
+
|
|
176
|
+
### Path-only `assert_url`
|
|
177
|
+
|
|
178
|
+
When checking paths, use path-only format (starts with `/`):
|
|
179
|
+
```json
|
|
180
|
+
{ "type": "assert_url", "value": "/dashboard" }
|
|
181
|
+
```
|
|
182
|
+
This compares against the pathname only, ignoring the `host.docker.internal` origin.
|
|
183
|
+
|
|
184
|
+
## Action Type Pre-Validation
|
|
185
|
+
|
|
186
|
+
All action types are validated at **load time** (before any browser connections). If a test file contains an unknown action type (e.g., a typo like `"clik"`), loading throws immediately with the location:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
Unknown action type(s) in auth.json: "clik" in test "login-test"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The `KNOWN_ACTION_TYPES` Set in `src/actions.js` is the single source of truth. Unknown actions also throw at runtime as a safety net.
|
|
193
|
+
|
|
194
|
+
## Screenshot Hashes
|
|
195
|
+
|
|
196
|
+
Every screenshot captured during a run is assigned a short hash (`ss:a3f2b1c9`) — the first 8 hex chars of the SHA-256 of its file path. Hashes are deterministic and computed identically on the server (Node `crypto`) and in the browser (Web Crypto API).
|
|
197
|
+
|
|
198
|
+
**Flow**: screenshot saved on disk → `saveRun()` registers hash in SQLite `screenshot_hashes` table → dashboard shows `[ss:XXXXXXXX]` badge (click to copy) → user pastes hash in Claude Code → `e2e_screenshot` MCP tool looks up hash, reads file, returns the image.
|
|
199
|
+
|
|
200
|
+
- Hashes are registered inside the `saveRun()` transaction (covers action, error, verification, and baseline screenshots)
|
|
201
|
+
- The `ss:` prefix is optional when calling `e2e_screenshot` — stripped during lookup
|
|
202
|
+
- Dashboard computes hashes client-side (Web Crypto) for the Live view (before `persistRun()` writes to DB)
|
|
203
|
+
- Run detail API (`/api/db/runs/:id`) includes `screenshotHashes` map per test result
|
|
204
|
+
- Dashboard endpoint `/api/screenshot-hash/:hash` serves the image by hash
|
|
205
|
+
- Dashboard Screenshots view has a **search bar** — type a hash to find and display the screenshot
|
|
206
|
+
|
|
207
|
+
## Web Dashboard
|
|
208
|
+
|
|
209
|
+
**`src/dashboard.js`** — HTTP server, REST API, WebSocket broadcast, pool polling.
|
|
210
|
+
**`templates/dashboard.html`** — SPA, dark theme, vanilla JS, safe DOM (textContent + createEl helper).
|
|
211
|
+
|
|
212
|
+
**Features:**
|
|
213
|
+
- Live test execution with WebSocket updates
|
|
214
|
+
- Run history with inline detail expansion
|
|
215
|
+
- Screenshots gallery with hash badges and hash search
|
|
216
|
+
- Network request logs with clickable expandable rows (full request/response detail)
|
|
217
|
+
- Pool status monitoring
|
|
218
|
+
- Multi-project support via project selector
|
|
219
|
+
- Variables tab with masked values, inline edit, add, and delete
|
|
220
|
+
|
|
221
|
+
**CLI:** `e2e-runner dashboard [--port 8484]`
|
|
222
|
+
**MCP tools:** `e2e_dashboard_start`, `e2e_dashboard_stop`
|
|
223
|
+
|
|
224
|
+
Config defaults: `dashboardPort: 8484`, `maxHistoryRuns: 100`
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Variables Reference
|
|
2
|
+
|
|
3
|
+
Variables replace hardcoded sensitive values (JWT tokens, user IDs, API keys, etc.) in test JSON. Stored in SQLite (`~/.e2e-runner/dashboard.db`), scoped per project and per suite, editable from the dashboard UI.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
{{var.TOKEN}} → resolves from DB (suite scope → project scope)
|
|
9
|
+
{{env.MY_VAR}} → resolves from process.env
|
|
10
|
+
{{param}} → existing module param substitution (unchanged)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Resolution priority:** suite vars > project vars > error if not found.
|
|
14
|
+
|
|
15
|
+
## Usage in Test JSON
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{ "$use": "auth-jwt", "params": { "token": "{{var.JWT_TOKEN}}", "orgId": "{{var.ORG_ID}}" } }
|
|
19
|
+
{ "type": "goto", "value": "/users/{{var.USER_ID}}/profile" }
|
|
20
|
+
{ "type": "gql", "value": "{ user(id: \"{{var.USER_ID}}\") { name } }" }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## MCP Tool (`e2e_vars`)
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
e2e_vars({ action: "set", key: "TOKEN", value: "abc123", scope: "project" })
|
|
27
|
+
e2e_vars({ action: "set", key: "TOKEN", value: "xyz789", scope: "auth" }) // suite-specific override
|
|
28
|
+
e2e_vars({ action: "list" })
|
|
29
|
+
e2e_vars({ action: "get", key: "TOKEN" })
|
|
30
|
+
e2e_vars({ action: "delete", key: "TOKEN", scope: "project" })
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Dashboard UI
|
|
34
|
+
|
|
35
|
+
Variables tab shows all variables grouped by scope. Values are masked by default (click to reveal). Inline edit, add new, and delete are supported.
|
|
36
|
+
|
|
37
|
+
## REST API
|
|
38
|
+
|
|
39
|
+
- `GET /api/db/projects/:id/variables` — list all vars for project
|
|
40
|
+
- `PUT /api/db/projects/:id/variables` — set a variable `{ scope, key, value }`
|
|
41
|
+
- `DELETE /api/db/projects/:id/variables/:scope/:key` — delete a variable
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Visual Verification Reference
|
|
2
|
+
|
|
3
|
+
Tests can include an `expect` field for AI-powered visual verification. No API key required — Claude Code itself does the visual judgment.
|
|
4
|
+
|
|
5
|
+
## Expect Field Formats
|
|
6
|
+
|
|
7
|
+
### String form — free-form description
|
|
8
|
+
```json
|
|
9
|
+
{
|
|
10
|
+
"name": "dashboard-loads",
|
|
11
|
+
"expect": "Should show the data table with at least 3 rows, no error messages, and the sidebar with navigation links",
|
|
12
|
+
"actions": [
|
|
13
|
+
{ "type": "goto", "value": "/dashboard" },
|
|
14
|
+
{ "type": "wait", "selector": ".data-table" }
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Array form — per-criterion checklist (each evaluated independently as PASS/FAIL)
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"name": "dashboard-loads",
|
|
23
|
+
"expect": [
|
|
24
|
+
"Data table visible with at least 3 rows",
|
|
25
|
+
"No error messages or red banners",
|
|
26
|
+
"Sidebar shows navigation links"
|
|
27
|
+
],
|
|
28
|
+
"actions": [
|
|
29
|
+
{ "type": "goto", "value": "/dashboard" },
|
|
30
|
+
{ "type": "wait", "selector": ".data-table" }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Double Screenshot (Before/After)
|
|
36
|
+
|
|
37
|
+
When `expect` is present, the runner captures TWO screenshots:
|
|
38
|
+
1. **Baseline** (`baseline-{name}-{timestamp}.png`) — captured BEFORE test actions run (after `beforeEach` hooks)
|
|
39
|
+
2. **Verification** (`verify-{name}-{timestamp}.png`) — captured AFTER all actions complete
|
|
40
|
+
|
|
41
|
+
Both hashes are registered in SQLite and returned in the MCP response for before/after comparison.
|
|
42
|
+
|
|
43
|
+
## Verification Strictness
|
|
44
|
+
|
|
45
|
+
Controls how strictly Claude Code evaluates visual verification. Set via:
|
|
46
|
+
- Config: `verificationStrictness: 'moderate'`
|
|
47
|
+
- CLI: `--verification-strictness strict`
|
|
48
|
+
- Env: `VERIFICATION_STRICTNESS=strict`
|
|
49
|
+
- MCP: `verificationStrictness: 'strict'` in `e2e_run` args
|
|
50
|
+
|
|
51
|
+
| Level | Behavior |
|
|
52
|
+
|-------|----------|
|
|
53
|
+
| **`strict`** | No ambiguity allowed. If any criterion is unclear, not fully visible, or doubtful → FAIL. |
|
|
54
|
+
| **`moderate`** (default) | Reasonable judgment. Minor cosmetic differences acceptable, functional mismatches → FAIL. |
|
|
55
|
+
| **`lenient`** | Only fail on clear, obvious contradictions. |
|
|
56
|
+
|
|
57
|
+
## MCP Response Format
|
|
58
|
+
|
|
59
|
+
The `e2e_run` response includes a `verifications` array:
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"verifications": [
|
|
63
|
+
{
|
|
64
|
+
"name": "dashboard-loads",
|
|
65
|
+
"expect": ["Data table visible...", "No error messages..."],
|
|
66
|
+
"success": true,
|
|
67
|
+
"screenshotHash": "ss:a3f2b1c9",
|
|
68
|
+
"baselineScreenshotHash": "ss:b4e1c2d8",
|
|
69
|
+
"isChecklist": true
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"verificationInstructions": "Verification strictness: MODERATE — ..."
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Verdict Format
|
|
77
|
+
|
|
78
|
+
After calling `e2e_screenshot` for each hash (after + baseline), Claude Code reports a structured verdict:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
TEST: dashboard-loads
|
|
82
|
+
VERDICT: PASS
|
|
83
|
+
STATE CHANGE: Page loaded from blank to populated dashboard
|
|
84
|
+
CRITERIA:
|
|
85
|
+
- "Data table visible with at least 3 rows": PASS
|
|
86
|
+
- "No error messages or red banners": PASS
|
|
87
|
+
- "Sidebar shows navigation links": PASS
|
|
88
|
+
REASON: All criteria met, dashboard fully loaded with expected content
|
|
89
|
+
```
|