Cause 1: the address was used before
A hard-coded address such as [email protected] still has last night's email when tonight's run starts. If the new email is slow, "get the latest email" returns the old one at once, and the test opens a link that has already been used. It fails with "link expired", which looks like an app bug.
Fix: build the address per test from something unique: a random suffix, or the CI run ID plus a counter.
import { randomUUID } from "node:crypto";
// Letters, digits and hyphens only in the tag.
export const newAddress = (tag: string) =>
`${process.env.ZYNTRA_TEAM_ID}.${tag}-${randomUUID().slice(0, 8)}@zyntramail.com`;Cause 2: two emails to one address in one test
Sign-up sends a confirmation; the test then asks for a password reset. Both go to the same user, so both go to the same address. Reading "the latest email" right after clicking "Send reset link" returns the confirmation, because the reset is still on its way.
Fix: wait for the email that matches what you expect, usually by subject. Comparing received_at with the time the test clicked works too, but the timestamp comes from the mail server's clock, not the test machine's, so a skew of a few seconds can break it. A subject check has no such problem.
Cause 3: parallel workers share an address
Two workers run the sign-up test against the same address, each reads the other's email, and the failure moves around between runs. Anything that builds an address from the test name alone has this problem as soon as you add a second worker or a browser project. Add the worker index, or use a random suffix as above.
Cause 4: a retry reuses the first attempt's address
If the address is created once per file (in a beforeAll, say), a retried test reuses it and finds the email from the failed attempt. Create the address inside the test, so every attempt starts clean.
A quick checklist
- Is the address created inside the test, with a unique part?
- Does the test send more than one email to it? Then match by subject.
- Does the assertion check the subject before using a link? A wrong email then fails with a clear message.