</>

Technology

Playwright

Difficulty

Advanced

Interview Question

How would you test a multi-step checkout flow in Playwright ensuring each step is validated?

Answer

Scenario: Multi-Step Checkout Flow Testing

Test Plan for Checkout Flow

  1. Select product and add to cart
  2. Proceed to cart and verify items
  3. Enter shipping details
  4. Enter payment details
  5. Place order and verify confirmation

Implementation with test.step() and POM

TypeScript
import { test, expect } from '@playwright/test';
import { ProductPage }  from '../pages/ProductPage';
import { CartPage }     from '../pages/CartPage';
import { CheckoutPage } from '../pages/CheckoutPage';

test('complete checkout flow', async ({ page, request }) => {
  const productPage  = new ProductPage(page);
  const cartPage     = new CartPage(page);
  const checkoutPage = new CheckoutPage(page);

  // Step 1: API Setup — create test product inventory
  const productId = await test.step('Setup: Create test product via API', async () => {
    const res = await request.post('/api/products', {
      data: { name: 'Test Laptop', price: 999, stock: 10 },
      headers: { Authorization: `Bearer ${process.env.ADMIN_TOKEN}` },
    });
    return (await res.json()).id;
  });

  // Step 2: Add to Cart
  await test.step('Add product to cart', async () => {
    await productPage.navigate(productId);
    await expect(productPage.title).toHaveText('Test Laptop');
    await expect(productPage.price).toHaveText('$999.00');
    await productPage.addToCart(1);
    await expect(page.locator('.cart-count')).toHaveText('1');
  });

  // Step 3: Review Cart
  await test.step('Review cart and verify items', async () => {
    await cartPage.navigate();
    await expect(cartPage.items).toHaveCount(1);
    await expect(cartPage.itemName.first()).toHaveText('Test Laptop');
    await expect(cartPage.subtotal).toHaveText('$999.00');
  });

  // Step 4: Shipping Details
  await test.step('Fill shipping details', async () => {
    await cartPage.proceedToCheckout();
    await expect(page).toHaveURL('/checkout/shipping');

    await checkoutPage.fillShipping({
      fullName: 'John Doe',
      address:  '123 Main Street',
      city:     'New York',
      state:    'NY',
      zip:      '10001',
      country:  'US',
    });

    // Validate address fields before proceeding
    await expect(page.getByLabel('Full Name')).toHaveValue('John Doe');
    await checkoutPage.continueToPayment();
    await expect(page).toHaveURL('/checkout/payment');
  });

  // Step 5: Payment Details
  await test.step('Enter payment details', async () => {
    const cardFrame = page.frameLocator('iframe[src*="stripe"]');
    await cardFrame.getByPlaceholder('Card number').fill('4242424242424242');
    await cardFrame.getByPlaceholder('MM / YY').fill('12/25');
    await cardFrame.getByPlaceholder('CVC').fill('123');

    await expect(page.locator('.order-summary .total')).toHaveText('$999.00');
  });

  // Step 6: Place Order
  const orderId = await test.step('Place order and confirm', async () => {
    await page.getByRole('button', { name: 'Place Order' }).click();
    await expect(page).toHaveURL(/\/order-confirmed/);

    await expect(page.getByRole('heading')).toContainText('Order Confirmed!');
    await expect(page.locator('.order-number')).toBeVisible();

    return await page.locator('.order-number').textContent();
  });

  // Step 7: Verify via API
  await test.step('Verify order in backend', async () => {
    const orderRes = await request.get(`/api/orders?number=${orderId}`, {
      headers: { Authorization: `Bearer ${process.env.ADMIN_TOKEN}` },
    });
    const order = await orderRes.json();
    expect(order.status).toBe('confirmed');
    expect(order.total).toBe(999);
    expect(order.items[0].productId).toBe(productId);
  });
});

Checkout POM Example

TypeScript
// pages/CheckoutPage.ts
export class CheckoutPage {
  constructor(private page: Page) {}

  async fillShipping(details: {
    fullName: string; address: string; city: string;
    state: string; zip: string; country: string;
  }) {
    await this.page.getByLabel('Full Name').fill(details.fullName);
    await this.page.getByLabel('Address').fill(details.address);
    await this.page.getByLabel('City').fill(details.city);
    await this.page.getByLabel('State').selectOption(details.state);
    await this.page.getByLabel('ZIP').fill(details.zip);
  }

  async continueToPayment() {
    await this.page.getByRole('button', { name: 'Continue to Payment' }).click();
  }
}

Follow AutomateQA

Related Topics