// @vitest-environment jsdom import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import { render, screen, fireEvent } from '@testing-library/react' vi.mock('@/actions/user-settings', () => ({ updateUserSettingsAction: vi.fn().mockResolvedValue({ success: true, settings: {} }), })) import { SplitPane } from '@/components/split-pane/split-pane' import { useUserSettingsStore } from '@/stores/user-settings/store' function seedPositions(key: string, positions: number[]) { useUserSettingsStore.setState((s) => { s.entities.settings = { layout: { splitPanePositions: { [key]: positions }, }, } }) } function resetStore() { useUserSettingsStore.setState((s) => { s.entities.settings = {} s.context.hydrated = false s.context.isDemo = false }) } describe('SplitPane', () => { beforeEach(() => { resetStore() // Default: desktop viewport Object.defineProperty(window, 'innerWidth', { writable: true, configurable: true, value: 1440 }) window.dispatchEvent(new Event('resize')) }) afterEach(() => { vi.restoreAllMocks() }) it('renders 2 panes', () => { render( Pane A,
Pane B
]} defaultSplit={[30, 70]} cookieKey="test-2pane" /> ) expect(screen.getByText('Pane A')).toBeTruthy() expect(screen.getByText('Pane B')).toBeTruthy() }) it('renders 3 panes with 2 dividers', () => { const { container } = render( Left,
Middle
,
Right
, ]} defaultSplit={[28, 35, 37]} cookieKey="test-3pane" /> ) expect(screen.getByText('Left')).toBeTruthy() expect(screen.getByText('Middle')).toBeTruthy() expect(screen.getByText('Right')).toBeTruthy() // 2 dividers: cursor-col-resize elements const dividers = container.querySelectorAll('.cursor-col-resize') expect(dividers).toHaveLength(2) }) it('restores splits from user-settings store on mount', () => { seedPositions('test-restore', [40, 60]) const { container } = render( A,
B
]} defaultSplit={[20, 80]} cookieKey="test-restore" /> ) // Left pane should have width 40%, not the default 20% const paneDiv = container.querySelector('[style*="40%"]') expect(paneDiv).toBeTruthy() }) it('falls back to defaultSplit when persisted positions are invalid', () => { // Wrong number of values for a 2-pane layout seedPositions('test-invalid', [10, 30, 60]) const { container } = render( A,
B
]} defaultSplit={[25, 75]} cookieKey="test-invalid" /> ) const paneDiv = container.querySelector('[style*="25%"]') expect(paneDiv).toBeTruthy() }) it('renders tabs on mobile viewport', () => { Object.defineProperty(window, 'innerWidth', { writable: true, configurable: true, value: 768 }) window.dispatchEvent(new Event('resize')) render( Content A,
Content B
]} defaultSplit={[50, 50]} cookieKey="test-mobile" tabLabels={['Tab A', 'Tab B']} /> ) expect(screen.getByText('Tab A')).toBeTruthy() expect(screen.getByText('Tab B')).toBeTruthy() // Only first tab content visible by default expect(screen.getByText('Content A')).toBeTruthy() expect(screen.queryByText('Content B')).toBeNull() }) it('switches tab content on mobile', () => { Object.defineProperty(window, 'innerWidth', { writable: true, configurable: true, value: 600 }) window.dispatchEvent(new Event('resize')) render( Content A,
Content B
]} defaultSplit={[50, 50]} cookieKey="test-mobile-switch" tabLabels={['Tab A', 'Tab B']} /> ) // Click second tab fireEvent.click(screen.getByText('Tab B')) expect(screen.queryByText('Content A')).toBeNull() expect(screen.getByText('Content B')).toBeTruthy() }) it('back button not visible on tab 0 in mobile', () => { Object.defineProperty(window, 'innerWidth', { writable: true, configurable: true, value: 600 }) window.dispatchEvent(new Event('resize')) render( A,
B
,
C
]} defaultSplit={[33, 33, 34]} cookieKey="test-back-hidden" tabLabels={['T1', 'T2', 'T3']} /> ) // On tab 0, no back button expect(screen.queryByLabelText('Terug')).toBeNull() }) it('back button visible on tab > 0 and navigates back', () => { Object.defineProperty(window, 'innerWidth', { writable: true, configurable: true, value: 600 }) window.dispatchEvent(new Event('resize')) render( A,
B
,
C
]} defaultSplit={[33, 33, 34]} cookieKey="test-back-nav" tabLabels={['T1', 'T2', 'T3']} /> ) // Switch to tab 2 fireEvent.click(screen.getByText('T3')) expect(screen.getByText('C')).toBeTruthy() expect(screen.getByLabelText('Terug')).toBeTruthy() // Click back → tab 1 fireEvent.click(screen.getByLabelText('Terug')) expect(screen.getByText('B')).toBeTruthy() // Click back again → tab 0, no back button fireEvent.click(screen.getByLabelText('Terug')) expect(screen.getByText('A')).toBeTruthy() expect(screen.queryByLabelText('Terug')).toBeNull() }) it('controlled activeTab prop switches the active pane', () => { Object.defineProperty(window, 'innerWidth', { writable: true, configurable: true, value: 600 }) window.dispatchEvent(new Event('resize')) const { rerender } = render( A,
B
,
C
]} defaultSplit={[33, 33, 34]} cookieKey="test-controlled" tabLabels={['T1', 'T2', 'T3']} activeTab={0} onActiveTabChange={vi.fn()} /> ) expect(screen.getByText('A')).toBeTruthy() rerender( A,
B
,
C
]} defaultSplit={[33, 33, 34]} cookieKey="test-controlled" tabLabels={['T1', 'T2', 'T3']} activeTab={2} onActiveTabChange={vi.fn()} /> ) expect(screen.getByText('C')).toBeTruthy() }) it('does not render dividers on mobile', () => { Object.defineProperty(window, 'innerWidth', { writable: true, configurable: true, value: 600 }) window.dispatchEvent(new Event('resize')) const { container } = render( A,
B
]} defaultSplit={[50, 50]} cookieKey="test-no-dividers" /> ) const dividers = container.querySelectorAll('.cursor-col-resize') expect(dividers).toHaveLength(0) }) })