sodcalculator

10 Tech How-To

Playwright Tests: Generate & Scale Browser Automation

Guide 10-37161 · Updated · Step by step

This guide shows you how to generate Playwright tests from recorded browser actions and then scale that automation into a reliable, maintainable test suite. You’ll learn the built-in codegen tool, how to structure tests for reuse, and how to run them in parallel across browsers. No prior Playwright experience is needed, but basic JavaScript or TypeScript knowledge helps.

What You’ll Need

  • Node.js 18 or newer installed on your machine
  • A code editor (VS Code recommended)
  • A terminal or command prompt
  • Basic familiarity with the command line

Step-by-Step

  1. Install Playwright. Open your terminal and run npm init -y in a new project folder, then npm install -D @playwright/test. After that, run npx playwright install to download the browser binaries (Chromium, Firefox, WebKit).
  2. Generate your first test. Run npx playwright codegen. A browser window opens alongside the Playwright Inspector. Navigate to the site you want to test and perform the actions you want to record—clicking buttons, filling forms, hovering menus. Each action appears as a line of test code in the inspector.
  3. Copy and save the generated code. When you finish recording, click the “Copy” button in the inspector, then paste the code into a new file at tests/example.spec.js. The generated test includes a test() block with your recorded steps and assertions for any visible text changes.
  4. Add meaningful assertions. The recorder captures basic checks, but you should strengthen them. For example, after a login form submission, add await expect(page.locator('h1')).toContainText('Welcome'). Use the expect library from @playwright/test—it provides auto-retrying assertions that wait for elements to appear.
  5. Run the test. Execute npx playwright test from your project root. Playwright runs the test in Chromium by default and prints a detailed report showing pass/fail status and a trace of each step.
  6. Parameterize with fixtures. Instead of hardcoding test data, use Playwright’s fixture system. Create a test.use() block to set base URL, viewport size, or storage state. For example, test.use({ viewport: { width: 1280, height: 720 } }) makes every test in that file use that viewport.
  7. Scale across browsers. Edit playwright.config.js to define multiple projects. A common setup has three projects—one for Chromium, one for Firefox, one for WebKit—each with its own use block. When you run npx playwright test, Playwright runs the same tests in all three browsers in parallel.
  8. Run tests in parallel. By default, Playwright runs test files in parallel using multiple worker processes. You can control this with the workers option in the config. Set workers: 4 to run four test files simultaneously. Tests within a single file run sequentially unless you use test.describe.parallel.
  9. Add retries for flaky tests. In your config, set retries: 2 to automatically re-run failed tests up to two times. This helps handle intermittent network or rendering issues without masking real bugs—failed tests still show in the final report.
  10. Use web-first assertions. Replace manual waits like page.waitForTimeout() with assertions that wait automatically. For example, await expect(page.locator('.spinner')).toBeHidden() waits until the spinner disappears, making your tests faster and less flaky.
  11. Set up global setup and teardown. If your tests need a logged-in session or seeded data, create a global-setup.js file that runs once before all tests. In your config, add globalSetup: './global-setup.js'. The file can log in and save the storage state to a JSON file, which tests then load via storageState.
  12. Run tests in CI. Use the official GitHub Actions workflow by running npx playwright install --with-deps in your CI environment, then npx playwright test. Playwright also offers a Docker image that includes all browsers and system dependencies.

Common Mistakes

  • Hardcoding selectors that change. The recorder often generates CSS selectors like #main-content > div:nth-child(2). These break when the page layout changes. Prefer stable attributes like data-testid or role-based locators: page.getByRole('button', { name: 'Submit' }).
  • Using fixed waits. page.waitForTimeout(3000) slows down your suite and still fails on slow connections. Replace with assertions that wait for the expected condition.
  • Ignoring test isolation. Tests that share state (like a logged-in user) can interfere with each other. Use a fresh browser context per test—Playwright does this automatically—and avoid writing to shared databases unless you clean up afterward.
  • Running everything serially. Leaving the default config means tests run one after another, wasting time. Enable parallel execution by using multiple workers and splitting tests across files.
  • Not checking the trace. When a test fails, Playwright generates a trace file. Open it with npx playwright show-trace to see the DOM snapshots, network requests, and console logs at each step—this is far more useful than a screenshot alone.

Tips

  • Use the page object model. For larger suites, create classes that represent pages (e.g., LoginPage) with methods like login(username, password). This reduces duplication and makes tests read like plain English.
  • Leverage test.describe for grouping. Group related tests together and use test.beforeEach for common setup like navigating to a page. This keeps your test files organized and reduces repetitive code.
  • Record in a separate browser profile. If the site requires login, use the codegen --storage-state option to save your session after logging in manually, then reuse that state for all generated tests.
  • Keep tests independent. Each test should be able to run on its own, in any order. Avoid depending on data created by a previous test. Use unique test data per run, like appending a timestamp to usernames.
  • Use environment variables for secrets. Store passwords and API keys in .env files and access them via process.env. Never commit real credentials to version control.

Troubleshooting

Test fails with “element not found.” First, run with --headed to watch the browser. Then open the trace to see the page state at failure. If the element is inside an iframe, use page.frameLocator(). If it’s a shadow DOM, use locator.and() or pierce with CSS selectors.

Tests pass locally but fail in CI. Common causes are missing system dependencies or a different screen size. Install all deps with npx playwright install --with-deps. Set a fixed viewport in your config so tests don’t depend on the CI machine’s display.

Browser doesn’t start. If you get an error about missing executables, run npx playwright install again. On Linux servers, you may need --with-deps to install required libraries like libnss3 and libatk.

Tests are slow. Check for unnecessary waits or large page loads. Use page.route() to mock API calls that aren’t relevant to the test. Also, ensure you’re running tests in parallel—if you have 20 test files but only 1 worker, they run serially.

Generated code has syntax errors. This can happen if you recorded actions in a page that changed mid-session. Delete the file and re-record, or manually fix the locators. The Playwright inspector also lets you edit and re-copy code before saving.

Frequently Asked Questions

Q: Does Playwright work with any JavaScript framework?
Yes. Playwright drives the browser directly, not the framework, so it works with React, Vue, Angular, Svelte, or plain HTML. It also supports testing in TypeScript out of the box.

Q: Can I generate tests for mobile devices?
Yes. In codegen, use the device emulation dropdown to pick a phone or tablet. You can also set test.use({ ...devices['iPhone 13'] }) in your config to run tests with a mobile viewport and user agent.

Q: How do I test file uploads or downloads?
For uploads, use page.setInputFiles() with a file path. For downloads, listen for the download event and call download.path(). The recorder can capture these actions if you perform them manually during recording.

Q: What’s the difference between page.click() and locator.click()?
page.click() is a shortcut that finds the first matching element. locator.click() is more explicit and allows chaining with filters like locator.first() or locator.filter(). Prefer locators for clarity and better error messages.

Q: How do I run a single test?
Use npx playwright test tests/login.spec.js --grep "should log in" to run tests matching a string in the test name. Or use --project=chromium to run only one browser.

Q: Is Playwright free for commercial use?
Yes. Playwright is open source under the Apache 2.0 license. There is also a paid cloud service, but the core tool is free to use in any project.

Related

General steps for current mainstream versions; menus can differ by device and version. See the disclaimer or report an error.

More in this bed

The whole Tech How-To bed →