Searching for Playwright scripts usually means one thing: you want code that works, not another page of theory. So this page is mostly code. Each of the 20 scripts below is short, uses the locators Playwright recommends, and points at a public practice site, so you can paste it into a project and watch it pass.
The examples are in JavaScript because it has no build step, but every line works in TypeScript too. If you have never installed Playwright, start with how to install Playwright, or read what Playwright is first.
Setup: Run Any Script in 2 Minutes
npm init playwright@latest # choose JavaScript, tests folder npx playwright test # run every script npx playwright test login.spec.js --headed # one script, visible browser npx playwright test --ui # UI Mode: watch, pick, re-run
Save each example below as its own file in tests/, for example tests/login.spec.js.
Anatomy of a Playwright Script
Every Playwright test script has the same four parts: import, test, actions, assertions.
// 1. Import the test runner and assertion library const { test, expect } = require('@playwright/test'); // 2. Declare a test. The { page } fixture is a fresh browser tab. test('describe what the user does', async ({ page }) => { // 3. Actions: every call is awaited await page.goto('https://example.com'); // 4. Assertions: web-first, they retry until they pass or time out await expect(page).toHaveTitle(/Example/); });
Why no sleep()? Playwright waits for elements to be visible, enabled and stable before it acts, and expect keeps retrying. You almost never need a manual wait. If you do, that is usually a sign the assertion is wrong.
Scripts 1–4: Pages, Logins & Forms
1. Open a page and check it loaded
const { test, expect } = require('@playwright/test'); test('Playwright homepage has a Get started link', async ({ page }) => { await page.goto('https://playwright.dev/'); await expect(page).toHaveTitle(/Playwright/); await page.getByRole('link', { name: 'Get started' }).click(); await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); });
2. Log in and verify the secure area
const { test, expect } = require('@playwright/test'); test('valid user can log in', async ({ page }) => { await page.goto('https://the-internet.herokuapp.com/login'); await page.getByLabel('Username').fill('tomsmith'); await page.getByLabel('Password').fill('SuperSecretPassword!'); await page.getByRole('button', { name: 'Login' }).click(); await expect(page).toHaveURL(/secure/); await expect(page.locator('#flash')).toContainText('You logged into a secure area!'); });
For a deeper walkthrough with negative cases, see the Playwright login test tutorial.
3. Dropdowns and checkboxes
const { test, expect } = require('@playwright/test'); test('select an option', async ({ page }) => { await page.goto('https://the-internet.herokuapp.com/dropdown'); await page.locator('#dropdown').selectOption('2'); await expect(page.locator('#dropdown')).toHaveValue('2'); }); test('tick a checkbox', async ({ page }) => { await page.goto('https://the-internet.herokuapp.com/checkboxes'); const first = page.getByRole('checkbox').first(); await first.check(); await expect(first).toBeChecked(); });
4. Add items in a to-do app
const { test, expect } = require('@playwright/test'); test('add two todos', async ({ page }) => { await page.goto('https://demo.playwright.dev/todomvc'); const input = page.getByPlaceholder('What needs to be done?'); for (const item of ['Buy milk', 'Write tests']) { await input.fill(item); await input.press('Enter'); } await expect(page.getByTestId('todo-title')).toHaveText(['Buy milk', 'Write tests']); });
Scripts 5–7: Files & Screenshots
5. Upload a file
const { test, expect } = require('@playwright/test'); test('upload a file', async ({ page }) => { await page.goto('https://the-internet.herokuapp.com/upload'); // Pass a path, or an in-memory buffer like this: await page.locator('#file-upload').setInputFiles({ name: 'notes.txt', mimeType: 'text/plain', buffer: Buffer.from('hello'), }); await page.locator('#file-submit').click(); await expect(page.getByRole('heading', { name: 'File Uploaded!' })).toBeVisible(); });
6. Download a file
const { test, expect } = require('@playwright/test'); test('download a file', async ({ page }) => { await page.goto('https://the-internet.herokuapp.com/download'); // Start waiting BEFORE the click, or you can miss the event const downloadPromise = page.waitForEvent('download'); await page.locator('#content a').first().click(); const download = await downloadPromise; expect(download.suggestedFilename()).toBeTruthy(); await download.saveAs('downloads/' + download.suggestedFilename()); });
More patterns, including multiple files, are in the file upload and download guide.
7. Full-page and element screenshots
const { test, expect } = require('@playwright/test'); test('take screenshots', async ({ page }) => { await page.goto('https://playwright.dev/'); await page.screenshot({ path: 'screens/home-full.png', fullPage: true }); await page.locator('nav').first().screenshot({ path: 'screens/nav.png' }); // Visual regression: compares against a saved baseline image await expect(page).toHaveScreenshot('home.png', { maxDiffPixelRatio: 0.02 }); });
The first run of toHaveScreenshot creates the baseline and fails on purpose; run again to compare. Details in visual regression testing.
Scripts 8–11: API & Network
8. API GET test (no browser needed)
const { test, expect } = require('@playwright/test'); test('GET a post', async ({ request }) => { const res = await request.get('https://jsonplaceholder.typicode.com/posts/1'); expect(res.ok()).toBeTruthy(); const body = await res.json(); expect(body).toMatchObject({ id: 1, userId: 1 }); });
9. API POST test
const { test, expect } = require('@playwright/test'); test('create a post', async ({ request }) => { const res = await request.post('https://jsonplaceholder.typicode.com/posts', { data: { title: 'Playwright', body: 'API testing', userId: 1 }, }); expect(res.status()).toBe(201); expect((await res.json()).title).toBe('Playwright'); });
Playwright's request fixture replaces Postman for many teams. See the full Playwright API testing guide.
10. Mock an API response
const { test, expect } = require('@playwright/test'); test('show mocked fruit', async ({ page }) => { // Register the route BEFORE navigating await page.route('*/**/api/v1/fruits', async (route) => { await route.fulfill({ json: [{ name: 'Strawberry', id: 21 }] }); }); await page.goto('https://demo.playwright.dev/api-mocking'); await expect(page.getByText('Strawberry')).toBeVisible(); });
11. Block images to speed up a run
const { test, expect } = require('@playwright/test'); test('page works without images', async ({ page }) => { await page.route('**/*.{png,jpg,jpeg,webp,svg}', (route) => route.abort()); await page.goto('https://playwright.dev/'); await expect(page.getByRole('link', { name: 'Get started' })).toBeVisible(); });
More routing tricks (modify responses, simulate errors and slow networks) are in the network interception guide.
Quick check: why does script 6 create downloadPromise before the click, while script 10 calls page.route before goto? If you can't answer in one sentence, you're not alone: event timing is one of the most-missed topics in Playwright interviews. Test yourself with the Playwright practice tests →
Scripts 12–15: Tabs, Dialogs, Frames & Hover
12. Handle a new tab or window
const { test, expect } = require('@playwright/test'); test('link opens a new window', async ({ page, context }) => { await page.goto('https://the-internet.herokuapp.com/windows'); const pagePromise = context.waitForEvent('page'); await page.getByRole('link', { name: 'Click Here' }).click(); const newPage = await pagePromise; await expect(newPage.getByRole('heading')).toHaveText('New Window'); });
13. Accept a JavaScript confirm dialog
const { test, expect } = require('@playwright/test'); test('accept confirm', async ({ page }) => { await page.goto('https://the-internet.herokuapp.com/javascript_alerts'); // Playwright dismisses dialogs by default, so opt in to accept page.once('dialog', (dialog) => dialog.accept()); await page.getByRole('button', { name: 'Click for JS Confirm' }).click(); await expect(page.locator('#result')).toHaveText('You clicked: Ok'); });
14. Read text inside nested frames
const { test, expect } = require('@playwright/test'); test('nested frames', async ({ page }) => { await page.goto('https://the-internet.herokuapp.com/nested_frames'); const middle = page .frameLocator('frame[name="frame-top"]') .frameLocator('frame[name="frame-middle"]'); await expect(middle.locator('body')).toHaveText('MIDDLE'); });
15. Hover to reveal hidden content
const { test, expect } = require('@playwright/test'); test('hover shows user name', async ({ page }) => { await page.goto('https://the-internet.herokuapp.com/hovers'); await page.locator('.figure').first().hover(); await expect(page.getByText('name: user1')).toBeVisible(); });
Scripts 16–19: Auth, Data, Mobile & POM
16. Log in once, reuse the session everywhere
const { test: setup, expect } = require('@playwright/test'); const authFile = 'playwright/.auth/user.json'; setup('authenticate', async ({ page }) => { await page.goto('https://the-internet.herokuapp.com/login'); await page.getByLabel('Username').fill('tomsmith'); await page.getByLabel('Password').fill('SuperSecretPassword!'); await page.getByRole('button', { name: 'Login' }).click(); await expect(page).toHaveURL(/secure/); await page.context().storageState({ path: authFile }); });
projects: [
{ name: 'setup', testMatch: /.*\.setup\.js/ },
{
name: 'chromium',
use: { ...devices['Desktop Chrome'], storageState: 'playwright/.auth/user.json' },
dependencies: ['setup'],
},
],
Every test in the chromium project now starts already logged in. Add playwright/.auth to .gitignore.
17. Data-driven tests from an array
const { test, expect } = require('@playwright/test'); const cases = [ { user: 'tomsmith', pass: 'wrong', msg: 'Your password is invalid!' }, { user: 'nobody', pass: 'x', msg: 'Your username is invalid!' }, ]; for (const c of cases) { test(`login fails for ${c.user}`, async ({ page }) => { await page.goto('https://the-internet.herokuapp.com/login'); await page.getByLabel('Username').fill(c.user); await page.getByLabel('Password').fill(c.pass); await page.getByRole('button', { name: 'Login' }).click(); await expect(page.locator('#flash')).toContainText(c.msg); }); }
18. Emulate a phone
const { test, expect, devices } = require('@playwright/test'); // Top level of the file: viewport, user agent, touch, device scale test.use({ ...devices['iPhone 13'] }); test('site works on iPhone size', async ({ page }) => { await page.goto('https://playwright.dev/'); const width = await page.evaluate(() => window.innerWidth); expect(width).toBe(390); });
See Playwright mobile testing for what emulation can and can't catch.
19. A small Page Object Model
class LoginPage { constructor(page) { this.page = page; this.username = page.getByLabel('Username'); this.password = page.getByLabel('Password'); this.submit = page.getByRole('button', { name: 'Login' }); this.flash = page.locator('#flash'); } async open() { await this.page.goto('https://the-internet.herokuapp.com/login'); } async login(user, pass) { await this.username.fill(user); await this.password.fill(pass); await this.submit.click(); } } module.exports = { LoginPage };
const { test, expect } = require('@playwright/test'); const { LoginPage } = require('../pages/LoginPage'); test('login with POM', async ({ page }) => { const login = new LoginPage(page); await login.open(); await login.login('tomsmith', 'SuperSecretPassword!'); await expect(login.flash).toContainText('secure area'); });
When the login form changes, you fix one file instead of fifty tests. The full pattern is in the Page Object Model tutorial.
Script 20: Playwright Without the Test Runner
Not every Playwright script is a test. For one-off automation (filling a form, grabbing data, making a PDF) use the playwright library directly and run it with Node.
const { chromium } = require('playwright'); // npm i playwright (async () => { const browser = await chromium.launch(); // { headless: false } to watch const page = await browser.newPage(); await page.goto('https://playwright.dev/docs/intro'); const headings = await page.getByRole('heading', { level: 2 }).allTextContents(); console.log(headings); await page.pdf({ path: 'intro.pdf' }); // Chromium only await browser.close(); })();
Only scrape sites you are allowed to; the web scraping tutorial covers the details.
Let Claude AI Write Your Next Playwright Script
Every script above took a human a few minutes to write. In 2026 most teams write the first draft with AI. The difference between a good and a bad AI-written script is whether the AI can see the real page. With the Playwright MCP Server, Claude AI opens a browser, reads the accessibility tree, and picks locators that actually exist.
Open https://the-internet.herokuapp.com/login. Write a Playwright test in JavaScript that: - logs in as tomsmith / SuperSecretPassword! - asserts the success message - logs out and asserts we are back on /login Use getByRole / getByLabel locators only. Run it and fix it until it passes.
You still review the code, but you move from writing to reviewing, which is where senior QA engineers spend their time. This workflow is the core of the Playwright + Claude AI course.
5 Playwright Script Mistakes to Avoid
| Mistake | Do this instead |
|---|---|
Missing await | Await every action and assertion. A missing await gives flaky, out-of-order failures. |
waitForTimeout(3000) | Use web-first assertions like toBeVisible(); they wait just long enough. |
| Long CSS / XPath selectors | Use getByRole, getByLabel, getByTestId. |
| Tests that depend on each other | Each test gets its own page; set up state with API calls or storageState. |
Checking values with isVisible() | isVisible() doesn't retry. Use await expect(locator).toBeVisible(). |
Frequently Asked Questions
What is a Playwright script?
A Playwright script is a JavaScript, TypeScript, Python, Java or C# file that uses the Playwright library to control a real browser: open pages, click, type, and check results. Most scripts are test files (*.spec.js) run with npx playwright test, but you can also run a plain Node.js script that launches a browser with chromium.launch().
How do I run a Playwright script?
Create a project with npm init playwright@latest, save your script in the tests folder as something.spec.js, then run npx playwright test. Add --headed to watch the browser, --ui for UI Mode, or a file name to run one script only.
Should I write Playwright scripts in JavaScript or TypeScript?
Both use the same API, so every example on this page works in either. JavaScript has no build step and is easier for beginners; TypeScript adds type checking that helps on large suites. Rename a .spec.js file to .spec.ts and swap require for import to convert.
Can Playwright record a script for me?
Yes. Run npx playwright codegen with a URL, click through your app, and Playwright writes the script as you go. The recorded script is a good first draft, but you should add assertions and clean up locators before relying on it.
Can AI write Playwright scripts?
Yes. With the Playwright MCP Server, Claude AI can open a real browser, explore your app, and write a script based on what it actually sees rather than guessing selectors. You still review the code and run it, just as you would with a teammate's pull request.
Where can I practise Playwright scripting?
Public practice sites such as the-internet.herokuapp.com, demo.playwright.dev/todomvc and jsonplaceholder.typicode.com are safe to automate. Every script on this page targets one of them.
Asim Noaman
Senior QA Automation Engineer & AI Testing Specialist
With years of hands-on experience building test automation frameworks for production applications, Asim specializes in combining traditional QA methodologies with cutting-edge AI tools. He has helped teams adopt Playwright and AI-driven testing workflows to ship faster with fewer bugs.
Playwright + Claude AI Course
From 20 Scripts to a Framework Employers Want to See
Copy-paste scripts get you started. A job needs more: a Page Object Model framework, API tests, CI/CD, and the skill to have Claude AI write and fix tests through the Playwright MCP Server. The course builds all of it with you, step by step, in JavaScript.
- Build a full Playwright framework in JavaScript from an empty folder
- Generate and debug tests with Claude AI and the Playwright MCP Server
- Run everything in a GitHub Actions pipeline
- 4.5★ on Udemy, 510+ students, 30-day money-back guarantee
Already comfortable with the scripts above? Test yourself with the Playwright practice tests.