Code Examples October 28, 2026 16 min read

Playwright Scripts: 20 Copy-Paste Examples You Can Run Today

Twenty short, working Playwright scripts in JavaScript, each one aimed at a public practice site so you can run it right now. Logins, forms, file uploads, API calls, network mocking, new tabs, iframes, mobile emulation and a Page Object Model, plus how to have Claude AI write the next one for you.

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

Terminal
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.

tests/anatomy.spec.js
// 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

tests/homepage.spec.js
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

tests/login.spec.js
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

tests/form-controls.spec.js
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

tests/todo.spec.js
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

tests/upload.spec.js
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

tests/download.spec.js
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

tests/screenshot.spec.js
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)

tests/api-get.spec.js
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

tests/api-post.spec.js
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

tests/mock-api.spec.js
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

tests/block-images.spec.js
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

tests/new-tab.spec.js
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

tests/dialog.spec.js
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

tests/frames.spec.js
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

tests/hover.spec.js
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

tests/auth.setup.js
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 });
});
playwright.config.js (projects section)
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

tests/login-data.spec.js
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

tests/mobile.spec.js
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

pages/LoginPage.js
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 };
tests/login-pom.spec.js
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.

scripts/headlines.js — run with: node scripts/headlines.js
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.

Prompt to Claude (with Playwright MCP connected)
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

MistakeDo this instead
Missing awaitAwait 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 selectorsUse getByRole, getByLabel, getByTestId.
Tests that depend on each otherEach 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().

Turn these scripts into a real framework

Hands-on Udemy course in JavaScript: Page Object Model, API tests, CI/CD and AI test generation with Claude and the Playwright MCP Server.

See the plan →

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 - Playwright and Claude AI course instructor

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.

Udemy Instructor Published course author
Playwright + AI Expert Specialized in AI-powered QA
Production Experience Enterprise-grade frameworks
Connect on LinkedIn

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
See the 2-Step Plan →

Already comfortable with the scripts above? Test yourself with the Playwright practice tests.