This episode covers real-world examples: e-commerce checkout, signup flows, and dashboard workflows, test design patterns for end-to-end automation, balancing UI tests with API and component tests, as well as prioritizing tests for critical business paths.

All the concepts you've learned find their meaning when applied to real applications. This episode 20 brings theory into practice: three of the most common use cases in digital business — e-commerce checkout, signup flows, and dashboard workflows — complete with test patterns proven in the field.
Beyond writing tests, this episode covers strategy: how to balance your investment between UI tests, API tests, and component tests, and how to prioritize the most important business paths to protect first. The ability to choose what to test is as important as how to test.
Checkout is the most critical path in e-commerce — a failure here means a direct loss of revenue. Checkout tests should use the Page Object (episode 6) and API mocks (episode 12) so they don't depend on a real payment gateway:
import { test, expect } from '@playwright/test';
import { CheckoutPage } from '../pages/CheckoutPage';
test('checkout succeeds through the confirmation page', async ({ page }) => {
await page.route('**/api/payment', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ status: 'success', orderId: 'ORD-001' }),
});
});
const checkout = new CheckoutPage(page);
await checkout.goto();
await checkout.fillShippingAddress();
await checkout.selectPaymentMethod('transfer');
await checkout.pay();
await expect(page.getByText('Order created successfully')).toBeVisible();
await expect(page).toHaveURL(/confirmation/);
});route.fulfill on **/api/payment replaces the payment gateway with a controlled success response. The full UI flow — from address to confirmation — is tested without real money.
Beyond the happy path, test the most common checkout failure paths:
await page.route('**/api/payment', (route) => {
route.fulfill({
status: 402,
contentType: 'application/json',
body: JSON.stringify({ error: 'Payment declined' }),
});
});
await checkout.pay();
await expect(page.getByText('Payment declined')).toBeVisible();status: 402 simulates a declined payment. Verify that the UI shows the error correctly and doesn't get stuck in a loading state.
Signup combines frontend and backend validation at once. The key to a stable test is using unique data (episode 13) so it doesn't collide with existing users:
import { test, expect } from '@playwright/test';
test('a new account registers successfully', async ({ page }) => {
const email = `user-${Date.now()}@example.com`;
await page.goto('/register');
await page.getByLabel('Full Name').fill('Budi Santoso');
await page.getByLabel('Email').fill(email);
await page.getByLabel('Password').fill('Secret123!');
await page.getByLabel('Confirm Password').fill('Secret123!');
await page.getByRole('button', { name: 'Register' }).click();
await expect(page.getByText('Verification email sent')).toBeVisible();
await expect(page).toHaveURL(/check-email/);
});email = 'user-${Date.now()}@example.com' guarantees uniqueness on every execution. After submit, verify the email-sent confirmation — the point where the UI flow ends for a new user.
Signup is prone to state leaking between fields. Test several validations within one describe so failures are collected:
await page.getByLabel('Password').fill('123');
await page.getByRole('button', { name: 'Register' }).click();
await expect(page.getByText('Password must be at least 8 characters')).toBeVisible();Verifying specific validation messages confirms that backend and frontend rules are in sync — a common bug when one side allows a password the other side rejects.
Dashboards usually load lots of async data from several APIs at once. Test stability is achieved by controlling every response:
import { test, expect } from '@playwright/test';
test('dashboard shows metrics from the API', async ({ page }) => {
await page.route('**/api/metrics', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ revenue: 12000000, orders: 340 }),
});
});
await page.goto('/dashboard');
await expect(page.getByText('12.000.000')).toBeVisible();
await expect(page.getByText('340')).toBeVisible();
});With a route controlling **/api/metrics, the rendered values can be predicted exactly. This removes the flakiness that appears when dashboard data changes between executions.
Dashboards often contain actions — filter, sort, export. Test the most important action flows:
await page.getByRole('combobox').selectOption({ label: 'This month' });
await expect(page.getByText('This Month Report')).toBeVisible();
await page.getByRole('button', { name: 'Export' }).click();
await expect(page.waitForEvent('download')).resolves.toBeTruthy();Verify that the filter action changes the content, then that the export button triggers a download — the two behaviors that most often break in a dashboard.
Not every flow has to be tested at the UI level. A practical division rule:
Use the test pyramid as a guide: most tests at the API and component level, a few but important at the UI level. When starting a suite, don't write UI tests for everything right away — start with API tests for contracts, add component tests for complex components, and use UI tests only for flows that genuinely need a real browser, like checkout or cross-page login.
Not all features have equal value. Start with those that have the biggest impact on revenue and trust:
Set a priority level that's visible in the test name or tag — for example, test('checkout - HIGH PRIORITY'). Critical flows get stricter retries and monitoring, while secondary features can be more relaxed.
Episode 20 connected all your skills with business reality: checkout tests with payment mocks, signup with unique data, and dashboards with controlled data. You also learned to balance investment across test layers and prioritize the critical business paths worth protecting first.
Key takeaways:
Date.now()) for repeatable signup tests.In the next episode we'll discuss the ecosystem and tools — related tools like Testing Library, Percy, and BrowserStack, using the Playwright Inspector, trace viewer, and codegen, community resources, as well as cloud execution and managed testing platforms.