import type { SnippetDetailPayload } from '@/models/snippet' import { render, screen } from '@testing-library/react' import SnippetPage from '..' const mockUseSnippetInit = vi.fn() let capturedWorkflowDefaultContextProps: | { nodes: unknown[] edges: unknown[] } | undefined vi.mock('../hooks/use-snippet-init', () => ({ useSnippetInit: (snippetId: string) => mockUseSnippetInit(snippetId), })) vi.mock('../components/snippet-main', () => ({ default: ({ hasPublishedWorkflow, snippetId, }: { hasPublishedWorkflow: boolean snippetId: string }) =>
{`${snippetId}:${String(hasPublishedWorkflow)}`}
, })) vi.mock('@/next/navigation', () => ({ useRouter: () => ({ replace: vi.fn(), push: vi.fn(), }), })) vi.mock('@/hooks/use-breakpoints', () => ({ default: () => 'desktop', MediaType: { mobile: 'mobile', desktop: 'desktop' }, })) vi.mock('@/hooks/use-document-title', () => ({ default: vi.fn(), })) vi.mock('@/app/components/workflow', () => ({ default: ({ children, nodes, edges, }: { children: React.ReactNode nodes: unknown[] edges: unknown[] }) => { capturedWorkflowDefaultContextProps = { nodes, edges, } return
{children}
}, })) vi.mock('@/app/components/workflow/context', () => ({ WorkflowContextProvider: ({ children }: { children: React.ReactNode }) => (
{children}
), })) vi.mock('@/app/components/workflow/utils', async (importOriginal) => { const actual = await importOriginal() return { ...actual, initialNodes: (nodes: unknown[]) => nodes, initialEdges: (edges: unknown[]) => edges, } }) vi.mock('@/app/components/app-sidebar', () => ({ default: ({ renderHeader, renderNavigation, }: { renderHeader?: (modeState: string) => React.ReactNode renderNavigation?: (modeState: string) => React.ReactNode }) => (
{renderHeader?.('expand')}
{renderNavigation?.('expand')}
), })) vi.mock('@/app/components/app-sidebar/nav-link', () => ({ default: ({ name, onClick }: { name: string; onClick?: () => void }) => ( ), })) vi.mock('@/app/components/app-sidebar/snippet-info', () => ({ default: () =>
, })) const createSnippetDetailPayload = (nodeId: string, edgeId: string): SnippetDetailPayload => ({ snippet: { id: 'snippet-1', name: 'Tone Rewriter', description: 'A static snippet mock.', updatedAt: 'Updated 2h ago', usage: 'Used 19 times', tags: [], status: 'Draft', }, graph: { viewport: { x: 0, y: 0, zoom: 1 }, nodes: [ { id: nodeId, position: { x: 0, y: 0 }, data: { title: nodeId }, }, ] as SnippetDetailPayload['graph']['nodes'], edges: [ { id: edgeId, source: nodeId, target: `${nodeId}-target`, }, ] as SnippetDetailPayload['graph']['edges'], }, inputFields: [], uiMeta: { inputFieldCount: 0, checklistCount: 0, autoSavedAt: 'Auto-saved ยท a few seconds ago', }, }) const mockPublishedSnippetDetail = createSnippetDetailPayload('published-node', 'published-edge') const mockDraftSnippetDetail = createSnippetDetailPayload('draft-node', 'draft-edge') describe('SnippetPage', () => { beforeEach(() => { vi.clearAllMocks() capturedWorkflowDefaultContextProps = undefined mockUseSnippetInit.mockReturnValue({ data: { snippet: mockDraftSnippetDetail.snippet, published: mockPublishedSnippetDetail, draft: mockDraftSnippetDetail, draftWorkflow: { hash: 'draft-hash', }, publishedWorkflow: { hash: 'published-hash', }, hasDraftChanges: true, }, isLoading: false, }) }) it('should render the orchestrate route shell without owning the main landmark', () => { render() expect(screen.getByTestId('workflow-context-provider')).toBeInTheDocument() expect(screen.getByTestId('workflow-default-context')).toBeInTheDocument() expect(screen.getByTestId('snippet-main')).toHaveTextContent('snippet-1') expect(screen.queryByRole('main')).not.toBeInTheDocument() }) it('should initialize workflow context with published graph data when the published workflow exists', () => { render() expect(capturedWorkflowDefaultContextProps?.nodes).toEqual( mockPublishedSnippetDetail.graph.nodes, ) expect(capturedWorkflowDefaultContextProps?.edges).toEqual( mockPublishedSnippetDetail.graph.edges, ) expect(screen.getByTestId('snippet-main')).toHaveTextContent('snippet-1:true') }) it('should initialize workflow context with draft graph data when the published workflow is empty', () => { mockUseSnippetInit.mockReturnValue({ data: { snippet: mockDraftSnippetDetail.snippet, published: mockPublishedSnippetDetail, draft: mockDraftSnippetDetail, draftWorkflow: { hash: 'draft-hash', }, publishedWorkflow: undefined, hasDraftChanges: true, }, isLoading: false, }) render() expect(capturedWorkflowDefaultContextProps?.nodes).toEqual(mockDraftSnippetDetail.graph.nodes) expect(capturedWorkflowDefaultContextProps?.edges).toEqual(mockDraftSnippetDetail.graph.edges) expect(screen.getByTestId('snippet-main')).toHaveTextContent('snippet-1:false') }) it('should render loading fallback when orchestrate data is unavailable', () => { mockUseSnippetInit.mockReturnValue({ data: null, isLoading: false, }) render() expect(screen.getByRole('status')).toBeInTheDocument() }) it('should keep the detail route shell while orchestrate data is loading', () => { mockUseSnippetInit.mockReturnValue({ data: undefined, isLoading: true, }) render() expect(screen.getByRole('status')).toBeInTheDocument() expect(screen.queryByRole('main')).not.toBeInTheDocument() }) })