import '@testing-library/jest-dom/vitest'; import { fireEvent, render, screen, waitFor, within } from '@testing-library/svelte'; import { describe, expect, it, vi } from 'vitest'; import ProjectWorkbench from './ProjectWorkbench.svelte'; describe('ProjectWorkbench', () => { it('renders project rail, channel sidebar, topbar, and inspector', () => { render(ProjectWorkbench, { props: { currentUser: { account: 'david@example.com' } } }); expect(screen.getByLabelText('Project list')).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Project A1' })).toHaveAttribute('aria-pressed', 'true'); expect(screen.getByLabelText('Project channels')).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Message Flow 36' })).toBeInTheDocument(); expect(screen.getByLabelText('Global search')).toBeDisabled(); expect(screen.getByLabelText('Object inspector')).toBeInTheDocument(); }); it('refreshes channels when switching projects', async () => { render(ProjectWorkbench, { props: { currentUser: { account: 'david@example.com' } } }); await fireEvent.click(screen.getByRole('button', { name: 'Project A2' })); expect(screen.getByRole('button', { name: 'Project A2' })).toHaveAttribute('aria-pressed', 'true'); expect(screen.getByText('Ops Dashboard')).toBeInTheDocument(); }); it('marks active channel with aria-pressed', async () => { render(ProjectWorkbench, { props: { currentUser: { account: 'david@example.com' } } }); await fireEvent.click(screen.getByRole('button', { name: 'Work Plan 8' })); expect(screen.getByRole('button', { name: 'Work Plan 8' })).toHaveAttribute('aria-pressed', 'true'); }); it('shows inspected task details in the inspector', async () => { render(ProjectWorkbench, { props: { currentUser: { account: 'david@example.com' } } }); const inspector = screen.getByLabelText('Object inspector'); expect(within(inspector).getByRole('button', { name: 'Discussion' })).toHaveAttribute('aria-pressed', 'true'); await fireEvent.click(screen.getByRole('button', { name: 'Work Plan 8' })); await fireEvent.click(screen.getByRole('button', { name: 'Inspect Confirm homepage information architecture' })); expect(within(inspector).getByRole('heading', { name: 'Confirm homepage information architecture' })).toBeInTheDocument(); await fireEvent.click(within(inspector).getByRole('button', { name: 'Properties' })); expect(within(inspector).getByRole('button', { name: 'Properties' })).toHaveAttribute('aria-pressed', 'true'); expect(within(inspector).getByText('Status')).toBeInTheDocument(); expect(within(inspector).getByText('Open')).toBeInTheDocument(); expect(within(inspector).getByText('Owner')).toBeInTheDocument(); expect(within(inspector).getByText('David')).toBeInTheDocument(); }); it('clears the inspector when the project or channel changes', async () => { render(ProjectWorkbench, { props: { currentUser: { account: 'david@example.com' } } }); const inspector = screen.getByLabelText('Object inspector'); await fireEvent.click(screen.getByRole('button', { name: 'Work Plan 8' })); await fireEvent.click(screen.getByRole('button', { name: 'Inspect Confirm homepage information architecture' })); expect(within(inspector).getByRole('heading', { name: 'Confirm homepage information architecture' })).toBeInTheDocument(); await fireEvent.click(screen.getByRole('button', { name: 'Project A2' })); expect(within(inspector).getByRole('heading', { name: 'Inspector' })).toBeInTheDocument(); expect(within(inspector).queryByRole('heading', { name: 'Confirm homepage information architecture' })).not.toBeInTheDocument(); await fireEvent.click(screen.getByRole('button', { name: 'Work Plan 8' })); await fireEvent.click(screen.getByRole('button', { name: 'Inspect Confirm homepage information architecture' })); await fireEvent.click(screen.getByRole('button', { name: 'Message Flow 36' })); expect(within(inspector).getByRole('heading', { name: 'Inspector' })).toBeInTheDocument(); }); it('preserves task completion when switching channels within a project', async () => { render(ProjectWorkbench, { props: { currentUser: { account: 'david@example.com' } } }); await fireEvent.click(screen.getByRole('button', { name: 'Work Plan 8' })); const task = screen.getByRole('checkbox', { name: 'Mark Confirm homepage information architecture complete' }); await fireEvent.click(task); expect(task).toBeChecked(); await fireEvent.click(screen.getByRole('button', { name: 'Message Flow 36' })); await fireEvent.click(screen.getByRole('button', { name: 'Work Plan 8' })); expect(screen.getByRole('checkbox', { name: 'Mark Confirm homepage information architecture complete' })).toBeChecked(); await fireEvent.click(screen.getByRole('button', { name: 'Project A2' })); await fireEvent.click(screen.getByRole('button', { name: 'Work Plan 8' })); expect(screen.getByRole('checkbox', { name: 'Mark Confirm homepage information architecture complete' })).not.toBeChecked(); }); it('provides Copy link feedback and disables unavailable archive actions', async () => { const originalClipboard = Object.getOwnPropertyDescriptor(navigator, 'clipboard'); Object.defineProperty(navigator, 'clipboard', { configurable: true, value: { writeText: vi.fn().mockResolvedValue(undefined) } as Clipboard, }); render(ProjectWorkbench, { props: { currentUser: { account: 'david@example.com' } } }); const inspector = screen.getByLabelText('Object inspector'); await fireEvent.click(screen.getByRole('button', { name: 'Work Plan 8' })); await fireEvent.click(screen.getByRole('button', { name: 'Inspect Confirm homepage information architecture' })); await fireEvent.click(within(inspector).getByRole('button', { name: 'More' })); await fireEvent.click(within(inspector).getByRole('button', { name: 'Copy link' })); await waitFor(() => expect(within(inspector).getByRole('status')).toHaveTextContent('Link copied to clipboard.')); expect(within(inspector).getByRole('button', { name: 'Archive is unavailable in this preview' })).toBeDisabled(); if (originalClipboard) { Object.defineProperty(navigator, 'clipboard', originalClipboard); } else { Reflect.deleteProperty(navigator, 'clipboard'); } }); });