Receiving email in a Cypress test
A Node task that keeps the key out of the browser, and a test that waits for the mail instead of pausing for a fixed number of seconds.
Cypress runs your test inside the browser, which raises two problems for reading email: your API key has no business in a page, and the network call answers to the origin of the site under test. One Node task solves both.
The config, where the key can stay
// cypress.config.js
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
setupNodeEvents(on) {
on('task', {
async waitForMail({ sentTo }) {
const response = await fetch('https://api.facteur.eu/v1/messages/search', {
method: 'POST',
headers: {
authorization: 'Bearer ' + process.env.FACTEUR_API_KEY,
'content-type': 'application/json',
},
body: JSON.stringify({ inbox: 'j3k9x2mq', sentTo, wait: 30000 }),
});
const { messages } = await response.json();
return messages[0] ?? null;
},
});
},
},
});
This file runs in the Cypress process, not in the page. The key is read from the environment there and never reaches the browser.
The test
it('the code arrives by email', () => {
const tag = 'signup-a' + (Cypress.currentRetry ?? 0) + '-' + Date.now();
cy.visit('/signup');
cy.get('[name=email]').type(tag + '@j3k9x2mq.inbox.facteur.eu');
cy.contains('button', 'Create my account').click();
cy.task('waitForMail', { sentTo: tag }, { timeout: 40000 }).should((message) => {
expect(message, 'no mail arrived').to.not.be.null;
expect(message.otp).to.match(/^[0-9]{6}$/);
});
});
Why there is no cy.wait
The waiting happens on our side: the request stays open until the message lands, with a maximum delay you set. A fixed pause is either too short on a loaded machine or wasted time on every run.
The timeout given to cy.task has to stay above the wait sent to the API: forty
seconds for a thirty-second wait. Otherwise Cypress abandons the task before the answer
arrives, and the failure blames the wrong thing.
The attempt number in the address
Cypress.currentRetry is 0 on the first run. Without it, a retried test reads the mail
from its own first attempt and passes for the wrong reason, exactly when something is
already flaky.
One inbox is enough for the whole suite
Every address ending in the inbox’s domain arrives there, with nothing to create. Each test therefore receives at its own address inside the same inbox. On a plan that includes a single inbox, that is the difference between a suite that runs and one that hits a ceiling.
What about a Cypress plugin?
It is planned and not published yet. The version above is the one that works today, in about thirty lines you keep. The state of each integration is on the Integrations page.
Checked on Is this article wrong or incomplete?