start-vibing 4.4.0 → 4.4.1
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/package.json +2 -2
- package/template/.claude/commands/e2e-audit.md +16 -0
- package/template/.claude/hooks/e2e-audit-session-start.sh +4 -0
- package/template/.claude/settings.json +4 -0
- package/template/.claude/skills/e2e-audit/SKILL.md +216 -660
- package/template/.claude/skills/e2e-audit/findings.schema.json +98 -0
- package/template/.claude/skills/e2e-audit/references/api-contract-playbook.md +66 -0
- package/template/.claude/skills/e2e-audit/references/auth-setup-playbook.md +78 -0
- package/template/.claude/skills/e2e-audit/references/coverage-gap-playbook.md +95 -0
- package/template/.claude/skills/e2e-audit/references/post-run-feedback-playbook.md +80 -0
- package/template/.claude/skills/e2e-audit/scripts/detect-stack.sh +205 -0
- package/template/.claude/skills/e2e-audit/scripts/detect-uncovered.sh +137 -0
- package/template/.claude/skills/e2e-audit/scripts/discover-api-surface.sh +242 -0
- package/template/.claude/skills/e2e-audit/scripts/discover-routes.sh +163 -0
- package/template/.claude/skills/e2e-audit/scripts/inventory-existing-tests.sh +161 -0
- package/template/.claude/skills/e2e-audit/scripts/verify-audit.sh +88 -0
- package/template/.claude/skills/e2e-audit/templates/auth-setup.ts.tpl +24 -0
- package/template/.claude/skills/e2e-audit/templates/base-fixture.ts.tpl +75 -0
- package/template/.claude/skills/e2e-audit/templates/findings-report.md.tpl +54 -0
- package/template/.claude/skills/e2e-audit/templates/post-run-feedback.md.tpl +36 -0
- package/template/.claude/skills/e2e-audit/DESIGN.md +0 -294
- package/template/.claude/skills/e2e-audit/e2e/fixtures/auth.setup.ts +0 -70
- package/template/.claude/skills/e2e-audit/e2e/fixtures/auth.ts +0 -21
- package/template/.claude/skills/e2e-audit/e2e/fixtures/base.ts +0 -90
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/.gitkeep +0 -0
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/admin.json +0 -50
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/manager.json +0 -50
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/member.json +0 -50
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/owner.json +0 -50
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-admin.page.ts +0 -141
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-billing.page.ts +0 -47
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-chat.page.ts +0 -35
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-home.page.ts +0 -134
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-integrations.page.ts +0 -334
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-knowledge.page.ts +0 -30
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-ontology.page.ts +0 -71
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-profile.page.ts +0 -38
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-teams.page.ts +0 -123
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-transcripts.page.ts +0 -109
- package/template/.claude/skills/e2e-audit/e2e/specs/auth/login.spec.ts +0 -59
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-admin.spec.ts +0 -233
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-billing.spec.ts +0 -44
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-chat.spec.ts +0 -50
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-home.spec.ts +0 -243
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-integrations.spec.ts +0 -472
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-knowledge.spec.ts +0 -57
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-ontology.spec.ts +0 -72
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-profile.spec.ts +0 -48
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-teams.spec.ts +0 -247
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-transcripts.spec.ts +0 -122
- package/template/.claude/skills/e2e-audit/e2e/specs/security/headers.spec.ts +0 -39
- package/template/.claude/skills/e2e-audit/e2e/specs/security/rbac.spec.ts +0 -92
- package/template/.claude/skills/e2e-audit/e2e/specs/security/xss.spec.ts +0 -74
- package/template/.claude/skills/e2e-audit/e2e/utils/console-collector.ts +0 -89
- package/template/.claude/skills/e2e-audit/e2e/utils/security-helpers.ts +0 -114
- package/template/.claude/skills/e2e-audit/e2e/utils/test-data.ts +0 -64
- package/template/.claude/skills/e2e-audit/runbook.md +0 -115
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
# E2E Audit Infrastructure - Design Specification
|
|
2
|
-
|
|
3
|
-
## Objective
|
|
4
|
-
|
|
5
|
-
Build a comprehensive E2E testing system for the Hakutaku Dashboard that:
|
|
6
|
-
|
|
7
|
-
1. **Discovers every interactive element** on every page (links, buttons, inputs, modals, dropdowns, tabs, tooltips, toasts)
|
|
8
|
-
2. **Auto-generates page specs** documenting all elements found per page
|
|
9
|
-
3. **Produces fixed `.spec.ts` Playwright test files** that run standalone (`bun run test:e2e`) without AI
|
|
10
|
-
4. **Validates UX/UI** — inputs, sanitization, notifications, tooltips, styles, toasts
|
|
11
|
-
5. **Tests security** — XSS, RBAC across 4 roles, console leaks, security headers
|
|
12
|
-
6. **Detects dead code** — unused/unreachable elements via static + dynamic analysis
|
|
13
|
-
7. **Supports agent-driven updates** — agent re-audits and updates test files when app changes
|
|
14
|
-
|
|
15
|
-
## Scope
|
|
16
|
-
|
|
17
|
-
- **Desktop only** (1280px+) — no mobile/tablet for now
|
|
18
|
-
- **All pages** — 25 pages across dashboard, auth, and public routes
|
|
19
|
-
- **All roles** — OWNER, ADMIN, MANAGER, MEMBER + unauthenticated
|
|
20
|
-
- **Public** — committed to git, no .gitignore
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## 1. Playwright Infrastructure
|
|
25
|
-
|
|
26
|
-
### Config
|
|
27
|
-
|
|
28
|
-
- `playwright.config.ts` at project root
|
|
29
|
-
- Desktop viewport: `1280x720`
|
|
30
|
-
- Base URL: `http://localhost:3000`
|
|
31
|
-
- `fullyParallel: true` for speed
|
|
32
|
-
- `trace: 'on-first-retry'` for CI debugging
|
|
33
|
-
- `screenshot: 'only-on-failure'`
|
|
34
|
-
- Test directory: `tests/e2e/`
|
|
35
|
-
- Output: `test-results/`
|
|
36
|
-
|
|
37
|
-
### Auth Fixtures
|
|
38
|
-
|
|
39
|
-
Reusable `storageState` per role:
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
tests/e2e/
|
|
43
|
-
├── fixtures/
|
|
44
|
-
│ ├── auth.ts # Auth setup — generates storageState per role
|
|
45
|
-
│ ├── base.ts # Extended test fixture with page helpers
|
|
46
|
-
│ └── storage/ # Generated auth state files (gitignored)
|
|
47
|
-
│ ├── owner.json
|
|
48
|
-
│ ├── admin.json
|
|
49
|
-
│ ├── manager.json
|
|
50
|
-
│ └── member.json
|
|
51
|
-
├── pages/ # Page Object Models
|
|
52
|
-
├── specs/ # Test spec files
|
|
53
|
-
└── utils/ # Shared test utilities
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Test Tags
|
|
57
|
-
|
|
58
|
-
| Tag | Purpose |
|
|
59
|
-
|-----|---------|
|
|
60
|
-
| `@smoke` | Critical path — must pass on every PR |
|
|
61
|
-
| `@regression` | Full suite — runs on main merges |
|
|
62
|
-
| `@security` | Security-specific tests |
|
|
63
|
-
| `@a11y` | Accessibility checks |
|
|
64
|
-
| `@ux` | UX/UI validation (toasts, tooltips, styles) |
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## 2. Page Spec Template
|
|
69
|
-
|
|
70
|
-
Each page gets a structured spec document in `docs/e2e-audit/page-specs/`:
|
|
71
|
-
|
|
72
|
-
```markdown
|
|
73
|
-
# Page: [Name]
|
|
74
|
-
Route: /dashboard/[route]
|
|
75
|
-
Auth Required: yes/no
|
|
76
|
-
Minimum Role: MEMBER/MANAGER/ADMIN/OWNER
|
|
77
|
-
|
|
78
|
-
## Interactive Elements
|
|
79
|
-
|
|
80
|
-
### Navigation
|
|
81
|
-
- [ ] Link: "Home" → /dashboard/home
|
|
82
|
-
- [ ] Link: "Knowledge" → /dashboard/knowledge
|
|
83
|
-
|
|
84
|
-
### Buttons
|
|
85
|
-
- [ ] Button: "Create New" → opens modal
|
|
86
|
-
- [ ] Button: "Delete" → confirmation dialog
|
|
87
|
-
|
|
88
|
-
### Inputs
|
|
89
|
-
- [ ] Input: "Search" (text, placeholder: "Search...")
|
|
90
|
-
- [ ] Select: "Filter by" (options: All, Active, Archived)
|
|
91
|
-
|
|
92
|
-
### Modals/Dialogs
|
|
93
|
-
- [ ] Modal: "Create Item" (trigger: "Create New" button)
|
|
94
|
-
- Input: "Name" (required)
|
|
95
|
-
- Input: "Description" (optional)
|
|
96
|
-
- Button: "Save" → POST + toast
|
|
97
|
-
- Button: "Cancel" → closes modal
|
|
98
|
-
|
|
99
|
-
### Toasts/Notifications
|
|
100
|
-
- [ ] Success: "Item created successfully"
|
|
101
|
-
- [ ] Error: "Failed to create item"
|
|
102
|
-
|
|
103
|
-
### Tooltips
|
|
104
|
-
- [ ] Tooltip: "Info icon" → "This is a tooltip"
|
|
105
|
-
|
|
106
|
-
### States
|
|
107
|
-
- [ ] Empty state: "No items found"
|
|
108
|
-
- [ ] Loading state: skeleton/spinner
|
|
109
|
-
- [ ] Error state: error message display
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## 3. E2E Audit Skill Architecture
|
|
115
|
-
|
|
116
|
-
The skill (`/.claude/skills/e2e-audit/SKILL.md`) instructs agents to:
|
|
117
|
-
|
|
118
|
-
1. **Crawl** — Navigate to each page, capture all interactive elements
|
|
119
|
-
2. **Generate page spec** — Write structured spec to `docs/e2e-audit/page-specs/`
|
|
120
|
-
3. **Generate test file** — Write `.spec.ts` to `tests/e2e/specs/`
|
|
121
|
-
4. **Generate page object** — Write POM to `tests/e2e/pages/`
|
|
122
|
-
5. **Validate** — Run tests via Playwright, fix failures
|
|
123
|
-
6. **Report** — Write results to `docs/e2e-audit/reports/`
|
|
124
|
-
|
|
125
|
-
### Agent Workflow
|
|
126
|
-
|
|
127
|
-
```
|
|
128
|
-
DISCOVER → SPEC → IMPLEMENT → VALIDATE → REPORT
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
- **DISCOVER**: Use Playwright MCP to navigate page, snapshot DOM, list all actionable elements
|
|
132
|
-
- **SPEC**: Write page spec markdown documenting every element found
|
|
133
|
-
- **IMPLEMENT**: Generate POM + test spec from the page spec
|
|
134
|
-
- **VALIDATE**: Run `bunx playwright test <file>` and fix any failures
|
|
135
|
-
- **REPORT**: Record pass/fail counts, coverage gaps, findings
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
## 4. Research-Oriented Testing Agent
|
|
140
|
-
|
|
141
|
-
Each page audit follows a 4-phase approach:
|
|
142
|
-
|
|
143
|
-
### Phase 1: RESEARCH
|
|
144
|
-
- Check OWASP Top 10 for relevant vulnerabilities
|
|
145
|
-
- Research common issues with the page's tech (e.g., file upload XSS, chat injection)
|
|
146
|
-
- Look up library-specific CVEs and edge cases
|
|
147
|
-
|
|
148
|
-
### Phase 2: DISCOVER
|
|
149
|
-
- Navigate to page via Playwright MCP
|
|
150
|
-
- Snapshot DOM to enumerate all elements
|
|
151
|
-
- Identify: links, buttons, inputs, selects, modals, dropdowns, tabs, tooltips
|
|
152
|
-
- Capture initial state, loading states, empty states, error states
|
|
153
|
-
|
|
154
|
-
### Phase 3: GENERATE
|
|
155
|
-
- Write page spec markdown
|
|
156
|
-
- Write Page Object Model class
|
|
157
|
-
- Write test spec with assertions for:
|
|
158
|
-
- Navigation (all links resolve correctly)
|
|
159
|
-
- Interactions (buttons, modals, forms work)
|
|
160
|
-
- Validation (required fields, error messages)
|
|
161
|
-
- UX (toasts appear, tooltips show, styles correct)
|
|
162
|
-
- Security (XSS in inputs, RBAC enforcement)
|
|
163
|
-
|
|
164
|
-
### Phase 4: DOCUMENT
|
|
165
|
-
- Record all findings
|
|
166
|
-
- Flag missing test-ids
|
|
167
|
-
- Flag accessibility issues
|
|
168
|
-
- Flag security concerns
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
## 5. Security Testing
|
|
173
|
-
|
|
174
|
-
### Layer 1: Passive (Every Page)
|
|
175
|
-
- `page.on('console')` — capture and assert no sensitive data leaks
|
|
176
|
-
- Response header checks: `X-Frame-Options`, `X-Content-Type-Options`, `Strict-Transport-Security`
|
|
177
|
-
- No stack traces in error responses
|
|
178
|
-
|
|
179
|
-
### Layer 2: RBAC Matrix
|
|
180
|
-
- Test each page with all 4 roles + unauthenticated
|
|
181
|
-
- Verify unauthorized access redirects to `/unauthorized` or `/auth`
|
|
182
|
-
- Verify UI elements show/hide based on permissions
|
|
183
|
-
|
|
184
|
-
### Layer 3: Active (Targeted)
|
|
185
|
-
- XSS payloads in text inputs (`<script>`, `<img onerror>`, `javascript:`)
|
|
186
|
-
- Verify dialog listeners don't fire (no XSS execution)
|
|
187
|
-
- IDOR checks on ID-based routes (`/knowledge/[id]`, `/chat/[id]`)
|
|
188
|
-
- CSRF token presence on mutations
|
|
189
|
-
|
|
190
|
-
---
|
|
191
|
-
|
|
192
|
-
## 6. Dead Code Detection
|
|
193
|
-
|
|
194
|
-
### Static Analysis
|
|
195
|
-
- `knip` for unused exports, files, dependencies
|
|
196
|
-
- Run: `bunx knip --reporter json > docs/e2e-audit/reports/dead-code-static.json`
|
|
197
|
-
|
|
198
|
-
### Dynamic Analysis
|
|
199
|
-
- Playwright coverage API to track which code executes during tests
|
|
200
|
-
- Compare covered vs total to identify unreachable code paths
|
|
201
|
-
- Output: `docs/e2e-audit/reports/dead-code-dynamic.json`
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
|
|
205
|
-
## 7. Documentation Output
|
|
206
|
-
|
|
207
|
-
```
|
|
208
|
-
docs/e2e-audit/
|
|
209
|
-
├── DESIGN.md # This file
|
|
210
|
-
├── page-specs/ # Per-page element inventories
|
|
211
|
-
│ ├── dashboard-home.md
|
|
212
|
-
│ ├── dashboard-knowledge.md
|
|
213
|
-
│ ├── dashboard-chat.md
|
|
214
|
-
│ └── ...
|
|
215
|
-
├── reports/ # Audit results
|
|
216
|
-
│ ├── master-audit.md # Summary of all findings
|
|
217
|
-
│ ├── security-report.md # Security-specific findings
|
|
218
|
-
│ ├── coverage-gaps.md # Missing test coverage
|
|
219
|
-
│ └── dead-code.md # Unused code findings
|
|
220
|
-
└── runbook.md # How to run, update, and maintain tests
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## 8. All Pages to Audit
|
|
226
|
-
|
|
227
|
-
### Dashboard (Authenticated)
|
|
228
|
-
| # | Page | Route | Min Role |
|
|
229
|
-
|---|------|-------|----------|
|
|
230
|
-
| 1 | Home | `/dashboard/home` | MEMBER |
|
|
231
|
-
| 2 | Knowledge List | `/dashboard/knowledge` | MEMBER |
|
|
232
|
-
| 3 | Knowledge Detail | `/dashboard/knowledge/[id]` | MEMBER |
|
|
233
|
-
| 4 | Chat | `/dashboard/chat` | MEMBER |
|
|
234
|
-
| 5 | Integrations List | `/dashboard/integrations` | MEMBER |
|
|
235
|
-
| 6 | Integration Detail | `/dashboard/integrations/[id]` | MEMBER |
|
|
236
|
-
| 7 | New Integration | `/dashboard/integrations/new` | MANAGER |
|
|
237
|
-
| 8 | Integration Success | `/dashboard/integrations/success` | MEMBER |
|
|
238
|
-
| 9 | Teams | `/dashboard/teams` | MEMBER |
|
|
239
|
-
| 10 | Profile | `/dashboard/profile` | MEMBER |
|
|
240
|
-
| 11 | Admin | `/dashboard/admin` | ADMIN |
|
|
241
|
-
| 12 | Billing | `/dashboard/billing` | OWNER |
|
|
242
|
-
| 13 | Ontology | `/dashboard/ontology` | MEMBER |
|
|
243
|
-
| 14 | Transcripts List | `/dashboard/transcripts` | MEMBER |
|
|
244
|
-
| 15 | Transcript Detail | `/dashboard/transcripts/[id]` | MEMBER |
|
|
245
|
-
| 16 | Transcript Document | `/dashboard/transcripts/documents/[id]` | MEMBER |
|
|
246
|
-
|
|
247
|
-
### Auth
|
|
248
|
-
| # | Page | Route |
|
|
249
|
-
|---|------|-------|
|
|
250
|
-
| 17 | Login/Landing | `/` |
|
|
251
|
-
| 18 | Desktop Auth | `/auth/desktop` |
|
|
252
|
-
| 19 | Auth Error | `/auth/error` |
|
|
253
|
-
| 20 | Logout | `/auth/logout` |
|
|
254
|
-
| 21 | OAuth Popup Callback | `/auth/popup-callback` |
|
|
255
|
-
| 22 | Integration Callback Success | `/auth/integration/callback/success` |
|
|
256
|
-
| 23 | Integration Callback Error | `/auth/integration/callback/error` |
|
|
257
|
-
|
|
258
|
-
### Other
|
|
259
|
-
| # | Page | Route |
|
|
260
|
-
|---|------|-------|
|
|
261
|
-
| 24 | Unauthorized | `/unauthorized` |
|
|
262
|
-
| 25 | Free Trial | `/freetrial` |
|
|
263
|
-
|
|
264
|
-
---
|
|
265
|
-
|
|
266
|
-
## 9. Running Tests
|
|
267
|
-
|
|
268
|
-
```bash
|
|
269
|
-
# Run all E2E tests
|
|
270
|
-
bun run test:e2e
|
|
271
|
-
|
|
272
|
-
# Run specific tag
|
|
273
|
-
bunx playwright test --grep @smoke
|
|
274
|
-
|
|
275
|
-
# Run specific page
|
|
276
|
-
bunx playwright test tests/e2e/specs/dashboard-home.spec.ts
|
|
277
|
-
|
|
278
|
-
# Run with UI mode
|
|
279
|
-
bunx playwright test --ui
|
|
280
|
-
|
|
281
|
-
# Run with trace viewer
|
|
282
|
-
bunx playwright show-trace test-results/trace.zip
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
---
|
|
286
|
-
|
|
287
|
-
## 10. Maintenance
|
|
288
|
-
|
|
289
|
-
When the app changes:
|
|
290
|
-
1. Agent re-runs discovery on affected pages
|
|
291
|
-
2. Compares new elements with existing page spec
|
|
292
|
-
3. Updates page spec, POM, and test file
|
|
293
|
-
4. Runs tests to validate
|
|
294
|
-
5. Updates reports with new findings
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import { test as setup } from '@playwright/test'
|
|
3
|
-
import { STORAGE_STATE } from './auth'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Auth setup — captures storage state for each role.
|
|
7
|
-
*
|
|
8
|
-
* If a storage state file already contains a session-token cookie,
|
|
9
|
-
* the setup SKIPS that role to avoid overwriting a valid session.
|
|
10
|
-
*
|
|
11
|
-
* To refresh sessions:
|
|
12
|
-
* 1. Delete the storage files in tests/e2e/fixtures/storage/
|
|
13
|
-
* 2. Log in manually at http://localhost:3000
|
|
14
|
-
* 3. Run `bunx playwright test --project=setup`
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
function hasValidSession(filePath: string): boolean {
|
|
18
|
-
try {
|
|
19
|
-
const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'))
|
|
20
|
-
return data.cookies?.some(
|
|
21
|
-
(c: { name: string }) => c.name === 'session-token'
|
|
22
|
-
)
|
|
23
|
-
} catch {
|
|
24
|
-
return false
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
setup('authenticate as owner', async ({ page }) => {
|
|
29
|
-
if (hasValidSession(STORAGE_STATE.owner)) {
|
|
30
|
-
// eslint-disable-next-line no-console
|
|
31
|
-
console.log('Owner session already valid, skipping setup')
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
await page.goto('/dashboard/home')
|
|
35
|
-
await page.waitForURL(/dashboard/, { timeout: 15000 }).catch(() => {})
|
|
36
|
-
await page.context().storageState({ path: STORAGE_STATE.owner })
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
setup('authenticate as admin', async ({ page }) => {
|
|
40
|
-
if (hasValidSession(STORAGE_STATE.admin)) {
|
|
41
|
-
// eslint-disable-next-line no-console
|
|
42
|
-
console.log('Admin session already valid, skipping setup')
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
await page.goto('/dashboard/home')
|
|
46
|
-
await page.waitForURL(/dashboard/, { timeout: 15000 }).catch(() => {})
|
|
47
|
-
await page.context().storageState({ path: STORAGE_STATE.admin })
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
setup('authenticate as manager', async ({ page }) => {
|
|
51
|
-
if (hasValidSession(STORAGE_STATE.manager)) {
|
|
52
|
-
// eslint-disable-next-line no-console
|
|
53
|
-
console.log('Manager session already valid, skipping setup')
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
await page.goto('/dashboard/home')
|
|
57
|
-
await page.waitForURL(/dashboard/, { timeout: 15000 }).catch(() => {})
|
|
58
|
-
await page.context().storageState({ path: STORAGE_STATE.manager })
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
setup('authenticate as member', async ({ page }) => {
|
|
62
|
-
if (hasValidSession(STORAGE_STATE.member)) {
|
|
63
|
-
// eslint-disable-next-line no-console
|
|
64
|
-
console.log('Member session already valid, skipping setup')
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
await page.goto('/dashboard/home')
|
|
68
|
-
await page.waitForURL(/dashboard/, { timeout: 15000 }).catch(() => {})
|
|
69
|
-
await page.context().storageState({ path: STORAGE_STATE.member })
|
|
70
|
-
})
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
import { fileURLToPath } from 'url'
|
|
3
|
-
|
|
4
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
5
|
-
const __dirname = path.dirname(__filename)
|
|
6
|
-
const STORAGE_DIR = path.join(__dirname, 'storage')
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Auth storage state paths per role.
|
|
10
|
-
*
|
|
11
|
-
* The actual auth setup tests live in auth.setup.ts.
|
|
12
|
-
* This file only exports the storage state paths and role type.
|
|
13
|
-
*/
|
|
14
|
-
export const STORAGE_STATE = {
|
|
15
|
-
owner: path.join(STORAGE_DIR, 'owner.json'),
|
|
16
|
-
admin: path.join(STORAGE_DIR, 'admin.json'),
|
|
17
|
-
manager: path.join(STORAGE_DIR, 'manager.json'),
|
|
18
|
-
member: path.join(STORAGE_DIR, 'member.json'),
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type UserRole = keyof typeof STORAGE_STATE
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { test as base, expect, type Page } from '@playwright/test'
|
|
2
|
-
import { STORAGE_STATE, type UserRole } from './auth'
|
|
3
|
-
|
|
4
|
-
type ApiError = {
|
|
5
|
-
url: string
|
|
6
|
-
status: number
|
|
7
|
-
method: string
|
|
8
|
-
statusText: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
type Fixtures = {
|
|
12
|
-
authenticatedPage: Page
|
|
13
|
-
role: UserRole
|
|
14
|
-
apiErrors: ApiError[]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Endpoints that return errors due to missing env vars (e.g., Stripe),
|
|
19
|
-
* not actual application bugs. These are excluded from automatic failure reporting.
|
|
20
|
-
*/
|
|
21
|
-
const IGNORED_API_PATTERNS = [
|
|
22
|
-
/\/api\/billing\//,
|
|
23
|
-
/ontology\.getGraph/,
|
|
24
|
-
/ontology\.getEntityTypes/,
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Extended test fixture with pre-authenticated page and API error tracking.
|
|
29
|
-
*
|
|
30
|
-
* Usage:
|
|
31
|
-
* import { test, expect } from '../fixtures/base'
|
|
32
|
-
* test('my test', async ({ authenticatedPage }) => { ... })
|
|
33
|
-
*
|
|
34
|
-
* Default role is 'owner'. Override per-describe:
|
|
35
|
-
* test.use({ role: 'member' })
|
|
36
|
-
*
|
|
37
|
-
* API errors (4xx, 5xx) are automatically tracked and reported in test failures.
|
|
38
|
-
* Env-dependent endpoints (billing, etc.) are excluded — see IGNORED_API_PATTERNS.
|
|
39
|
-
* Access via `apiErrors` fixture for explicit assertions.
|
|
40
|
-
*/
|
|
41
|
-
export const test = base.extend<Fixtures>({
|
|
42
|
-
role: ['owner', { option: true }],
|
|
43
|
-
|
|
44
|
-
authenticatedPage: async ({ browser, role }, use) => {
|
|
45
|
-
const context = await browser.newContext({
|
|
46
|
-
storageState: STORAGE_STATE[role],
|
|
47
|
-
viewport: { width: 1280, height: 720 },
|
|
48
|
-
})
|
|
49
|
-
const page = await context.newPage()
|
|
50
|
-
await use(page)
|
|
51
|
-
await context.close()
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
apiErrors: async ({ authenticatedPage }, use) => {
|
|
55
|
-
const errors: ApiError[] = []
|
|
56
|
-
|
|
57
|
-
authenticatedPage.on('response', (response) => {
|
|
58
|
-
const status = response.status()
|
|
59
|
-
if (status >= 400) {
|
|
60
|
-
const url = response.url()
|
|
61
|
-
if (url.includes('/api/') || url.includes('/v1/')) {
|
|
62
|
-
const path = url.replace(/^https?:\/\/[^/]+/, '')
|
|
63
|
-
const isIgnored = IGNORED_API_PATTERNS.some((p) => p.test(path))
|
|
64
|
-
if (!isIgnored) {
|
|
65
|
-
errors.push({
|
|
66
|
-
url: path,
|
|
67
|
-
status,
|
|
68
|
-
method: response.request().method(),
|
|
69
|
-
statusText: response.statusText(),
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
await use(errors)
|
|
77
|
-
|
|
78
|
-
if (errors.length > 0) {
|
|
79
|
-
const report = errors
|
|
80
|
-
.map((e) => ` ${e.method} ${e.url} → ${e.status} ${e.statusText}`)
|
|
81
|
-
.join('\n')
|
|
82
|
-
expect(
|
|
83
|
-
errors,
|
|
84
|
-
`API errors detected during test:\n${report}`,
|
|
85
|
-
).toHaveLength(0)
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
export { expect }
|
|
File without changes
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"cookies": [
|
|
3
|
-
{
|
|
4
|
-
"name": "session-token",
|
|
5
|
-
"value": "594120f0-6282-43d8-ba54-aeb6f65f9098",
|
|
6
|
-
"domain": "localhost",
|
|
7
|
-
"path": "/",
|
|
8
|
-
"expires": 1776952153.911341,
|
|
9
|
-
"httpOnly": true,
|
|
10
|
-
"secure": false,
|
|
11
|
-
"sameSite": "Lax"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"name": "csrf-token",
|
|
15
|
-
"value": "398b6e02b0b7171f76c0733b02ebf0dbd1c92b52c544d53cfc644b2ef3acbc90%7C2ea44d71177afdfd46dc06f295164ae67743b12c8164a058f786ae3fdf12d330",
|
|
16
|
-
"domain": "localhost",
|
|
17
|
-
"path": "/",
|
|
18
|
-
"expires": 1776918372.994304,
|
|
19
|
-
"httpOnly": true,
|
|
20
|
-
"secure": false,
|
|
21
|
-
"sameSite": "Lax"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"name": "NEXT_LOCALE",
|
|
25
|
-
"value": "pt-BR",
|
|
26
|
-
"domain": "localhost",
|
|
27
|
-
"path": "/",
|
|
28
|
-
"expires": 1807043134.189633,
|
|
29
|
-
"httpOnly": false,
|
|
30
|
-
"secure": false,
|
|
31
|
-
"sameSite": "Lax"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"name": "callback-url",
|
|
35
|
-
"value": "http%3A%2F%2Flocalhost%3A3000",
|
|
36
|
-
"domain": "localhost",
|
|
37
|
-
"path": "/",
|
|
38
|
-
"expires": 1776348335.004668,
|
|
39
|
-
"httpOnly": true,
|
|
40
|
-
"secure": false,
|
|
41
|
-
"sameSite": "Lax"
|
|
42
|
-
}
|
|
43
|
-
],
|
|
44
|
-
"origins": [
|
|
45
|
-
{
|
|
46
|
-
"origin": "http://localhost:3000",
|
|
47
|
-
"localStorage": []
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"cookies": [
|
|
3
|
-
{
|
|
4
|
-
"name": "session-token",
|
|
5
|
-
"value": "594120f0-6282-43d8-ba54-aeb6f65f9098",
|
|
6
|
-
"domain": "localhost",
|
|
7
|
-
"path": "/",
|
|
8
|
-
"expires": 1776952153.911341,
|
|
9
|
-
"httpOnly": true,
|
|
10
|
-
"secure": false,
|
|
11
|
-
"sameSite": "Lax"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"name": "csrf-token",
|
|
15
|
-
"value": "398b6e02b0b7171f76c0733b02ebf0dbd1c92b52c544d53cfc644b2ef3acbc90%7C2ea44d71177afdfd46dc06f295164ae67743b12c8164a058f786ae3fdf12d330",
|
|
16
|
-
"domain": "localhost",
|
|
17
|
-
"path": "/",
|
|
18
|
-
"expires": 1776918372.994304,
|
|
19
|
-
"httpOnly": true,
|
|
20
|
-
"secure": false,
|
|
21
|
-
"sameSite": "Lax"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"name": "NEXT_LOCALE",
|
|
25
|
-
"value": "pt-BR",
|
|
26
|
-
"domain": "localhost",
|
|
27
|
-
"path": "/",
|
|
28
|
-
"expires": 1807043134.189633,
|
|
29
|
-
"httpOnly": false,
|
|
30
|
-
"secure": false,
|
|
31
|
-
"sameSite": "Lax"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"name": "callback-url",
|
|
35
|
-
"value": "http%3A%2F%2Flocalhost%3A3000",
|
|
36
|
-
"domain": "localhost",
|
|
37
|
-
"path": "/",
|
|
38
|
-
"expires": 1776348335.004668,
|
|
39
|
-
"httpOnly": true,
|
|
40
|
-
"secure": false,
|
|
41
|
-
"sameSite": "Lax"
|
|
42
|
-
}
|
|
43
|
-
],
|
|
44
|
-
"origins": [
|
|
45
|
-
{
|
|
46
|
-
"origin": "http://localhost:3000",
|
|
47
|
-
"localStorage": []
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"cookies": [
|
|
3
|
-
{
|
|
4
|
-
"name": "session-token",
|
|
5
|
-
"value": "594120f0-6282-43d8-ba54-aeb6f65f9098",
|
|
6
|
-
"domain": "localhost",
|
|
7
|
-
"path": "/",
|
|
8
|
-
"expires": 1776952153.911341,
|
|
9
|
-
"httpOnly": true,
|
|
10
|
-
"secure": false,
|
|
11
|
-
"sameSite": "Lax"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"name": "csrf-token",
|
|
15
|
-
"value": "398b6e02b0b7171f76c0733b02ebf0dbd1c92b52c544d53cfc644b2ef3acbc90%7C2ea44d71177afdfd46dc06f295164ae67743b12c8164a058f786ae3fdf12d330",
|
|
16
|
-
"domain": "localhost",
|
|
17
|
-
"path": "/",
|
|
18
|
-
"expires": 1776918372.994304,
|
|
19
|
-
"httpOnly": true,
|
|
20
|
-
"secure": false,
|
|
21
|
-
"sameSite": "Lax"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"name": "NEXT_LOCALE",
|
|
25
|
-
"value": "pt-BR",
|
|
26
|
-
"domain": "localhost",
|
|
27
|
-
"path": "/",
|
|
28
|
-
"expires": 1807043134.189633,
|
|
29
|
-
"httpOnly": false,
|
|
30
|
-
"secure": false,
|
|
31
|
-
"sameSite": "Lax"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"name": "callback-url",
|
|
35
|
-
"value": "http%3A%2F%2Flocalhost%3A3000",
|
|
36
|
-
"domain": "localhost",
|
|
37
|
-
"path": "/",
|
|
38
|
-
"expires": 1776348335.004668,
|
|
39
|
-
"httpOnly": true,
|
|
40
|
-
"secure": false,
|
|
41
|
-
"sameSite": "Lax"
|
|
42
|
-
}
|
|
43
|
-
],
|
|
44
|
-
"origins": [
|
|
45
|
-
{
|
|
46
|
-
"origin": "http://localhost:3000",
|
|
47
|
-
"localStorage": []
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"cookies": [
|
|
3
|
-
{
|
|
4
|
-
"name": "session-token",
|
|
5
|
-
"value": "594120f0-6282-43d8-ba54-aeb6f65f9098",
|
|
6
|
-
"domain": "localhost",
|
|
7
|
-
"path": "/",
|
|
8
|
-
"expires": 1776952153.911341,
|
|
9
|
-
"httpOnly": true,
|
|
10
|
-
"secure": false,
|
|
11
|
-
"sameSite": "Lax"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"name": "csrf-token",
|
|
15
|
-
"value": "398b6e02b0b7171f76c0733b02ebf0dbd1c92b52c544d53cfc644b2ef3acbc90%7C2ea44d71177afdfd46dc06f295164ae67743b12c8164a058f786ae3fdf12d330",
|
|
16
|
-
"domain": "localhost",
|
|
17
|
-
"path": "/",
|
|
18
|
-
"expires": 1776918372.994304,
|
|
19
|
-
"httpOnly": true,
|
|
20
|
-
"secure": false,
|
|
21
|
-
"sameSite": "Lax"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"name": "NEXT_LOCALE",
|
|
25
|
-
"value": "pt-BR",
|
|
26
|
-
"domain": "localhost",
|
|
27
|
-
"path": "/",
|
|
28
|
-
"expires": 1807043134.189633,
|
|
29
|
-
"httpOnly": false,
|
|
30
|
-
"secure": false,
|
|
31
|
-
"sameSite": "Lax"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"name": "callback-url",
|
|
35
|
-
"value": "http%3A%2F%2Flocalhost%3A3000",
|
|
36
|
-
"domain": "localhost",
|
|
37
|
-
"path": "/",
|
|
38
|
-
"expires": 1776348335.004668,
|
|
39
|
-
"httpOnly": true,
|
|
40
|
-
"secure": false,
|
|
41
|
-
"sameSite": "Lax"
|
|
42
|
-
}
|
|
43
|
-
],
|
|
44
|
-
"origins": [
|
|
45
|
-
{
|
|
46
|
-
"origin": "http://localhost:3000",
|
|
47
|
-
"localStorage": []
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
}
|