reigncode-app 1.3.2

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.
Files changed (300) hide show
  1. package/AGENTS.md +30 -0
  2. package/Dockerfile +21 -0
  3. package/README.md +51 -0
  4. package/bunfig.toml +3 -0
  5. package/create-effect-simplification-spec.md +515 -0
  6. package/e2e/AGENTS.md +226 -0
  7. package/e2e/actions.ts +1018 -0
  8. package/e2e/app/home.spec.ts +24 -0
  9. package/e2e/app/navigation.spec.ts +10 -0
  10. package/e2e/app/palette.spec.ts +20 -0
  11. package/e2e/app/server-default.spec.ts +58 -0
  12. package/e2e/app/session.spec.ts +16 -0
  13. package/e2e/app/titlebar-history.spec.ts +120 -0
  14. package/e2e/commands/input-focus.spec.ts +15 -0
  15. package/e2e/commands/panels.spec.ts +33 -0
  16. package/e2e/commands/tab-close.spec.ts +32 -0
  17. package/e2e/files/file-open.spec.ts +31 -0
  18. package/e2e/files/file-tree.spec.ts +56 -0
  19. package/e2e/files/file-viewer.spec.ts +156 -0
  20. package/e2e/fixtures.ts +154 -0
  21. package/e2e/models/model-picker.spec.ts +48 -0
  22. package/e2e/models/models-visibility.spec.ts +61 -0
  23. package/e2e/projects/project-edit.spec.ts +43 -0
  24. package/e2e/projects/projects-close.spec.ts +54 -0
  25. package/e2e/projects/projects-switch.spec.ts +116 -0
  26. package/e2e/projects/workspace-new-session.spec.ts +94 -0
  27. package/e2e/projects/workspaces.spec.ts +375 -0
  28. package/e2e/prompt/context.spec.ts +95 -0
  29. package/e2e/prompt/prompt-async.spec.ts +76 -0
  30. package/e2e/prompt/prompt-drop-file-uri.spec.ts +22 -0
  31. package/e2e/prompt/prompt-drop-file.spec.ts +30 -0
  32. package/e2e/prompt/prompt-history.spec.ts +184 -0
  33. package/e2e/prompt/prompt-mention.spec.ts +26 -0
  34. package/e2e/prompt/prompt-multiline.spec.ts +24 -0
  35. package/e2e/prompt/prompt-shell.spec.ts +62 -0
  36. package/e2e/prompt/prompt-slash-open.spec.ts +22 -0
  37. package/e2e/prompt/prompt-slash-share.spec.ts +64 -0
  38. package/e2e/prompt/prompt-slash-terminal.spec.ts +18 -0
  39. package/e2e/prompt/prompt.spec.ts +55 -0
  40. package/e2e/selectors.ts +75 -0
  41. package/e2e/session/session-child-navigation.spec.ts +37 -0
  42. package/e2e/session/session-composer-dock.spec.ts +530 -0
  43. package/e2e/session/session-model-persistence.spec.ts +359 -0
  44. package/e2e/session/session-review.spec.ts +426 -0
  45. package/e2e/session/session-undo-redo.spec.ts +233 -0
  46. package/e2e/session/session.spec.ts +174 -0
  47. package/e2e/settings/settings-keybinds.spec.ts +389 -0
  48. package/e2e/settings/settings-models.spec.ts +122 -0
  49. package/e2e/settings/settings-providers.spec.ts +136 -0
  50. package/e2e/settings/settings.spec.ts +519 -0
  51. package/e2e/sidebar/sidebar-popover-actions.spec.ts +118 -0
  52. package/e2e/sidebar/sidebar-session-links.spec.ts +30 -0
  53. package/e2e/sidebar/sidebar.spec.ts +40 -0
  54. package/e2e/status/status-popover.spec.ts +94 -0
  55. package/e2e/terminal/terminal-init.spec.ts +28 -0
  56. package/e2e/terminal/terminal-reconnect.spec.ts +46 -0
  57. package/e2e/terminal/terminal-tabs.spec.ts +168 -0
  58. package/e2e/terminal/terminal.spec.ts +18 -0
  59. package/e2e/thinking-level.spec.ts +25 -0
  60. package/e2e/tsconfig.json +9 -0
  61. package/e2e/utils.ts +63 -0
  62. package/happydom.ts +75 -0
  63. package/index.html +23 -0
  64. package/package.json +77 -0
  65. package/playwright.config.ts +45 -0
  66. package/public/_headers +17 -0
  67. package/public/oc-theme-preload.js +35 -0
  68. package/script/e2e-local.ts +180 -0
  69. package/src/addons/serialize.test.ts +319 -0
  70. package/src/addons/serialize.ts +634 -0
  71. package/src/app.tsx +308 -0
  72. package/src/components/debug-bar.tsx +443 -0
  73. package/src/components/dialog-connect-provider.tsx +617 -0
  74. package/src/components/dialog-custom-provider-form.ts +158 -0
  75. package/src/components/dialog-custom-provider.test.ts +80 -0
  76. package/src/components/dialog-custom-provider.tsx +329 -0
  77. package/src/components/dialog-edit-project.tsx +255 -0
  78. package/src/components/dialog-fork.tsx +108 -0
  79. package/src/components/dialog-manage-models.tsx +101 -0
  80. package/src/components/dialog-release-notes.tsx +144 -0
  81. package/src/components/dialog-select-directory.tsx +392 -0
  82. package/src/components/dialog-select-file.tsx +466 -0
  83. package/src/components/dialog-select-mcp.tsx +107 -0
  84. package/src/components/dialog-select-model-unpaid.tsx +137 -0
  85. package/src/components/dialog-select-model.tsx +220 -0
  86. package/src/components/dialog-select-provider.tsx +86 -0
  87. package/src/components/dialog-select-server.tsx +649 -0
  88. package/src/components/dialog-settings.tsx +73 -0
  89. package/src/components/file-tree.test.ts +78 -0
  90. package/src/components/file-tree.tsx +507 -0
  91. package/src/components/link.tsx +26 -0
  92. package/src/components/model-tooltip.tsx +91 -0
  93. package/src/components/prompt-input/attachments.test.ts +44 -0
  94. package/src/components/prompt-input/attachments.ts +201 -0
  95. package/src/components/prompt-input/build-request-parts.test.ts +312 -0
  96. package/src/components/prompt-input/build-request-parts.ts +175 -0
  97. package/src/components/prompt-input/context-items.tsx +88 -0
  98. package/src/components/prompt-input/drag-overlay.tsx +25 -0
  99. package/src/components/prompt-input/editor-dom.test.ts +99 -0
  100. package/src/components/prompt-input/editor-dom.ts +148 -0
  101. package/src/components/prompt-input/files.ts +66 -0
  102. package/src/components/prompt-input/history.test.ts +153 -0
  103. package/src/components/prompt-input/history.ts +256 -0
  104. package/src/components/prompt-input/image-attachments.tsx +58 -0
  105. package/src/components/prompt-input/paste.ts +24 -0
  106. package/src/components/prompt-input/placeholder.test.ts +48 -0
  107. package/src/components/prompt-input/placeholder.ts +15 -0
  108. package/src/components/prompt-input/slash-popover.tsx +141 -0
  109. package/src/components/prompt-input/submit.test.ts +346 -0
  110. package/src/components/prompt-input/submit.ts +579 -0
  111. package/src/components/prompt-input.tsx +1595 -0
  112. package/src/components/server/server-row.tsx +130 -0
  113. package/src/components/session/index.ts +5 -0
  114. package/src/components/session/session-context-breakdown.test.ts +61 -0
  115. package/src/components/session/session-context-breakdown.ts +132 -0
  116. package/src/components/session/session-context-format.ts +20 -0
  117. package/src/components/session/session-context-metrics.test.ts +101 -0
  118. package/src/components/session/session-context-metrics.ts +82 -0
  119. package/src/components/session/session-context-tab.tsx +339 -0
  120. package/src/components/session/session-header.tsx +486 -0
  121. package/src/components/session/session-new-view.tsx +91 -0
  122. package/src/components/session/session-sortable-tab.tsx +70 -0
  123. package/src/components/session/session-sortable-terminal-tab.tsx +193 -0
  124. package/src/components/session-context-usage.tsx +122 -0
  125. package/src/components/settings-general.tsx +585 -0
  126. package/src/components/settings-keybinds.tsx +453 -0
  127. package/src/components/settings-list.tsx +5 -0
  128. package/src/components/settings-models.tsx +137 -0
  129. package/src/components/settings-providers.tsx +251 -0
  130. package/src/components/status-popover.tsx +419 -0
  131. package/src/components/terminal.tsx +653 -0
  132. package/src/components/titlebar-history.test.ts +63 -0
  133. package/src/components/titlebar-history.ts +57 -0
  134. package/src/components/titlebar.tsx +312 -0
  135. package/src/constants/file-picker.ts +89 -0
  136. package/src/context/command-keybind.test.ts +69 -0
  137. package/src/context/command.test.ts +25 -0
  138. package/src/context/command.tsx +437 -0
  139. package/src/context/comments.test.ts +186 -0
  140. package/src/context/comments.tsx +243 -0
  141. package/src/context/file/content-cache.ts +88 -0
  142. package/src/context/file/path.test.ts +360 -0
  143. package/src/context/file/path.ts +151 -0
  144. package/src/context/file/tree-store.ts +170 -0
  145. package/src/context/file/types.ts +41 -0
  146. package/src/context/file/view-cache.ts +146 -0
  147. package/src/context/file/watcher.test.ts +149 -0
  148. package/src/context/file/watcher.ts +53 -0
  149. package/src/context/file-content-eviction-accounting.test.ts +65 -0
  150. package/src/context/file.tsx +280 -0
  151. package/src/context/global-sdk.tsx +232 -0
  152. package/src/context/global-sync/bootstrap.ts +206 -0
  153. package/src/context/global-sync/child-store.test.ts +38 -0
  154. package/src/context/global-sync/child-store.ts +281 -0
  155. package/src/context/global-sync/event-reducer.test.ts +552 -0
  156. package/src/context/global-sync/event-reducer.ts +359 -0
  157. package/src/context/global-sync/eviction.ts +28 -0
  158. package/src/context/global-sync/queue.ts +83 -0
  159. package/src/context/global-sync/session-cache.test.ts +102 -0
  160. package/src/context/global-sync/session-cache.ts +62 -0
  161. package/src/context/global-sync/session-load.ts +25 -0
  162. package/src/context/global-sync/session-prefetch.test.ts +96 -0
  163. package/src/context/global-sync/session-prefetch.ts +100 -0
  164. package/src/context/global-sync/session-trim.test.ts +59 -0
  165. package/src/context/global-sync/session-trim.ts +56 -0
  166. package/src/context/global-sync/types.ts +133 -0
  167. package/src/context/global-sync/utils.ts +25 -0
  168. package/src/context/global-sync.test.ts +122 -0
  169. package/src/context/global-sync.tsx +408 -0
  170. package/src/context/highlights.tsx +233 -0
  171. package/src/context/language.tsx +248 -0
  172. package/src/context/layout-scroll.test.ts +64 -0
  173. package/src/context/layout-scroll.ts +126 -0
  174. package/src/context/layout.test.ts +69 -0
  175. package/src/context/layout.tsx +937 -0
  176. package/src/context/local.tsx +422 -0
  177. package/src/context/model-variant.test.ts +86 -0
  178. package/src/context/model-variant.ts +52 -0
  179. package/src/context/models.tsx +163 -0
  180. package/src/context/notification.tsx +373 -0
  181. package/src/context/permission-auto-respond.test.ts +102 -0
  182. package/src/context/permission-auto-respond.ts +51 -0
  183. package/src/context/permission.tsx +277 -0
  184. package/src/context/platform.tsx +99 -0
  185. package/src/context/prompt.tsx +297 -0
  186. package/src/context/sdk.tsx +49 -0
  187. package/src/context/server.tsx +295 -0
  188. package/src/context/settings.tsx +241 -0
  189. package/src/context/sync-optimistic.test.ts +123 -0
  190. package/src/context/sync.tsx +618 -0
  191. package/src/context/terminal-title.ts +51 -0
  192. package/src/context/terminal.test.ts +82 -0
  193. package/src/context/terminal.tsx +437 -0
  194. package/src/entry.tsx +144 -0
  195. package/src/env.d.ts +18 -0
  196. package/src/hooks/use-providers.ts +44 -0
  197. package/src/i18n/ar.ts +855 -0
  198. package/src/i18n/br.ts +867 -0
  199. package/src/i18n/bs.ts +943 -0
  200. package/src/i18n/da.ts +937 -0
  201. package/src/i18n/de.ts +879 -0
  202. package/src/i18n/en.ts +948 -0
  203. package/src/i18n/es.ts +950 -0
  204. package/src/i18n/fr.ts +878 -0
  205. package/src/i18n/ja.ts +861 -0
  206. package/src/i18n/ko.ts +860 -0
  207. package/src/i18n/no.ts +944 -0
  208. package/src/i18n/parity.test.ts +32 -0
  209. package/src/i18n/pl.ts +865 -0
  210. package/src/i18n/ru.ts +946 -0
  211. package/src/i18n/th.ts +933 -0
  212. package/src/i18n/tr.ts +952 -0
  213. package/src/i18n/zh.ts +930 -0
  214. package/src/i18n/zht.ts +925 -0
  215. package/src/index.css +29 -0
  216. package/src/index.ts +6 -0
  217. package/src/pages/directory-layout.tsx +88 -0
  218. package/src/pages/error.tsx +327 -0
  219. package/src/pages/home.tsx +131 -0
  220. package/src/pages/layout/deep-links.ts +50 -0
  221. package/src/pages/layout/helpers.test.ts +211 -0
  222. package/src/pages/layout/helpers.ts +98 -0
  223. package/src/pages/layout/inline-editor.tsx +126 -0
  224. package/src/pages/layout/sidebar-items.tsx +437 -0
  225. package/src/pages/layout/sidebar-project.tsx +384 -0
  226. package/src/pages/layout/sidebar-shell.tsx +125 -0
  227. package/src/pages/layout/sidebar-workspace.tsx +504 -0
  228. package/src/pages/layout.tsx +2509 -0
  229. package/src/pages/session/composer/index.ts +2 -0
  230. package/src/pages/session/composer/session-composer-region.tsx +255 -0
  231. package/src/pages/session/composer/session-composer-state.test.ts +128 -0
  232. package/src/pages/session/composer/session-composer-state.ts +249 -0
  233. package/src/pages/session/composer/session-followup-dock.tsx +109 -0
  234. package/src/pages/session/composer/session-permission-dock.tsx +74 -0
  235. package/src/pages/session/composer/session-question-dock.tsx +449 -0
  236. package/src/pages/session/composer/session-request-tree.ts +52 -0
  237. package/src/pages/session/composer/session-revert-dock.tsx +99 -0
  238. package/src/pages/session/composer/session-todo-dock.tsx +330 -0
  239. package/src/pages/session/file-tab-scroll.test.ts +40 -0
  240. package/src/pages/session/file-tab-scroll.ts +67 -0
  241. package/src/pages/session/file-tabs.tsx +456 -0
  242. package/src/pages/session/handoff.ts +36 -0
  243. package/src/pages/session/helpers.test.ts +181 -0
  244. package/src/pages/session/helpers.ts +198 -0
  245. package/src/pages/session/message-gesture.test.ts +62 -0
  246. package/src/pages/session/message-gesture.ts +21 -0
  247. package/src/pages/session/message-id-from-hash.ts +6 -0
  248. package/src/pages/session/message-timeline.tsx +1013 -0
  249. package/src/pages/session/review-tab.tsx +170 -0
  250. package/src/pages/session/session-layout.ts +20 -0
  251. package/src/pages/session/session-model-helpers.test.ts +51 -0
  252. package/src/pages/session/session-model-helpers.ts +16 -0
  253. package/src/pages/session/session-side-panel.tsx +453 -0
  254. package/src/pages/session/terminal-label.ts +16 -0
  255. package/src/pages/session/terminal-panel.test.ts +25 -0
  256. package/src/pages/session/terminal-panel.tsx +326 -0
  257. package/src/pages/session/use-session-commands.tsx +495 -0
  258. package/src/pages/session/use-session-hash-scroll.test.ts +16 -0
  259. package/src/pages/session/use-session-hash-scroll.ts +197 -0
  260. package/src/pages/session.tsx +1841 -0
  261. package/src/sst-env.d.ts +12 -0
  262. package/src/testing/model-selection.ts +80 -0
  263. package/src/testing/prompt.ts +56 -0
  264. package/src/testing/session-composer.ts +84 -0
  265. package/src/testing/terminal.ts +118 -0
  266. package/src/theme-preload.test.ts +46 -0
  267. package/src/utils/agent.ts +23 -0
  268. package/src/utils/aim.ts +138 -0
  269. package/src/utils/base64.ts +10 -0
  270. package/src/utils/comment-note.ts +88 -0
  271. package/src/utils/id.ts +99 -0
  272. package/src/utils/notification-click.test.ts +27 -0
  273. package/src/utils/notification-click.ts +13 -0
  274. package/src/utils/persist.test.ts +115 -0
  275. package/src/utils/persist.ts +476 -0
  276. package/src/utils/prompt.test.ts +44 -0
  277. package/src/utils/prompt.ts +203 -0
  278. package/src/utils/runtime-adapters.test.ts +62 -0
  279. package/src/utils/runtime-adapters.ts +39 -0
  280. package/src/utils/same.ts +6 -0
  281. package/src/utils/scoped-cache.test.ts +69 -0
  282. package/src/utils/scoped-cache.ts +104 -0
  283. package/src/utils/server-errors.test.ts +131 -0
  284. package/src/utils/server-errors.ts +80 -0
  285. package/src/utils/server-health.test.ts +123 -0
  286. package/src/utils/server-health.ts +91 -0
  287. package/src/utils/server.ts +22 -0
  288. package/src/utils/solid-dnd.tsx +49 -0
  289. package/src/utils/sound.ts +117 -0
  290. package/src/utils/terminal-writer.test.ts +64 -0
  291. package/src/utils/terminal-writer.ts +65 -0
  292. package/src/utils/time.ts +22 -0
  293. package/src/utils/uuid.test.ts +78 -0
  294. package/src/utils/uuid.ts +12 -0
  295. package/src/utils/worktree.test.ts +46 -0
  296. package/src/utils/worktree.ts +73 -0
  297. package/sst-env.d.ts +10 -0
  298. package/tsconfig.json +26 -0
  299. package/vite.config.ts +15 -0
  300. package/vite.js +26 -0
package/e2e/AGENTS.md ADDED
@@ -0,0 +1,226 @@
1
+ # E2E Testing Guide
2
+
3
+ ## Build/Lint/Test Commands
4
+
5
+ ```bash
6
+ # Run all e2e tests
7
+ bun test:e2e
8
+
9
+ # Run specific test file
10
+ bun test:e2e -- app/home.spec.ts
11
+
12
+ # Run single test by title
13
+ bun test:e2e -- -g "home renders and shows core entrypoints"
14
+
15
+ # Run tests with UI mode (for debugging)
16
+ bun test:e2e:ui
17
+
18
+ # Run tests locally with full server setup
19
+ bun test:e2e:local
20
+
21
+ # View test report
22
+ bun test:e2e:report
23
+
24
+ # Typecheck
25
+ bun typecheck
26
+ ```
27
+
28
+ ## Test Structure
29
+
30
+ All tests live in `packages/app/e2e/`:
31
+
32
+ ```
33
+ e2e/
34
+ ├── fixtures.ts # Test fixtures (test, expect, gotoSession, sdk)
35
+ ├── actions.ts # Reusable action helpers
36
+ ├── selectors.ts # DOM selectors
37
+ ├── utils.ts # Utilities (serverUrl, modKey, path helpers)
38
+ └── [feature]/
39
+ └── *.spec.ts # Test files
40
+ ```
41
+
42
+ ## Test Patterns
43
+
44
+ ### Basic Test Structure
45
+
46
+ ```typescript
47
+ import { test, expect } from "../fixtures"
48
+ import { promptSelector } from "../selectors"
49
+ import { withSession } from "../actions"
50
+
51
+ test("test description", async ({ page, sdk, gotoSession }) => {
52
+ await gotoSession() // or gotoSession(sessionID)
53
+
54
+ // Your test code
55
+ await expect(page.locator(promptSelector)).toBeVisible()
56
+ })
57
+ ```
58
+
59
+ ### Using Fixtures
60
+
61
+ - `page` - Playwright page
62
+ - `sdk` - OpenCode SDK client for API calls
63
+ - `gotoSession(sessionID?)` - Navigate to session
64
+
65
+ ### Helper Functions
66
+
67
+ **Actions** (`actions.ts`):
68
+
69
+ - `openPalette(page)` - Open command palette
70
+ - `openSettings(page)` - Open settings dialog
71
+ - `closeDialog(page, dialog)` - Close any dialog
72
+ - `openSidebar(page)` / `closeSidebar(page)` - Toggle sidebar
73
+ - `waitTerminalReady(page, { term? })` - Wait for a mounted terminal to connect and finish rendering output
74
+ - `runTerminal(page, { cmd, token, term?, timeout? })` - Type into the terminal via the browser and wait for rendered output
75
+ - `withSession(sdk, title, callback)` - Create temp session
76
+ - `withProject(...)` - Create temp project/workspace
77
+ - `sessionIDFromUrl(url)` - Read session ID from URL
78
+ - `slugFromUrl(url)` - Read workspace slug from URL
79
+ - `waitSlug(page, skip?)` - Wait for resolved workspace slug
80
+ - `trackSession(sessionID, directory?)` - Register session for fixture cleanup
81
+ - `trackDirectory(directory)` - Register directory for fixture cleanup
82
+ - `clickListItem(container, filter)` - Click list item by key/text
83
+
84
+ **Selectors** (`selectors.ts`):
85
+
86
+ - `promptSelector` - Prompt input
87
+ - `terminalSelector` - Terminal panel
88
+ - `sessionItemSelector(id)` - Session in sidebar
89
+ - `listItemSelector` - Generic list items
90
+
91
+ **Utils** (`utils.ts`):
92
+
93
+ - `modKey` - Meta (Mac) or Control (Linux/Win)
94
+ - `serverUrl` - Backend server URL
95
+ - `sessionPath(dir, id?)` - Build session URL
96
+
97
+ ## Code Style Guidelines
98
+
99
+ ### Imports
100
+
101
+ Always import from `../fixtures`, not `@playwright/test`:
102
+
103
+ ```typescript
104
+ // ✅ Good
105
+ import { test, expect } from "../fixtures"
106
+
107
+ // ❌ Bad
108
+ import { test, expect } from "@playwright/test"
109
+ ```
110
+
111
+ ### Naming Conventions
112
+
113
+ - Test files: `feature-name.spec.ts`
114
+ - Test names: lowercase, descriptive: `"sidebar can be toggled"`
115
+ - Variables: camelCase
116
+ - Constants: SCREAMING_SNAKE_CASE
117
+
118
+ ### Error Handling
119
+
120
+ Tests should clean up after themselves. Prefer fixture-managed cleanup:
121
+
122
+ ```typescript
123
+ test("test with cleanup", async ({ page, sdk, gotoSession }) => {
124
+ await withSession(sdk, "test session", async (session) => {
125
+ await gotoSession(session.id)
126
+ // Test code...
127
+ }) // Auto-deletes session
128
+ })
129
+ ```
130
+
131
+ - Prefer `withSession(...)` for temp sessions
132
+ - In `withProject(...)` tests that create sessions or extra workspaces, call `trackSession(sessionID, directory?)` and `trackDirectory(directory)`
133
+ - This lets fixture teardown abort, wait for idle, and clean up safely under CI concurrency
134
+ - Avoid calling `sdk.session.delete(...)` directly
135
+
136
+ ### Timeouts
137
+
138
+ Default: 60s per test, 10s per assertion. Override when needed:
139
+
140
+ ```typescript
141
+ test.setTimeout(120_000) // For long LLM operations
142
+ test("slow test", async () => {
143
+ await expect.poll(() => check(), { timeout: 90_000 }).toBe(true)
144
+ })
145
+ ```
146
+
147
+ ### Selectors
148
+
149
+ Use `data-component`, `data-action`, or semantic roles:
150
+
151
+ ```typescript
152
+ // ✅ Good
153
+ await page.locator('[data-component="prompt-input"]').click()
154
+ await page.getByRole("button", { name: "Open settings" }).click()
155
+
156
+ // ❌ Bad
157
+ await page.locator(".css-class-name").click()
158
+ await page.locator("#id-name").click()
159
+ ```
160
+
161
+ ### Keyboard Shortcuts
162
+
163
+ Use `modKey` for cross-platform compatibility:
164
+
165
+ ```typescript
166
+ import { modKey } from "../utils"
167
+
168
+ await page.keyboard.press(`${modKey}+B`) // Toggle sidebar
169
+ await page.keyboard.press(`${modKey}+Comma`) // Open settings
170
+ ```
171
+
172
+ ### Terminal Tests
173
+
174
+ - In terminal tests, type through the browser. Do not write to the PTY through the SDK.
175
+ - Use `waitTerminalReady(page, { term? })` and `runTerminal(page, { cmd, token, term?, timeout? })` from `actions.ts`.
176
+ - These helpers use the fixture-enabled test-only terminal driver and wait for output after the terminal writer settles.
177
+ - After opening the terminal, use `waitTerminalFocusIdle(...)` before the next keyboard action when prompt focus or keyboard routing matters.
178
+ - This avoids racing terminal mount, focus handoff, and prompt readiness when the next step types or sends shortcuts.
179
+ - Avoid `waitForTimeout` and custom DOM or `data-*` readiness checks.
180
+
181
+ ### Wait on state
182
+
183
+ - Never use wall-clock waits like `page.waitForTimeout(...)` to make a test pass
184
+ - Avoid race-prone flows that assume work is finished after an action
185
+ - Wait or poll on observable state with `expect(...)`, `expect.poll(...)`, or existing helpers
186
+ - Prefer locator assertions like `toBeVisible()`, `toHaveCount(0)`, and `toHaveAttribute(...)` for normal UI state, and reserve `expect.poll(...)` for probe, mock, or backend state
187
+ - Prefer semantic app state over transient DOM visibility when behavior depends on active selection, focus ownership, or async retry loops
188
+ - Do not treat a visible element as proof that the app will route the next action to it
189
+ - When fixing a flake, validate with `--repeat-each` and multiple workers when practical
190
+
191
+ ### Add hooks
192
+
193
+ - If required state is not observable from the UI, add a small test-only driver or probe in app code instead of sleeps or fragile DOM checks
194
+ - Keep these hooks minimal and purpose-built, following the style of `packages/app/src/testing/terminal.ts`
195
+ - Test-only hooks must be inert unless explicitly enabled; do not add normal-runtime listeners, reactive subscriptions, or per-update allocations for e2e ceremony
196
+ - When mocking routes or APIs, expose explicit mock state and wait on that before asserting post-action UI
197
+ - Add minimal test-only probes for semantic state like the active list item or selected command when DOM intermediates are unstable
198
+ - Prefer probing committed app state over asserting on transient highlight, visibility, or animation states
199
+
200
+ ### Prefer helpers
201
+
202
+ - Prefer fluent helpers and drivers when they make intent obvious and reduce locator-heavy noise
203
+ - Use direct locators when the interaction is simple and a helper would not add clarity
204
+ - Prefer helpers that both perform an action and verify the app consumed it
205
+ - Avoid composing helpers redundantly when one already includes the other or already waits for the resulting state
206
+ - If a helper already covers the required wait or verification, use it directly instead of layering extra clicks, keypresses, or assertions
207
+
208
+ ## Writing New Tests
209
+
210
+ 1. Choose appropriate folder or create new one
211
+ 2. Import from `../fixtures`
212
+ 3. Use helper functions from `../actions` and `../selectors`
213
+ 4. When validating routing, use shared helpers from `../actions`. Workspace URL slugs can be canonicalized on Windows, so assert against canonical or resolved workspace slugs.
214
+ 5. Clean up any created resources
215
+ 6. Use specific selectors (avoid CSS classes)
216
+ 7. Test one feature per test file
217
+
218
+ ## Local Development
219
+
220
+ For UI debugging, use:
221
+
222
+ ```bash
223
+ bun test:e2e:ui
224
+ ```
225
+
226
+ This opens Playwright's interactive UI for step-through debugging.