Skip to content

Commit 274840d

Browse files
JohnMcLearclaude
andcommitted
docs: test coverage audit at docs/test-coverage-audit.md
Pre-pass: 143 unit / 38 E2E, ~32 surfaces covered, ~100+ gaps. Post-pass: 273 unit / 41 E2E, all surfaces covered. Documents one real bug found: AddWorkspaceDialog test setup was missing the window.setActiveWorkspace mock; the dialog's submit() calls that IPC before closeDialog(), so the dialog never closed in test. Fixed in dialog test beforeEach. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent cf40e36 commit 274840d

1 file changed

Lines changed: 241 additions & 0 deletions

File tree

docs/test-coverage-audit.md

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
# Test Coverage Audit
2+
3+
**Date:** 2026-05-03
4+
**Branch:** `feat/linux-mvp`
5+
**Baseline:** 143 unit tests, 38 E2E tests
6+
**Post-fill:** 273 unit tests, 41 E2E tests
7+
8+
---
9+
10+
## Renderer Components
11+
12+
| Surface | Type | Pre-audit | Gap | Post-fill |
13+
|---|---|---|---|---|
14+
| WorkspaceRail – renders workspaces in order | render ||||
15+
| WorkspaceRail – click workspace → setActiveWorkspaceId + IPC | click | ⚠️ (store only, not IPC) | IPC assert missing ||
16+
| WorkspaceRail – settings cog → opens SettingsDialog | click || no test ||
17+
| WorkspaceRail – + button → opens AddWorkspaceDialog | click ||||
18+
| WorkspaceRail – active workspace shows ring style | render || no test ||
19+
| WorkspaceRail – button shows first 2 uppercase letters | render || no test ||
20+
| PadSidebar – shows Pinned + Recent sections | render ||||
21+
| PadSidebar – click recent pad → tab.open | click ||||
22+
| PadSidebar – + New Pad → opens OpenPadDialog | click ||||
23+
| PadSidebar – no active workspace → empty aside | render ||||
24+
| PadSidebar – empty history (has wsId) → no Pinned section | render || no test ||
25+
| PadSidebar – click pinned pad → tab.open | click || no test ||
26+
| PadSidebar – uses title over padName when available | render || no test ||
27+
| PadSidebar – caps recent list at 50 | render || no test ||
28+
| TabStrip – renders tabs per workspace | render ||||
29+
| TabStrip – click tab → ipc.tab.focus | click ||||
30+
| TabStrip – click ✕ → ipc.tab.close | click ||||
31+
| TabStrip – error indicator for state=error | render ||||
32+
| TabStrip – error indicator for state=crashed | render || no test ||
33+
| TabStrip – active tab has aria-selected=true | render || no test ||
34+
| TabStrip – no tabs when activeWorkspaceId=null | render || no test ||
35+
| TabStrip – only shows tabs for active workspace | render || no test (duplicate test existed) ||
36+
| TabStrip – no error indicator for state=loaded | render || no test ||
37+
| TabErrorOverlay – hidden when state=loaded | render || **no test file** ||
38+
| TabErrorOverlay – hidden when no active tab | render || **no test file** ||
39+
| TabErrorOverlay – shows errorMessage in error state | render || **no test file** ||
40+
| TabErrorOverlay – falls back to serverUrl when no errorMessage | render || **no test file** ||
41+
| TabErrorOverlay – shows crashed message | render || **no test file** ||
42+
| TabErrorOverlay – Retry → ipc.tab.reload (error state) | click || **no test file** ||
43+
| TabErrorOverlay – Reload → ipc.tab.reload (crashed state) | click || **no test file** ||
44+
| TabErrorOverlay – Close tab → ipc.tab.close (error) | click || **no test file** ||
45+
| TabErrorOverlay – Close tab → ipc.tab.close (crashed) | click || **no test file** ||
46+
| TabErrorOverlay – Retry label in error state, Reload in crashed | render || **no test file** ||
47+
| EmptyState – Open Pad button → opens openPad dialog | click ||||
48+
| ErrorBoundary – renders children | render ||||
49+
| ErrorBoundary – fallback + onReload | click ||||
50+
| ErrorBoundary – show details toggles stack trace | click ||||
51+
52+
---
53+
54+
## Dialogs
55+
56+
| Surface | Type | Pre-audit | Gap | Post-fill |
57+
|---|---|---|---|---|
58+
| AddWorkspaceDialog – submit happy path | click ||||
59+
| AddWorkspaceDialog – ServerUnreachableError message | click ||||
60+
| AddWorkspaceDialog – NotAnEtherpadServerError message | click || no test ||
61+
| AddWorkspaceDialog – UrlValidationError message | click || no test ||
62+
| AddWorkspaceDialog – Cancel when dismissable | click ||||
63+
| AddWorkspaceDialog – Cancel hidden when not dismissable | render ||||
64+
| AddWorkspaceDialog – Add disabled when fields empty | render || no test ||
65+
| AddWorkspaceDialog – Add disabled when only name filled | render || no test ||
66+
| AddWorkspaceDialog – Add enabled when both filled | render || no test ||
67+
| AddWorkspaceDialog – color swatch click changes pressed state | click || no test ||
68+
| AddWorkspaceDialog – submit uses selected color | click || no test ||
69+
| AddWorkspaceDialog – successful submit closes dialog | click || no test ||
70+
| OpenPadDialog – submit calls tab.open with name | click ||||
71+
| OpenPadDialog – autocomplete shows on type | render ||||
72+
| OpenPadDialog – create mode calls tab.open with mode=create | click ||||
73+
| OpenPadDialog – Open disabled when name empty | render || no test ||
74+
| OpenPadDialog – Open enabled when name filled | render || no test ||
75+
| OpenPadDialog – Cancel closes dialog | click || no test ||
76+
| OpenPadDialog – click autocomplete suggestion → tab.open | click || no test ||
77+
| OpenPadDialog – autocomplete suggestion click closes dialog | click || no test ||
78+
| OpenPadDialog – successful submit closes dialog | click || no test ||
79+
| OpenPadDialog – empty name submit does nothing (button disabled) | render || no test ||
80+
| SettingsDialog – zoom change + save | click ||||
81+
| SettingsDialog – language dropdown options | render ||||
82+
| SettingsDialog – language change + save | click ||||
83+
| SettingsDialog – rememberTabs checkbox toggle + save | click || no test ||
84+
| SettingsDialog – Save closes dialog | click || no test ||
85+
| SettingsDialog – Cancel closes without calling update | click || no test ||
86+
| SettingsDialog – Clear All History → padHistory.clearAll | click || no test ||
87+
| SettingsDialog – Remove workspace → opens RemoveWorkspaceDialog | click || no test ||
88+
| SettingsDialog – Remove button per workspace | render || no test ||
89+
| RemoveWorkspaceDialog – confirm calls workspace.remove | click ||||
90+
| RemoveWorkspaceDialog – shows workspace name | render || no test ||
91+
| RemoveWorkspaceDialog – Cancel closes without remove | click || no test ||
92+
| RemoveWorkspaceDialog – confirm closes dialog on success | click || no test ||
93+
| RemoveWorkspaceDialog – confirm calls setActiveWorkspace | click || no test ||
94+
| RemoveWorkspaceDialog – renders nothing for unknown workspaceId | render || no test ||
95+
| RemoveWorkspaceDialog – shows error on failed remove | click || no test ||
96+
| HttpAuthDialog – submit with credentials | click ||||
97+
| HttpAuthDialog – Cancel → respond(cancel=true) | click || no test ||
98+
| HttpAuthDialog – Cancel closes dialog | click || no test ||
99+
| HttpAuthDialog – Sign In disabled when username empty | render || no test ||
100+
| HttpAuthDialog – Sign In enabled after typing username | render || no test ||
101+
| HttpAuthDialog – shows URL from context | render || no test ||
102+
| HttpAuthDialog – Sign In closes dialog on success | click || no test ||
103+
| AboutDialog – heading visible + Close closes | click ||||
104+
105+
---
106+
107+
## IPC Handlers (main process)
108+
109+
| Surface | Type | Pre-audit | Gap | Post-fill |
110+
|---|---|---|---|---|
111+
| workspace.add – happy path | handler ||||
112+
| workspace.add – NotAnEtherpadServerError | handler ||||
113+
| workspace.add – ServerUnreachableError | handler ||||
114+
| workspace.remove – happy path with ordering | handler ||||
115+
| workspace.remove – WorkspaceNotFoundError | handler ||||
116+
| workspace.update – happy path | handler || no test ||
117+
| workspace.update – WorkspaceNotFoundError | handler || no test ||
118+
| workspace.update – InvalidPayloadError (bad uuid) | handler || no test ||
119+
| workspace.list – returns all workspaces | handler || no test ||
120+
| workspace.reorder – happy path | handler || no test ||
121+
| workspace.reorder – id set mismatch error | handler || no test ||
122+
| tab.open – happy path + history stamp | handler ||||
123+
| tab.open – WorkspaceNotFoundError | handler ||||
124+
| tab.open – InvalidPayloadError (empty padName) | handler || no test ||
125+
| tab.open – mode=create passes through | handler || no test ||
126+
| tab.open – emits tabsChanged + padHistoryChanged | handler || no test ||
127+
| tab.close – calls closeInAnyWindow + emits | handler || no test ||
128+
| tab.close – InvalidPayloadError (empty tabId) | handler || no test ||
129+
| tab.focus – calls focusInAnyWindow + emits | handler || no test ||
130+
| tab.focus – InvalidPayloadError (empty tabId) | handler || no test ||
131+
| tab.reload – calls reloadInAnyWindow | handler || no test ||
132+
| tab.reload – InvalidPayloadError (empty tabId) | handler || no test ||
133+
| window.setActiveWorkspace – happy path + emits | handler || no test ||
134+
| window.setActiveWorkspace – accepts null workspaceId | handler || no test ||
135+
| window.setActiveWorkspace – InvalidPayloadError | handler || no test ||
136+
| window.reloadShell – calls reloadShellOfActiveWindow | handler || no test ||
137+
| window.setPadViewsHidden – true/false paths | handler || no test ||
138+
| window.setPadViewsHidden – InvalidPayloadError | handler || no test ||
139+
| padHistory.list – empty workspace | handler || **no test file** ||
140+
| padHistory.list – known workspace | handler || **no test file** ||
141+
| padHistory.list – InvalidPayloadError | handler || **no test file** ||
142+
| padHistory.pin – pins entry + emits | handler || **no test file** ||
143+
| padHistory.pin – silent no-op for unknown padName | handler || **no test file** ||
144+
| padHistory.pin – InvalidPayloadError (empty padName) | handler || **no test file** ||
145+
| padHistory.unpin – unpins entry + emits | handler || **no test file** ||
146+
| padHistory.unpin – silent no-op for unknown | handler || **no test file** ||
147+
| padHistory.clearRecent – clears workspace entries | handler || **no test file** ||
148+
| padHistory.clearRecent – no-op on unknown workspace | handler || **no test file** ||
149+
| padHistory.clearAll – clears all entries | handler || **no test file** ||
150+
| settings.get – returns defaults | handler || no test ||
151+
| settings.update – single field | handler || no test ||
152+
| settings.update – multiple fields | handler || no test ||
153+
| settings.update – triggers reloadAllPadsWithLanguage | handler || no test ||
154+
| settings.update – no reload when language unchanged | handler || no test ||
155+
| settings.update – InvalidPayloadError (unknown field) | handler || no test ||
156+
| settings.update – persists across reads | handler || no test ||
157+
| state.getInitial – returns workspaces + order + settings | handler ||||
158+
159+
---
160+
161+
## App Event Subscriptions (renderer)
162+
163+
| Surface | Type | Pre-audit | Gap | Post-fill |
164+
|---|---|---|---|---|
165+
| onWorkspacesChanged – updates workspaces + order | event || **no test file** ||
166+
| onWorkspacesChanged – replaces with empty list | event || **no test file** ||
167+
| onTabsChanged – replaces tabs + activeTabId | event || **no test file** ||
168+
| onTabsChanged – activeTabId=null sets to null | event || **no test file** ||
169+
| onTabsChanged – missing activeTabId leaves existing unchanged | event || **no test file** ||
170+
| onTabState – updates state field | event || **no test file** ||
171+
| onTabState – updates errorMessage | event || **no test file** ||
172+
| onTabState – updates title | event || **no test file** ||
173+
| onTabState – does not touch other tabs | event || **no test file** ||
174+
| onTabState – no-op for unknown tabId | event || **no test file** ||
175+
| onSettingsChanged – updates settings store | event || **no test file** ||
176+
| onHttpLoginRequest – opens httpAuth dialog with payload | event || **no test file** ||
177+
| onMenuShellMessage – menu.newTab → openPad dialog | event || **no test file** ||
178+
| onMenuShellMessage – menu.openPad → openPad dialog | event || **no test file** ||
179+
| onMenuShellMessage – menu.settings → settings dialog | event || **no test file** ||
180+
| onMenuShellMessage – menu.about → about dialog | event || **no test file** ||
181+
| onMenuShellMessage – menu.reload → ipc.tab.reload | event || **no test file** ||
182+
| onMenuShellMessage – menu.reload no-ops when no active tab | event || **no test file** ||
183+
| onMenuShellMessage – unknown kind → no dialog | event || **no test file** ||
184+
| onPadHistoryChanged – calls list + stores result | event || **no test file** ||
185+
| onPadHistoryChanged – no-op when activeWorkspaceId=null | event || **no test file** ||
186+
187+
---
188+
189+
## Menu Template (main process unit)
190+
191+
| Surface | Type | Pre-audit | Gap | Post-fill |
192+
|---|---|---|---|---|
193+
| Contains File/Edit/View/Window/Help | unit ||||
194+
| File menu accelerators | unit ||||
195+
| File > New Tab click → cb.newTab() | unit ||||
196+
| File > Open Pad… click → cb.openPad() | unit || no test ||
197+
| File > Settings click → cb.settings() | unit || no test ||
198+
| File > Quit click → cb.quit() | unit || no test ||
199+
| View > Reload Pad click → cb.reload() | unit || no test ||
200+
| View > Reload Pad has CmdOrCtrl+R | unit || no test ||
201+
| Help > About click → cb.about() | unit || no test ||
202+
| Help > Open Log Folder click → cb.openLogs() | unit || no test ||
203+
| Edit roles (undo/redo/cut/copy/paste/selectAll) | unit || no test ||
204+
| View roles (resetZoom/zoomIn/zoomOut/togglefullscreen) | unit || no test ||
205+
| Window roles (minimize/close) | unit || no test ||
206+
| File > Close Tab has role=close | unit || no test ||
207+
208+
---
209+
210+
## E2E Menu Tests
211+
212+
| Surface | Type | Pre-audit | Gap | Post-fill |
213+
|---|---|---|---|---|
214+
| File > New Tab → OpenPadDialog visible | e2e ||||
215+
| File > Open Pad… → OpenPadDialog visible | e2e ||||
216+
| File > Settings → SettingsDialog visible | e2e ||||
217+
| Help > About → AboutDialog visible | e2e ||||
218+
| Help > Open Log Folder → item exists + no crash | e2e || no test ||
219+
| View > Reload Pad → item exists + fires | e2e || no test ||
220+
| File > Quit → item exists (click omitted to not kill process) | e2e || no test ||
221+
222+
---
223+
224+
## Summary
225+
226+
**Pre-pass:**
227+
- Unit tests: 143
228+
- E2E tests: 38
229+
- ✅ Covered: ~32 surfaces
230+
- ⚠️ Fluffy: ~4 surfaces (IPC call side not asserted)
231+
- ❌ Gaps: ~100+ surfaces
232+
233+
**Post-pass:**
234+
- Unit tests: 273 (+130)
235+
- E2E tests: 41 (+3)
236+
- ✅ All surfaces covered
237+
- ⚠️ Fluffy: 0
238+
- ❌ Gaps: 0
239+
240+
**Bugs found during pass:**
241+
- `AddWorkspaceDialog` test setup was missing `window.etherpadDesktop.window.setActiveWorkspace` mock, causing the "successful submit closes dialog" test to fail (the dialog's `submit()` calls `ipc.window.setActiveWorkspace` synchronously before `dialogActions.closeDialog()`). Fixed by adding the mock to `beforeEach`.

0 commit comments

Comments
 (0)