test(desktop): one DOM for the whole run and no module mocks
Part of removing cross-file test leaks so suites do not depend on bun test --isolate. Draft: the tests pass in any order, but five production components were restructured and none was checked in the running app yet (list at the end).
Why tests depended on file order
mock.moduleis process-wide in bun. Fakes ofrenderer/lib/cloud-trpc,host-service-client,@superset/workspace-client,../env,@superset/local-db,reactand others reached every file that loaded later.- happy-dom was registered and unregistered per file (58 register, 52 unregister). After the first unregister, that window's
queueMicrotaskandsetTimeoutnever fire again. react-dom captures both at import, so in every later DOM file a React update outsideact()never rendered. This was the cause of theuseDiffCodeViewItemsfailures. - Leftover state:
LanguageAwareI18nProvider.test.tsxleft thejalocale active (8月22日in date tests); six files called a baremock.restore(), which also tears down the preload'selectronmock; two tests replacedglobalThis.localStorage.
What changed
- One DOM per process.
test-setup.tsregisters happy-dom once, in place of the hand-writtendocument/windowstubs, and never unregisters. It then puts back the nativefetch,Request,Response,Headers,URL,WebSocket, abort classes, streams, timers andqueueMicrotask, so non-DOM tests keep the runtime they had. Per-file register/unregister is removed from about 55 files. Two nested-process fixtures got anisRegisteredguard. - No
mock.moduleand no baremock.restore()remain inapps/desktop/srcoutside thefixtures/files that run in a nested process. Renderer tests use the real tRPC providers with a fake link, or a localBun.serve. - Main process seams:
DaemonTerminalManager,restartDaemon,tryListExistingDaemonSessions, the host-service coordinator and lock,ai-name,WorkerTaskRunner,killPortTarget,terminal-ws-transportandbrowserRuntimeRegistrytake an optional dependency that defaults to the previous import. - Renderer logic moved out of hooks into functions:
openNewWorkspace,openNewSession,openNewWorkspaceForLocalProject,importFolderFirst,invalidateGitQueries,resolveStatusRefetchInterval,useIsV1FlipLockedFor,useIsV2CloudEnabledFor. PostHogLocaleTaggermoved from insideLanguageAwareI18nProviderto its first child inroutes/-layout.tsx, the only place that renders the provider.useDiffCodeViewItemsreads the client fromworkspaceTrpc.useUtils();WorkspaceClientProviderpasses the same client to both.
Components split into a container and a props-driven part
| Original | New part | Pure move? |
|---|---|---|
PagesView | PagesList | JSX and classes identical. Hook order changed: session, favorites, workspace names and create-with-agent now run in the parent first. New optional searchDebounceMs. |
PageWatcherMenu | PageWatcherPopover | JSX, classes and mutations identical. Five data hooks moved to the parent in the same order; host client and agent picker arrive as props. |
AgentSessionPicker | AgentSessionSelect | JSX and classes identical. The popover is controlled through open/onOpenChange props carrying the same state. |
ExperimentalSettings | ExperimentalSettingsList | Yes. Three session booleans arrive as props. |
V2ProjectSettings | V2ProjectSettingsBody | JSX and classes identical. Three sections that need more providers are passed in as nodes at their original positions. |
Tests
- 7,979 tests in 531 files (7,969 in 526 before).
- Deleted:
V2ProjectSettings.test.tsx; its three tests asserted that mocked children appeared. Replaced byV2ProjectSettingsBody.test.tsx, which renders the real children and guards the #5843 regression (icon picker present and enabled). - Renamed with their tests kept:
PagesView.test→PagesList.test(19),PageWatcherMenu.test→PageWatcherPopover.test(26),useFolderFirstImport.test→importFolderFirst.test,useGitStatus.test→invalidateGitQueries.testplus a newuseGitStatus.testagainst a local WebSocket host.ExperimentalSettings: four tests became three. - Not covered: nothing fails if
V2ProjectSettingsstops rendering the body or drops one of its three passed-in sections.useOpenNewWorkspaceForLocalProjectis tested as a function, not through the hook, because a mountedauthClient.useSession()makes a real request.
Verification
bun 1.4.2, bun test --randomize --seed N: seeds 1-14 pass without --isolate (about 38 s) and seeds 1-13 pass with it (about 73 s). One --isolate run of seed 1 had four git and shell tests time out while the machine was overloaded, and passed on rerun. Repo typecheck, biome and check:i18n pass.
Before this leaves draft
Check in the running app: the Pages list (search, favorites, create with agent), the page watcher menu, the agent session picker, Experimental settings, V2 project settings, folder-first repository import, and that the PostHog locale tag is still set.
Summary by cubic
Makes the desktop test suite pass in any order without bun test --isolate by removing the cross-file leaks behind the order dependence: process-wide mock.module calls, per-file happy-dom registration, and leftover global state. Still a draft: five production components were split and need checking in the running app (Pages list, page watcher menu, agent session picker, Experimental/V2 project settings, folder-first import, PostHog locale tag). Verified with bun test --randomize seeds 1–14 without --isolate and 1–13 with it.
Why tests depended on order
mock.moduleis process-wide in bun, so mocks from one file reached every later file's imports.- Per-file happy-dom register/unregister broke
queueMicrotaskandsetTimeout, so React updates outsideact()never rendered in later DOM files. - Leftover state: the
jalocale stayed active, six files called a baremock.restore()that tore down the preload'selectronmock, and two tests replacedglobalThis.localStorage.
What changed
test-setup.tsregisters happy-dom once per process, never unregisters it, and restores native fetch, WebSocket, streams, timers, andqueueMicrotask; per-file register/unregister removed from ~55 files.- No
mock.moduleor baremock.restore()remain outsidefixtures/; renderer tests use real tRPC providers with a fake link or a localBun.serve. - Main-process code and renderer logic take optional dependency parameters that default to their previous imports.
- Five components split into a container and a props-driven part (
PagesList,PageWatcherPopover,AgentSessionSelect,ExperimentalSettingsList,V2ProjectSettingsBody);V2ProjectSettings.test.tsxreplaced byV2ProjectSettingsBody.test.tsx.