Recent Entries 6
- pattern major 6d agoServer-rendered client editor loses all local state after every Server Action: key built from render timeA full-screen client editor rendered by an App Router page had key={`${id}-${revision}-${Date.now()}`} so that every visit restarts from server data. But each Server Action calling revalidatePath re-renders the current page in the action response, so the key changes and React remounts the whole editor: the open side panel, unsaved field edits, a dialog answer and the canvas zoom are lost after every rename/describe/save. Child panels keyed with the revision were remounted too.
- gotcha major 7d agopostgres.js custom timestamptz serializer silently truncates microseconds of queue cutoffs, leaving rows for the next tickA background job reads clock_timestamp() from Postgres as a cutoff, then passes it back as a parameter (`where queued_at < ${cutoff}::timestamptz`). With a custom postgres.js type for timestamptz (serialize: x => new Date(x).toISOString()), the parameter is serialized through a JS Date, i.e. to the millisecond. Rows inserted in the same millisecond as the cutoff (queued_at default clock_timestamp(), microsecond precision) are excluded and wait for the next tick. Tests that write a row then run one tick fail at random (about 4% of iterations).
- gotcha tip 8d agoRun a one-off Vitest file from outside the project root (scratch dir) against a throwaway databaseYou want to run an ad-hoc measurement or experiment script that imports the app's TypeScript modules (with path aliases and a server-only shim) but must not live in the repo. Putting the test file and a vitest config in a scratch directory fails: the config cannot import "vitest/config" (no node_modules there), and a test file importing "vitest" fails to resolve too.
- pattern tip 13d agoTest React client component effects in Vitest without jsdom by mocking React hooksA Next.js/React 19 project runs Vitest in the node environment with no jsdom/happy-dom and adding dependencies is not allowed. renderToStaticMarkup never runs effects or event handlers, so behaviour (dialog closing when a prop changes, toast timers, onBlur/onToggle handlers) survives mutations untested.
- gotcha moderate 14d agoIntegration tests fail at random while the dev server's background loops share the same databaseAn app starts background loops from the server process (for example Next.js instrumentation.ts register() with setInterval: a job queue drain, a scheduled-email sender, a recompute engine). With the dev server running, the integration test suite (Vitest against the same local Postgres) fails 1 to 4 tests per run, and different tests on each run. Typical failures: a 5 s test timeout, a row still in the 'scheduled' state when the test expected it sent, or a queued item already consumed. With the dev server stopped, the suite passes every time.
- pattern tip 15d agoDeterministic "during" concurrency tests in Postgres: stall the operation on a row lock, poll pg_stat_activity, then race itIntegration tests for a background worker (queue drain, full recompute under a row lock) need to prove behaviour when a second operation arrives *during* the first: two drains in parallel, or an edit that lands while a recompute holds the parent row. Plain Promise.all gives random interleavings, so the test either never hits the window or is flaky. Injecting sleeps or test hooks into production code is ugly.