Sign up with an email code

Test a sign-up that sends a code, with a new address on every run.

This example uses Parcel, a demo store at parcel.example.com. Parcel is a demo project, not a customer.

Parcel's sign-up asks for an email and a password, then emails a code to finish. A sign-up test needs a new address on every run. With the same address, the second run would find the account already there.

Before you start Link to Before you start

A sign-up needs no test account. Re:agent opens its browser as a new user, with a new address and a password that Rehearsal keeps.

Every run gets a new address in the test inbox, such as fresh-55a96c3ffd89f41f2379c02b9bfed590036a9b08b973006c@inbox.rehearsal.dev. Each one starts with fresh-, so you can find these accounts in your app's data later.

Ask Re:agent Link to Ask Re:agent

Open Re:agent and type: "Sign up for Parcel with a new account. Parcel emails a code. Check that the welcome page opens."

What Re:agent does Link to What Re:agent does

Each line below is a row in the chat, in the app's own words. What follows a dot is the detail the chat shows beside the row.

  • Checked your projects
  • Opened a browser on parcel.example.com as a new user
  • Opened parcel.example.com/sign-up
  • Filled Email
  • Filled Password
  • Clicked Create account
  • Read the test inbox · 1 message
  • Filled Code
  • Clicked Verify
  • Read the page · Welcome to Parcel
  • Saved the flow

Re:agent types {{email}} and {{password}}, and Rehearsal puts the real address and password in the fields. It reads the code from the inbox and types it, and the flow writes it down as {{code}}. Nothing shows beside these rows, and the chat does not keep the code.

The test Link to The test

Rehearsal writes a Playwright test from the saved flow. It looks like this.

TypeScript
import { expect, test } from '@playwright/test';import { participant } from '@rehearsal/codegen/runtime';test('New visitor signs up with an email code', async ({ browser }) => {  const visitor = await participant(browser, 'visitor');  try {    await test.step('Open the sign-up page', async () => {      await visitor.page.goto('/sign-up');    });    await test.step('Sign up with a new address', async () => {      const { email } = await visitor.identity();      await visitor.page.getByLabel('Email').fill(email);      await visitor.page.getByLabel('Password').fill(visitor.password());      await visitor.page.getByRole('button', { name: 'Create account' }).click();    });    await test.step('Enter the code from the email', async () => {      const message = await visitor.latestEmail();      await visitor.page.getByLabel('Code').fill(message.codes[0]);      await visitor.page.getByRole('button', { name: 'Verify' }).click();    });    await test.step('Check the welcome page', async () => {      await expect(visitor.page.getByRole('heading', { name: 'Welcome to Parcel' }))        .toBeVisible();    });  }  finally {    await visitor.close();  }});

visitor.identity() gives the run its new address, and visitor.password() gives the password. visitor.latestEmail() waits up to 30 seconds for the email, then returns its codes and links. The code is read again on every run, so it is never in the file.

If your app emails a link instead, Re:agent opens it, and the flow writes it down as {{link}}. The test then opens the link from latestEmail().

Run it Link to Run it

Press Run now on the card in the chat, or Run on the test's row in Tests. From the terminal:

Terminal
rehearsal runs start --test <test-id>rehearsal runs wait <run-id>

Every run signs up a new account in Parcel. Remove old ones from your app's data when you no longer need them.

Next steps Link to Next steps