openpencil/tests/e2e/recovery.spec.ts
Danila Poyarkov 631fc25ee6
fix(app): retain recovery after closing unsaved tabs (#505)
* fix(app): retain recovery after closing unsaved tabs

- Persist source-less tab snapshots before disposing editors

- Keep retained snapshots available for startup restore or explicit discard

- Cover close, reload, and restore behavior in Playwright

* fix(app): surface recovery persistence failures

- Propagate explicit close and reload snapshot failures

- Keep background debounce failures logged without unhandled rejections

- Exercise close-time persistence in recovery coverage

* test(app): cover recovery persistence retry

* docs: restore recovery retention note
2026-08-13 21:39:35 +03:00

48 lines
1.9 KiB
TypeScript

import { expect, test } from '#tests/e2e/fixtures'
import { CanvasHelper } from '#tests/helpers/canvas'
test('keeps an unsaved document recoverable after its tab closes', async ({ browser, baseURL }) => {
const context = await browser.newContext({ baseURL })
const page = await context.newPage()
await page.goto('/')
const canvas = new CanvasHelper(page)
await canvas.waitForInit()
await page.evaluate(async () => {
const store = window.openPencil?.getStore?.()
if (!store) throw new Error('OpenPencil store not initialized')
const id = store.createShape('RECTANGLE', 120, 120, 240, 140)
await store.persistRecoveryNow()
store.updateNode(id, { name: 'Retained recovery rectangle' })
})
await page.keyboard.press('ControlOrMeta+t')
await expect(page.getByRole('button', { name: 'New tab' })).toBeVisible()
await page.getByTestId('tabbar-tab').first().getByTestId('tabbar-close').click()
await expect(page.getByRole('button', { name: 'New tab' })).toBeHidden()
await expect
.poll(() =>
page.evaluate(async () => {
const request = indexedDB.open('open-pencil-recovery')
const database = await new Promise<IDBDatabase>((resolve, reject) => {
request.onsuccess = () => resolve(request.result)
request.onerror = () => reject(request.error)
})
const transaction = database.transaction('meta')
const countRequest = transaction.objectStore('meta').count()
return new Promise<number>((resolve, reject) => {
countRequest.onsuccess = () => resolve(countRequest.result)
countRequest.onerror = () => reject(countRequest.error)
})
})
)
.toBe(1)
await page.reload()
await expect(page.getByRole('alertdialog', { name: 'Recover unsaved work' })).toBeVisible()
await page.getByRole('button', { name: 'Restore' }).click()
await expect(page.getByText('Retained recovery rectangle')).toBeVisible()
await context.close()
})