Files
tolaria/src/hooks/useAppNavigation.test.ts
lucaronin 5b1ecd7a94 refactor: remove tab bar — single note open at a time
Replace the multi-tab model with single-note navigation. One note is
open at a time; clicking any note (sidebar, wikilink, relationship)
replaces the current note in the editor. Back/Forward history still
works via Cmd+[/].

Removed: TabBar component, useClosedTabHistory, tab reorder/close/
reopen logic, Cmd+W/Cmd+Shift+T shortcuts, Close Tab and Reopen
Closed Tab menu items, tabLayout utility, and all related tests.

Simplified: useTabManagement (single-note), useAppNavigation (no tab
lookup), useKeyboardNavigation (note nav only), useNoteCreation (no
close-tab cleanup), useDeleteActions (deselect instead of close tab),
Editor (no TabBar render), Rust menu (removed 2 menu items).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:24:49 +01:00

104 lines
3.4 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { renderHook, act } from '@testing-library/react'
import { useAppNavigation } from './useAppNavigation'
import type { VaultEntry } from '../types'
function makeEntry(path: string): VaultEntry {
return { path, filename: path.split('/').pop()!, title: path, isA: null, aliases: [] } as VaultEntry
}
describe('useAppNavigation', () => {
let onSelectNote: ReturnType<typeof vi.fn>
beforeEach(() => {
onSelectNote = vi.fn()
})
function renderNav(overrides: {
entries?: VaultEntry[]
activeTabPath?: string | null
} = {}) {
const entries = overrides.entries ?? [makeEntry('/a.md'), makeEntry('/b.md'), makeEntry('/c.md')]
const activeTabPath = overrides.activeTabPath ?? null
return renderHook(() =>
useAppNavigation({ entries, activeTabPath, onSelectNote }),
)
}
// --- entriesByPath ---
describe('entriesByPath', () => {
it('builds a Map from entries for O(1) lookup', () => {
const entries = [makeEntry('/a.md'), makeEntry('/b.md')]
const { result } = renderNav({ entries })
expect(result.current.entriesByPath.get('/a.md')).toBe(entries[0])
expect(result.current.entriesByPath.get('/b.md')).toBe(entries[1])
expect(result.current.entriesByPath.get('/missing.md')).toBeUndefined()
})
})
// --- canGoBack / canGoForward initial state ---
describe('initial state', () => {
it('starts with canGoBack=false and canGoForward=false', () => {
const { result } = renderNav()
expect(result.current.canGoBack).toBe(false)
expect(result.current.canGoForward).toBe(false)
})
})
// --- navigation history integration ---
describe('navigation via activeTabPath changes', () => {
it('pushes to history when activeTabPath changes, enabling goBack', () => {
const entries = [makeEntry('/a.md'), makeEntry('/b.md')]
const { result, rerender } = renderHook(
({ activeTabPath }) =>
useAppNavigation({ entries, activeTabPath, onSelectNote }),
{ initialProps: { activeTabPath: '/a.md' as string | null } },
)
// Navigate to /b.md
rerender({ activeTabPath: '/b.md' })
expect(result.current.canGoBack).toBe(true)
expect(result.current.canGoForward).toBe(false)
})
it('handleGoBack calls onSelectNote with the previous entry', () => {
const entries = [makeEntry('/a.md'), makeEntry('/b.md')]
const { result, rerender } = renderHook(
({ activeTabPath }) =>
useAppNavigation({ entries, activeTabPath, onSelectNote }),
{ initialProps: { activeTabPath: '/a.md' as string | null } },
)
rerender({ activeTabPath: '/b.md' })
act(() => { result.current.handleGoBack() })
expect(onSelectNote).toHaveBeenCalledWith(entries[0])
})
it('handleGoForward works after going back', () => {
const entries = [makeEntry('/a.md'), makeEntry('/b.md')]
const { result, rerender } = renderHook(
({ activeTabPath }) =>
useAppNavigation({ entries, activeTabPath, onSelectNote }),
{ initialProps: { activeTabPath: '/a.md' as string | null } },
)
rerender({ activeTabPath: '/b.md' })
act(() => { result.current.handleGoBack() })
expect(result.current.canGoForward).toBe(true)
act(() => { result.current.handleGoForward() })
expect(onSelectNote).toHaveBeenCalledWith(entries[1])
})
})
})