From 3e01c87f6423be415e52c4afdb3757bfe45a8a63 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Thu, 7 May 2026 17:26:51 +0200 Subject: [PATCH] test: cover note history edit stability --- package.json | 2 +- tests/smoke/note-history-edit-loop.spec.ts | 79 ++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 tests/smoke/note-history-edit-loop.spec.ts diff --git a/package.json b/package.json index 64bc8722..ef2ac426 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "test": "vitest run", "test:watch": "vitest", "test:e2e": "playwright test", - "playwright:smoke": "playwright test --config playwright.smoke.config.ts tests/smoke/autosave-low-end-typing.spec.ts tests/smoke/create-note-backing-file.spec.ts tests/smoke/delete-note-nonblocking.spec.ts tests/smoke/example.spec.ts tests/smoke/fix-ai-chat-empty-body-v3.spec.ts tests/smoke/fix-crash-create-note.spec.ts tests/smoke/fresh-start-telemetry-onboarding.spec.ts tests/smoke/frontmatter-date-picker.spec.ts tests/smoke/getting-started-template.spec.ts tests/smoke/h1-title-decoupled.spec.ts tests/smoke/h1-untitled-auto-rename.spec.ts tests/smoke/keyboard-command-routing.spec.ts tests/smoke/linkify-init-warnings.spec.ts tests/smoke/missing-active-vault-recovery.spec.ts tests/smoke/missing-string-metadata-open-note.spec.ts tests/smoke/multibyte-search-snippet.spec.ts tests/smoke/multi-selection-shortcuts.spec.ts tests/smoke/pull-refresh-open-note.spec.ts tests/smoke/type-derived-properties.spec.ts tests/smoke/vault-loading-skeleton.spec.ts tests/smoke/wikilink-path-fix.spec.ts", + "playwright:smoke": "playwright test --config playwright.smoke.config.ts tests/smoke/autosave-low-end-typing.spec.ts tests/smoke/create-note-backing-file.spec.ts tests/smoke/delete-note-nonblocking.spec.ts tests/smoke/example.spec.ts tests/smoke/fix-ai-chat-empty-body-v3.spec.ts tests/smoke/fix-crash-create-note.spec.ts tests/smoke/fresh-start-telemetry-onboarding.spec.ts tests/smoke/frontmatter-date-picker.spec.ts tests/smoke/getting-started-template.spec.ts tests/smoke/h1-title-decoupled.spec.ts tests/smoke/note-history-edit-loop.spec.ts tests/smoke/h1-untitled-auto-rename.spec.ts tests/smoke/keyboard-command-routing.spec.ts tests/smoke/linkify-init-warnings.spec.ts tests/smoke/missing-active-vault-recovery.spec.ts tests/smoke/missing-string-metadata-open-note.spec.ts tests/smoke/multibyte-search-snippet.spec.ts tests/smoke/multi-selection-shortcuts.spec.ts tests/smoke/pull-refresh-open-note.spec.ts tests/smoke/type-derived-properties.spec.ts tests/smoke/vault-loading-skeleton.spec.ts tests/smoke/wikilink-path-fix.spec.ts", "playwright:regression": "playwright test tests/smoke/", "playwright:integration": "playwright test --config playwright.integration.config.ts", "test:coverage": "node scripts/run-vitest-coverage.mjs", diff --git a/tests/smoke/note-history-edit-loop.spec.ts b/tests/smoke/note-history-edit-loop.spec.ts new file mode 100644 index 00000000..59d4971e --- /dev/null +++ b/tests/smoke/note-history-edit-loop.spec.ts @@ -0,0 +1,79 @@ +import { test, expect, type Page } from '@playwright/test' +import { + createFixtureVaultCopy, + openFixtureVault, + removeFixtureVaultCopy, +} from '../helpers/fixtureVault' +import { sendShortcut } from './helpers' + +let tempVaultDir: string + +function isReactUpdateLoop(message: string): boolean { + return ( + message.includes('Maximum update depth') || + message.includes('React error #185') || + message.includes('#185') + ) +} + +function collectReactUpdateLoopErrors(page: Page): string[] { + const errors: string[] = [] + page.on('pageerror', (error) => { + if (isReactUpdateLoop(error.message)) errors.push(error.message) + }) + page.on('console', (message) => { + if (message.type() === 'error' && isReactUpdateLoop(message.text())) { + errors.push(message.text()) + } + }) + return errors +} + +async function openNote(page: Page, title: string) { + await page.getByTestId('note-list-container').getByText(title, { exact: true }).click() + await expect(page.getByRole('heading', { name: title, level: 1 })).toBeVisible({ timeout: 5_000 }) +} + +async function openPropertiesPanel(page: Page) { + const openProperties = page.getByRole('button', { name: 'Open the properties panel' }) + if (await openProperties.count()) await openProperties.click() + await expect(page.getByText('History')).toBeVisible({ timeout: 5_000 }) +} + +async function focusHeadingEnd(page: Page, titlePattern: RegExp | string) { + const heading = page.getByRole('heading', { name: titlePattern, level: 1 }) + await expect(heading).toBeVisible({ timeout: 5_000 }) + await heading.click() + await page.keyboard.press('End') +} + +test.beforeEach(async ({ page }, testInfo) => { + testInfo.setTimeout(60_000) + tempVaultDir = createFixtureVaultCopy() + await openFixtureVault(page, tempVaultDir) + await page.setViewportSize({ width: 1180, height: 760 }) +}) + +test.afterEach(() => { + removeFixtureVaultCopy(tempVaultDir) +}) + +test('opening note history then editing and saving the same note stays stable @smoke', async ({ page }) => { + const errors = collectReactUpdateLoopErrors(page) + const titleSuffix = ` History Loop ${Date.now()}` + const updatedTitle = `Alpha Project${titleSuffix}` + + await openNote(page, 'Alpha Project') + await openPropertiesPanel(page) + + await page.getByRole('button', { name: /a1b2c3d.*Update alpha-project with latest changes/i }).click() + await expect(page.getByText('Updated paragraph at commit a1b2c3d')).toBeVisible({ timeout: 5_000 }) + + await page.getByRole('button', { name: 'Return to the editor' }).click() + await focusHeadingEnd(page, 'Alpha Project') + await page.keyboard.type(titleSuffix) + await sendShortcut(page, 's', ['Control']) + + await expect(page.getByRole('heading', { name: updatedTitle, level: 1 })).toBeVisible({ timeout: 5_000 }) + expect(errors).toEqual([]) +})