End-to-End Testing with Playwright/Cypress
Automated browser tests that simulate real user interactions through an actual, full application stack, providing the highest-confidence but slowest and most expensive layer of the testing pyramid.
An end-to-end test drives a real browser through the real application.
End-to-end testing is like a full dress rehearsal of an entire theatrical production with the real cast, real costumes, real lighting, and a real live audience seated in the actual theater, versus a unit test which is more like one actor privately rehearsing a single line in front of a mirror — the dress rehearsal catches problems (a costume malfunction, a lighting cue firing at the wrong real moment) that rehearsing individual lines alone could never reveal, but you obviously can't afford to run a full dress rehearsal for every single line in the entire show.
Key Concepts
test('a user can check out', async ({ page }) => {
await page.goto('/products');
await page.getByRole('button', { name: 'Add to cart' }).click();
await page.getByRole('link', { name: 'Checkout' }).click();
await page.getByLabel('Card number').fill('4242424242424242');
await page.getByRole('button', { name: 'Pay' }).click();
await expect(page.getByText('Order confirmed')).toBeVisible();
}); page.locator('.btn-primary') breaks on a CSS change
page.locator('#submit-btn-2') breaks on a refactor
page.getByRole('button', { name: 'Pay' }) stable, and accessible
page.getByTestId('submit') explicit, stable await page.waitForTimeout(2000); // arbitrary -- flaky
await expect(el).toBeVisible(); // retries until true