import { test, expect, type Page } from '@playwright/test' 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 outgoingLinks: string[] properties: Record template: null sort: null } interface VaultSwitcherPaths { gettingStartedPath: string personalVaultPath: string workVaultPath: string } interface VaultSwitcherInitData { allContent: Record defaultVaultExists: boolean entriesByVault: Record paths: VaultSwitcherPaths } interface InstallVaultSwitcherMocksOptions { defaultVaultExists?: boolean } function createVaultSwitcherPaths(): VaultSwitcherPaths { return { gettingStartedPath: '/Users/mock/Documents/Getting Started', personalVaultPath: '/Users/mock/Personal', workVaultPath: '/Users/mock/Work', } } 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: 1700000000, createdAt: null, fileSize: 256, snippet, wordCount: 12, relationships: {}, outgoingLinks: [], properties: {}, template: null, sort: null, } } function buildEntriesByVault(paths: VaultSwitcherPaths): Record { return { [paths.gettingStartedPath]: [createMockEntry(paths.gettingStartedPath, 'getting-started.md', 'Getting Started Note', 'Getting Started snippet')], [paths.workVaultPath]: [createMockEntry(paths.workVaultPath, 'work-home.md', 'Work Home', 'Work Home snippet')], [paths.personalVaultPath]: [createMockEntry(paths.personalVaultPath, 'personal-home.md', 'Personal Home', 'Personal Home snippet')], } } function buildAllContent(entriesByVault: Record): Record { return Object.fromEntries( Object.values(entriesByVault) .flat() .map((entry) => [entry.path, `# ${entry.title}\n\n${entry.snippet}`]), ) } function buildVaultSwitcherInitData(defaultVaultExists: boolean): VaultSwitcherInitData { const paths = createVaultSwitcherPaths() const entriesByVault = buildEntriesByVault(paths) return { allContent: buildAllContent(entriesByVault), defaultVaultExists, entriesByVault, paths, } } async function installVaultSwitcherMocks( page: Page, options: InstallVaultSwitcherMocksOptions = {}, ) { const initData = buildVaultSwitcherInitData(options.defaultVaultExists ?? true) await page.addInitScript((data: VaultSwitcherInitData) => { localStorage.clear() localStorage.setItem('tolaria:claude-code-onboarding-dismissed', '1') let ref: Record | null = null Object.defineProperty(window, '__mockHandlers', { configurable: true, set(value) { ref = value as Record ref.load_vault_list = () => ({ vaults: [ { label: 'Work Vault', path: data.paths.workVaultPath }, { label: 'Personal Vault', path: data.paths.personalVaultPath }, ], active_vault: data.paths.workVaultPath, hidden_defaults: [], }) ref.get_default_vault_path = () => data.paths.gettingStartedPath ref.check_vault_exists = (args: { path?: string }) => { if (!args?.path) { return false } if (args.path === data.paths.gettingStartedPath) { return data.defaultVaultExists } return data.defaultVaultExists || args.path === data.paths.workVaultPath || args.path === data.paths.personalVaultPath } ref.list_vault = (args: { path?: string }) => data.entriesByVault[args?.path ?? ''] ?? [] ref.list_vault_folders = () => [] ref.list_views = () => [] ref.get_all_content = () => data.allContent ref.get_note_content = (args: { path?: string }) => data.allContent[args?.path ?? ''] ?? '' ref.get_modified_files = () => [] ref.get_file_history = () => [] }, get() { return ref }, }) }, initData) } test('bottom bar vault switching works with keyboard and mouse @smoke', async ({ page }) => { await installVaultSwitcherMocks(page) await page.goto('/', { waitUntil: 'domcontentloaded' }) const trigger = page.getByTestId('status-vault-trigger') const noteList = page.getByTestId('note-list-container') await expect(trigger).toContainText('Work Vault') await expect(noteList.getByText('Work Home', { exact: true })).toBeVisible() await trigger.focus() await expect(trigger).toBeFocused() await page.keyboard.press('Enter') await page.keyboard.press('Tab') await expect(page.getByTestId('vault-menu-item-Getting Started')).toBeFocused() await page.getByTestId('vault-menu-item-Personal Vault').focus() await expect(page.getByTestId('vault-menu-item-Personal Vault')).toBeFocused() await page.keyboard.press('Enter') await expect(trigger).toContainText('Personal Vault') await expect(noteList.getByText('Personal Home', { exact: true })).toBeVisible() await expect(noteList.getByText('Work Home', { exact: true })).toHaveCount(0) await trigger.click() await page.getByTestId('vault-menu-item-Work Vault').click() await expect(trigger).toContainText('Work Vault') await expect(noteList.getByText('Work Home', { exact: true })).toBeVisible() await expect(noteList.getByText('Personal Home', { exact: true })).toHaveCount(0) }) test('missing Getting Started vault stays hidden while remove actions still work @smoke', async ({ page }) => { await installVaultSwitcherMocks(page, { defaultVaultExists: false }) await page.goto('/', { waitUntil: 'domcontentloaded' }) const trigger = page.getByTestId('status-vault-trigger') await expect(trigger).toContainText('Work Vault') await trigger.click() await expect(page.getByTestId('vault-menu-item-Getting Started')).toHaveCount(0) const personalVaultItem = page.getByTestId('vault-menu-item-Personal Vault') const removeButton = page.getByRole('button', { name: 'Remove Personal Vault from list' }) await expect(removeButton).toHaveCSS('opacity', '0') await personalVaultItem.hover() await expect(removeButton).toHaveCSS('opacity', '1') const itemBounds = await personalVaultItem.boundingBox() const removeBounds = await removeButton.boundingBox() expect(itemBounds).not.toBeNull() expect(removeBounds).not.toBeNull() expect(removeBounds!.x).toBeGreaterThanOrEqual(itemBounds!.x - 1) expect(removeBounds!.y).toBeGreaterThanOrEqual(itemBounds!.y - 1) expect(removeBounds!.x + removeBounds!.width).toBeLessThanOrEqual(itemBounds!.x + itemBounds!.width + 1) expect(removeBounds!.y + removeBounds!.height).toBeLessThanOrEqual(itemBounds!.y + itemBounds!.height + 1) await removeButton.click() await trigger.click() await expect(page.getByTestId('vault-menu-item-Personal Vault')).toHaveCount(0) await expect(page.getByTestId('vault-menu-item-Getting Started')).toHaveCount(0) })