// 回归(docs/29 高危):EditorPane 卸载最终回写的守卫。 // 分片载入未收尾(muted)时 cleanup 跳过回写——否则部分序列化会把干净 // 标签静默截断。该交错(卸载提交时追加仍在途)在 jsdom 里无法自动化 // 复现:React act 会把待办异步工作抽干,分片追加总是先跑完(已实测 // 三种卸载方式均如此),故中途卸载一例靠代码复核保证(muted 仅在追加 // 完成/失败的收尾回调里解除,cleanup 只在 !muted 时回写),这里只锁 // 对照组:正常文档卸载时最终回写照常发生(守卫不能误伤正常路径)。 import { describe, it, expect, vi } from 'vitest' import { createRoot } from 'react-dom/client' import { act } from 'react-dom/test-utils' import EditorPane from '../src/components/EditorPane' const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)) describe('EditorPane 卸载回写', () => { it('普通文档卸载:最终回写照常', async () => { const container = document.createElement('div') document.body.appendChild(container) const root = createRoot(container) const onFinalMarkdown = vi.fn() let readyApi: { editor: unknown } | null = null const onReady = vi.fn((api: { editor: unknown }) => { if (api.editor) readyApi = api }) await act(async () => { root.render( undefined} onFinalMarkdown={onFinalMarkdown} onCursor={() => undefined} onReady={onReady} focusMode={false} typewriter={false} />, ) }) for (let i = 0; i < 200 && !readyApi; i++) await sleep(10) expect(readyApi).not.toBeNull() await act(async () => { root.unmount() }) expect(onFinalMarkdown).toHaveBeenCalledTimes(1) expect(onFinalMarkdown.mock.calls[0][1]).toContain('你好,世界。') }, 30000) })