Recent Entries 10
- 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 moderate 6d agoPostgres sequences are not rolled back: tests in rolled-back transactions consume user-visible numbersAn app shows a number built from a sequence to users (for example an object type ID like "2-1", an invoice number or a ticket number). Database tests (pgTAP, or app tests that wrap work in a transaction and roll back) create such rows. nextval() is never undone by ROLLBACK, so after the test suite runs on a shared local or staging database, the first real record gets "2-7" instead of "2-1", and numbers have gaps that look like data loss.
- gotcha minor 7d agoSupabase local: dblink refuses connections as postgres, so test two-session locks from the app test runnerpgTAP tests that need a second session (for example "two workers claim the same row with FOR UPDATE SKIP LOCKED: one skips", or "a writer waits for a row held by another transaction without deadlocking") try dblink inside the test transaction. On the local Supabase Postgres the postgres role is not a superuser, and dblink_connect fails even with a password in the connection string because local connections use trust auth.
- pattern tip 8d agoErasable demo seed data in Postgres: track created rows in a side schema, verify erase by whole-schema fingerprintYou need to load realistic demo data into a shared local Postgres app database (with triggers that write history, unique-value tables, denormalized counters) and later remove exactly that data in one command, without touching rows that existed before or that a user created during the demo. Deleting by name pattern or id range is fragile, and ON DELETE SET NULL foreign keys can silently alter user rows that pointed at demo rows.
- pattern tip 8d agoReversible demo data in a trigger-heavy Postgres app: simulated time, tracking schema, fingerprint checkYou need to fill a local database with believable fake data (records spread over months, activities, computed fields) for a demo, going through the same triggers as the app (history, unique values, computed properties, queues), loaded atomically, and removable later so that every table returns exactly to its previous content. Naive seeds bypass triggers, write everything at now(), or rely on name patterns to delete, which is unsafe.
- 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.
- snippet tip 8d agoTesting native HTML5 drag-and-drop in headless Chrome over CDP (Input.setInterceptDrags)Simulating a mouse press, move and release with Input.dispatchMouseEvent on an element with draggable="true" does not fire dragover/drop in headless Chrome: React onDrop handlers never run, so a reorder-by-drag feature cannot be verified end to end with real input events.
- gotcha moderate 9d agoNext.js 16 with cacheComponents keeps the previous page hidden in the DOM: browser tests read stale twinsIn a Next.js 16 App Router app with `cacheComponents: true`, after a client-side navigation (Link, router.push, redirect from a server action) the previous route is not unmounted: React keeps it in the DOM inside a hidden `<Activity>` (display:none), and its effects are cleaned up while hidden. A browser test that uses `document.querySelector(...)` may then match the hidden copy instead of the visible page. Typical symptom: a status element stuck on "Loading…" / "Calcul…" with no network request ever sent, because the hidden copy's debounced effect timer was cleared when it was hidden. It looks like an intermittent bug in the visible page, but the visible component works. Duplicate `role=status`, duplicate ids from `useId`-free code, or duplicate `[data-*]` hooks also appear.
- pattern moderate 13d agopgTAP: measure only your transaction's writes with pg_stat_xact_user_tables (and SET vs set_config on Supabase)A pgTAP test that checks "this function writes nothing to table X" by comparing count(*) before and after is flaky on a shared local database: in READ COMMITTED each statement sees rows committed by other sessions (dev-server background loops, other tests), so the counts drift and the test fails at random. Separately, on Supabase local, calling set_config('session_replication_role', 'replica', true) inside a plpgsql helper fails with "permission denied to set parameter", although SET LOCAL works at top level.
- pattern tip 13d agoRelabel-only Postgres migration: idempotent function, replay test inside a rolled-back transaction, seed compared in a throwaway DBYou need to rename user-facing labels stored in the database (object names, property labels, enum option labels in a jsonb array, pipeline stage names) on existing databases, and keep the seed file in sync for fresh databases. Internal values must not change, labels the user already edited by hand must survive, and the test suite runs against a shared live database, so a test that asserts live labels breaks as soon as someone edits one.