Adds a third editor mode (alongside WYSIWYG and Diff) that shows the raw markdown file in a monospaced textarea. Includes: - useRawMode hook with derived state (no setState-in-effect) and onFlushPending - RawEditorView: textarea with 500ms debounce, YAML error banner, wikilink autocomplete via [[ trigger, Cmd+S save - BreadcrumbBar Code icon toggles raw mode; mutual exclusion with diff mode - Command palette: "Toggle Raw Editor" (View group, requires active tab) - useEditorModeExclusion hook extracted to keep Editor.tsx under complexity threshold - buildViewCommands extracted from useCommandRegistry for same reason - RawToggleButton and RawModeEditorSection components extracted for clean CC Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88 lines
2.6 KiB
TypeScript
88 lines
2.6 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
import { renderHook, act } from '@testing-library/react'
|
|
import { useRawMode } from './useRawMode'
|
|
|
|
describe('useRawMode', () => {
|
|
let onFlushPending: ReturnType<typeof vi.fn>
|
|
|
|
beforeEach(() => {
|
|
onFlushPending = vi.fn().mockResolvedValue(true)
|
|
})
|
|
|
|
function renderRawHook(activeTabPath: string | null = '/note.md') {
|
|
return renderHook(
|
|
({ path }) => useRawMode({ activeTabPath: path, onFlushPending }),
|
|
{ initialProps: { path: activeTabPath } },
|
|
)
|
|
}
|
|
|
|
it('starts with raw mode off', () => {
|
|
const { result } = renderRawHook()
|
|
expect(result.current.rawMode).toBe(false)
|
|
})
|
|
|
|
it('toggles raw mode on', async () => {
|
|
const { result } = renderRawHook()
|
|
|
|
await act(async () => { await result.current.handleToggleRaw() })
|
|
|
|
expect(result.current.rawMode).toBe(true)
|
|
})
|
|
|
|
it('flushes pending edits when activating raw mode', async () => {
|
|
const { result } = renderRawHook()
|
|
|
|
await act(async () => { await result.current.handleToggleRaw() })
|
|
|
|
expect(onFlushPending).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('does not flush pending edits when deactivating raw mode', async () => {
|
|
const { result } = renderRawHook()
|
|
|
|
await act(async () => { await result.current.handleToggleRaw() })
|
|
onFlushPending.mockClear()
|
|
|
|
await act(async () => { await result.current.handleToggleRaw() })
|
|
|
|
expect(onFlushPending).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('toggles raw mode off when already on', async () => {
|
|
const { result } = renderRawHook()
|
|
|
|
await act(async () => { await result.current.handleToggleRaw() })
|
|
expect(result.current.rawMode).toBe(true)
|
|
|
|
await act(async () => { await result.current.handleToggleRaw() })
|
|
expect(result.current.rawMode).toBe(false)
|
|
})
|
|
|
|
it('resets raw mode when activeTabPath changes', async () => {
|
|
const { result, rerender } = renderRawHook('/note-a.md')
|
|
|
|
await act(async () => { await result.current.handleToggleRaw() })
|
|
expect(result.current.rawMode).toBe(true)
|
|
|
|
rerender({ path: '/note-b.md' })
|
|
expect(result.current.rawMode).toBe(false)
|
|
})
|
|
|
|
it('works without onFlushPending callback', async () => {
|
|
const { result } = renderHook(() => useRawMode({ activeTabPath: '/note.md' }))
|
|
|
|
await act(async () => { await result.current.handleToggleRaw() })
|
|
|
|
expect(result.current.rawMode).toBe(true)
|
|
})
|
|
|
|
it('does not activate raw mode when activeTabPath is null', async () => {
|
|
const { result } = renderRawHook(null)
|
|
|
|
await act(async () => { await result.current.handleToggleRaw() })
|
|
|
|
// Cannot activate raw mode without an active tab path
|
|
expect(result.current.rawMode).toBe(false)
|
|
})
|
|
})
|