test(phase-11): add Nyquist validation coverage

This commit is contained in:
curo1305
2026-06-17 09:58:59 +02:00
parent f5fc8d111b
commit 64aa960d20
2 changed files with 165 additions and 0 deletions
@@ -0,0 +1,109 @@
---
phase: 11
slug: visual-design-responsive-layout-cleanup
status: partial
nyquist_compliant: false
wave_0_complete: true
created: 2026-06-17
updated: 2026-06-17
---
# Phase 11 - Validation Strategy
> Retroactive Nyquist validation for Phase 11: Visual Design, Responsive Layout & Cleanup.
---
## Test Infrastructure
| Property | Value |
|----------|-------|
| Framework | Vitest 4.1.7 + Vue Test Utils + happy-dom |
| Config file | `frontend/vitest.config.js` |
| Quick run command | `cd frontend && npm run test -- --run src/components/ui/__tests__/TreeItem.test.js` |
| Full suite command | `cd frontend && npm run test -- --run` |
| Build gate | `cd frontend && npm run build` |
| Estimated runtime | ~4 seconds for full Vitest, ~2 seconds for build |
---
## Sampling Rate
- After every Phase 11 task commit: run the focused component/router test touched by the task.
- After every Phase 11 plan: run `cd frontend && npm run test -- --run`.
- Before `/gsd:verify-work`: run `cd frontend && npm run test -- --run` and `cd frontend && npm run build`.
- Max feedback latency: under 10 seconds locally for the automated frontend gate.
---
## Per-Task Verification Map
| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
| 11-01-01 | 11-01 | 1 | PERF-02 | - | Analyzer is opt-in and normal builds do not emit `stats.html` | build/static | `cd frontend && npm run build`; `cd frontend && ANALYZE=true npm run build` | yes: `.planning/perf/phase11-baseline.html`, `.planning/perf/phase11-baseline-summary.md` | green |
| 11-02-01 | 11-02 | 1 | PERF-03 | S-01 | Lazy route components preserve auth/admin guard behavior | unit/integration | `cd frontend && npm run test -- --run src/router/__tests__/router.guard.test.js` | yes | green |
| 11-03-01 | 11-03 | 1 | RESP-01 | - | User drawer opens, closes by backdrop, and closes on route change | unit | `cd frontend && npm run test -- --run src/__tests__/keyboard.test.js` | yes | green |
| 11-03-02 | 11-03 | 1 | RESP-02 | - | Storage list uses mobile-first columns; size hidden below `md`, modified hidden below `sm` | unit | `cd frontend && npm run test -- --run src/components/storage/__tests__/StorageBrowser.skeleton.test.js` | yes | green |
| 11-03-03 | 11-03 | 1 | RESP-03 | - | Storage row action buttons expose 36px mobile touch target classes | unit | `cd frontend && npm run test -- --run src/components/storage/__tests__/StorageBrowser.skeleton.test.js` | yes | green |
| 11-03-04 | 11-03 | 1 | RESP-05 | - | Admin drawer mirrors the user drawer pattern | unit | `cd frontend && npm run test -- --run src/__tests__/keyboard.test.js` | yes | green |
| 11-04-01 | 11-04 | 1 | VISUAL-02 | T-01 | Forms keep normalized focus classes and rely on active Tailwind forms plugin | unit/static | `cd frontend && npm run test -- --run src/components/sharing/__tests__/ShareModal.mobile.test.js src/components/cloud/__tests__/CloudCredentialModal.mobile.test.js` | yes | green |
| 11-04-02 | 11-04 | 1 | RESP-04 | - | Modals fit small viewports with scrollable panels or responsive full-screen header | unit | `cd frontend && npm run test -- --run src/components/sharing/__tests__/ShareModal.mobile.test.js src/components/cloud/__tests__/CloudCredentialModal.mobile.test.js src/components/folders/__tests__/FolderDeleteModal.mobile.test.js src/components/documents/__tests__/DocumentPreviewModal.mobile.test.js` | yes | green |
| 11-05-01 | 11-05 | 1 | VISUAL-01 | - | Skeleton widths use Tailwind width classes, not inline width styles | unit/static | `cd frontend && npm run test -- --run src/components/layout/__tests__/AppSidebar.visual.test.js src/components/ui/__tests__/TreeItem.test.js` | yes | green |
| 11-05-02 | 11-05 | 1 | VISUAL-03 | T-01 | TreeItem interactive elements expose focus-visible rings; sidebar controls retain focus rings | unit/static | `cd frontend && npm run test -- --run src/components/layout/__tests__/AppSidebar.visual.test.js src/components/ui/__tests__/TreeItem.test.js` | yes | green |
| 11-05-03 | 11-05 | 1 | VISUAL-04 | - | Page and panel headings use normalized font size/weight classes | unit/static | `cd frontend && npm run test -- --run src/views/__tests__/typography.visual.test.js` | yes | green |
| 11-06-01 | 11-06 | 1 | CODE-07 | - | Confirmed dead route/components removed and stale tests deleted | static/test | `find frontend/src -name 'AccountView.vue' -o -name 'FolderRow.vue' -o -name 'HomeView.vue' -o -name 'FolderView.vue' -o -name 'AdminView.vue'`; `cd frontend && npm run test -- --run` | yes | green |
| 11-06-02 | 11-06 | 1 | PERF-02 | - | Final bundle report exists and compares baseline to final chunks | build/static | `cd frontend && npm run build`; `cd frontend && ANALYZE=true npm run build` | yes: `.planning/perf/phase11-final.html`, `.planning/perf/phase11-final-summary.md` | green |
---
## Wave 0 Requirements
Existing infrastructure covered the phase requirements. No Wave 0 framework installation was needed.
Retroactive validation added direct TreeItem assertions in `frontend/src/components/ui/__tests__/TreeItem.test.js` for:
- `VISUAL-01`: loading skeleton labels use `w-12`, `w-16`, `w-20` and do not set inline width.
- `VISUAL-03`: expand button, selectable row button, and router-link row include `focus-visible` ring classes.
---
## Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|----------|-------------|------------|-------------------|
| Cross-browser form rendering | VISUAL-02 | Vitest can assert classes and plugin wiring, but cannot prove Chrome/Firefox/Safari native control rendering is visually identical. | Open ShareModal, CloudCredentialModal, and SettingsAccountTab in Chrome, Firefox, and Safari. Inspect inputs, selects, textareas, checkboxes, and focus states for consistent appearance. |
| Focus ring visibility at 375px | VISUAL-03, RESP-01, RESP-05 | DOM tests assert focus classes and drawer state, but visual focus contrast and clipping require browser rendering. | In a 375x667 viewport, tab through hamburger buttons, sidebar links, StorageBrowser actions, and TreeItem expand/label controls. Confirm focus rings are visible and not clipped. |
| Touch target usability at 375px | RESP-03 | Tests assert `min-w-[36px] min-h-[36px]`; actual tap comfort requires emulated or physical touch input. | At 375px width, tap Rename, Delete, Share, and Move icon buttons in document/folder rows. Confirm each tap registers without precision targeting. |
---
## Validation Audit 2026-06-17
| Metric | Count |
|--------|-------|
| Requirements mapped | 12 |
| Automated coverage green | 12 |
| Automated gaps found | 1 |
| Automated gaps resolved | 1 |
| Manual-only checks remaining | 3 |
### Commands Run
| Command | Result |
|---------|--------|
| `cd frontend && npm run test -- --run src/components/ui/__tests__/TreeItem.test.js` | pass: 1 file, 6 tests |
| `cd frontend && npm run test -- --run` | pass: 36 files, 271 tests |
| `cd frontend && npm run build` | pass: production build succeeded; existing `auth.js` mixed static/dynamic import warning remains |
---
## Validation Sign-Off
- [x] All tasks have automated verify commands or documented build/static evidence.
- [x] Sampling continuity: no 3 consecutive Phase 11 tasks lack automated verification.
- [x] Wave 0 not required; existing Vitest infrastructure covers frontend validation.
- [x] No watch-mode flags used.
- [x] Feedback latency is under 10 seconds for the full local frontend gate.
- [ ] `nyquist_compliant: true` set in frontmatter.
**Approval:** partial 2026-06-17. Automated Phase 11 coverage is green; final sign-off still requires the three manual visual/touch checks above.
@@ -66,3 +66,59 @@ describe('Gap 1 regression: non-loading branches unchanged', () => {
expect(w.text()).toContain('Empty')
})
})
describe('Phase 11 validation: TreeItem visual invariants', () => {
it('applies focus-visible rings to the expand control and selectable row button', () => {
const w = mountItem()
const buttons = w.findAll('button')
expect(buttons).toHaveLength(2)
for (const button of buttons) {
const cls = button.attributes('class') || ''
expect(cls).toContain('focus-visible:outline-none')
expect(cls).toContain('focus-visible:ring-2')
expect(cls).toContain('focus-visible:ring-indigo-500')
expect(cls).toContain('focus-visible:ring-offset-1')
}
})
it('applies focus-visible rings to the router-link row', () => {
const w = mountItem({ to: '/folders/archive' })
const link = w.findComponent({ name: 'RouterLink' })
expect(link.exists()).toBe(true)
const cls = link.attributes('class') || ''
expect(cls).toContain('focus-visible:outline-none')
expect(cls).toContain('focus-visible:ring-2')
expect(cls).toContain('focus-visible:ring-indigo-500')
expect(cls).toContain('focus-visible:ring-offset-1')
})
it('uses Tailwind width classes for loading skeleton labels without inline width styles', async () => {
let resolveFn
const loadChildren = vi.fn()
.mockReturnValueOnce(Promise.resolve([]))
.mockReturnValue(new Promise(resolve => { resolveFn = resolve }))
const w = mountItem({ loadChildren })
await w.vm.$.setupState.toggleExpand()
await flushPromises()
w.vm.$.setupState.refresh()
await nextTick()
const skeletonLabels = w.findAll('.h-3.animate-pulse')
expect(skeletonLabels).toHaveLength(3)
expect(skeletonLabels.map(el => el.classes().find(cls => /^w-\d+$/.test(cls)))).toEqual([
'w-12',
'w-16',
'w-20',
])
for (const label of skeletonLabels) {
expect(label.attributes('style') || '').not.toMatch(/width\s*:/)
}
resolveFn([])
await flushPromises()
})
})