What goes wrong with \d{6}
- Other numbers: an order ID, a support phone number, a postcode, a date written as 20261016.
- Split codes: "123 456" or "123-456" for readability, or each digit in its own table cell.
- Non-breaking spaces between groups, which
\sin some engines and tools does not cover the way you expect. - HTML-only emails: no plain-text part, so the text you search is empty.
- Codes with letters:
A7K-29Qnever matches a digit pattern at all.
A safer reader
const stripHtml = (html: string) =>
html
.replace(/<(style|script)[\s\S]*?<\/\1>/gi, " ")
.replace(/<[^>]+>/g, " ")
.replace(/ /g, " ");
export function readCode(email: { body: string; html: string | null }) {
const text = (email.body || stripHtml(email.html ?? ""))
.replace(/\u00a0/g, " ")
.replace(/\s+/g, " ");
// Anchor to the words your template uses around the code.
const pattern = /(?:code|passcode)(?: is)?:? (\d{3})[ -]?(\d{3})\b/gi;
const found = [...text.matchAll(pattern)];
if (found.length !== 1) {
throw new Error(`Expected one code, found ${found.length}`);
}
return found[0]![1]! + found[0]![2]!;
}Write the anchor from your own template ("Your code is", "Enter this code"). If the wording changes, the test fails with "found 0" rather than passing with a wrong number.
Check the subject too
Many apps put the code in the subject ("123456 is your sign-in code") so it shows in notifications. If yours does, read it from there: the subject is short and has no footer. Then assert that the body contains the same code, which checks both at once.
Make expiry part of the test
Codes usually expire within minutes. Read the email right after the step that sends it, and add a separate test that enters a used or expired code and expects an error. For time-based expiry, shorten the lifetime in the test environment instead of waiting.