diff --git a/mobile/src/components/mobile-markdown-image-paragraph.test.tsx b/mobile/src/components/mobile-markdown-image-paragraph.test.tsx deleted file mode 100644 index 633a4faa39e..00000000000 --- a/mobile/src/components/mobile-markdown-image-paragraph.test.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { createElement } from 'react' -import { describe, expect, it, vi } from 'vitest' -import { renderImageParagraph } from './mobile-markdown-image-paragraph' -import { mobileMarkdownParagraphImages } from './mobile-markdown-image-placement' -import { parseMobileMarkdownTree } from './mobile-markdown-tree' -import { MARKDOWN_TONES } from './mobile-markdown-styles' -import type { MarkdownBlockContext } from './mobile-markdown-blocks' - -vi.mock('react-native', () => ({ - Linking: { openURL: () => Promise.resolve() }, - Platform: { OS: 'ios' }, - StyleSheet: { create: (styles: unknown) => styles, hairlineWidth: 1 }, - Text: 'Text', - View: 'View' -})) - -describe('phone Markdown image paragraphs', () => { - it.each([100, 1000])('draws %i images with linear eligibility reads', (count) => { - const root = parseMobileMarkdownTree( - Array(count).fill('![](/tmp/p.png)').join(' '), - false, - true - ) - const paragraph = root.children[0]! - if (paragraph.type !== 'paragraph') { - throw new Error('expected an image paragraph') - } - let reads = 0 - const images = mobileMarkdownParagraphImages(paragraph, 0) - images.forEach((image, index) => { - Object.defineProperty(images, index, { - get() { - reads += 1 - return image - } - }) - }) - const drawn: string[] = [] - const ctx: MarkdownBlockContext = { - onOpenLink: () => {}, - tone: MARKDOWN_TONES.default, - textScale: 1, - chatMath: true, - growing: false, - listText: {}, - listMarker: {}, - directives: [], - mermaidOccurrences: new Map(), - renderImage: (href) => { - drawn.push(href) - return createElement('Image', { href }) - } - } - expect( - renderImageParagraph(paragraph.children, images, 0, ctx, { - style: {}, - selectable: true - }) - ).not.toBeNull() - expect(drawn).toEqual(Array(count).fill('/tmp/p.png')) - // Each eligible image needs one read to index and one to draw, independent of paragraph width. - expect(reads).toBeLessThanOrEqual(2 * count) - }) -}) diff --git a/src/main/acp/launch/acp-structured-launch-env.test.ts b/src/main/acp/launch/acp-structured-launch-env.test.ts deleted file mode 100644 index 7406980a391..00000000000 --- a/src/main/acp/launch/acp-structured-launch-env.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { afterEach, expect, it, vi } from 'vitest' -import { acpLaunchSpecFor } from './acp-launch-registry' -import { resolveAcpLaunchInvocation } from './acp-structured-launch-resolution' - -afterEach(() => vi.unstubAllGlobals()) - -it('composes configured Windows values before ACP command lookup and account pinning', async () => { - vi.stubGlobal('process', { ...process, platform: 'win32' }) - const spec = acpLaunchSpecFor('grok') - if (!spec) { - throw new Error('Missing Grok spec') - } - const launch = await resolveAcpLaunchInvocation( - { ...spec, installDirectories: () => [] }, - { variable: 'GROK_HOME', path: '/pinned' }, - { - dataDirectory: '/isolated', - inheritedEnv: {}, - resolveEnvironment: async () => ({ PATH: '/inherited/bin', TOKEN: 'inherited' }), - resolveLaunchEnv: () => ({ - path: '/configured/bin', - tOKEN: 'configured', - grok_home: '/configured/home' - }), - resolveCommand: (_command, options) => { - expect(options?.pathEnv).toBe('/configured/bin') - return '/configured/bin/grok.exe' - } - } - ) - expect(launch.env).toEqual({ PATH: '/configured/bin', TOKEN: 'configured', GROK_HOME: '/pinned' }) -}) diff --git a/src/main/acp/timeline/acp-timeline-opencode-subagents.test.ts b/src/main/acp/timeline/acp-timeline-opencode-subagents.test.ts deleted file mode 100644 index dce3a8ca956..00000000000 --- a/src/main/acp/timeline/acp-timeline-opencode-subagents.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { afterEach, describe, expect, it } from 'vitest' -import type { AgentJournalRenderItem } from '../../../shared/agent-session/journal/agent-session-journal-types' -import { closeProviderTimelineRigs } from '../../native-chat/agent-session-timeline/provider-timeline-assembler-test-support' -import { OPENCODE_ACP_DIALECT } from '../acp-dialects/opencode-dialect' - -import { openAcpFixtureRig } from './testing/acp-timeline-fixture.test-support' - -afterEach(closeProviderTimelineRigs) - -function texts(rows: AgentJournalRenderItem[], agentId: string) { - return rows.flatMap((row) => - row.agentId === agentId && row.body.kind === 'message' - ? row.body.blocks.flatMap((block) => (block.type === 'text' ? [block.text] : [])) - : [] - ) -} - -const opened = () => openAcpFixtureRig(OPENCODE_ACP_DIALECT, { journalOwnership: true }) - -describe('OpenCode subagents through the shared timeline', () => { - it('a reply that resumes after the subagent thinks or runs a tool keeps all of its text', async () => { - const fixture = await opened() - const lane = fixture.lane() - const report = (body: Record) => - fixture.apply(lane.notification('opencode/session/child_update', { ...CHILD, ...body }, 1000)) - report({ type: 'status', status: 'created', title: 'Helper' }) - report(chunk('agent_message_chunk', 'msg-1', 'A')) - report(chunk('agent_thought_chunk', 'msg-1:reasoning:0', 'R')) - report({ - type: 'update', - update: { - sessionUpdate: 'tool_call', - toolCallId: 'session-2:t1', - title: 'shell', - status: 'pending' - } - }) - report(chunk('agent_message_chunk', 'msg-1', 'B')) - report({ type: 'status', status: 'completed' }) - expect(texts(await fixture.rig.rows(), 'session-2')).toEqual(['AB', 'R']) - }) -}) - -const CHILD = { - rootSessionId: 'session-1', - childSessionId: 'session-2', - parentSessionId: 'session-1' -} - -function chunk(sessionUpdate: string, messageId: string, text: string) { - return { - type: 'update', - update: { sessionUpdate, messageId, content: { type: 'text', text } } - } -} diff --git a/src/main/browser/agent-browser/observation/agent-browser-action-effects.test.ts b/src/main/browser/agent-browser/observation/agent-browser-action-effects.test.ts index 2a0c3ca96ee..1d5a99ccfb8 100644 --- a/src/main/browser/agent-browser/observation/agent-browser-action-effects.test.ts +++ b/src/main/browser/agent-browser/observation/agent-browser-action-effects.test.ts @@ -71,28 +71,6 @@ function fakeHost( } describe('runWithActionEffects', () => { - it('lists the lines an action added and the console errors it logged', async () => { - const guest = fakeGuest() - const { host } = fakeHost(guest, [UNCHANGED, INVALID_LOGIN]) - - const result = await runWithActionEffects(host, async () => { - guest.emit('console-message', { level: 'error', message: 'Uncaught TypeError: x' }) - guest.emit('console-message', { level: 'info', message: 'booted' }) - return { clicked: '@e6' } - }) - - expect(readBrowserActionEffects(result)).toEqual({ - settled: true, - consoleErrors: ['Uncaught TypeError: x'], - page: { - lines: [' - StaticText "Invalid username or password"'], - removedLines: 0, - full: false, - truncated: false - } - }) - }) - it('waits for a navigation the action started and reports where it landed', async () => { const guest = fakeGuest() const appTree = '- heading "Welcome ada" [level=1, ref=e9]\n- button "Submit ticket" [ref=e10]' diff --git a/src/main/browser/guest-lifecycle/input/shortcuts/browser-guest-dictation-shortcut.test.ts b/src/main/browser/guest-lifecycle/input/shortcuts/browser-guest-dictation-shortcut.test.ts deleted file mode 100644 index 7905acaa995..00000000000 --- a/src/main/browser/guest-lifecycle/input/shortcuts/browser-guest-dictation-shortcut.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' - -vi.mock('electron', async () => { - const { EventEmitter } = await import('node:events') - return { - BrowserWindow: class { - webContents = new (class extends EventEmitter { - send = vi.fn() - })() - }, - screen: { getCursorScreenPoint: vi.fn(() => ({ x: 0, y: 0 })) }, - webContents: { fromId: vi.fn() } - } -}) - -import { BrowserWindow } from 'electron' -import { setupGuestShortcutForwarding } from './browser-guest-shortcut-forwarding' - -function pressDictationInPage(isComposing: boolean) { - const guest = new BrowserWindow().webContents - const renderer = new BrowserWindow().webContents - const cleanup = setupGuestShortcutForwarding({ - browserTabId: 'tab-1', - guest, - resolveRenderer: () => renderer, - shouldForwardDictationShortcut: () => true, - getKeybindings: () => ({}) - }) - const isMac = process.platform === 'darwin' - const event: Electron.Event = { preventDefault: vi.fn(), defaultPrevented: false } - const input: Electron.Input = { - type: 'keyDown', - code: 'KeyE', - key: 'e', - meta: isMac, - control: !isMac, - alt: false, - shift: false, - isAutoRepeat: false, - isComposing, - location: 0, - modifiers: [] - } - guest.emit('before-input-event', event, input) - cleanup() - return { prevented: vi.mocked(event.preventDefault).mock.calls.length > 0, renderer } -} - -describe('dictation shortcut from a focused browser page', () => { - it('leaves the chord to the page while an IME composition owns it', () => { - const composing = pressDictationInPage(true) - expect(composing.prevented).toBe(false) - expect(composing.renderer.send).not.toHaveBeenCalledWith('ui:dictationKeyDown') - - const typed = pressDictationInPage(false) - expect(typed.prevented).toBe(true) - expect(typed.renderer.send).toHaveBeenCalledWith('ui:dictationKeyDown') - }) -}) diff --git a/src/main/browser/guest-lifecycle/navigation/browser-guest-metadata.test.ts b/src/main/browser/guest-lifecycle/navigation/browser-guest-metadata.test.ts index c73babff06c..1663d0022c9 100644 --- a/src/main/browser/guest-lifecycle/navigation/browser-guest-metadata.test.ts +++ b/src/main/browser/guest-lifecycle/navigation/browser-guest-metadata.test.ts @@ -2,11 +2,7 @@ import { EventEmitter } from 'node:events' import { describe, expect, it, vi } from 'vitest' import type { BrowserGuestMetadata } from '../../../../shared/browser/guest-lifecycle/browser-guest-metadata' import type { BrowserLoadError } from '../../../../shared/browser-workspace-types' -import { - installBrowserGuestMetadata, - publishBrowserGuestMetadata, - readBrowserGuestMetadata -} from './browser-guest-metadata' +import { installBrowserGuestMetadata, publishBrowserGuestMetadata } from './browser-guest-metadata' function fixture() { let url = 'https://example.test/start' @@ -59,24 +55,6 @@ function fixture() { } describe('native guest metadata', () => { - it('samples native loading before navigation-start and keeps subsequent title reports current', () => { - const f = fixture() - f.setLoading(true) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: fixture supplies the native metadata methods. - const state = readBrowserGuestMetadata(f.guest as unknown as Electron.WebContents) - expect(state).toMatchObject({ - url: 'https://example.test/start', - loading: true, - navigationRevision: 0 - }) - f.guest.emit('page-title-updated', {}, 'Current title') - expect(f.reports.at(-1)).toMatchObject({ title: 'Current title', loading: true }) - f.setLoading(false) - f.guest.emit('did-stop-loading') - expect(f.reports.at(-1)).toMatchObject({ url: 'https://example.test/start', loading: false }) - f.dispose() - }) - it('projects same-URL native history and retains it through a failed destination without publishing Chromium internals', () => { const f = fixture() // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: fixture supplies the production publisher native methods. diff --git a/src/main/browser/network/tunnels/browser-network-tunnel-destination-policy.test.ts b/src/main/browser/network/tunnels/browser-network-tunnel-destination-policy.test.ts index a7e3da1dc67..1eef73fed3f 100644 --- a/src/main/browser/network/tunnels/browser-network-tunnel-destination-policy.test.ts +++ b/src/main/browser/network/tunnels/browser-network-tunnel-destination-policy.test.ts @@ -24,24 +24,6 @@ afterEach(async () => { }) describe('browser tunnel destination policy', () => { - it.each([ - ['localhost', 3000], - ['127.0.0.1', 5173], - ['::1', 8080], - ['0.0.0.0', 8000], - ['192.168.1.20', 80], - ['10.0.0.5', 443], - ['172.16.4.2', 8443], - ['100.75.218.44', 3000], - ['fd12:3456::1', 80], - ['example.com', 443], - ['2130706433', 3000], - ['1.2.3', 80], - ['v1.2.3.4', 80] - ])('allows %s:%i', (host, port) => { - expect(browserTunnelTargetBlockReason(host, port)).toBeNull() - }) - it.each([ ['169.254.169.254', 80], ['169.254.170.2', 80], @@ -181,22 +163,6 @@ describe('native browser tunnel dial', () => { ).resolves.toBe('connected') }) - it("refuses metadata and the host's own Orca listener before dialing", async () => { - vi.spyOn(console, 'warn').mockImplementation(() => {}) - const port = await startServer() - cleanups.push(reserveBrowserTunnelOrcaListenerPort(port)) - - expect(() => connectNativeBrowserNetworkTarget({ host: '127.0.0.1', port }, {})).toThrow( - BrowserTunnelDestinationBlockedError - ) - expect(() => - connectNativeBrowserNetworkTarget({ host: '169.254.169.254', port: 80 }, {}) - ).toThrow(BrowserTunnelDestinationBlockedError) - expect(() => connectNativeBrowserNetworkTarget({ host: 'localhost', port }, {})).toThrow( - BrowserTunnelDestinationBlockedError - ) - }) - it('restores unrestricted dialing when the host opts out', async () => { const port = await startServer() cleanups.push(reserveBrowserTunnelOrcaListenerPort(port)) diff --git a/src/main/github/body-image-urls.test.ts b/src/main/github/body-image-urls.test.ts index 07ee91dac34..3dceb105550 100644 --- a/src/main/github/body-image-urls.test.ts +++ b/src/main/github/body-image-urls.test.ts @@ -1,18 +1,4 @@ -import type * as HtmlParser from 'hast-util-from-html-isomorphic' -import { describe, expect, it, vi } from 'vitest' -const traversal = vi.hoisted(() => ({ fail: false })) -vi.mock('hast-util-from-html-isomorphic', async (load) => { - const actual = await load() - return { - ...actual, - fromHtmlIsomorphic: (...args: Parameters) => { - if (traversal.fail) { - throw new Error('Optional image HTML could not be parsed') - } - return actual.fromHtmlIsomorphic(...args) - } - } -}) +import { describe, expect, it } from 'vitest' import { githubBodyImageUrls } from './body-image-urls' const original = 'https://github.com/user-attachments/assets/ce11040a-fb66-4289-927f-547b16dfc488' @@ -20,15 +6,6 @@ const signed = 'https://private-user-images.githubusercontent.com/1/2-ce11040a-fb66-4289-927f-547b16dfc488.png?jwt=TEST&download=1' describe('GitHub body image sources', () => { - it('leaves the optional map absent if HTML extraction fails', () => { - traversal.fail = true - try { - expect(githubBodyImageUrls(`![shot](${original})`, ``)).toBeUndefined() - } finally { - traversal.fail = false - } - }) - it('matches rendered images to canonical assets without changing editable Markdown', () => { const body = `![shot](${original})\n\n![missing](https://github.com/user-attachments/assets/cc11040a-fb66-4289-927f-547b16dfc488)\n\`https://github.com/user-attachments/assets/dd11040a-fb66-4289-927f-547b16dfc488\`` const html = `https://private-user-images.githubusercontent.com/1/dd11040a-fb66-4289-927f-547b16dfc488.png` diff --git a/src/main/github/project-view/body-image-mutations.test.ts b/src/main/github/project-view/body-image-mutations.test.ts deleted file mode 100644 index 2230f380082..00000000000 --- a/src/main/github/project-view/body-image-mutations.test.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' - -const { - acquireMock, - ghExecFileAsyncMock, - noteRepositoryRateLimitSpendMock, - projectHostAuthenticationErrorMock, - repositoryRateLimitGuardMock, - releaseMock, - runGraphqlMock, - runRestMock -} = vi.hoisted(() => ({ - acquireMock: vi.fn(), - ghExecFileAsyncMock: vi.fn(), - noteRepositoryRateLimitSpendMock: vi.fn(), - projectHostAuthenticationErrorMock: vi.fn(), - repositoryRateLimitGuardMock: vi.fn(), - releaseMock: vi.fn(), - runGraphqlMock: vi.fn(), - runRestMock: vi.fn() -})) - -vi.mock('./internals', () => ({ - acquire: acquireMock, - release: releaseMock, - extractExecError: (err: unknown) => ({ - stderr: err instanceof Error ? err.message : String(err), - stdout: '' - }), - ghExecFileAsync: ghExecFileAsyncMock, - rateLimitGuard: () => ({ blocked: false }), - noteRateLimitSpend: vi.fn(), - repositoryRateLimitGuard: repositoryRateLimitGuardMock, - noteRepositoryRateLimitSpend: noteRepositoryRateLimitSpendMock, - projectHostAuthenticationError: projectHostAuthenticationErrorMock, - projectGhExecOptions: (host?: string) => ({ host: host ?? 'github.com' }), - classifyProjectError: (stderr: string) => ({ type: 'unknown', message: stderr }), - rateLimitedError: () => ({ type: 'rate_limited', message: 'rate limited' }), - runGraphql: runGraphqlMock, - runRest: runRestMock, - validateSlugArgs: (owner: string, repo: string) => - owner && repo ? { ok: true } : { ok: false, error: { type: 'validation_error' } }, - assertPositiveInt: (value: number, name: string) => - Number.isInteger(value) && value > 0 - ? { ok: true, value } - : { ok: false, error: { type: 'validation_error', message: `${name} invalid` } } -})) - -import { updateIssueBySlug } from './mutations' - -describe('Project accepted body representations', () => { - beforeEach(() => { - acquireMock.mockReset().mockResolvedValue(undefined) - ghExecFileAsyncMock.mockReset().mockResolvedValue({ stdout: '', stderr: '' }) - noteRepositoryRateLimitSpendMock.mockReset() - projectHostAuthenticationErrorMock.mockReset().mockResolvedValue(null) - repositoryRateLimitGuardMock.mockReset().mockReturnValue({ blocked: false }) - releaseMock.mockReset() - runRestMock.mockReset().mockResolvedValue({ ok: true, data: {} }) - }) - - it('does not attach a Project issue map from a different accepted body snapshot', async () => { - const body = - '![shot](https://github.com/user-attachments/assets/ce11040a-fb66-4289-927f-547b16dfc488)' - runRestMock.mockResolvedValueOnce({ - ok: true, - data: { - body: `${body}\nchanged elsewhere`, - body_html: - '' - } - }) - await expect( - updateIssueBySlug({ owner: 'acme', repo: 'widgets', number: 12, updates: { body } }) - ).resolves.toEqual({ ok: true, bodyImageUrls: undefined }) - }) - - it.each([null, true])( - 'finishes a Project compound save with unusable optional HTML (%s)', - async (html) => { - const body = - '![new](https://github.com/user-attachments/assets/ce11040a-fb66-4289-927f-547b16dfc488)' - runRestMock.mockResolvedValueOnce({ - ok: true, - data: html === null ? null : { body, body_html: html } - }) - await expect( - updateIssueBySlug({ - owner: 'acme', - repo: 'widgets', - number: 12, - updates: { body, addLabels: ['saved-label'] } - }) - ).resolves.toEqual({ ok: true, bodyImageUrls: undefined }) - expect(runRestMock).toHaveBeenCalledWith( - [ - '-X', - 'POST', - 'repos/acme/widgets/issues/12/labels', - '--raw-field', - 'labels[]=saved-label' - ], - undefined, - 'core', - { host: 'github.com' } - ) - } - ) -}) diff --git a/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.test.tsx b/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.test.tsx index 25e49914f90..447ae4083c3 100644 --- a/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.test.tsx +++ b/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.test.tsx @@ -14,24 +14,14 @@ import { } from '@/lib/workspace/creation/project-host-setup-options' import { NewWorkspaceComposerProjectSection } from '@/components/new-workspace/NewWorkspaceComposerProjectSection' import { SidebarHeaderActions } from '../../sidebar-header-actions' -import AddRepoDialog from '../dialog/AddRepoDialog' import type { AddRepoDialogEntryRequest } from '../dialog/add-repo-dialog-types' -const browseRuntimeServerDirectoryMock = vi.fn() -const callRuntimeRpcMock = vi.fn() - vi.mock('@/components/new-workspace/use-recent-project-ids', () => ({ useRecentProjectIds: () => [] })) vi.mock('@/components/sidebar/host-admission/ssh/AddRemoteHostDialog', () => ({ AddRemoteHostDialog: () => null })) -vi.mock('@/runtime/files/reads/runtime-server-directory-browser', () => ({ - browseRuntimeServerDirectory: (...args: unknown[]) => browseRuntimeServerDirectoryMock(...args) -})) -vi.mock('@/runtime/rpc/calls/runtime-rpc-call', () => ({ - callRuntimeRpc: (...args: unknown[]) => callRuntimeRpcMock(...args) -})) const UBUNTU_RUNTIME: PublicKnownRuntimeEnvironment = { id: 'wsl-ubuntu', @@ -57,8 +47,6 @@ const pickFoldersMock = vi.fn<() => Promise>() beforeEach(() => { pickFoldersMock.mockReset() - browseRuntimeServerDirectoryMock.mockReset() - callRuntimeRpcMock.mockReset() Object.defineProperty(window, 'api', { configurable: true, value: { @@ -289,33 +277,3 @@ describe('AddProjectMenu default host sync in worktree composer', () => { ) }) }) - -describe('AddRepoDialog direct menu entry', () => { - it('opens directly on Create new project for the requested host without a Back button or cancelled host defaults', async () => { - browseRuntimeServerDirectoryMock.mockResolvedValue({ - resolvedPath: '/home/alice', - parentPath: '/home', - entries: [] - }) - callRuntimeRpcMock.mockResolvedValue({ available: true }) - - render( - - - - ) - - useAppStore.getState().openModal('add-repo', { - entryRequest: { - id: 1, - kind: 'create', - hostId: 'runtime:wsl-ubuntu' - } - }) - - expect(await screen.findByRole('dialog', { name: 'Create a new project' })).not.toBeNull() - expect(screen.queryByRole('button', { name: /Back/i })).toBeNull() - expect(await screen.findByText('Git repository in /home/alice/orca/projects')).not.toBeNull() - expect(browseRuntimeServerDirectoryMock).toHaveBeenCalledWith('wsl-ubuntu', '~') - }) -})