keepcalculator

06 Numbers and Powers

How to Write End-to-End Tests for Websites

Card No. 06·55326 · Updated · Standard formula, shown below

End-to-end (E2E) tests simulate a real user’s journey through your website—clicking buttons, filling forms, navigating pages—to verify that the entire system works together. This guide walks you through writing your first E2E tests using Playwright, a popular open-source tool, but the principles apply to Cypress, Selenium, or any other framework. You’ll learn how to set up the environment, write a test, run it, and debug failures.

What You’ll Need

  • Node.js (version 16 or later) installed on your machine
  • A code editor (VS Code recommended)
  • Basic knowledge of JavaScript or TypeScript
  • A test website (you can use a local dev server or a public demo site)
  • Command line / terminal access

Step-by-Step

  1. Set up a new project
    Create a folder for your tests and initialize it: mkdir e2e-tests && cd e2e-tests then npm init -y. This creates a package.json file.
  2. Install Playwright
    Run npm install -D @playwright/test. Then install the browsers: npx playwright install. This downloads Chromium, Firefox, and WebKit so you can test across browsers.
  3. Create a test file
    Create a folder named tests and inside it a file example.spec.js. Playwright uses the .spec.js extension by default.
  4. Write your first test
    Open the file and add:
    const { test, expect } = require('@playwright/test');
    
    test('homepage has title', async ({ page }) => {
      await page.goto('https://example.com');
      await expect(page).toHaveTitle(/Example Domain/);
    });

    This test navigates to the site and checks that the page title matches a pattern.

  5. Run the test
    In the terminal, run npx playwright test. Playwright will run the test in headless mode (no browser window) and report pass/fail. To see the browser, use --headed.
  6. Test a user interaction
    Let’s test a login form. Add a second test:
    test('login flow', async ({ page }) => {
      await page.goto('https://example.com/login');
      await page.fill('#username', 'testuser');
      await page.fill('#password', 'password123');
      await page.click('button[type="submit"]');
      await expect(page).toHaveURL(/dashboard/);
    });

    Replace the selectors with those on your site. Use the browser’s DevTools to inspect elements and get reliable selectors (prefer data-testid attributes if available).

  7. Use test fixtures for setup
    If your test needs a logged-in state, use test.beforeEach to navigate and log in before each test. For example:
    test.beforeEach(async ({ page }) => {
      await page.goto('https://example.com/login');
      await page.fill('#username', 'testuser');
      await page.fill('#password', 'password123');
      await page.click('button[type="submit"]');
    });

    This keeps tests independent and avoids repeating code.

  8. Add assertions for key elements
    After an action, check that the expected result appears. Use await expect(page.locator('.success-message')).toBeVisible() or await expect(page.locator('text=Welcome')).toBeVisible(). Assertions are crucial—they decide pass/fail.
  9. Run tests in multiple browsers
    Playwright runs tests in all configured browsers by default. You can specify a browser with --project=chromium. To change default browsers, edit playwright.config.js (create it if missing) and set projects.
  10. Use trace viewer for debugging
    When a test fails, Playwright generates a trace file. Run npx playwright show-trace to open the trace viewer, which shows screenshots, network requests, and console logs at each step.

Common Mistakes

  • Using brittle selectors – Relying on CSS classes that change often or index-based selectors like div:nth-child(2) breaks tests quickly. Use stable attributes like data-testid or ARIA roles.
  • Not waiting for elements – Using page.click() without waiting for the element to be ready can cause flaky tests. Playwright automatically waits for actionability, but if you use page.waitForTimeout() (which is discouraged), you introduce fixed delays that slow tests and cause failures.
  • Testing implementation details – Asserting on internal CSS classes or DOM structure makes tests fragile. Test user-visible behavior, like text, URLs, and visible elements.
  • Not isolating tests – If tests share state (e.g., a user already logged in), they can interfere. Use beforeEach to reset state or create fresh data.
  • Ignoring test failures – When a test fails, don’t just re-run; investigate why. Use the trace viewer and fix the root cause (either the test or the app).

Tips

  • Start small – Begin with the most critical user journeys: login, search, add to cart, checkout. You can expand later.
  • Use data-testid attributes – Add data-testid to elements that are important for tests. This decouples tests from CSS changes.
  • Run tests in CI – Integrate E2E tests into your continuous integration pipeline so they run on every commit. Playwright provides GitHub Actions examples.
  • Keep tests independent – Each test should be able to run alone and in any order. Avoid dependencies between tests.
  • Use environment variables – Store URLs and credentials in environment variables or a .env file to avoid hardcoding.
  • Parallelize – Playwright runs tests in parallel by default. Use multiple workers to speed up execution, but be aware of shared resources.

Troubleshooting

If your tests fail or behave unexpectedly, here’s what to check:

  • Test times out – Increase the timeout in the config or per test: test.setTimeout(60000). Also check if the page is loading slowly or if a network request hangs.
  • Element not found – Use the Playwright inspector to see what’s on the page. Run npx playwright codegen to open a browser and generate selectors interactively. Verify your selector matches exactly.
  • Test passes locally but fails in CI – Differences in browser versions, screen size, or network speed can cause issues. Use the same browser configuration in CI, and consider using a fixed viewport size in your tests.
  • Flaky tests (pass sometimes, fail sometimes) – This usually indicates a race condition. Add explicit waits for the element to be visible or enabled instead of relying on timeouts. Use expect(locator).toBeVisible() which auto-waits.
  • Can’t install browsers – If npx playwright install fails, check your internet connection and disk space. You can install a specific browser: npx playwright install chromium.
  • Tests are slow – Use --workers=4 to run more in parallel. Also, avoid using page.waitForTimeout(); instead, wait for specific conditions.

Frequently Asked Questions

What is the difference between E2E tests and unit tests?

Unit tests check individual functions or components in isolation, while E2E tests verify the entire application flow from the user’s perspective, including the UI, backend, and database.

How many E2E tests should I write?

Focus on the critical user journeys that, if broken, would prevent users from completing core tasks. Quality over quantity; 10-20 well-chosen E2E tests are often more valuable than 100 redundant ones.

Can I run E2E tests on a staging server?

Yes, it’s common to run E2E tests against a staging environment that mirrors production. Ensure the test data is predictable and doesn’t affect real user data.

How do I handle authentication in E2E tests?

You can log in through the UI in beforeEach, or use Playwright’s storageState to save and reuse a logged-in session. The latter is faster and more reliable.

What if my website uses iframes or pop-ups?

Playwright supports iframes with page.frameLocator() and pop-ups with page.waitForEvent('popup'). Handle these elements explicitly in your tests.

Do I need to test on all browsers?

It’s best to test on the browsers your users actually use. Playwright makes it easy to run tests on Chromium, Firefox, and WebKit. If you have limited time, start with Chromium and add others later.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →