๐ŸŽญ Playwright ยท Free cheat sheet

Flake-Free Tests with Playwright + TS

Find it, act on it, prove it โ€” the AWU way to write UI tests that stay green

Playwright + TypeScript ยท Writing Reliable Tests ยท Beginner โ†’ Intermediate

Before We Start โ€” What Playwright Fixes

Pain we used to live withHow Playwright removes it
Thread.sleep and hand-written waitsEvery action waits until the element is ready to receive it
Long, brittle XPath chainsLocators that read like the screen: role, label, text, test id
Testing one browser at a timeChromium, Firefox and WebKit through one API, plus device emulation
Assertions that check only onceAssertions that keep retrying until true or timed out
Five extra libraries per projectRunner, reports, traces, API client and mocking ship together

Step A ยท Get a Project Running

AutomateWithUs suite layout

awu-pw-suite/
โ”œโ”€โ”€ playwright.config.ts
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ specs/
โ”‚   โ”œโ”€โ”€ web/
โ”‚   โ”‚   โ”œโ”€โ”€ sign-in.spec.ts
โ”‚   โ”‚   โ””โ”€โ”€ enrolment.spec.ts
โ”‚   โ””โ”€โ”€ service/
โ”‚       โ””โ”€โ”€ courses.api.spec.ts
โ”œโ”€โ”€ screens/               # screen objects (POM)
โ”‚   โ”œโ”€โ”€ SignInScreen.ts
โ”‚   โ””โ”€โ”€ CatalogScreen.ts
โ”œโ”€โ”€ kit/
โ”‚   โ””โ”€โ”€ awu-test.ts        # extended test with custom fixtures
โ”œโ”€โ”€ fixtures/
โ”‚   โ””โ”€โ”€ learners.json      # test data
โ””โ”€โ”€ support/
    โ””โ”€โ”€ formatters.ts

Step B ยท Anatomy of a Test โ€” Arrange, Act, Assert

specs/web/enrolment.spec.ts
import { test, expect } from '@playwright/test';
 
test('learner can add a course to cart', async ({ page }) => {
  // Arrange
  await page.goto('/courses');
 
  // Act
  await page.getByRole('link', { name: 'API Automation' }).click();
  await page.getByRole('button', { name: 'Add to cart' }).click();
 
  // Assert
  await expect(page.getByTestId('cart-count')).toHaveText('1');
});
Most Common Mistake

Every Playwright call that touches the browser returns a Promise. Missing `await` is the #1 reason beginner tests pass locally and fail randomly.

Step C ยท Finding Elements โ€” the AWU Locator Ladder

RungLocatorAWU exampleFits best
1getByRole()page.getByRole('button', { name: 'Pay now' })Buttons, links, headings, checkboxes
2getByLabel()page.getByLabel('Mobile number')Form fields with labels
3getByPlaceholder()page.getByPlaceholder('Search courses')Inputs without labels
4getByText()page.getByText('Order confirmed')Non-interactive visible text
5getByAltText() / getByTitle()page.getByAltText('AWU logo')Images, tooltips
6getByTestId()page.getByTestId('cart-count')Stable dev-added hooks
7locator() CSS / XPathpage.locator('#coupon-code')Last resort

Narrowing and chaining

// Filter rows by text, then act inside the matching row
await page.getByRole('row')
  .filter({ hasText: 'Playwright Batch' })
  .getByRole('button', { name: 'Enroll' })
  .click();
 
// Position (use only when order is guaranteed)
await page.locator('.course-card').nth(2).click();
await page.locator('.course-card').last().click();
 
// Scope inside a section
const sidebar = page.getByRole('navigation', { name: 'Learner menu' });
await sidebar.getByRole('link', { name: 'My certificates' }).click();
Key Takeaway

Configure a custom test id attribute once in config โ€” use: { testIdAttribute: 'data-qa' } โ€” and getByTestId() will look for data-qa.

Step D ยท Interacting โ€” Grouped by Intent

IntentWhat to call
Navigateawait page.goto('/batches') ยท page.goBack() ยท page.reload()
Mouselocator.click() ยท dblclick() ยท click({ button: 'right' }) ยท hover()
Keyboardlocator.press('Tab') ยท pressSequentially('400708', { delay: 60 })
Text fieldslocator.fill('priya.k@awu.test') (clears, then sets the value)
Choiceslocator.check() ยท uncheck() ยท selectOption({ label: 'Navi Mumbai' })
Fileslocator.setInputFiles('fixtures/id-proof.pdf')
Move thingssourceCard.dragTo(doneColumn)
Evidencepage.screenshot({ path: 'evidence/learner-home.png', fullPage: true })

Step E ยท Proving It โ€” Assertions That Retry

An expect on a locator or page keeps re-checking until it passes or expect.timeout (5 s by default) runs out โ€” no manual waits needed.

What you want to proveAssertion
Right pageexpect(page).toHaveURL(/learner-home/) ยท toHaveTitle(/AutomateWithUs/)
Shown or gonetoBeVisible() ยท toBeHidden()
WordingtoHaveText('Enrolled') ยท toContainText('Batch')
Field contenttoHaveValue('400708')
StatetoBeEnabled() ยท toBeChecked()
How manytoHaveCount(3)
Attributes / styletoHaveAttribute('aria-expanded', 'true') ยท toHaveCSS('background-color', 'rgb(124, 181, 24)')
Keep going after a missexpect.soft(locator).toHaveText('โ€ฆ')
Interview Trap

expect(await locator.textContent()).toBe('Enrolled') does not retry โ€” it reads once. Prefer await expect(locator).toHaveText('Enrolled').

Step F ยท Screen Objects (POM) in TypeScript

screens/SignInScreen.ts
// screens/SignInScreen.ts
import { type Page, type Locator, expect } from '@playwright/test';
 
export class SignInScreen {
  readonly learnerEmail: Locator;
  readonly secretKey: Locator;
  readonly continueBtn: Locator;
  readonly alertBanner: Locator;
 
  constructor(private readonly page: Page) {
    this.learnerEmail = page.getByLabel('Learner email');
    this.secretKey = page.getByLabel('Account password');
    this.continueBtn = page.getByRole('button', { name: 'Continue to portal' });
    this.alertBanner = page.getByRole('alert');
  }
 
  async visit() {
    await this.page.goto('/sign-in');
  }
 
  async enterPortal(email: string, secret: string) {
    await this.learnerEmail.fill(email);
    await this.secretKey.fill(secret);
    await this.continueBtn.click();
  }
 
  async expectAlert(text: string) {
    await expect(this.alertBanner).toContainText(text);
  }
}
specs/web/sign-in.spec.ts
import { test } from '@playwright/test';
import { SignInScreen } from '../../screens/SignInScreen';
 
test('wrong secret shows the alert banner', async ({ page }) => {
  const signIn = new SignInScreen(page);
  await signIn.visit();
  await signIn.enterPortal('priya.k@awu.test', 'not-my-secret');
  await signIn.expectAlert('Email or password is incorrect');
});

Step G ยท Hooks & Keeping the Session

HookFires
test.describe()Not a hook โ€” groups specs so hooks can be scoped
test.beforeEach()Before each test in the group (most used)
test.afterEach()After each test โ€” attach evidence, clean data
test.beforeAll()Once per worker, before the group starts
test.afterAll()Once per worker, after the group ends
// Save the signed-in session
await page.context().storageState({ path: '.awu-session/learner-state.json' });
 
// Reuse it from config
use: { storageState: '.awu-session/learner-state.json' }

Step H ยท Owning the Network

Call an API inside a test

specs/service/courses.api.spec.ts
test('API: course catalogue is not empty', async ({ request }) => {
  const res = await request.get('/api/courses');
  expect(res.status()).toBe(200);
  const body = await res.json();
  expect(body.length).toBeGreaterThan(0);
});

Fake a response to test UI edge cases

specs/web/courses-mock.spec.ts
test('shows empty-state when no courses', async ({ page }) => {
  await page.route('**/api/courses', (route) =>
    route.fulfill({ status: 200, contentType: 'application/json', body: '[]' }),
  );
  await page.goto('/courses');
  await expect(page.getByText('No courses yet')).toBeVisible();
});
 
test('shows error banner on 500', async ({ page }) => {
  await page.route('**/api/courses', (route) => route.fulfill({ status: 500 }));
  await page.goto('/courses');
  await expect(page.getByRole('alert')).toContainText('Something went wrong');
});
Key Takeaway

Mocking lets you test states the real backend rarely produces โ€” empty lists, slow responses, server errors โ€” without touching test data.

Other built-in powers

Step I ยท Playwright MCP โ€” AI Meets the Browser

@playwright/mcp is Microsoft's MCP server that lets AI assistants (Claude, Copilot, Cursor and others) drive a real browser through the page's accessibility tree rather than screenshots. Useful for exploring an app, generating first-draft tests and quick checks.

MCP client config
{
  "mcpServers": {
    "playwright": { "command": "npx", "args": ["@playwright/mcp@latest"] }
  }
}
Watch Out

AI-generated tests are a starting point. Review locators, add real assertions and remove hard-coded data before committing.

Step J ยท Everyday Commands

CommandPurpose
npx playwright testRun every spec
npx playwright test enrolmentRun files matching "enrolment"
npx playwright test --headedWatch the browser
npx playwright test --project=desktop-chromeOne project
npx playwright test --grep "@sanity"Filter by tag/title
npx playwright test --uiInteractive UI Mode
npx playwright test --debugStep through with Inspector
npx playwright codegen https://โ€ฆRecord a test
npx playwright show-reportOpen HTML report
npx playwright show-trace trace.zipOpen a trace

Interview Questions & Answers

Q1. What is auto-waiting?

Before an action Playwright waits until the element is attached, visible, stable, enabled and able to receive events.

Q2. Why prefer getByRole?

It matches how users and assistive tech see the page, so it survives CSS/DOM refactors.

Q3. fill() vs pressSequentially()?

fill() sets the value at once; pressSequentially() types key by key โ€” needed when the app reacts to each keystroke.

Q4. What is a web-first assertion?

An expect on a locator/page that retries until the condition holds or times out.

Q5. locator vs elementHandle?

Locators are lazy and re-query the DOM each time (no stale elements); handles point to one fixed node โ€” avoid them.

Q6. How do you handle a new tab?

const p = await page.waitForEvent('popup') (or context.waitForEvent('page')) around the click that opens it.

Q7. How do you test a 500 error screen?

Mock the API with page.route() and route.fulfill({ status: 500 }).

Q8. What is storageState?

A JSON snapshot of cookies and localStorage used to start tests already signed in.

Q9. Context vs page?

A context is an isolated browser profile; a page is a tab inside it.

Q10. What is a trace?

A recording of actions, DOM snapshots, network and console you open in Trace Viewer to debug failures.

Want to learn this live?

Small batches, real projects, mock interviews included. Sit in on a free demo class first.

See all coursesBook a mock interview