</>

Technology

Playwright

Difficulty

Beginner

Interview Question

How do you write a Playwright test in TypeScript?

Answer

Writing Playwright Tests in TypeScript

TypeScript is the recommended and default language for Playwright. It provides full type safety, autocomplete, and catches errors at development time.

Project Setup

Bash
npm init playwright@latest
# Choose "TypeScript" (default)

Playwright runs TypeScript tests directly without a separate compilation step — it uses its own transpiler internally.

Basic TypeScript Test

TypeScript
// tests/login.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Login Flow', () => {

  test.beforeEach(async ({ page }) => {
    await page.goto('/login');
  });

  test('successful login', async ({ page }) => {
    await page.getByLabel('Email').fill('user@test.com');
    await page.getByLabel('Password').fill('pass123');
    await page.getByRole('button', { name: 'Sign in' }).click();

    await expect(page).toHaveURL('/dashboard');
    await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible();
  });
});

TypeScript Page Object Model

TypeScript
// pages/LoginPage.ts
import { Page, Locator } from '@playwright/test';

export class LoginPage {
  private readonly emailInput: Locator;
  private readonly passwordInput: Locator;
  private readonly submitButton: Locator;
  readonly errorMessage: Locator;

  constructor(private page: Page) {
    this.emailInput    = page.getByLabel('Email');
    this.passwordInput = page.getByLabel('Password');
    this.submitButton  = page.getByRole('button', { name: 'Sign in' });
    this.errorMessage  = page.locator('[data-testid="error-message"]');
  }

  async navigate(): Promise<void> {
    await this.page.goto('/login');
  }

  async login(email: string, password: string): Promise<void> {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }
}
TypeScript
// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';

test('login with typed POM', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.navigate();
  await loginPage.login('user@test.com', 'pass123');

  await expect(page).toHaveURL('/dashboard');
});

TypeScript Custom Fixtures

TypeScript
// fixtures.ts
import { test as base } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';
import { DashboardPage } from './pages/DashboardPage';

type Fixtures = {
  loginPage: LoginPage;
  dashboardPage: DashboardPage;
};

export const test = base.extend<Fixtures>({
  loginPage: async ({ page }, use) => {
    await use(new LoginPage(page));
  },
  dashboardPage: async ({ page }, use) => {
    await use(new DashboardPage(page));
  },
});

export { expect } from '@playwright/test';
TypeScript
// tests/dashboard.spec.ts
import { test, expect } from '../fixtures';

test('dashboard loads correctly', async ({ loginPage, dashboardPage }) => {
  await loginPage.navigate();
  await loginPage.login('admin@test.com', 'admin123');

  await expect(dashboardPage.title).toBeVisible();
});

TypeScript tsconfig.json

JSON
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "./dist"
  },
  "include": ["tests/**/*", "pages/**/*", "fixtures.ts"]
}

Key Advantage: TypeScript catches type mismatches in Page Objects at compile time — you get autocomplete for all Playwright APIs and your own page object methods.

Follow AutomateQA

Related Topics