Cypress
Waiting for mail without cy.wait
Cypress runs your test inside the browser, which raises two problems for reading email: the API key must not be there, and the network call answers to the origin of the page under test. One Node task solves both.
The config, then the test
The task lives in the Cypress process, where the key can stay. The test only ever sees a command.
// cypress.config.js — the key stays here, out of the browser.
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;
},
});
},
},
});
// the test — the attempt number keeps a retry apart from the run before it.
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}$/);
});
});The key stays on the Node side
Your spec is served to a page: a key written into it would be a key in a page’s JavaScript. The task keeps it on the other side.
cy.task yields a value, so the chain continues
The message comes back into the usual command queue, and .should() behaves as it does on a cy.get().
The attempt number is in the address
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.
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 cy.wait is either too short on a loaded machine or wasted time on every run.
The code is already extracted
The message arrives with its one-time code in a field of its own.
One mailbox is enough for the suite
Each test receives at its own address inside the same mailbox, with nothing to create. On a plan that includes a single mailbox, that is the difference between a suite that runs and one that hits a ceiling.
What about a Cypress plugin?
Planned, not published: the version above is the one that works today, in about thirty lines you keep.
Write this test today
A test mailbox and a read-only key are enough. The free plan asks for no card.