// src/layout/hooks/__tests__/useWorkspaceViews.test.tsx import { describe, it, expect } from 'vitest' import { renderHook, act } from '@testing-library/react' import { Workspace } from '../../../plugin/workspace' import { ItemView } from '../../../plugin/views/item-view' import { useWorkspaceViews } from '../useWorkspaceViews' import { ReamsEvents } from '../../../plugin/reams/events' import type { App } from '../../../plugin/app' class TestView extends ItemView { getViewType() { return 'test-view' } getDisplayText() { return 'Test' } } class TestViewB extends ItemView { getViewType() { return 'test-view-b' } getDisplayText() { return 'Test B' } } /** * Create a minimal App mock for testing hooks that need app.reamsEvents */ function createAppMock(ws: Workspace): App { const reamsEvents = new ReamsEvents() const app = { workspace: ws, reamsEvents, } as unknown as App ws.app = app return app } // Core views (#406) registered by Workspace constructor for content-type routing. const CORE_VIEW_TYPES = ['markdown', 'reams-unsupported'] describe('useWorkspaceViews', () => { it('returns empty leaves and only core view registrations initially', () => { const ws = new Workspace(document.createElement('div')) const app = createAppMock(ws) const { result } = renderHook(() => useWorkspaceViews(app)) expect(result.current.leftLeaves).toEqual([]) expect(result.current.rightLeaves).toEqual([]) expect([...result.current.registeredTypes].sort()).toEqual([...CORE_VIEW_TYPES].sort()) }) it('updates when a view is registered', () => { const ws = new Workspace(document.createElement('div')) const app = createAppMock(ws) const { result } = renderHook(() => useWorkspaceViews(app)) act(() => { ws.registerViewType('test-view', (leaf) => new TestView(leaf)) }) expect(result.current.registeredTypes).toContain('test-view') }) it('updates when a view is unregistered', () => { const ws = new Workspace(document.createElement('div')) const app = createAppMock(ws) ws.registerViewType('test-view', (leaf) => new TestView(leaf)) const { result } = renderHook(() => useWorkspaceViews(app)) expect(result.current.registeredTypes).toContain('test-view') act(() => { ws.unregisterViewType('test-view') }) expect(result.current.registeredTypes).not.toContain('test-view') }) it('updates leftLeaves when layout changes', () => { const ws = new Workspace(document.createElement('div')) const app = createAppMock(ws) const { result } = renderHook(() => useWorkspaceViews(app)) act(() => { ws.getLeftLeaf(true) }) expect(result.current.leftLeaves).toHaveLength(1) }) it('updates rightLeaves when layout changes', () => { const ws = new Workspace(document.createElement('div')) const app = createAppMock(ws) const { result } = renderHook(() => useWorkspaceViews(app)) act(() => { ws.getRightLeaf(true) }) expect(result.current.rightLeaves).toHaveLength(1) }) it('detects view swap on existing leaf (same leaf, different view)', async () => { const ws = new Workspace(document.createElement('div')) const app = createAppMock(ws) ws.registerViewType('test-view', (leaf) => new TestView(leaf)) ws.registerViewType('test-view-b', (leaf) => new TestViewB(leaf)) // Create a right leaf with test-view const leaf = ws.getRightLeaf(true) await leaf.openView('test-view') const { result } = renderHook(() => useWorkspaceViews(app)) // Verify initial state expect(result.current.rightLeaves).toHaveLength(1) expect(result.current.rightLeaves[0].view?.getViewType()).toBe('test-view') // Swap the view on the SAME leaf — openView triggers layout-change await act(async () => { await leaf.openView('test-view-b') }) // Hook should have re-rendered and picked up the new view expect(result.current.rightLeaves).toHaveLength(1) expect(result.current.rightLeaves[0]).toBe(leaf) // same leaf object expect(result.current.rightLeaves[0].view?.getViewType()).toBe('test-view-b') }) it('cleans up event listeners on unmount', () => { const ws = new Workspace(document.createElement('div')) const app = createAppMock(ws) const { unmount } = renderHook(() => useWorkspaceViews(app)) unmount() // After unmount, registering a view should not cause errors expect(() => ws.registerViewType('test-view', (leaf) => new TestView(leaf))).not.toThrow() }) })