Files
tolaria/src/test/setup.ts

102 lines
3.6 KiB
TypeScript
Raw Normal View History

import '@testing-library/jest-dom/vitest'
import { vi } from 'vitest'
import { createElement, type ReactNode, type ComponentType } from 'react'
// Stub fetch to prevent jsdom@28 + Node 22 undici incompatibility.
// jsdom's JSDOMDispatcher passes an onError handler that Node 22's bundled
// undici rejects with InvalidArgumentError (UND_ERR_INVALID_ARG).
// Tests should never make real network requests — individual tests can
// override this stub via vi.mocked(fetch).mockImplementation(...).
globalThis.fetch = vi.fn(() =>
Promise.resolve(new Response(null, { status: 418 })),
) as typeof globalThis.fetch
// Stub WebSocket to prevent Node 22 + undici WebSocket incompatibility.
// undici's WebSocket dispatchEvent crashes with "The event argument must be
// an instance of Event" when running in jsdom environment.
// Tests should never open real WebSocket connections.
globalThis.WebSocket = class MockWebSocket {
static CONNECTING = 0
static OPEN = 1
static CLOSING = 2
static CLOSED = 3
readyState = MockWebSocket.OPEN
onopen: ((event: Event) => void) | null = null
onclose: ((event: CloseEvent) => void) | null = null
onmessage: ((event: MessageEvent) => void) | null = null
onerror: ((event: Event) => void) | null = null
constructor(/* url: string, protocols?: string | string[] */) {
// No-op: don't open real connections in tests
}
send(/* data: unknown */) {}
close() { this.readyState = MockWebSocket.CLOSED }
addEventListener() {}
removeEventListener() {}
dispatchEvent() { return true }
} as unknown as typeof WebSocket
feat: full-text search with qmd backend and SearchPanel UI (Cmd+Shift+F) (#64) * feat: add search backend (qmd CLI) and design file - Add search.rs: qmd integration for keyword (BM25), semantic (vsearch), and hybrid search modes via CLI JSON output - Register search_vault Tauri command in lib.rs - Design file with 3 frames: empty, results, no-results states - Fix pre-existing test failures: install @tauri-apps/plugin-opener, add mock in test setup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add SearchPanel UI with Cmd+Shift+F shortcut - SearchPanel component: full-text search overlay with keyword/semantic mode toggle, debounced search (200ms), arrow key navigation, result count + elapsed time display, note type badges from vault entries - Cmd+Shift+F shortcut registered in useAppKeyboard - Mock search_vault handler in mock-tauri for browser dev mode - SearchResult/SearchResponse types in types.ts - Tests: 15 new tests for SearchPanel + 2 for keyboard shortcut - scrollIntoView mock in test setup for jsdom compatibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: correct SearchPanel imports for Tauri/browser dual-mode Use invoke from @tauri-apps/api/core and mockInvoke from mock-tauri with a searchCall wrapper, fixing the TypeScript build error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: make test_detect_collection_fallback robust when qmd not installed * fix: reset localStorage between App tests to prevent view mode state leak --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:09:50 +01:00
// Mock scrollIntoView for jsdom (not implemented)
Element.prototype.scrollIntoView = vi.fn()
// Mock ResizeObserver for jsdom (not implemented)
globalThis.ResizeObserver = class {
observe() {}
unobserve() {}
disconnect() {}
} as unknown as typeof ResizeObserver
// Mock IntersectionObserver for jsdom (not implemented)
globalThis.IntersectionObserver = class {
observe() {}
unobserve() {}
disconnect() {}
} as unknown as typeof IntersectionObserver
feat: full-text search with qmd backend and SearchPanel UI (Cmd+Shift+F) (#64) * feat: add search backend (qmd CLI) and design file - Add search.rs: qmd integration for keyword (BM25), semantic (vsearch), and hybrid search modes via CLI JSON output - Register search_vault Tauri command in lib.rs - Design file with 3 frames: empty, results, no-results states - Fix pre-existing test failures: install @tauri-apps/plugin-opener, add mock in test setup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add SearchPanel UI with Cmd+Shift+F shortcut - SearchPanel component: full-text search overlay with keyword/semantic mode toggle, debounced search (200ms), arrow key navigation, result count + elapsed time display, note type badges from vault entries - Cmd+Shift+F shortcut registered in useAppKeyboard - Mock search_vault handler in mock-tauri for browser dev mode - SearchResult/SearchResponse types in types.ts - Tests: 15 new tests for SearchPanel + 2 for keyboard shortcut - scrollIntoView mock in test setup for jsdom compatibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: correct SearchPanel imports for Tauri/browser dual-mode Use invoke from @tauri-apps/api/core and mockInvoke from mock-tauri with a searchCall wrapper, fixing the TypeScript build error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: make test_detect_collection_fallback robust when qmd not installed * fix: reset localStorage between App tests to prevent view mode state leak --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:09:50 +01:00
// Mock @tauri-apps/plugin-opener for test environment
vi.mock('@tauri-apps/plugin-opener', () => ({
openUrl: vi.fn(),
}))
// Mock react-day-picker: Calendar component uses DayPicker which needs real DOM APIs not available in jsdom
vi.mock('react-day-picker', () => ({
DayPicker: () => null,
getDefaultClassNames: () => ({}),
}))
// Mock react-virtuoso: JSDOM has no real viewport, so render all items directly
vi.mock('react-virtuoso', () => ({
Virtuoso: ({ data, itemContent, components }: {
data?: unknown[]
itemContent?: (index: number, item: unknown) => ReactNode
components?: { Header?: ComponentType }
}) => {
const Header = components?.Header
return createElement('div', { 'data-testid': 'virtuoso-mock' },
Header ? createElement(Header) : null,
data?.map((item: unknown, index: number) =>
createElement('div', { key: index }, itemContent?.(index, item))
)
)
},
GroupedVirtuoso: ({ groupCounts, groupContent, itemContent }: {
groupCounts: number[]
groupContent: (index: number) => ReactNode
itemContent: (index: number, groupIndex: number) => ReactNode
}) => {
let globalIndex = 0
return createElement('div', { 'data-testid': 'grouped-virtuoso-mock' },
groupCounts?.map((count: number, groupIndex: number) => {
const items = []
for (let i = 0; i < count; i++) {
items.push(createElement('div', { key: globalIndex }, itemContent(globalIndex, groupIndex)))
globalIndex++
}
return createElement('div', { key: `group-${groupIndex}` },
groupContent(groupIndex),
...items
)
})
)
},
}))