diff --git a/desktop/src/__tests__/generalSettings.test.tsx b/desktop/src/__tests__/generalSettings.test.tsx
index b9ed6c06..6027709c 100644
--- a/desktop/src/__tests__/generalSettings.test.tsx
+++ b/desktop/src/__tests__/generalSettings.test.tsx
@@ -68,7 +68,7 @@ vi.mock('../api/providers', () => ({
}))
vi.mock('../lib/desktopNotifications', () => desktopNotificationsMock)
-vi.mock('../components/chat/clipboard', () => clipboardMock)
+vi.mock('@/lib/clipboard', () => clipboardMock)
vi.mock('@tauri-apps/api/core', () => tauriCoreMock)
vi.mock('@tauri-apps/plugin-dialog', () => tauriDialogMock)
vi.mock('@tauri-apps/plugin-process', () => tauriProcessMock)
@@ -883,7 +883,7 @@ describe('Settings > General tab', () => {
const trigger = screen.getByRole('button', { name: 'Response Language' })
expect(trigger).toHaveTextContent('Default (English)')
fireEvent.click(trigger)
- fireEvent.click(screen.getByRole('button', { name: '中文 (Chinese)' }))
+ fireEvent.click(screen.getByRole('option', { name: '中文 (Chinese)' }))
expect(useSettingsStore.getState().setResponseLanguage).toHaveBeenCalledWith('chinese')
})
@@ -1724,8 +1724,9 @@ describe('Settings > Providers tab', () => {
expect(within(dialog).queryByRole('combobox')).not.toBeInTheDocument()
fireEvent.click(within(dialog).getByRole('button', { name: /Anthropic Messages \(native\)/i }))
- fireEvent.click(within(dialog).getByRole('button', { name: /OpenAI Responses API \(proxy\)/i }))
+ fireEvent.click(within(dialog).getByRole('option', { name: /OpenAI Responses API \(proxy\)/i }))
+ // The panel is closed now; this finds the trigger, which reflects the pick.
expect(within(dialog).getByRole('button', { name: /OpenAI Responses API \(proxy\)/i })).toBeInTheDocument()
expect(within(dialog).getByText('Requests will be translated via the local proxy')).toBeInTheDocument()
})
diff --git a/desktop/src/__tests__/mockPaths.test.ts b/desktop/src/__tests__/mockPaths.test.ts
new file mode 100644
index 00000000..a00cb8d9
--- /dev/null
+++ b/desktop/src/__tests__/mockPaths.test.ts
@@ -0,0 +1,57 @@
+import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'
+import { dirname, join, resolve } from 'node:path'
+
+import { describe, expect, it } from 'vitest'
+
+/**
+ * A `vi.mock` whose path does not resolve is not an error — Vitest registers
+ * the factory against a specifier nothing imports, the real module loads, and
+ * the test keeps passing with its isolation quietly gone.
+ *
+ * That is exactly what happened when components moved out of `shared/`:
+ * `RepositoryLaunchControls.test.tsx` kept mocking `./DirectoryPicker` while
+ * the component had moved to `@/components/composite/DirectoryPicker`, so the
+ * real picker (and its API calls) rendered in a test that believed it was
+ * stubbed.
+ */
+
+const SRC = join(process.cwd(), 'src')
+
+function collectTests(dir: string, out: string[] = []): string[] {
+ for (const entry of readdirSync(dir)) {
+ if (entry === 'node_modules' || entry.startsWith('.')) continue
+ const path = join(dir, entry)
+ if (statSync(path).isDirectory()) collectTests(path, out)
+ else if (/\.test\.tsx?$/.test(entry)) out.push(path)
+ }
+ return out
+}
+
+/** Mirrors the resolution in vite.config.ts: `@/` maps to `src/`. */
+function resolvesToFile(specifier: string, fromFile: string): boolean {
+ let base: string
+ if (specifier.startsWith('@/')) base = join(SRC, specifier.slice(2))
+ else if (specifier.startsWith('.')) base = resolve(dirname(fromFile), specifier)
+ else return true // bare package specifier — node_modules, not our concern
+
+ return ['.ts', '.tsx', '.js', '.jsx', '/index.ts', '/index.tsx', '']
+ .some((suffix) => existsSync(base + suffix))
+}
+
+describe('vi.mock specifiers', () => {
+ it('all resolve to a real module', () => {
+ const broken: string[] = []
+
+ for (const file of collectTests(SRC)) {
+ readFileSync(file, 'utf8').split('\n').forEach((line, index) => {
+ const match = line.match(/vi\.mock\(\s*['"]([^'"]+)['"]/)
+ if (!match) return
+ if (!resolvesToFile(match[1]!, file)) {
+ broken.push(`${file.replace(SRC, 'src')}:${index + 1} vi.mock('${match[1]}')`)
+ }
+ })
+ }
+
+ expect(broken).toEqual([])
+ })
+})
diff --git a/desktop/src/__tests__/pages.test.tsx b/desktop/src/__tests__/pages.test.tsx
index 97a50bf9..d31ff054 100644
--- a/desktop/src/__tests__/pages.test.tsx
+++ b/desktop/src/__tests__/pages.test.tsx
@@ -75,9 +75,7 @@ vi.mock('../api/sessions', async (importOriginal) => {
// Import all pages
import { EmptySession } from '../pages/EmptySession'
import { ActiveSession } from '../pages/ActiveSession'
-import { AgentTeams } from '../pages/AgentTeams'
import { ScheduledTasks } from '../pages/ScheduledTasks'
-import { ToolInspection } from '../pages/ToolInspection'
// Layout components (chrome is now here, not in pages)
import { Sidebar } from '../components/layout/Sidebar'
@@ -1243,25 +1241,11 @@ describe('Content-only pages render without errors', () => {
useSessionRuntimeStore.setState({ selections: {} })
})
- it('AgentTeams renders team strip and members', () => {
- const { container } = render()
- expect(container.innerHTML).toContain('Architect')
- expect(container.innerHTML).toContain('session-dev')
- expect(container.innerHTML).toContain('groups')
- })
-
it('ScheduledTasks renders (store-connected)', async () => {
const { container } = render()
await screen.findByText('Scheduled tasks')
expect(container.innerHTML).toContain('Scheduled tasks')
})
-
- it('ToolInspection renders diff viewer', () => {
- const { container } = render()
- expect(container.innerHTML).toContain('edit_file')
- expect(container.innerHTML).toContain('Split')
- expect(container.innerHTML).toContain('Unified')
- })
})
describe('Chat attachments', () => {
@@ -1301,7 +1285,7 @@ describe('AppShell layout renders chrome', () => {
describe('Design system compliance', () => {
it('Pages use Material Symbols Outlined icons', () => {
- const pages = [EmptySession, AgentTeams, ToolInspection]
+ const pages = [EmptySession]
for (const Page of pages) {
const { container, unmount } = render()
const icons = container.querySelectorAll('.material-symbols-outlined')
@@ -1326,13 +1310,3 @@ describe('Design system compliance', () => {
}
})
})
-
-describe('Mock data integration', () => {
- it('AgentTeams shows team members from mock data', () => {
- const { container } = render()
- expect(container.innerHTML).toContain('Architect')
- expect(container.innerHTML).toContain('Frontend Dev')
- expect(container.innerHTML).toContain('Backend Dev')
- expect(container.innerHTML).toContain('Tester')
- })
-})
diff --git a/desktop/src/components/markdown/MarkdownRenderer.tsx b/desktop/src/components/markdown/MarkdownRenderer.tsx
index 37de1b82..5d67ce76 100644
--- a/desktop/src/components/markdown/MarkdownRenderer.tsx
+++ b/desktop/src/components/markdown/MarkdownRenderer.tsx
@@ -6,7 +6,7 @@ import 'katex/dist/katex.min.css'
import { marked, type Tokens } from 'marked'
import { CodeViewer } from '../chat/CodeViewer'
import { MermaidRenderer } from '../chat/MermaidRenderer'
-import { copyTextToClipboard } from '../chat/clipboard'
+import { copyTextToClipboard } from '@/lib/clipboard'
type Props = {
content: string