Files
tolaria/tests/smoke/h1-title-decoupled.spec.ts
Test 8cfe7de66a fix: decouple H1 from title sync + non-blocking TitleField rename
H1 headings are body content, not the title source of truth. Remove
useHeadingTitleSync hook and frontmatter-title-from-H1 logic in the
editor. Rust rename_note no longer modifies H1 in file content — only
updates the frontmatter title: field.

TitleField now uses optimistic UI: the new title displays immediately
after commit while the async file rename runs in background, preventing
UI freezes on slow filesystems or large vaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 15:42:48 +01:00

86 lines
3.1 KiB
TypeScript

import { test, expect } from '@playwright/test'
import { sendShortcut } from './helpers'
/** Known BlockNote/ProseMirror error that fires during editor re-mount after rename. */
const KNOWN_EDITOR_ERRORS = ['isConnected']
function isKnownEditorError(msg: string): boolean {
return KNOWN_EDITOR_ERRORS.some(k => msg.includes(k))
}
test.describe('H1 decoupled from title + non-blocking rename', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/')
await page.waitForLoadState('networkidle')
await page.waitForTimeout(2500)
})
test('editing H1 in editor does NOT update TitleField', async ({ page }) => {
const errors: string[] = []
page.on('pageerror', (err) => { if (!isKnownEditorError(err.message)) errors.push(err.message) })
// Create a new note
await page.locator('body').click()
await sendShortcut(page, 'n', ['Control'])
await expect(page.getByText(/Untitled note/).first()).toBeVisible({ timeout: 3000 })
// Note the initial TitleField value
const titleInput = page.getByTestId('title-field-input')
await titleInput.waitFor({ timeout: 3000 })
const initialTitle = await titleInput.inputValue()
// Type an H1 in the editor body
const heading = page.locator('[data-content-type="heading"] h1')
if (await heading.isVisible()) {
await heading.click({ clickCount: 3 })
} else {
// Click into the editor and type an H1
const editor = page.locator('.bn-editor')
await editor.click()
}
await page.keyboard.type('My Custom H1 Heading', { delay: 15 })
// Wait well past the old debounce time (500ms)
await page.waitForTimeout(1500)
// TitleField should NOT have changed from its initial value
const titleAfterH1 = await titleInput.inputValue()
expect(titleAfterH1).toBe(initialTitle)
expect(errors).toEqual([])
})
test('TitleField rename is non-blocking (no freeze)', async ({ page }) => {
const errors: string[] = []
page.on('pageerror', (err) => { if (!isKnownEditorError(err.message)) errors.push(err.message) })
// Create a new note
await page.locator('body').click()
await sendShortcut(page, 'n', ['Control'])
await expect(page.getByText(/Untitled note/).first()).toBeVisible({ timeout: 3000 })
// Focus the TitleField and type a new title
const titleInput = page.getByTestId('title-field-input')
await titleInput.waitFor({ timeout: 3000 })
await titleInput.click()
await titleInput.fill('Responsive Rename Test')
// Measure time for blur (commit) — should be near-instant
const start = Date.now()
await titleInput.press('Enter')
const elapsed = Date.now() - start
// The blur/commit should complete in under 500ms (no blocking rename)
expect(elapsed).toBeLessThan(500)
// TitleField should show the new title optimistically
await expect(titleInput).toHaveValue('Responsive Rename Test', { timeout: 1000 })
// Wait for rename to complete in background
const toast = page.locator('.fixed.bottom-8')
await expect(toast).toContainText('Renamed', { timeout: 5000 })
expect(errors).toEqual([])
})
})