The `span.truncate` selector was matching sidebar note titles in addition to search results, causing false positives in the full-text search test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
112 lines
4.3 KiB
TypeScript
112 lines
4.3 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { sendShortcut } from './helpers'
|
|
|
|
const QUICK_OPEN_INPUT = 'input[placeholder="Search notes..."]'
|
|
const SEARCH_INPUT = 'input[placeholder="Search in all notes..."]'
|
|
|
|
/** Known trashed note titles from mock data */
|
|
const TRASHED_TITLES = ['Old Draft Notes', 'Deprecated API Notes', 'Failed SEO Experiment']
|
|
/** Query specific enough to find a known active note */
|
|
const ACTIVE_QUERY = 'Laputa App'
|
|
|
|
async function openQuickOpen(page: import('@playwright/test').Page) {
|
|
await page.locator('body').click()
|
|
await sendShortcut(page, 'p', ['Control'])
|
|
await expect(page.locator(QUICK_OPEN_INPUT)).toBeVisible()
|
|
}
|
|
|
|
/** Get result titles scoped to a specific container (avoids matching sidebar items). */
|
|
function getResultTitles(container: import('@playwright/test').Locator) {
|
|
return container.locator('span.truncate').allTextContents()
|
|
}
|
|
|
|
/** The Quick Open dialog overlay */
|
|
function quickOpenPanel(page: import('@playwright/test').Page) {
|
|
return page.locator('.fixed.inset-0').filter({ has: page.locator(QUICK_OPEN_INPUT) })
|
|
}
|
|
|
|
/** The full-text search dialog overlay */
|
|
function searchPanel(page: import('@playwright/test').Page) {
|
|
return page.locator('.fixed.inset-0').filter({ has: page.locator(SEARCH_INPUT) })
|
|
}
|
|
|
|
test.describe('Exclude trashed notes from search and autocomplete', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/')
|
|
await page.waitForLoadState('networkidle')
|
|
})
|
|
|
|
test('trashed notes do not appear in Quick Open search', async ({ page }) => {
|
|
await openQuickOpen(page)
|
|
const panel = quickOpenPanel(page)
|
|
for (const title of TRASHED_TITLES) {
|
|
// Use a unique enough substring to trigger results
|
|
const query = title.split(' ')[0]
|
|
await page.locator(QUICK_OPEN_INPUT).fill(query)
|
|
await page.waitForTimeout(400)
|
|
const titles = await getResultTitles(panel)
|
|
expect(titles, `"${title}" should not appear for query "${query}"`).not.toContain(title)
|
|
}
|
|
})
|
|
|
|
test('active notes still appear in Quick Open search', async ({ page }) => {
|
|
await openQuickOpen(page)
|
|
const panel = quickOpenPanel(page)
|
|
await page.locator(QUICK_OPEN_INPUT).fill(ACTIVE_QUERY)
|
|
await page.waitForTimeout(400)
|
|
const titles = await getResultTitles(panel)
|
|
expect(titles.some(t => t.includes('Laputa App'))).toBe(true)
|
|
})
|
|
|
|
test('trashed notes do not appear in full-text search', async ({ page }) => {
|
|
// Full-text search panel opened via Ctrl/Cmd+Shift+F
|
|
const searchVisible = async () => {
|
|
await page.locator('body').click()
|
|
await sendShortcut(page, 'f', ['Control', 'Shift'])
|
|
try {
|
|
await expect(page.locator(SEARCH_INPUT)).toBeVisible({ timeout: 2000 })
|
|
return true
|
|
} catch { return false }
|
|
}
|
|
if (!await searchVisible()) {
|
|
// Retry with Meta (macOS Cmd key) in case Playwright routes it differently
|
|
await sendShortcut(page, 'f', ['Meta', 'Shift'])
|
|
try {
|
|
await expect(page.locator(SEARCH_INPUT)).toBeVisible({ timeout: 2000 })
|
|
} catch {
|
|
test.skip()
|
|
return
|
|
}
|
|
}
|
|
await page.locator(SEARCH_INPUT).fill('Old Draft')
|
|
await page.waitForTimeout(600)
|
|
const titles = await getResultTitles(searchPanel(page))
|
|
expect(titles).not.toContain('Old Draft Notes')
|
|
})
|
|
|
|
test('wikilink autocomplete does not suggest trashed notes', async ({ page }) => {
|
|
// Open any note first (click the first note in the sidebar)
|
|
const firstNote = page.locator('[data-testid="note-item"]').first()
|
|
if (await firstNote.isVisible()) {
|
|
await firstNote.click()
|
|
await page.waitForTimeout(300)
|
|
}
|
|
|
|
// Focus the editor and type [[ to trigger autocomplete
|
|
const editor = page.locator('.bn-editor').first()
|
|
if (await editor.isVisible()) {
|
|
await editor.click()
|
|
await page.keyboard.type('[[Old Draft')
|
|
await page.waitForTimeout(500)
|
|
|
|
// Check autocomplete suggestions - should not contain the trashed note
|
|
const suggestions = page.locator('.bn-suggestion-menu [class*="item"], .bn-suggestion-menu button, [data-testid="wikilink-suggestion"]')
|
|
const count = await suggestions.count()
|
|
for (let i = 0; i < count; i++) {
|
|
const text = await suggestions.nth(i).textContent()
|
|
expect(text).not.toContain('Old Draft Notes')
|
|
}
|
|
}
|
|
})
|
|
})
|