updateEntry's .map() always returned a new array even when no entry matched, causing unnecessary state changes. During note creation, addEntry uses startTransition (deferred) while markContentPending calls updateEntry synchronously — the entry doesn't exist yet, so the no-op .map() produced a new reference that cascaded into "Maximum update depth exceeded" (which surfaced as React error #185 in the production WKWebView build). The fix makes updateEntry bail out (return prev) when no entry was changed, preventing the spurious state update. Also removes the defensive try-catch from the previous fix attempt and cleans up an unnecessary setToastMessage dependency. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
40 lines
1.5 KiB
TypeScript
40 lines
1.5 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
|
|
/** Errors that indicate the app has crashed (not just minor internal warnings). */
|
|
function isCrashError(msg: string): boolean {
|
|
return msg.includes('Maximum update depth') || msg.includes('Invalid hook call') || msg.includes('#185')
|
|
}
|
|
|
|
test('create note via Cmd+N does not crash', async ({ page }) => {
|
|
const errors: string[] = []
|
|
page.on('pageerror', (err) => { if (isCrashError(err.message)) errors.push(err.message) })
|
|
|
|
await page.goto(process.env.BASE_URL ?? 'http://localhost:5201')
|
|
await page.waitForSelector('[data-testid="sidebar-top-nav"]', { timeout: 10000 })
|
|
await page.waitForTimeout(500)
|
|
|
|
await page.keyboard.press('Meta+n')
|
|
await page.waitForTimeout(2000)
|
|
|
|
expect(errors).toHaveLength(0)
|
|
await expect(page.locator('[data-testid="title-field-input"]')).toBeVisible()
|
|
})
|
|
|
|
test('create note via sidebar + button does not crash', async ({ page }) => {
|
|
const errors: string[] = []
|
|
page.on('pageerror', (err) => { if (isCrashError(err.message)) errors.push(err.message) })
|
|
|
|
await page.goto(process.env.BASE_URL ?? 'http://localhost:5201')
|
|
await page.waitForSelector('[data-testid="sidebar-top-nav"]', { timeout: 10000 })
|
|
await page.waitForTimeout(500)
|
|
|
|
const plusButtons = page.locator('button[aria-label*="Create new"]')
|
|
if (await plusButtons.count() > 0) {
|
|
await plusButtons.first().click()
|
|
await page.waitForTimeout(2000)
|
|
}
|
|
|
|
expect(errors).toHaveLength(0)
|
|
await expect(page.locator('[data-testid="title-field-input"]')).toBeVisible()
|
|
})
|