👉 NEW: Automatic performance & error tracing on every browser check. Learn more

E2E Checkout

On this page

Another key website flow that needs to be closely monitored is any sort of checkout. You want to be 100% sure your users are able to pay for goods or services you might be offering, at any given time.


Checkout procedures can vary dramatically depending on what is being bought or sold. A few constants will be:

  1. a shopping basket page / section, in cases where multiple items can be bought - this is normally where the checkout procedure starts
  2. a page to enter or edit billing and, where applicable, shipping information
  3. a summary to review all the different parameters of the purchase

Modelled on the above structure is the following example running against our test website. We will add a few products to the shopping cart, then proceed until the summary screen shows up and verify that the transaction has been confirmed. Here we can get creative and, for example, iterate through a number of products to fill the cart:

const { chromium } = require('playwright')
const productsNumber = process.env.PRODUCTS_NUMBER || 3;

(async () => {
  const browser = await chromium.launch()
  const page = await browser.newPage()

  const navigationPromise = page.waitForNavigation()

  await page.goto('https://danube-webshop.herokuapp.com/')

  for (i = 1; i <= productsNumber; i++) {
    await page.click(`.preview:nth-child(${i}) > .preview-author`)
    await page.click('.detail-wrapper > .call-to-action')
    await page.click('#logo')

    await navigationPromise

  await page.click('#cart')

  await page.click('.cart > .call-to-action')

  await page.click('#app-content #s-name')

  await page.type('#s-name', 'Max')
  await page.type('#s-surname', 'Mustermann')
  await page.type('#s-address', 'Charlottenstr. 57')
  await page.type('#s-zipcode', '10117')
  await page.type('#s-city', 'Berlin')
  await page.type('#s-company', 'Firma GmbH')

  await page.click('.checkout > form')

  await page.click('#asap')

  await page.click('.checkout > .call-to-action')

  await page.waitForSelector('#order-confirmation', { visible: true })

  await browser.close()

Run in Checkly

const puppeteer = require("puppeteer");
const productsNumber = process.env.PRODUCTS_NUMBER || 3;

(async () => {
  const browser = await puppeteer.launch();

  const page = await browser.newPage();

  const navigationPromise = page.waitForNavigation();

  await page.goto("https://danube-webshop.herokuapp.com/");

  await page.setViewport({ width: 1200, height: 800 });

  for (i = 1; i <= productsNumber; i++) {
    await page.waitForSelector(`.preview:nth-child(${i}) > .preview-author`);
    await page.click(`.preview:nth-child(${i}) > .preview-author`);

    await page.waitForSelector(".detail-wrapper > .call-to-action");
    await page.click(".detail-wrapper > .call-to-action");

    await page.waitForSelector("#logo");
    await page.click("#logo");

    await navigationPromise;

  await page.waitForSelector("#cart");
  await page.click("#cart");

  await page.waitForSelector(".cart > .call-to-action");
  await page.click(".cart > .call-to-action");

  await page.waitForSelector("#s-name");
  await page.click("#s-name");

  await page.type("#s-name", "Max");
  await page.type("#s-surname", "Mustermann");
  await page.type("#s-address", "Charlottenstr. 57");
  await page.type("#s-zipcode", "10117");
  await page.type("#s-city", "Berlin");
  await page.type("#s-company", "Firma GmbH");

  await page.waitForSelector(".checkout > form");
  await page.click(".checkout > form");

  await page.waitForSelector("#asap");
  await page.click("#asap");

  await page.waitForSelector(".checkout > .call-to-action");
  await page.click(".checkout > .call-to-action");

  await page.waitForSelector("#order-confirmation", { visible: true });

  await browser.close();

Run in Checkly

Run this example as follows:

PRODUCTS_NUMBER=3 node checkout.js
node checkout.js

Note: In some cases, users will need to log in before they can proceed to a purchase. When users are allowed to buy both with and without having a pre-existing account on the platform, it might be worthwhile to test both flows separately.


Checkout is a peculiar flow: unlike Login and others, it almost always involves an actual exchange of currency. This is usually not an issue in pre-production environments, as payment providers are not yet involved or are set up in sandbox/test mode. For Production, low-value transactions can be performed with low frequency and immediately voided after the test has been completed.


  1. Checkout flows will vary depending on what is being purchased.
  2. Remember to check different flows (e.g. with and without login) if needed.
  3. Additional care is needed on production systems as real transactions will take place.