Using MailFlat with Cucumber
Keep the mailbox out of the feature file: the scenario says a user signs up and enters the code, the step definitions do the work.
How it fits together
A feature file should read like the business rule, not like an API client. Open the inbox in a Before hook, keep the address on the world object, and let each step use it.
The feature
No mention of MailFlat: the scenario describes what a person does.
Feature: Signup
Scenario: A new user verifies their email
Given I am on the signup page
When I sign up with a fresh email address
And I enter the verification code from my inbox
Then I land on the dashboard
The step definitions
Hooks own the lifecycle so no step has to remember to clean up.
// features/steps/signup.steps.js
const { Before, After, When, Then } = require("@cucumber/cucumber");
const { MailFlat } = require("@mailflat/sdk");
const mf = new MailFlat({ apiKey: process.env.MAILFLAT_API_KEY });
Before(async function () {
this.inbox = await mf.create({
prefix: `cuke-${Date.now().toString(36)}`,
retentionHours: 2,
});
});
After(async function () {
await this.inbox?.delete();
});
When("I sign up with a fresh email address", async function () {
await this.page.fill("#email", this.inbox.address);
await this.page.click("#submit");
});
When("I enter the verification code from my inbox", async function () {
await this.page.fill("#code", await this.inbox.waitForOtp({ timeout: 60000 }));
});
Worth knowing
Cucumber's default step timeout is 5 seconds — raise it with setDefaultTimeout(70000) or the waiting step is killed early.
See also
JavaScript
Zero dependencies, built on the runtime's own fetch. Drop it into Jest, Vitest, Playwright, Cypress or a Node script.
JavaBuilt on java.net.http, no HTTP dependency of its own. Made for Selenium and JUnit suites that need a real address per test.
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.