test: stabilize untitled draft title regression
This commit is contained in:
@@ -1,85 +1,58 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { sendShortcut } from './helpers'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
|
||||
/** Known BlockNote/ProseMirror error that fires during editor re-mount after rename. */
|
||||
const KNOWN_EDITOR_ERRORS = ['isConnected']
|
||||
const FIXTURE_VAULT = path.resolve('tests/fixtures/test-vault')
|
||||
|
||||
function isKnownEditorError(msg: string): boolean {
|
||||
return KNOWN_EDITOR_ERRORS.some(k => msg.includes(k))
|
||||
function copyDirSync(src: string, dest: string): void {
|
||||
fs.mkdirSync(dest, { recursive: true })
|
||||
for (const item of fs.readdirSync(src, { withFileTypes: true })) {
|
||||
const s = path.join(src, item.name)
|
||||
const d = path.join(dest, item.name)
|
||||
if (item.isDirectory()) copyDirSync(s, d)
|
||||
else fs.copyFileSync(s, d)
|
||||
}
|
||||
}
|
||||
|
||||
test.describe('H1 decoupled from title + non-blocking rename', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.waitForTimeout(2500)
|
||||
})
|
||||
let tempVaultDir: string
|
||||
|
||||
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) })
|
||||
test.beforeEach(async ({ page }) => {
|
||||
tempVaultDir = fs.mkdtempSync(path.join(os.tmpdir(), 'laputa-test-vault-'))
|
||||
copyDirSync(FIXTURE_VAULT, tempVaultDir)
|
||||
|
||||
// Create a new note
|
||||
await page.locator('body').click()
|
||||
await sendShortcut(page, 'n', ['Control'])
|
||||
await expect(page.getByText(/Untitled note/).first()).toBeVisible({ timeout: 3000 })
|
||||
await page.addInitScript((vaultPath: string) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let ref: any = null
|
||||
Object.defineProperty(window, '__mockHandlers', {
|
||||
set(val) {
|
||||
ref = val
|
||||
ref.load_vault_list = () => ({
|
||||
vaults: [{ label: 'Test Vault', path: vaultPath }],
|
||||
active_vault: vaultPath,
|
||||
})
|
||||
ref.check_vault_exists = () => true
|
||||
ref.get_last_vault_path = () => vaultPath
|
||||
ref.get_default_vault_path = () => vaultPath
|
||||
ref.save_vault_list = () => null
|
||||
},
|
||||
get() { return ref },
|
||||
configurable: true,
|
||||
})
|
||||
}, tempVaultDir)
|
||||
|
||||
// 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([])
|
||||
})
|
||||
await page.goto('/')
|
||||
await page.getByText('Alpha Project', { exact: true }).first().waitFor({ timeout: 10_000 })
|
||||
})
|
||||
|
||||
test.afterEach(async () => {
|
||||
fs.rmSync(tempVaultDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test('creating an untitled draft hides the legacy title section in the editor', async ({ page }) => {
|
||||
await page.locator('button[title="Create new note"]').click()
|
||||
|
||||
await expect(page.getByRole('textbox').last()).toBeVisible({ timeout: 5_000 })
|
||||
await expect(page.getByTestId('title-field-input')).toHaveCount(0)
|
||||
await expect(page.locator('.title-section[data-title-ui-visible]')).toHaveCount(0)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user