Email testing with Vitest
The same flow as Jest with typed fixtures: Vitest picks up the client's TypeScript types with no extra setup.
How it fits together
Vitest inherits Vite's TypeScript handling, so the client's types work out of the box. As with Jest, the only trap is the default timeout being shorter than real email delivery.
Skip the setup
The steps below are what a working project looks like. If you want that project rather than the explanation, generate it — one runnable smoke test, one signup test to edit, and a gitignored .env for your key.
npm create mailflat@latest -- --template vitest
Zero dependencies of its own; the generated project pulls the SDK.
The test
// signup.test.ts
import { beforeEach, afterEach, expect, test } from "vitest";
import { MailFlat, type Inbox } from "@mailflat/sdk";
const mf = new MailFlat({ apiKey: process.env.MAILFLAT_API_KEY! });
let inbox: Inbox;
beforeEach(async () => {
inbox = await mf.create({
prefix: `vitest-${Date.now().toString(36)}`,
retentionHours: 2,
});
});
afterEach(async () => {
await inbox.delete();
});
test("signup sends a verification code", async () => {
await api.post("/signup", { email: inbox.address });
const otp = await inbox.waitForOtp({ timeout: 60_000 });
expect(otp).toMatch(/^\d{6}$/);
}, 70_000);
Or in the config
// vitest.config.ts
export default defineConfig({
test: { testTimeout: 70_000, hookTimeout: 30_000 },
});
Worth knowing
hookTimeout matters too: creating an inbox in beforeEach is an HTTP call.
See also
TypeScript
The same package as JavaScript, with types shipped in the box: Message, CreateInboxOptions, WaitOptions and typed errors.
One-time codesMailFlat pulls the verification code out of the message for you and hands it over as a field, so your test never runs a regex against an email body.
Testing & CIGive every CI run its own real inbox: the four API calls, parallel shards, teardown, and how to keep an end-to-end suite fast and honest.