fix: recover procedure editor schema errors
This commit is contained in:
@@ -45,6 +45,9 @@ describe('isRecoverableEditorTransformError', () => {
|
||||
it('recognizes ProseMirror transform and mismatched transaction failures', () => {
|
||||
expect(isRecoverableEditorTransformError(transformError())).toBe(true)
|
||||
expect(isRecoverableEditorTransformError(new RangeError('Applying a mismatched transaction'))).toBe(true)
|
||||
expect(isRecoverableEditorTransformError(new RangeError(
|
||||
'Invalid content for node blockContainer: <paragraph("Procedures are long-running"), blockGroup(blockContainer(bulletListItem("Step")))>',
|
||||
))).toBe(true)
|
||||
expect(isRecoverableEditorTransformError(new Error('unrelated'))).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -74,6 +77,20 @@ describe('installRichEditorTransformErrorRecovery', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('recovers invalid-content schema transactions from mixed paragraph and list editing', () => {
|
||||
const schemaError = new RangeError(
|
||||
'Invalid content for node blockContainer: <paragraph("Procedures are long-running"), blockGroup(blockContainer(bulletListItem("Step")))>',
|
||||
)
|
||||
const { currentDoc, view } = createView(schemaError)
|
||||
|
||||
installRichEditorTransformErrorRecovery(view)
|
||||
|
||||
expect(() => view.dispatch({ before: currentDoc })).not.toThrow()
|
||||
expect(trackEvent).toHaveBeenCalledWith('rich_editor_transform_error_recovered', {
|
||||
reason: 'transform_error',
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps non-ProseMirror dispatch failures visible', () => {
|
||||
const { view } = createView(new Error('plugin failed'))
|
||||
|
||||
|
||||
@@ -47,10 +47,15 @@ function isMismatchedTransactionError(error: unknown): boolean {
|
||||
return error instanceof Error && error.message.includes('Applying a mismatched transaction')
|
||||
}
|
||||
|
||||
function isInvalidContentTransactionError(error: unknown): boolean {
|
||||
return error instanceof RangeError && error.message.startsWith('Invalid content for node ')
|
||||
}
|
||||
|
||||
export function isRecoverableEditorTransformError(error: unknown): boolean {
|
||||
return error instanceof Error && (
|
||||
error.name === 'TransformError'
|
||||
|| isMismatchedTransactionError(error)
|
||||
|| isInvalidContentTransactionError(error)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,21 @@ Intro paragraph before the list.
|
||||
Plain paragraph after the numbered list.
|
||||
`
|
||||
|
||||
const PROCEDURE_NOTE_CONTENT = `---
|
||||
title: Procedure
|
||||
type: Procedure
|
||||
status: Active
|
||||
---
|
||||
|
||||
# Procedure
|
||||
|
||||
Procedures are long-running processes tied to a [[responsibility|Responsibility]].
|
||||
- Status: Active
|
||||
- Owner: The person responsible
|
||||
|
||||
Plain follow-up paragraph after the procedure checklist.
|
||||
`
|
||||
|
||||
let tempVaultDir: string
|
||||
|
||||
test.beforeEach(async ({ page }, testInfo) => {
|
||||
@@ -203,3 +218,46 @@ test('numbered list content stays editable after autosave and re-entry', async (
|
||||
expect(raw).toContain(reentryMarker)
|
||||
expectNoBlockContainerCrash(crashSignals)
|
||||
})
|
||||
|
||||
test('Procedure prose and adjacent bullet-list content stay editable through keydown edits', async ({ page }) => {
|
||||
const crashSignals = collectEditorCrashSignals(page)
|
||||
const markerId = Date.now()
|
||||
const paragraphMarker = `procedure-paragraph-${markerId}`
|
||||
const ownerMarker = `procedure-owner-${markerId}`
|
||||
const cadenceMarker = `procedure-cadence-${markerId}`
|
||||
|
||||
await openNote(page, 'Note B')
|
||||
await toggleRawMode(page, '.cm-content')
|
||||
await setRawEditorContent(page, PROCEDURE_NOTE_CONTENT)
|
||||
await page.waitForTimeout(700)
|
||||
await toggleRawMode(page, '.bn-editor')
|
||||
|
||||
const procedureParagraph = page.locator('.bn-block-content', {
|
||||
hasText: 'Procedures are long-running processes',
|
||||
}).first()
|
||||
await expect(procedureParagraph).toBeVisible({ timeout: 5_000 })
|
||||
await procedureParagraph.click()
|
||||
await page.keyboard.press('End')
|
||||
await page.keyboard.insertText(` ${paragraphMarker}`)
|
||||
await page.waitForTimeout(700)
|
||||
|
||||
const ownerItem = page.locator('.bn-block-content[data-content-type="bulletListItem"]', {
|
||||
hasText: 'Owner: The person responsible',
|
||||
}).first()
|
||||
await expect(ownerItem).toBeVisible({ timeout: 5_000 })
|
||||
await ownerItem.click()
|
||||
await page.keyboard.press('End')
|
||||
await page.keyboard.insertText(` ${ownerMarker}`)
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.insertText(`Cadence: Weekly ${cadenceMarker}`)
|
||||
await page.waitForTimeout(900)
|
||||
|
||||
expectNoBlockContainerCrash(crashSignals)
|
||||
|
||||
await toggleRawMode(page, '.cm-content')
|
||||
const raw = await getRawEditorContent(page)
|
||||
expect(raw).toContain(paragraphMarker)
|
||||
expect(raw).toContain(`- Owner: The person responsible ${ownerMarker}`)
|
||||
expect(raw).toContain(`- Cadence: Weekly ${cadenceMarker}`)
|
||||
expectNoBlockContainerCrash(crashSignals)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user