Write End-to-End Tests with Playwright
Automate browser testing for a web app using Playwright's resilient locators.
Introduction
30 Second Summary
Every time a web app gets updated, something can break. A button stops working, a filter shows the wrong results, or an item disappears. Manual testing catches some of these issues, but it's slow and unreliable.
In this project, you will write automated tests that open a real web browser, interact with a todo app, and verify everything works correctly. Your test suite will check an entire application's functionality in seconds.
What You'll Build
A complete automated test suite using Playwright that verifies the core functionality of a todo web application by adding items, completing them, and filtering by status.
By the end of this project, you'll have:
- A Playwright project configured for automated browser testing with Chromium.
- A test file containing four end-to-end tests that add, complete, and filter todo items in a real browser.
- An HTML test report showing pass/fail results and timing for each test.
- Secret Mission: Run your tests across multiple browsers with a single command.
Are there any prerequisites?
None. This guide covers everything from installing Node.js to running your first test. You just need a Mac with VS Code installed.
Install Node.js and Set Up Playwright
Automated browser testing needs a runtime to execute your test scripts and a framework to drive the browser. In this project, that means Node.js (the JavaScript runtime) and Playwright (the end-to-end testing framework).
In this step, you will install both tools, scaffold a Playwright project with TypeScript, and confirm everything works by running the included example test.
In this step, get ready to:
- Install Node.js on your Mac.
- Scaffold a new Playwright project with TypeScript.
- Run your first test and view the HTML report.
Install Node.js
Node.js is the JavaScript runtime that Playwright uses to execute your test files. You need version 24 or higher installed on your machine.
- Open a terminal in VS Code by pressing Ctrl+` (Control + backtick).
- Check if Node.js is already installed by running:
node --version
✔️ I see version 24 or higher
You are all set. Skip ahead to the next substep.
ⓧ I see an older version
You have Node.js installed, but you need version 24 or higher for Playwright to work correctly.
- Open https://nodejs.org/en/download in your browser.
- Download the macOS .pkg installer for the LTS version (v24).
- Run the installer and follow the prompts to complete installation.
- Close and reopen your VS Code terminal, then run node --version again to confirm you see v24 or higher.
Why close and reopen the terminal?
The installer updates your system PATH, but your current terminal session still uses the old PATH. Reopening it picks up the new Node.js location.
ⓧ Command not found
Node.js is not installed yet. Let's fix that.
- Open https://nodejs.org/en/download in your browser.
- Download the macOS .pkg installer for the LTS version (v24).
- Run the installer and follow the prompts to complete installation.
- Close and reopen your VS Code terminal, then run node --version to confirm you see v24 or higher.
Why close and reopen the terminal?
The installer updates your system PATH, but your current terminal session still uses the old PATH. Reopening it picks up the new Node.js location.
- Confirm npm (the Node.js package manager) is available by running:
npm --version
You should see a version number printed. This confirms both Node.js and npm are ready to use.
npm command not found?
If node --version works but npm --version does not, try closing and reopening your terminal one more time. The .pkg installer always bundles npm alongside Node.js.
help me debug why npm is not found after installing Node.js
Create your Playwright project
Now that Node.js is installed, you can scaffold a full Playwright project with one command. The scaffolder creates your config file, test folder, and downloads browser binaries.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir playwright-tests
cd playwright-tests
- Scaffold the Playwright project by running:
npm init playwright@latest
What prompts will I see?
The command asks you four questions. Answer each one as listed below:
- TypeScript or JavaScript? Press Enter to accept the default (TypeScript).
- Where to put your end-to-end tests? Press Enter to accept the default (tests).
- Add a GitHub Actions workflow? Type N and press Enter.
- Install Playwright browsers? Press Enter to accept the default (yes).
The browser download takes a minute or two. It fetches Chromium, Firefox, and WebKit binaries (~500MB total).
Once the scaffold finishes, you should see a success message in the terminal.
- Confirm the scaffold created your files by running:
ls
You should see playwright.config.ts, package.json, and a tests folder in the output.
Scaffold command failed?
Make sure you are inside the playwright-tests folder before running the init command. If you see a permissions error, try running sudo npm init playwright@latest instead.
help me fix a Playwright scaffold error
The scaffold generates a default config with three browser projects (Chromium, Firefox, and WebKit). For this project, you will focus on Chromium only and add the others in the Secret Mission later.
- Open the project in VS Code by running:
code .
- In the Explorer sidebar, click playwright.config.ts to open it.
- Replace the entire file contents with the following configuration:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'https://demo.playwright.dev/todomvc',
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
});
What does this config do?
- testDir: './tests' tells Playwright where to find your test files.
- reporter: 'html' generates an HTML report after each test run.
- baseURL sets the default URL for navigation, so your tests can use relative paths.
- projects limits test execution to Chromium only for now. You will add Firefox and WebKit in the Secret Mission.
- Save playwright.config.ts by pressing Cmd+S.
✔️ Awesome, I've got everything!
Great. Make sure you have saved the file before moving on.
ⓧ I'd like to double check the full code
Your playwright.config.ts should look exactly like this:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'https://demo.playwright.dev/todomvc',
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
});
Run the example test and view the report
The scaffold included an example test file at tests/example.spec.ts. Running it confirms that Playwright, the browser binaries, and your config all work together.
- Run the example test by executing this command in the terminal:
npx playwright test
You should see output indicating the test passed. It runs headless (no visible browser window) and finishes in a few seconds.
What is headless mode?
Headless means the browser runs in the background without opening a visible window. This is faster and is the default for automated testing. You will use headed mode later to watch your tests interact with the page.
- View the HTML test report by running:
npx playwright show-report
Your default browser opens with a report page showing your test results. You should see the example test listed with a green pass indicator.
Tests failing or report not opening?
If the test fails, make sure you saved playwright.config.ts before running. A syntax error in the config will prevent tests from starting.
If the report does not open automatically, check the terminal output for a URL (usually http://localhost:9323) and open it manually in your browser.
help me debug why my Playwright test is failing
Your testing environment is fully set up. Next, you will delete the example test and write your own tests against the TodoMVC app.
Write Your First Test
Your Playwright project is scaffolded and the example test passes. You have a working test runner, an HTML report, and browser binaries ready to go.
Now it's time to replace the example with your own tests. You'll target the TodoMVC demo app and verify it behaves correctly when a user visits the page and adds items to their list.
In this step, get ready to:
- Delete the scaffold example test and create your own test file.
- Write two end-to-end tests that verify the TodoMVC app.
- Run your tests in headed mode to watch browser automation in action.
Delete the example test and create your test file
The scaffold came with tests/example.spec.ts to prove the setup works. You no longer need it. You'll replace it with a fresh file targeting the TodoMVC app.
- Delete the example file and create your new test file by running these commands:
rm tests/example.spec.ts
touch tests/todo-app.spec.ts
- Confirm the swap worked by running:
ls tests
You should see only todo-app.spec.ts in the output.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type VS Code and press Enter to open it.
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Select your Playwright project folder on the Desktop, then click Open.
- In the Explorer sidebar on the left, expand the tests folder and click todo-app.spec.ts to open it in the editor.
Don't see the Explorer sidebar?
Press Cmd+Shift+E (macOS) or Ctrl+Shift+E (Windows) to toggle it open.
Help me find the Explorer sidebar in VS Code.
Write a test that checks the page title
Your first test is simple. Navigate to the TodoMVC app and confirm the page loads with the expected title. This pattern is the foundation of every end-to-end test: go to a page, then assert something about it.
- In todo-app.spec.ts, paste the following code:
import { test, expect } from '@playwright/test';
// Navigate to the TodoMVC app and verify the page title
test('has correct title', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
await expect(page).toHaveTitle(/TodoMVC/);
});
What does this code do?
import { test, expect } from '@playwright/test' brings in the test runner and assertion library. Every Playwright test file starts with this import.
test('has correct title', ...) defines a named test. The string is what appears in your terminal output and HTML report.
page.goto() navigates the browser to a URL. expect(page).toHaveTitle(/TodoMVC/) asserts the page title matches a regular expression. Playwright automatically waits for the title to appear before checking.
- Save todo-app.spec.ts with Cmd+S (macOS) or Ctrl+S (Windows).
You won't see a visible change yet. The next chunk adds a second test, and then you'll run both together.
Seeing red squiggles in the editor?
If VS Code shows import errors, make sure you're editing the file inside your Playwright project folder (the one with package.json). The @playwright/test package must be in node_modules for the import to resolve.
Help me fix import errors in my test file.
Write a test that adds a todo item
The second test simulates a real user interaction. It types text into the input field, presses Enter, and confirms the new item appears on the page.
Playwright uses locators to find elements reliably, even if the page layout changes.
- In todo-app.spec.ts, add the following test below the closing }); of your first test:
test('can add a todo item', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
// Type a todo into the input field and submit it
await page.getByPlaceholder('What needs to be done?').fill('Buy groceries');
await page.getByPlaceholder('What needs to be done?').press('Enter');
// Confirm the new todo appears on the page
await expect(page.getByTestId('todo-title')).toBeVisible();
await expect(page.getByTestId('todo-title')).toHaveText('Buy groceries');
});
What does this code do?
getByPlaceholder('What needs to be done?') finds the input field by its placeholder text. This is a resilient locator because it targets what the user actually sees, not a brittle CSS selector.
.fill('Buy groceries') types text into the input. .press('Enter') submits the form, just like a real user pressing the Enter key.
getByTestId('todo-title') locates the newly created todo by its data-testid attribute. toBeVisible() confirms the element is on screen. toHaveText('Buy groceries') verifies the text matches what you typed.
- Save todo-app.spec.ts.
Syntax error after pasting?
Make sure the second test starts on a new line after the }); that closes the first test. There should be a blank line between the two tests for readability.
Help me debug a syntax error in my test file.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file, then continue below to run the tests.
ⓧ I'd like to double check the full code
Here's the complete tests/todo-app.spec.ts file at this point:
import { test, expect } from '@playwright/test';
test('has correct title', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
await expect(page).toHaveTitle(/TodoMVC/);
});
test('can add a todo item', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
await page.getByPlaceholder('What needs to be done?').fill('Buy groceries');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await expect(page.getByTestId('todo-title')).toBeVisible();
await expect(page.getByTestId('todo-title')).toHaveText('Buy groceries');
});
Run your tests in headed mode
By default, Playwright runs tests headless (no visible browser window). Adding the --headed flag opens a real browser so you can watch your tests interact with the page in real time.
- Open the integrated terminal in VS Code by pressing Ctrl+` (backtick key, top-left of your keyboard).
- Run your tests in headed mode by running this command:
npx playwright test --headed
A Chromium browser window opens, navigates to the TodoMVC app, and interacts with it. Both tests should pass in the terminal output.
What should I see?
The terminal prints something like 2 passed along with the total time taken. In the browser window, you briefly see the TodoMVC app load and a todo item appear before the window closes.
Each test gets a fresh browser context. Playwright isolates tests automatically so they never interfere with each other.
Tests failing or browser not opening?
Make sure you saved todo-app.spec.ts before running the command. Unsaved changes won't be picked up by the test runner.
If you see a network error, confirm you have an internet connection. The tests navigate to https://demo.playwright.dev/todomvc which requires network access.
Help me troubleshoot my failing Playwright tests.
You have two passing tests that verify the TodoMVC app loads and accepts user input. Next up, you'll test more complex interactions like completing todos and checking the items-left counter.
Test Completing and Counting Todos
Your test suite can already navigate and add items. Now it's time to test user interactions that change an item's state.
In a real todo app, marking an item as "done" should visually strike it through and update the remaining-items counter. In this step, you'll write a test that verifies both of those behaviors automatically.
In this step, get ready to:
- Write a test that completes a todo and asserts its CSS class changes.
- Verify the items-left counter updates after completion.
- Run the full suite and read the HTML report.
Write the completion and counting test
When a user clicks a todo's checkbox, the app adds a completed CSS class to that item and decrements the counter at the bottom. Your test will verify both of these changes happen correctly.
- In tests/todo-app.spec.ts, scroll to the bottom of the file (after the closing }); of your second test).
- Add the following test below your existing tests:
test('can complete a todo and update count', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
// Add two todos
await page.getByPlaceholder('What needs to be done?').fill('Buy groceries');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await page.getByPlaceholder('What needs to be done?').fill('Walk the dog');
await page.getByPlaceholder('What needs to be done?').press('Enter');
// Complete the first todo
await page.getByRole('checkbox', { name: 'Toggle Todo' }).first().click();
// Assert the first todo has completed class
const firstTodo = page.getByTestId('todo-item').first();
await expect(firstTodo).toHaveClass(/completed/);
// Assert items left count
await expect(page.getByTestId('todo-count')).toContainText('1');
});
What does this test do?
- It adds two todos ("Buy groceries" and "Walk the dog") so you have multiple items to work with.
- getByRole('checkbox', { name: 'Toggle Todo' }).first() finds the toggle checkbox by its ARIA role and accessible name, then .first() targets only the first item's checkbox.
- toHaveClass(/completed/) uses a regex to check that the element's class attribute contains the word "completed". The app adds this class when a todo is checked off.
- toContainText('1') on the todo-count element confirms only one item remains active after completing one of the two todos.
Why use a regex with toHaveClass?
The toHaveClass() assertion matches against the element's full class attribute when you pass a string. Since the todo item might have additional classes beyond just "completed", passing a regex like /completed/ checks that "completed" appears somewhere in the class list without requiring an exact full match.
- Save tests/todo-app.spec.ts.
Seeing a syntax error?
- Make sure the new test starts after the closing }); of your previous test. Each test() call is a standalone block at the top level of the file.
- Check that you have the import { test, expect } from '@playwright/test'; line at the very top of the file. Both test and expect must be imported.
If you're stuck, help me fix a syntax error in my Playwright test file.
✔️ Awesome, I've got everything!
Great. Make sure you've saved tests/todo-app.spec.ts before moving on.
ⓧ I'd like to double check the full code
Here's the complete tests/todo-app.spec.ts file with all three tests:
import { test, expect } from '@playwright/test';
test('has correct title', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
await expect(page).toHaveTitle(/TodoMVC/);
});
test('can add a todo item', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
await page.getByPlaceholder('What needs to be done?').fill('Buy groceries');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await expect(page.getByTestId('todo-title')).toBeVisible();
await expect(page.getByTestId('todo-title')).toHaveText('Buy groceries');
});
test('can complete a todo and update count', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
// Add two todos
await page.getByPlaceholder('What needs to be done?').fill('Buy groceries');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await page.getByPlaceholder('What needs to be done?').fill('Walk the dog');
await page.getByPlaceholder('What needs to be done?').press('Enter');
// Complete the first todo
await page.getByRole('checkbox', { name: 'Toggle Todo' }).first().click();
// Assert the first todo has completed class
const firstTodo = page.getByTestId('todo-item').first();
await expect(firstTodo).toHaveClass(/completed/);
// Assert items left count
await expect(page.getByTestId('todo-count')).toContainText('1');
});
Run the full suite and check the HTML report
Now that you have three tests covering navigation, adding, and completing todos, run the entire suite to confirm everything passes together.
- Run all three tests by executing this command in your terminal:
npx playwright test
You should see output indicating 3 tests passed.
- Open the HTML report to see detailed results by running:
npx playwright show-report
Your browser opens with the Playwright HTML report. You should see all three tests listed with their names, durations, and green pass status indicators.
What should I see in the report?
The HTML report shows each test as a row with its name, how long it took to run, and a pass/fail badge. Click on any test name to drill into its details and see the individual steps Playwright executed.
The three tests you should see are: "has correct title", "can add a todo item", and "can complete a todo and update count".
Tests failing unexpectedly?
- If the completion test fails on toHaveClass, make sure you're using .first() after getByTestId('todo-item') to target just the first item.
- If the count assertion fails, confirm you're checking toContainText('1') and not toHaveText('1'). The counter element contains extra text like "1 item left", so you need a partial match.
- If you see a timeout error, check your internet connection. The tests navigate to a live demo site.
If you're still stuck, help me debug my failing Playwright tests.
Three tests passing. Your suite now verifies that the app handles state changes correctly. Next up, you'll test the filter buttons that let users switch between active, completed, and all todos.
Test Filtering Todos
Your test suite can now add todos and verify that completing one updates the item count. The next piece of app functionality to cover is filtering.
TodoMVC has three filter links at the bottom of the list: Active, Completed, and All. Your new test will add items, complete one, then click each filter and confirm the correct number of items appear.
In this step, get ready to:
- Write a test that filters todos by Active, Completed, and All status.
- Run the full test suite and review the HTML report.
Write the filter test
The filter test follows the same pattern as your previous tests. You set up data (add todos, complete one), perform actions (click filter links), and assert the results (count visible items).
- In tests/todo-app.spec.ts, add this new test after the last test(...) block:
test('can filter todos by status', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
// Add three todos
await page.getByPlaceholder('What needs to be done?').fill('Buy groceries');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await page.getByPlaceholder('What needs to be done?').fill('Walk the dog');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await page.getByPlaceholder('What needs to be done?').fill('Read a book');
await page.getByPlaceholder('What needs to be done?').press('Enter');
// Complete one todo
await page.getByRole('checkbox', { name: 'Toggle Todo' }).first().click();
// Filter by Active
await page.getByRole('link', { name: 'Active' }).click();
await expect(page.getByTestId('todo-item')).toHaveCount(2);
// Filter by Completed
await page.getByRole('link', { name: 'Completed' }).click();
await expect(page.getByTestId('todo-item')).toHaveCount(1);
// Filter by All
await page.getByRole('link', { name: 'All' }).click();
await expect(page.getByTestId('todo-item')).toHaveCount(3);
});
What does this test do?
This test exercises the three filter states in TodoMVC:
- It adds three todos and completes the first one, creating a mix of active and completed items.
- getByRole('link', { name: 'Active' }) targets the Active filter link by its accessible role and visible text. After clicking, toHaveCount(2) asserts exactly two items remain visible.
- The Completed filter should show only the one completed item, and the All filter should restore all three.
- Save tests/todo-app.spec.ts.
Seeing a syntax error?
- Make sure the new test is placed after the closing }); of the previous test, not inside it.
- Check that you have matching parentheses and curly braces. Each test() call ends with });.
- Why is my filter test not being recognized?
✔️ Awesome, I've got everything!
Great. Double check you have saved your file before running the tests.
ⓧ I'd like to double check the full code
import { test, expect } from '@playwright/test';
test('has correct title', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
await expect(page).toHaveTitle(/TodoMVC/);
});
test('can add a todo item', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
await page.getByPlaceholder('What needs to be done?').fill('Buy groceries');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await expect(page.getByTestId('todo-title')).toBeVisible();
await expect(page.getByTestId('todo-title')).toHaveText('Buy groceries');
});
test('can complete a todo and update count', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
// Add two todos
await page.getByPlaceholder('What needs to be done?').fill('Buy groceries');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await page.getByPlaceholder('What needs to be done?').fill('Walk the dog');
await page.getByPlaceholder('What needs to be done?').press('Enter');
// Complete the first todo
await page.getByRole('checkbox', { name: 'Toggle Todo' }).first().click();
// Assert the first todo has completed class
const firstTodo = page.getByTestId('todo-item').first();
await expect(firstTodo).toHaveClass(/completed/);
// Assert items left count
await expect(page.getByTestId('todo-count')).toContainText('1');
});
test('can filter todos by status', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
// Add three todos
await page.getByPlaceholder('What needs to be done?').fill('Buy groceries');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await page.getByPlaceholder('What needs to be done?').fill('Walk the dog');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await page.getByPlaceholder('What needs to be done?').fill('Read a book');
await page.getByPlaceholder('What needs to be done?').press('Enter');
// Complete one todo
await page.getByRole('checkbox', { name: 'Toggle Todo' }).first().click();
// Filter by Active
await page.getByRole('link', { name: 'Active' }).click();
await expect(page.getByTestId('todo-item')).toHaveCount(2);
// Filter by Completed
await page.getByRole('link', { name: 'Completed' }).click();
await expect(page.getByTestId('todo-item')).toHaveCount(1);
// Filter by All
await page.getByRole('link', { name: 'All' }).click();
await expect(page.getByTestId('todo-item')).toHaveCount(3);
});
Run the full suite and review the HTML report
With four tests in your file, it is time to run the full suite and confirm everything passes together.
- Run all four tests by running this command:
npx playwright test
You should see output showing 4 tests passed across 1 project (Chromium).
- Open the HTML report by running this command:
npx playwright show-report
Your browser opens with the full HTML report. You should see all four test names listed with green pass indicators and their durations.
How do I read the HTML report?
The report's landing page shows a summary row for each test. Click any test name to drill into its details and see timing for each step (navigation, fill, click, assertion).
This drill-down view is especially useful when a test fails. Playwright captures the exact assertion that failed and a trace of what happened leading up to it.
Tests failing that passed before?
- Make sure you saved tests/todo-app.spec.ts before running. Unsaved changes are not picked up by the test runner.
- If only the new filter test fails, check that your filter link names match exactly: 'Active', 'Completed', and 'All'.
- Why is my Playwright filter test failing?
Secret mission
Cross-Browser Testing
Your tests pass on Chromium, but real users browse on Firefox and Safari too. Can you configure Playwright to verify your app works across all three browser engines with a single command?
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- Playwright project folder on your Desktop (contains test files and config).
- Playwright browser binaries (Chromium, Firefox, and WebKit downloaded to your system cache).
Keep everything running
No action needed. Choose this if you plan to keep writing more tests or exploring Playwright features.
- Your project folder stays on your Desktop, ready for you to add more test files.
- Playwright browser binaries remain cached so future test runs start instantly.
- Node.js remains installed globally. You will use it for other JavaScript and TypeScript projects.
Pause - I'll come back to this later
Free up disk space from the browser binaries while keeping your test code intact.
- Remove the downloaded browser binaries (about 500MB) by running this command from inside your project folder:
- Your project files stay untouched so you can reinstall browsers later.
npx playwright uninstall
When you are ready to come back, run npx playwright install from your project folder to re-download the browsers.
Delete - I don't want to use this again
Remove all project resources and start fresh.
- Remove the Playwright browser binaries by running:
npx playwright uninstall
- Delete the project folder from your Desktop. On macOS, run:
cd ~/Desktop
rm -rf playwright-tests
- On Windows, run:
cd ~\Desktop
rmdir /s /q playwright-tests
What about Node.js?
Node.js stays installed on your system. It is useful for many other projects and tools, so most developers keep it installed. The macOS .pkg installer does not provide a simple uninstall path.
Nice Work!
Nice Work!
You just built a complete automated test suite that verifies a web application's core functionality in seconds. From a blank folder to four passing end-to-end tests, you now have hands-on experience with industry-standard browser automation tooling.
Here's what you accomplished:
- Wrote four end-to-end tests using resilient locators (getByPlaceholder, getByRole, getByTestId) and auto-waiting assertions (toBeVisible, toHaveCount, toContainText, toHaveClass).
- Ran tests in headed mode to watch browser automation in real time and headless mode for speed.
- Generated and interpreted HTML test reports to understand pass/fail results across test runs.
- Secret Mission: Configured cross-browser testing across Chromium, Firefox, and WebKit with a single command.
Ready to quiz yourself?