Playwright Automation with TypeScript: A Modern Approach

November 20, 2024

Playwright
TypeScript
Automation
Testing
Development

Playwright Automation with TypeScript: A Modern Approach

Playwright has revolutionized browser automation with its powerful API and excellent TypeScript support. Let's explore why this combination is a game-changer for modern test automation.

Why Playwright + TypeScript?

The combination of Playwright and TypeScript offers several compelling advantages:

  • Type Safety: Catch errors at compile time, not runtime
  • IntelliSense: Get intelligent code completion in your IDE
  • Refactoring: Safely rename and restructure your test code
  • Better Tooling: Enhanced debugging and code navigation

Getting Started

First, install Playwright with TypeScript support:

bash
1npm init playwright@latest

This command sets up a complete TypeScript project with:

  • Playwright test runner
  • TypeScript configuration
  • Example tests
  • GitHub Actions workflow

Writing Your First Test

Here's a simple but powerful test example:

typescript
1import { test, expect } from '@playwright/test';
2
3test('homepage has correct title', async ({ page }) => {
4  await page.goto('https://example.com');
5  
6  // Type-safe selectors with IntelliSense
7  const title = await page.title();
8  expect(title).toContain('Example Domain');
9  
10  // Auto-waiting and retry logic built-in
11  await expect(page.locator('h1')).toBeVisible();
12});

Advanced Patterns

Page Object Model with TypeScript

Create type-safe page objects:

typescript
1export class LoginPage {
2  constructor(private page: Page) {}
3  
4  readonly emailInput = this.page.locator('#email');
5  readonly passwordInput = this.page.locator('#password');
6  readonly submitButton = this.page.locator('button[type="submit"]');
7  
8  async login(email: string, password: string): Promise<void> {
9    await this.emailInput.fill(email);
10    await this.passwordInput.fill(password);
11    await this.submitButton.click();
12  }
13}

Custom Fixtures

Extend Playwright's capabilities with typed fixtures:

typescript
1import { test as base } from '@playwright/test';
2import { LoginPage } from './pages/login-page';
3
4type CustomFixtures = {
5  loginPage: LoginPage;
6};
7
8export const test = base.extend<CustomFixtures>({
9  loginPage: async ({ page }, use) => {
10    await use(new LoginPage(page));
11  },
12});

Best Practices

1. Use Strict Selectors

typescript
1// Good - specific and maintainable
2await page.locator('button[data-testid="submit-button"]').click();
3
4// Avoid - fragile and unclear
5await page.locator('div > button:nth-child(3)').click();

2. Leverage Auto-Waiting

Playwright automatically waits for elements to be actionable:

typescript
1// No need for manual waits!
2await page.locator('#button').click();
3// Playwright waits for element to be visible, enabled, and stable

3. Use TypeScript Enums for Test Data

typescript
1enum UserRole {
2  Admin = 'admin',
3  User = 'user',
4  Guest = 'guest',
5}
6
7test('admin can access dashboard', async ({ page }) => {
8  await loginAs(page, UserRole.Admin);
9  await expect(page.locator('#admin-panel')).toBeVisible();
10});

Debugging TypeScript Tests

Playwright offers excellent debugging tools:

bash
1# Debug with Playwright Inspector
2npx playwright test --debug
3
4# Generate trace files for post-mortem debugging
5npx playwright test --trace on

Real-World Example: E2E Shopping Flow

typescript
1test.describe('Shopping Cart Flow', () => {
2  test('user can complete purchase', async ({ page }) => {
3    // Navigate and add product
4    await page.goto('/products');
5    await page.locator('[data-product-id="123"]').click();
6    await page.locator('button:has-text("Add to Cart")').click();
7    
8    // Verify cart update
9    await expect(page.locator('.cart-count')).toHaveText('1');
10    
11    // Complete checkout
12    await page.locator('.cart-icon').click();
13    await page.locator('button:has-text("Checkout")').click();
14    
15    // Fill shipping info with type-safe data
16    await page.fill('#email', 'customer@example.com');
17    await page.fill('#card-number', '4242424242424242');
18    
19    // Submit and verify
20    await page.locator('button:has-text("Place Order")').click();
21    await expect(page.locator('.success-message')).toBeVisible();
22  });
23});

Performance Optimization

Parallel Execution

typescript
1// playwright.config.ts
2export default defineConfig({
3  workers: process.env.CI ? 2 : undefined,
4  fullyParallel: true,
5});

Test Sharding for CI/CD

bash
1# Split tests across multiple machines
2npx playwright test --shard=1/3
3npx playwright test --shard=2/3
4npx playwright test --shard=3/3

Conclusion

Playwright with TypeScript provides a powerful, type-safe approach to browser automation. The combination offers:

āœ… Compile-time error checking āœ… Excellent IDE support āœ… Maintainable test code āœ… Fast and reliable test execution

Whether you're building end-to-end tests, scraping websites, or automating browser tasks, Playwright + TypeScript is an excellent choice for modern development workflows.

Next Steps

  • Explore Playwright's visual regression testing
  • Integrate with CI/CD pipelines
  • Learn about API testing with Playwright
  • Master advanced selectors and locators

Happy automating! šŸŽ­


Was this helpful?

0

0

0


Comments (0)

Join the Discussion

Sign in to share your thoughts and connect with other readers