Files
tolaria/tests/smoke/mounted-workspaces-unified-graph.spec.ts
2026-05-11 18:21:12 +02:00

222 lines
7.7 KiB
TypeScript

import { test, expect, type Page } from '@playwright/test'
import { sendShortcut } from './helpers'
const PRIMARY_VAULT_PATH = '/Users/mock/Field Notes'
const RESEARCH_VAULT_PATH = '/Users/mock/Research Lab'
const GETTING_STARTED_PATH = '/Users/mock/Getting Started'
const QUICK_OPEN_INPUT = 'input[placeholder="Search notes..."]'
const GLOBAL_SEARCH_INPUT = 'input[placeholder="Search in all notes..."]'
const SEARCH_NEEDLE = 'crossgraph-needle'
interface MockEntry {
path: string
filename: string
title: string
isA: string
aliases: string[]
belongsTo: string[]
relatedTo: string[]
status: string | null
archived: boolean
modifiedAt: number | null
createdAt: number | null
fileSize: number
snippet: string
wordCount: number
relationships: Record<string, string[]>
outgoingLinks: string[]
properties: Record<string, unknown>
template: null
sort: null
}
interface SearchResultData {
title: string
path: string
snippet: string
score: number
note_type: string | null
}
type MockHandler = (args?: Record<string, unknown>) => unknown
type MockWindow = Window & {
__mockHandlers?: Record<string, MockHandler>
__mountedWorkspaceSearchCalls?: string[]
}
interface MountedWorkspaceInitData {
contentByPathData: Record<string, string>
entriesByVaultData: Record<string, MockEntry[]>
gettingStartedPath: string
primaryVaultPath: string
researchVaultPath: string
}
function createMockEntry(vaultPath: string, filename: string, title: string, snippet: string): MockEntry {
return {
path: `${vaultPath}/${filename}`,
filename,
title,
isA: 'Note',
aliases: [],
belongsTo: [],
relatedTo: [],
status: null,
archived: false,
modifiedAt: 1_700_000_000,
createdAt: null,
fileSize: 256,
snippet,
wordCount: 12,
relationships: {},
outgoingLinks: [],
properties: {},
template: null,
sort: null,
}
}
async function installMountedWorkspaceMocks(page: Page): Promise<void> {
await page.route('**/api/vault/**', (route) => route.abort())
const primaryEntry = createMockEntry(
PRIMARY_VAULT_PATH,
'primary-signal.md',
'Primary Signal',
`Primary workspace has ${SEARCH_NEEDLE}`,
)
const researchEntry = createMockEntry(
RESEARCH_VAULT_PATH,
'research-beacon.md',
'Research Beacon',
`Research workspace has ${SEARCH_NEEDLE}`,
)
const entriesByVault: Record<string, MockEntry[]> = {
[PRIMARY_VAULT_PATH]: [primaryEntry],
[RESEARCH_VAULT_PATH]: [researchEntry],
}
const contentByPath = Object.fromEntries(
[primaryEntry, researchEntry].map((entry) => [entry.path, `# ${entry.title}\n\n${entry.snippet}`]),
)
await page.addInitScript((data: MountedWorkspaceInitData) => {
localStorage.clear()
localStorage.setItem('tolaria:claude-code-onboarding-dismissed', '1')
const mockWindow = window as MockWindow
mockWindow.__mountedWorkspaceSearchCalls = []
let ref: Record<string, MockHandler> | undefined
const entriesByVaultData = data.entriesByVaultData
const contentByPathData = data.contentByPathData
Object.defineProperty(window, '__mockHandlers', {
configurable: true,
set(value: Record<string, MockHandler>) {
ref = value
ref.load_vault_list = () => ({
vaults: [
{
label: 'Field Notes',
path: data.primaryVaultPath,
alias: 'field',
color: 'blue',
icon: null,
mounted: true,
},
{
label: 'Research Lab',
path: data.researchVaultPath,
alias: 'lab',
color: 'green',
icon: null,
mounted: true,
},
],
active_vault: data.primaryVaultPath,
default_workspace_path: data.researchVaultPath,
hidden_defaults: [],
})
ref.save_vault_list = () => null
ref.get_settings = () => ({ multi_workspace_enabled: true })
ref.get_default_vault_path = () => data.gettingStartedPath
ref.check_vault_exists = (args?: Record<string, unknown>) =>
args?.path === data.primaryVaultPath || args?.path === data.researchVaultPath
ref.list_vault = (args?: Record<string, unknown>) => entriesByVaultData[String(args?.path ?? '')] ?? []
ref.reload_vault = (args?: Record<string, unknown>) => entriesByVaultData[String(args?.path ?? '')] ?? []
ref.list_vault_folders = () => []
ref.list_views = () => []
ref.get_all_content = () => contentByPathData
ref.get_note_content = (args?: Record<string, unknown>) => contentByPathData[String(args?.path ?? '')] ?? ''
ref.get_modified_files = () => []
ref.get_file_history = () => []
ref.search_vault = (args?: Record<string, unknown>) => {
const vaultPath = String(args?.vaultPath ?? '')
mockWindow.__mountedWorkspaceSearchCalls?.push(vaultPath)
const query = String(args?.query ?? '').toLowerCase()
const results = (entriesByVaultData[vaultPath] ?? [])
.filter((entry) => `${entry.title} ${entry.snippet}`.toLowerCase().includes(query))
.map((entry): SearchResultData => ({
title: entry.title,
path: entry.path,
snippet: entry.snippet,
score: 1,
note_type: entry.isA,
}))
return { results, elapsed_ms: 1 }
}
},
get() {
return ref
},
})
}, {
contentByPathData: contentByPath,
entriesByVaultData: entriesByVault,
gettingStartedPath: GETTING_STARTED_PATH,
primaryVaultPath: PRIMARY_VAULT_PATH,
researchVaultPath: RESEARCH_VAULT_PATH,
})
}
async function openQuickOpen(page: Page): Promise<void> {
await page.locator('body').click()
await sendShortcut(page, 'p', ['Control'])
await expect(page.locator(QUICK_OPEN_INPUT)).toBeVisible()
}
async function openGlobalSearch(page: Page): Promise<void> {
await page.locator('body').click()
await sendShortcut(page, 'f', ['Control', 'Shift'])
await expect(page.locator(GLOBAL_SEARCH_INPUT)).toBeVisible()
}
test('mounted workspaces share the graph across note list, quick open, and search @smoke', async ({ page }) => {
await installMountedWorkspaceMocks(page)
await page.goto('/', { waitUntil: 'domcontentloaded' })
await page.getByText('All Notes', { exact: true }).click()
const noteList = page.getByTestId('note-list-container')
await expect(noteList.getByText('Primary Signal').first()).toBeVisible()
await expect(noteList.getByText('Research Beacon').first()).toBeVisible()
await expect(noteList.getByText('FN').first()).toBeVisible()
await expect(noteList.getByText('RL').first()).toBeVisible()
await openQuickOpen(page)
await page.locator(QUICK_OPEN_INPUT).fill('Beacon')
const quickOpen = page.getByTestId('quick-open-palette')
await expect(quickOpen.getByText('Research Beacon')).toBeVisible()
await expect(quickOpen.getByTestId('note-search-workspace-badge').filter({ hasText: 'RL' })).toBeVisible()
await page.keyboard.press('Escape')
await openGlobalSearch(page)
await page.locator(GLOBAL_SEARCH_INPUT).fill(SEARCH_NEEDLE)
await expect(page.getByText('Primary Signal')).toBeVisible({ timeout: 5_000 })
await expect(page.getByText('Research Beacon')).toBeVisible()
await expect(page.getByTestId('search-result-workspace-badge').filter({ hasText: 'FN' })).toBeVisible()
await expect(page.getByTestId('search-result-workspace-badge').filter({ hasText: 'RL' })).toBeVisible()
const searchCalls = await page.evaluate(() => (window as MockWindow).__mountedWorkspaceSearchCalls ?? [])
expect(new Set(searchCalls)).toEqual(new Set([PRIMARY_VAULT_PATH, RESEARCH_VAULT_PATH]))
})