# E2E Testing Signup and Login Workflows with Playwright

[Synthetic monitoring](https://betterstack.com/community/guides/monitoring/what-is-synthetic-monitoring/) is a powerful strategy for
keeping tabs on mission-critical application workflows such as user registration
and authentication, ordering and checkout, etc.

It involves employing browser automation to mimic the behavior of actual users,
effectively reproducing steps a visitor might take, such as filling a
registration form, clicking on an email verification link, and navigating to a
dashboard.

By continuously testing and monitoring these key workflows, you'll be able to
detect and resolve production issues on your website before real users notice
them.

In the last few years, [Playwright](https://playwright.dev) has emerged as one
of the best tools for cross-platform, end-to-end web application testing and
automation.

Designed for speed, reliability, and robustness, Playwright's API is compatible
with major rendering engines like Chromium, Firefox, and WebKit, and it supports
both headed and headless browser modes.

In this tutorial, you'll learn to write E2E tests for realistic user
registration and authentication workflows and implement continuous monitoring so
that production failures are detected, reported, and addressed immediately.

Let's get started!

## Prerequisites

To proceed with this article, you only need basic JavaScript knowledge and a
working [Node.js](https://nodejs.org/en/download) environment on your machine.
If you're completely new to Playwright, you may find it helpful to review our
[Playwright testing essentials article](https://betterstack.com/community/guides/testing/playwright-intro/) before proceeding.

## Step 1 — Setting up the demo project

To showcase the automation of end-to-end tests within registration and
authentication workflows, I've prepared a sample application equipped with both
registration and login functionalities, which we will be testing later on.

Begin by
[forking the project repo](https://github.com/betterstack-community/playwright-signup-login)
to your GitHub account, then clone it to your local environment using the
command below:

```command
git clone https://github.com/<your_username>/playwright-signup-login
```

After cloning, navigate to the project's directory and proceed to install its
dependencies, including Playwright:

```command
cd playwright-signup-login
```

```command
npm install
```

With the dependencies installed, start the development server on port 3000
using:

```command
npm start
```

You'll see output similar to the following, indicating that the server is up and
running:

```text
[output]
> playwright-signup-demo@1.0.0 start
> nodemon src/server.js

[nodemon] 3.1.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node src/server.js`
{"level":30,"time":1711128683520,"pid":412818,"hostname":"fedora","msg":"Server listening at http://[::1]:3000"}
{"level":30,"time":1711128683522,"pid":412818,"hostname":"fedora","msg":"Server listening at http://127.0.0.1:3000"}
{"level":30,"time":1711128683522,"pid":412818,"hostname":"fedora","msg":"Fastify is listening on port: http://[::1]:3000"}
```

When you visit http://localhost:3000 in your browser, you will be greeted with
the application homepage:

![demo-app-home.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/81efa21b-58ce-4ab0-449b-4b875001c400/lg1x
=2596x1474)

You can also check out the login and sign up pages by visiting the `/login` and
`/signup` routes respectively:

![demo-app-login.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/4743e1e2-4566-420e-008b-a5b1e3714100/md2x
=2596x1474)

![demo-app-signup.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/b9300e52-18f2-45c1-e71e-4ca77ede5300/lg2x
=2596x1474)

This demo project is designed with a simplistic user registration and
authentication flow that closely mirrors what you might find in real-world
applications. It uses an in-memory store for user management and also simulates
sending an email for account verification.

Although the application lacks the extensive security features of a full-fledged
application, it will serve the purpose of this demonstration very well.

Our focus is on testing from the user's perspective, ensuring the ability to
register, verify email, sign in, and sign out smoothly, thus aiming for
consistent user experience in all browsers.

In the next section, you'll create the first test that ensures that unauthorized
users cannot access protected routes in your application.

## Step 2 — Redirecting unauthenticated users

Let's start our testing journey with a simple test that verifies that all
unauthenticated users are redirected to the login page.

The application dashboard is a protected route that has been configured to
accessible only by users who have successfully logged in.

This security measure is enforced by verifying the presence and validity of a
`signedIn` cookie. The absence or invalidity of this cookie triggers a
redirection to the `/login` route, as illustrated in the server configuration
below:

```javascript
[label src/server.js]
. . .
fastify.get('/dashboard', (req, reply) => {
  const signedIn = req.cookies.signedIn;
  if (!signedIn) {
    return reply.redirect(303, '/login');
  }

  reply.view('./src/templates/dashboard.pug', {
    title: 'Dashboard',
  });
});
. . .
```

To test this behavior, open the `tests/auth.spec.js` file and adjust its
contents with the following code snippet:

```javascript
[label tests/auth.spec.js]
import { test, expect } from '@playwright/test';

test('ensure unauthenticated users are redirected to the login page', async ({
  page,
}) => {
  await page.goto('/dashboard');

  await expect(page).toHaveURL('/login');
});
```

This test attempts to navigate directly to the `/dashboard` route. It then
checks if the application appropriately redirects to the `/login` page,
confirming that the protective mechanism against unauthorized access is in
effect.

You may execute the above test with the command below:

```command
npx playwright test
```

It should pass on all browsers:

```text
[output]
Running 3 tests using 3 workers
  3 passed (16.3s)

To open last HTML report run:

  npx playwright show-report
```

Though straightforward, this test crucially confirms that access to sensitive
pages is securely restricted to logged-in users, providing some assurance
against unauthorized entry.

## Step 3 — Validating user registration errors

Ensuring that users receive appropriate error messages when filling out the sign
up form incorrectly is an essential part of user interface testing. It's not
just about validating the submitted data on the backend, but also about making
sure users are informed of what they did wrong in a clear and helpful manner.

In this section, you'll add a new test that intentionally inputs invalid data on
the signup form to trigger and verify error messages:

```javascript
[label tests/auth.spec.js]
. . .

test('display sign up errors with invalid details', async ({ page }) => {
  await page.goto('/signup');

  await page.getByLabel('Username').fill('ay');

  await page.getByLabel('Email').fill('name@example');

  await page.getByLabel('Password', { exact: true }).fill('12345');

  await page.getByLabel('Confirm password').fill('1234');

  await page.getByRole('button', { name: 'Sign up!' }).click();

  await page.waitForLoadState();

  await expect(
    page.getByText('Username must be no less than 3 characters')
  ).toBeVisible();

  await expect(
    page.getByText('Please provide a valid email address')
  ).toBeVisible();

  await expect(
    page.getByText('Password must not be less than 6 characters')
  ).toBeVisible();

  await expect(
    page.getByText('The provided passwords do not match')
  ).toBeVisible();
});
```

This script simulates entering incorrect information in the signup form and
checks if the application correctly displays the expected error messages,
ensuring the validation system is effectively communicating with the user.

Upon running this test:

```command
npx playwright test -g 'display'
```

You should see it pass, indicating the application correctly handles and
displays validation errors:

```text
[output]
Running 3 tests using 3 workers
  3 passed (15.7s)

To open last HTML report run:

  npx playwright show-report
```

To add an extra layer of certainty that errors are not only present but also
displayed correctly, we'll incorporate a visual regression test:

```javascript
[label auth.spec.js]
test('display sign up errors with invalid details', async ({ page }) => {
 . . .

  await expect(
    page.getByText('The provided passwords do not match')
  ).toBeVisible();

[highlight]
  await expect(page).toHaveScreenshot('signup-errors.png');
[/highlight]
});
```

Running the test now will fail due to the absence of a baseline screenshot for
comparison:

```command
npx playwright test -g 'display'
```

```text
[output]
Running 3 test using 3 workers
  1) [chromium] › auth.spec.js:23:1 › display sign up errors with invalid details ──────────────────

    Error: A snapshot doesn't exist at /home/ayo/dev/betterstack/demo/playwright-signup/tests/auth.spec.js-snapshots/signup-errors-chromium-linux.png, writing actual.

      52 |   ).toBeVisible();
      53 |
    > 54 |   await expect(page).toHaveScreenshot('signup-errors.png');
         |   ^
      55 | });
      56 |

        at /home/ayo/dev/betterstack/demo/playwright-signup/tests/auth.spec.js:54:3

    attachment #1: signup-errors-actual.png (image/png) ────────────────────────────────────────────
    test-results/auth-display-sign-up-errors-with-invalid-details-chromium/signup-errors-actual.png
    ────────────────────────────────────────────────────────────────────────────────────────────────

  . . .

  3 failed
    [chromium] › auth.spec.js:23:1 › display sign up errors with invalid details ───────────────────
    [firefox] › auth.spec.js:23:1 › display sign up errors with invalid details ───────────────────
    [webkit] › auth.spec.js:23:1 › display sign up errors with invalid details ───────────────────

  Serving HTML report at http://localhost:9323. Press Ctrl+C to quit.
```

The HTML report should open automatically in your default browser, or you can go
to `http://localhost:9323` as mentioned in the error message:

![playwright-iscreenshot-errors.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/7e322fa0-31ba-4662-fda0-56b5193ced00/public
=2252x1148)

When you click on any of the errors and scroll down the page, you will see a
**Screenshots** section showing how the signup page looks at the point of
capture:

![playwright-initial-screenshot.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/1e92da95-a700-4b0c-b851-898f54455900/lg2x
=2378x1520)

This failure is expected and leads to the automatic creation of an initial
screenshot, stored in `tests/auth.spec.js-snapshots`, which serves as the
baseline for future test runs:

```command
tree tests/auth.spec.js-snapshots
```

```text
[output]
tests/auth.spec.js-snapshots/
├── signup-errors-chromium-linux.png
├── signup-errors-firefox-linux.png
└── signup-errors-webkit-linux.png
```

After validating that the screenshots represent the correct error states,
subsequent tests will compare against these baselines, ensuring that any
deviation gets detected, whether due to a bug or a deliberate change.

You may re-run the test once again to confirm that it succeeds since there were
no modifications to the page:

```command
npx playwright test -g 'display'
```

```text
[output]
Running 3 tests using 3 workers
  3 passed (15.7s)

To open last HTML report run:

  npx playwright show-report
```

Even the tiniest change to the page will cause the screenshots to differ,
leading to an error. In such instances, you decide if you want to update the
baseline screenshots (if the change is intentional) or fix the bug until the
test passes again:

```command
npx playwright test -g 'display' --update-snapshots # if the change is intentional
```

For more detailed information on working with screenshots and visual comparisons
in Playwright, refer to their documentation on
[Screenshots](https://playwright.dev/docs/screenshots) and
[Visual comparisons](https://playwright.dev/docs/test-snapshots).

## Step 4 — Testing the user registration workflow

In this section, you will verify the account registration process, ensuring that
a user can sign up successfully and receive a confirmation email. Automating
email verification testing is a crucial skill for many projects, as it adds a
layer of realism in your testing environment.

To facilitate sending verification emails, our demo application utilizes the
[Nodemailer package](https://nodemailer.com/), configured to work with an SMTP
server. I'll guide you through setting it up with a Gmail account first.

Start by activating **2-Step Verification** for your Gmail at
[Google's security settings](https://myaccount.google.com/u/0/security?pageId=none).
Then, generate a unique
[App Password](https://myaccount.google.com/apppasswords) for the demo
application, which will be used in your `.env` file:

![google-2fa.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/52011c35-6183-4e35-6e33-ce6dc476b200/md1x
=1726x446)

![google-app-password.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/f8992e4f-b633-4da7-c216-1b1531b0dc00/md1x
=1660x1014)

```text
[label .env]
GMAIL_USER=<your_gmail_address>
GMAIL_PASSWORD=<your_app_password>
```

To ensure the server process recognizes the updated values, restart it by using
`Ctrl-C` and `npm start` respectively.

Before you can automate email testing, you must use an email testing service
that allows you to create disposable email addresses and access emails sent to
them through a web interface or API.

There are a few such services out there, but we'll be using
[Mail7](https://www.mail7.io/) in this tutorial.

Let's test out the signup functionality by manually signing up for a Demo app
account using a Mail7 email address.

Head over to http://localhost:3000/signup and fill in the form using a random
`mail7.io` email such as:

```text
demoapptestuser@mail7.io
```

Mail7 allows you to instantly create email addresses by prepending a random
string to the `@mail7.io` suffix. This provides you with a public inbox that is
accessible through the URL below:

```text
https://console.mail7.io/admin/inbox/inbox?username=demoapptestuser@mail7.io
```

![mail7.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/18020561-1d1b-44fd-2990-77a2b5debe00/md2x
=2596x1474)

Once you submit the signup form, you will be presented with the following page:

![confirm-email.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/726ce742-2f68-465a-6b7d-5d6dec0d9f00/orig
=2596x1474)

Head over to your Mail7 email public inbox to see the confirmation email in your
inbox:

![mail-received.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/85780a9f-a806-48a7-e715-0c04dcfc4d00/lg1x
=2596x1474)

Click it and find the confirmation link, then click on it:

![confirmation-link.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/58fa9f09-6ef7-4979-1a15-23336aaa0900/md1x
=2596x1474)

You will subsequently be redirected to a page confirming that your email has
been verified successfully:

![email-verified.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/7114b752-ca9a-4bf4-9ab3-6fa1048fe900/orig
=2596x1474)

Let's now translate the above steps into a Playwright test. Return to your
`auth.spec.js` file and write the following test:

```javascript
[label tests/auth.spec.js]
import { test, expect } from '@playwright/test';
[highlight]
import crypto from 'node:crypto';
[/highlight]

. . .

test('ensure successful user registration and email verification', async ({ page, browser, context }) => {
  const userEmail = `${crypto.randomBytes(20).toString('hex')}@mail7.io`;

  await page.goto('/signup');

  await page.getByLabel('Username').fill('testuser');

  await page.getByLabel('Email').fill(userEmail);

  await page.getByLabel('Password', { exact: true }).fill('123456');

  await page.getByLabel('Confirm password').fill('123456');

  await page.getByRole('button', { name: 'Sign up!' }).click();

  await page.waitForLoadState();

  await expect(
    page.getByRole('heading', { name: 'Verify your email address' })
  ).toBeVisible();

  await expect(page.getByText(userEmail)).toBeVisible();

  const mail7Page = await browser.newPage();

  await mail7Page.goto(
    `https://console.mail7.io/admin/inbox/inbox?username=${userEmail}`,
    {
      waitUntil: 'domcontentloaded',
    }
  );

  await mail7Page.waitForSelector(
    '.subject:has-text("Confirm your email address")'
  );
  await mail7Page.click('.subject:has-text("Confirm your email address")');

  const iframe = await mail7Page.waitForSelector('.message iframe');
  const iframeContent = await iframe.contentFrame();

  if (!iframeContent) {
    throw new Error('message iframe is null!');
  }

  const confirmLink = await iframeContent.waitForSelector(
    'a:has-text("confirming your account")'
  );

  const pagePromise = mail7Page.context().waitForEvent('page');

  await confirmLink.click();

  const newTab = await pagePromise;
  await newTab.waitForLoadState();

  await expect(
    newTab.getByRole('heading', {
      name: `Your email, ${userEmail}, is verified successfully!`,
    })
  ).toBeVisible();
});
```

This test script generates a unique email address using the `crypto` module and
navigates to the application's registration page. It then fills in the required
field before submitting the form by clicking the "Sign up!" button.

After submission, it checks for a page change that prompts the user to verify
their email address, ensuring that the user's email is visible on this page. The
script proceeds by opening a new browser page to access the Mail7 admin console,
specifically targeting the inbox of the generated email address.

It waits for an email with the subject "Confirm your email address" to appear,
clicks on it, and navigates to the content of the email loaded in an iframe. The
script looks for a link within the iframe content including the text "confirming
your account" and clicks it.

This action triggers a new page or tab that the script waits for, verifying that
the email address has been successfully verified by checking for a heading
indicating a successful verification. This comprehensive automation covers user
registration, email verification, and the final assertion of a successful
account setup.

When you execute the test, it should pass on all browsers, proving that your
registration flow is working as intended:

```command
npx playwright test -g 'ensure successful user registration'
```

```text
[output]
Running 3 tests using 3 workers
  3 passed (16.3s)

To open last HTML report run:

  npx playwright show-report
```

With this successful result, you can be confident that your application's signup
and email confirmation processes are working as intended.

In the next section, we'll focus on verifying login functionality to complete
the user authentication journey.

## Step 5 — Verifying login and logout functionality

Building on the previous test, this section focuses on incorporating login and
logout operations into your testing suite. The aim to ensure that once a user
has registered and confirmed their email, they can log in and log out of the
application seamlessly.

Update the existing "user registration" test by including the highlighted
sections and change its name as shown below:

```javascript
[label tests/auth.spec.js]
[highlight]
test('ensure successful user registration and session management', async ({ page, browser }) => {
[highlight]
  . . .

  await expect(
    newTab.getByRole('heading', {
      name: `Your email, ${userEmail}, is verified successfully!`,
    })
  ).toBeVisible();

[highlight]
  // Login

  await newTab.getByRole('link', { name: 'You may now login' }).click();

  await newTab.getByLabel('Email').fill(userEmail);

  await newTab.getByLabel('Password', { exact: true }).fill(password);

  await newTab.getByRole('button', { name: 'Log in' }).click();

  await newTab.waitForLoadState();

  await expect(
    newTab.getByRole('heading', {
      name: 'Welcome to your dashboard!',
    })
  ).toBeVisible();

  // Logout

  await newTab.getByRole('link', { name: 'Logout' }).click();

  await newTab.waitForLoadState();

  await expect(newTab.url()).toContain('/login');
[/highlight]
});
```

The modifications above transition the test to the login phase, where login page
is accessed via the provided link, and the user's credentials are inputted. Once
the login button is clicked, it checks that the dashboard is successfully
accessed post-login.

Finally, the "Logout" link is clicked, and it verifies that the browser is
redirected to the login page, completing the end-to-end user registration
journey test.

You can verify the test succeeds by executing the following command:

```command
npx playwright test -g 'ensure successful'
```

```text
[output]
Running 3 tests using 3 workers
  3 passed (16.3s)

To open last HTML report run:

  npx playwright show-report
```

Now that we have reasonable coverage of the application's registration and
authentication flow, let's ensure that regressions are not introduced by future
development efforts by integrating Playwright tests into a CI/CD pipeline.

## Step 6 — Running Playwright tests with GitHub Actions

To prevent bugs and regressions from making it into your production environment,
it's necessary to integrate E2E testing in your CI/CD pipeline so that issues
are caught and fixed before the code is merged and deployed to production.

In this section, you will run your Playwright tests through GitHub actions when
changes are made against the `main` branch. I've already created the GitHub
workflow for you in the `.github/workflows/playwright.yml` file:

```yaml
[label .github/workflows/playwright.yml]
name: Run Playwright tests

on:
  push:
  pull_request:
    branches:
      - main

jobs:
  playwright:
    timeout-minutes: 60
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install application dependencies
        run: npm install

      - name: Install Playwright Browsers
        run: npx playwright install --with-deps

      - name: Run end-to-end tests
        run: npx playwright test
        env:
          GMAIL_USER: ${{ secrets.GMAIL_USER }}
          GMAIL_PASSWORD: ${{ secrets.GMAIL_PASSWORD }}

      - name: Upload test report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 7
```

This action script defines a `playwright` job that is executed when a push or
pull request is made to the `main` branch. The workflow sets up a Node.js
environment, installs all the application dependencies, and runs the tests. It
also creates an HTML report with a seven day retention.

There's no need to start the application server before running the tests because
Playwright is already configured to do so in its configuration file:

```javascript
[playwright.config.js]
// @ts-check
import { defineConfig, devices } from '@playwright/test';

. . .

export default defineConfig({
  . . .

  /* Run your local dev server before starting the tests */
  webServer: {
    [highlight]
    command: 'npm run playwright-server',
    [/highlight]
    url: 'http://127.0.0.1:3001',
    reuseExistingServer: !process.env.CI,
  },
});
```

Before you can send the confirmation emails in GitHub actions, you need to add
the `GMAIL_USER` and `GMAIL_PASSWORD` environmental variables to your repository
secrets.

Head over to your **Settings** tab, and find the **Actions** option under
**Secrets and variables**. Create the `GMAIL_USER` and `GMAIL_PASSWORD` entries
respectively with the appropriate values and save them.

![github-secrets.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/4527ee7b-30a4-440a-31d1-8f8b8c955100/public
=1940x624)

Once done, return to your terminal and commit all the changes you've made to the
project so far, including the Playwright test script and the generated reference
screenshots.

```command
git add -A
```

```command
git commit -m 'Add Playwright tests'
```

Then push the changes to your repository:

```command
git push origin main
```

Head back to your repo and check the **Actions** tab. You should observe the
"Run Playwright test" workflow in progress. After a few moments, it should
complete successfully:

![github-actions-success.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/df6305db-41cc-4bbb-bf92-d8144a395600/orig
=2194x332)

You can click the entry to view the artifacts created during the test run:

![github-actions-artifacts.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/d0c55767-505f-497b-81ce-e8e1d987b100/public
=3012x1402)

A report of the test is always present and made downloadable as a zip file which
is helpful for debugging test failures when they occur.

You can download this file by clicking on it, then extract it to a location that
preferably already has Playwright installed:

```command
unzip playwright-report.zip -d playwright-report
```

Once downloaded and extracted, run the command below to serve the report on a
local server and open it in your default browser.

```command
npx playwright show-report
```

## Step 7 — Playwright test monitoring with Better Stack

End-to-end test automation continues beyond the CI/CD level if you're serious
about detecting production issues for your critical application workflows. You
also need to continuously test and monitor these workflows through a platform
that can promptly alert you to failures.

With [Better Stack](https://betterstack.com/website-monitoring), you can
schedule your Playwright checks to run on a schedule against your production
systems to keep tabs on your business-critical workflows.

To demonstrate this, let's continuously monitor the user registration, email
verification, and session management workflows of our demo app in Better Stack.

Begin by making your local server accessible online using
[Ngrok](https://ngrok.com/download), which creates a secure tunnel to your
localhost. After installation, initiate Ngrok to expose port 3000:

```command
ngrok http 3000
```

```text
[output]
Session Status                online
Account                       Ayo (Plan: Free)
Version                       3.8.0
Region                        United States (California) (us-cal-1)
Latency                       242ms
Web Interface                 http://127.0.0.1:4040
[highlight]
Forwarding                    https://9d5a-146-70-173-76.ngrok-free.app -> http://localhost:3000
[/highlight]

Connections                   ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00
```

Take note of the generated Forwarding URL, as it will be your application's
temporary public address.

Go ahead and
[create a free Better Stack account](https://uptime.betterstack.com/users/sign-up)
or log in to your existing account. Navigate to the **Monitors** section within
your dashboard and opt to create a new monitor, selecting the **Playwright
scenario fails** as your monitor type.

Name your scenario and paste the "successful user registration" test function
into the provided field, ensuring to adjust the script's navigation URL to your
public Ngrok address for external accessibility:

```javascript
await page.goto('/signup'); // change this to:

await page.goto('https://<your_ngrok_url>/signup');
```

![better-stack-new-playwright.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/991cbd5f-e8d8-4e8f-6273-281fe1bc8400/md1x
=2302x1814)

After configuring your monitor, use the **Test Scenario** feature to verify that
it works. A successful execution indicates proper setup, and you can then
finalize your monitor's settings, including the **On-call escalation** policies.

![better-stack-test-scenario.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/96f943d8-26c9-4d86-22d3-619cd98e7500/lg2x
=1376x340)

Once you've saved your changes, your monitor will be shown as "Up":

![playwright-up.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/62c47ef6-a786-4a7c-b134-0d07d8923100/md1x
=2232x652)

Better Stack defaults to running these checks every three minutes, though
adjustments are available in the advanced settings section.

By default, Better Stack runs the test every three minutes, but you can
configure this in the **Advanced settings** section depending on your tolerance
for how long you are comfortable not knowing about a production issue.

To illustrate failure handling, intentionally introduce a breaking change in
your application's code, such as altering the redirection behavior post-logout
to navigate users to the home page instead of the login page.

```javascript
[label src/server.js]
. . .
fastify.get('/logout', (req, reply) => {
  reply
    .clearCookie('signedIn', {
      domain: appURL.hostname,
      path: '/',
    })
[highlight]
    .redirect(303, '/');
[/highlight]
});
```

This modification should trigger a test failure, reflected in Better Stack's
dashboard as a "Down" status and generating a notification based on your
configured escalation policy.

![playwright-down.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/a9c550c6-db0f-4da7-ca81-a74cd7d7a500/lg2x
=2232x652)

![betterstack-email.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/5658e655-1960-4630-a639-11721e893300/orig =1729x1328)

You can click on the **Ongoing incident** button to view the details of the
failure. In the **Logs** tab, you will see the entire output from Playwright
test runner which can help you figure out why the test failed.

![betterstack-logs.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/aaa48fbc-b0ed-4297-302f-44f073fa2800/lg2x
=2100x648)

You can also switch to the **Artifacts** section, where you're able to download
the Playwright report and other details to your computer.

![betterstack-artifacts.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/a9381afc-5489-4580-bc9f-bc526e3cbf00/public
=2100x648)

These insights facilitate quick identification and correction of issues, with
the monitor returning to an "Up" status following resolution (in this case by
reverting your change), confirmed by a subsequent notification.

![betterstack-resolved.png](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/4abb5dfa-ec86-4444-6c54-30bed11c2600/public
=1836x1542)

By leveraging Better Stack for real-time monitoring in this manner, you'll
establish a vigilant watch over your application's critical workflows, ensuring
quick issue discovery and resolution before your customers notice them.

## Final thoughts

Throughout this article, I've guided you through the process of crafting tests
for user registration and authentication workflows, integrating them into your
CI/CD pipeline, and implementing continuous monitoring through Better Stack to
detect production issues.

While we've explored a selection of test cases, there's ample scope to expand
upon this foundation with additional scenarios tailored to your needs. It's also
equally important to include tests for often-overlooked functionalities such as
password recovery, password resetting, account deletion, and more.

Thanks for reading, and happy testing!