// @vitest-environment jsdom import { cleanup, render, screen, waitFor } from '@testing-library/react'; import type { Brand } from '@open-design/contracts'; import type { ComponentProps, ReactNode } from 'react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { ProjectView } from '../../src/components/ProjectView'; import { BROWSER_PAGE_ARCHIVE_SCHEMA } from '../../src/components/design-browser-tools'; import type { AgentInfo, AppConfig, ChatMessage, Conversation, DesignSystemSummary, Project, SkillSummary, } from '../../src/types'; import { createConversation, listConversations, listMessages, saveMessage, } from '../../src/state/projects'; import { fetchPreviewComments } from '../../src/providers/registry'; import { cancelBrandExtraction, continueBrandExtraction, extractBrandFromHtml, fetchBrands, } from '../../src/runtime/brands'; const brandBrowserBridgeMocks = vi.hoisted(() => ({ getBrandBrowser: vi.fn(), })); const fileWorkspaceSpy = vi.hoisted(() => vi.fn()); const chatPaneSpy = vi.hoisted(() => vi.fn()); type MockChatPaneProps = { messages?: ChatMessage[]; activeConversationId?: string | null; initialDraft?: string; onBrandBrowserAssistConfirm?: (card: { kind: 'brand-browser-assist'; brandId: string; browserTabId?: string; url?: string; reason?: string; }) => Promise<{ ok: boolean; action?: 'opened' | 'confirmed'; message?: string } | void>; onContinueBrandExtraction?: () => void; continueBrandExtractionBusy?: boolean; onContinueBrandAgentExtraction?: () => void; continueBrandAgentExtractionBusy?: boolean; }; vi.mock('../../src/i18n', () => ({ useI18n: () => ({ locale: 'en', setLocale: () => undefined, t: (key: string) => key, }), useT: () => (key: string) => key, })); vi.mock('../../src/router', () => ({ navigate: vi.fn(), })); vi.mock('../../src/providers/anthropic', () => ({ streamMessage: vi.fn(), })); vi.mock('../../src/providers/daemon', () => ({ fetchChatRunStatus: vi.fn(), listActiveChatRuns: vi.fn().mockResolvedValue([]), listProjectRuns: vi.fn().mockResolvedValue([]), reattachDaemonRun: vi.fn(), streamViaDaemon: vi.fn(), })); vi.mock('../../src/providers/project-events', () => ({ useProjectFileEvents: vi.fn(), })); vi.mock('../../src/runtime/brands', async () => { const actual = await vi.importActual( '../../src/runtime/brands', ); return { ...actual, cancelBrandExtraction: vi.fn().mockResolvedValue({ ok: true, status: 'failed' }), continueBrandExtraction: vi.fn().mockResolvedValue({ ok: true, result: { id: 'brand-retry', projectId: 'brand-retry', conversationId: 'conv-brand-retry', sourceUrl: 'https://economist.com/', status: 'extracting', designSystemId: 'user:brand-retry', }, }), extractBrandFromHtml: vi.fn().mockResolvedValue({ ok: true, result: { id: 'brand-retry', brand: null, designSystemId: 'user:brand-retry', projectId: 'brand-retry', files: [], }, }), fetchBrands: vi.fn().mockResolvedValue([]), }; }); vi.mock('../../src/runtime/brand-browser-bridge', () => ({ BRAND_BROWSER_TAB_ID: '__browser__:1', getBrandBrowser: brandBrowserBridgeMocks.getBrandBrowser, })); vi.mock('../../src/providers/registry', async () => { const actual = await vi.importActual( '../../src/providers/registry', ); return { ...actual, deletePreviewComment: vi.fn(), fetchDesignSystem: vi.fn(), fetchLiveArtifacts: vi.fn().mockResolvedValue([]), fetchPreviewComments: vi.fn(), fetchProjectFiles: vi.fn().mockResolvedValue([]), fetchSkill: vi.fn(), getTemplate: vi.fn(), patchPreviewCommentStatus: vi.fn(), upsertPreviewComment: vi.fn(), writeProjectTextFile: vi.fn(), }; }); vi.mock('../../src/state/projects', async () => { const actual = await vi.importActual( '../../src/state/projects', ); return { ...actual, createConversation: vi.fn(), listConversations: vi.fn(), listMessages: vi.fn(), loadTabs: vi.fn().mockResolvedValue({ tabs: [], active: null }), patchConversation: vi.fn(), patchProject: vi.fn(), saveMessage: vi.fn(), saveTabs: vi.fn(), }; }); vi.mock('../../src/components/AppChromeHeader', () => ({ AppChromeHeader: ({ children }: { children: ReactNode }) => (
{children}
), })); vi.mock('../../src/components/AvatarMenu', () => ({ AvatarMenu: () => null, })); vi.mock('../../src/components/FileWorkspace', () => ({ DESIGN_SYSTEM_TAB: '__design_system__', FileWorkspace: (props: { openRequest?: { name: string } | null; browserOpenRequest?: { tabId?: string; url: string; nonce: number } | null; onBrandExtractionStopRequest?: () => void; designSystemEditable?: boolean; filesRefreshKey?: number; }) => { fileWorkspaceSpy(props); return
; }, })); vi.mock('../../src/components/Loading', () => ({ CenteredLoader: () =>
, })); vi.mock('../../src/components/ChatPane', () => ({ ChatPane: (props: MockChatPaneProps) => { chatPaneSpy(props); return ( <>
{props.activeConversationId ?? ''}
{props.messages?.map((message) => message.content).join('\n') ?? ''}