Answer
Scenario: Multi-Step Checkout Flow Testing
Test Plan for Checkout Flow
- ✓Select product and add to cart
- ✓Proceed to cart and verify items
- ✓Enter shipping details
- ✓Enter payment details
- ✓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();
}
}
