</>

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