Files
tolaria/src/hooks/useDiffMode.test.ts

123 lines
4.2 KiB
TypeScript
Raw Normal View History

refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { renderHook, act } from '@testing-library/react'
import { useDiffMode } from './useDiffMode'
describe('useDiffMode', () => {
let onLoadDiff: ReturnType<typeof vi.fn>
let onLoadDiffAtCommit: ReturnType<typeof vi.fn>
beforeEach(() => {
onLoadDiff = vi.fn()
onLoadDiffAtCommit = vi.fn()
})
function renderDiffHook(activeTabPath: string | null = '/note.md') {
return renderHook(
({ path }) => useDiffMode({ activeTabPath: path, onLoadDiff, onLoadDiffAtCommit }),
{ initialProps: { path: activeTabPath } },
)
}
it('starts with diff mode off', () => {
const { result } = renderDiffHook()
expect(result.current.diffMode).toBe(false)
expect(result.current.diffContent).toBeNull()
expect(result.current.diffLoading).toBe(false)
})
it('toggles diff mode on and loads content', async () => {
onLoadDiff.mockResolvedValue('diff content here')
const { result } = renderDiffHook()
await act(async () => { await result.current.handleToggleDiff() })
expect(onLoadDiff).toHaveBeenCalledWith('/note.md')
expect(result.current.diffMode).toBe(true)
expect(result.current.diffContent).toBe('diff content here')
expect(result.current.diffLoading).toBe(false)
})
it('toggles diff mode off when already on', async () => {
onLoadDiff.mockResolvedValue('diff')
const { result } = renderDiffHook()
await act(async () => { await result.current.handleToggleDiff() })
expect(result.current.diffMode).toBe(true)
await act(async () => { await result.current.handleToggleDiff() })
expect(result.current.diffMode).toBe(false)
expect(result.current.diffContent).toBeNull()
})
it('does nothing when activeTabPath is null', async () => {
const { result } = renderDiffHook(null)
await act(async () => { await result.current.handleToggleDiff() })
expect(onLoadDiff).not.toHaveBeenCalled()
expect(result.current.diffMode).toBe(false)
})
it('does nothing when onLoadDiff is not provided', async () => {
const { result } = renderHook(() => useDiffMode({ activeTabPath: '/note.md' }))
await act(async () => { await result.current.handleToggleDiff() })
expect(result.current.diffMode).toBe(false)
})
it('resets diff state when activeTabPath changes', async () => {
onLoadDiff.mockResolvedValue('diff')
const { result, rerender } = renderDiffHook('/note-a.md')
await act(async () => { await result.current.handleToggleDiff() })
expect(result.current.diffMode).toBe(true)
rerender({ path: '/note-b.md' })
expect(result.current.diffMode).toBe(false)
expect(result.current.diffContent).toBeNull()
})
it('handles load error gracefully', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
onLoadDiff.mockRejectedValue(new Error('network'))
const { result } = renderDiffHook()
await act(async () => { await result.current.handleToggleDiff() })
expect(result.current.diffMode).toBe(false)
expect(result.current.diffLoading).toBe(false)
expect(warn).toHaveBeenCalled()
warn.mockRestore()
})
it('loads diff at specific commit', async () => {
onLoadDiffAtCommit.mockResolvedValue('commit diff')
const { result } = renderDiffHook()
await act(async () => { await result.current.handleViewCommitDiff('abc123') })
expect(onLoadDiffAtCommit).toHaveBeenCalledWith('/note.md', 'abc123')
expect(result.current.diffMode).toBe(true)
expect(result.current.diffContent).toBe('commit diff')
})
it('skips commit diff when no callback', async () => {
const { result } = renderHook(() => useDiffMode({ activeTabPath: '/note.md' }))
await act(async () => { await result.current.handleViewCommitDiff('abc123') })
expect(result.current.diffMode).toBe(false)
})
it('handles commit diff error gracefully', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
onLoadDiffAtCommit.mockRejectedValue(new Error('fail'))
const { result } = renderDiffHook()
await act(async () => { await result.current.handleViewCommitDiff('abc123') })
expect(result.current.diffMode).toBe(false)
expect(result.current.diffLoading).toBe(false)
warn.mockRestore()
})
})