PracticeQAAutomation logoPracticeQAAutomation
Pw

TypeScript · @playwright/test

Travel & Airline with Playwright

One-way or round trip with airport autocomplete and swap, passenger steppers with infant rules, filters and sort tabs, fare families, traveller forms, a seat map, payment and PNR.

App under test

✈️ SkyWays

URL
/apps/flights
Login
not required
Max travellers
9 (1 infant/adult)
Card
any 16 digits

Scenarios to automate

Work top to bottom — each one adds a new technique.

  1. 1

    Airport autocomplete

    Beginner

    Type in from-city, pick from-option-<code>; free text without picking fails search-errors.

  2. 2

    Swap & same-city rule

    Beginner

    swap-cities exchanges both fields; the same city on both sides is rejected.

  3. 3

    Passenger rules

    Intermediate

    infants-inc is disabled once infants = adults; the total never exceeds 9.

  4. 4

    Round trip dates

    Intermediate

    return-day chips before the departure date are disabled.

  5. 5

    Filters & sorting

    Intermediate

    Combine stops-*, airline-*, dep-* and max-fare; sort-fastest orders by duration.

  6. 6

    Fare families

    Intermediate

    select-flight expands fare-basic / fare-flex inside that card only.

  7. 7

    Seat map

    Advanced

    Pick seats with data-status="available"; selection auto-advances to the next traveller (assigned-seat-*).

  8. 8

    Round-trip booking

    Advanced

    Select outbound, then the return list appears; the ticket shows two ticket-leg rows.

Sample Playwright suite

A working starting point that finds elements every way Playwright can — by role, label, text, id, name, CSS, XPath and data-testid — so you practice them all. Pick your language, switch to data-testid only to compare, or try your own in the Locator Playground.

Language
skyways.spec.ts
import { test, expect } from '@playwright/test';

// playwright.config.ts → use: { baseURL: 'https://www.practiceqaautomation.com' }
// Locators in this suite: getByRole ×4 · getByLabel ×6 · getByPlaceholder ×1 · CSS ×7 · XPath ×3 · getByTestId ×9
test('books a one-way flight for two', async ({ page }) => {
  await page.goto('/apps/flights');
  await page.getByRole('button', { name: 'One way' }).click();
  await page.getByLabel('From').fill('Mum');
  await page.getByTestId('from-option-bom').click();
  await page.locator('#to-city').fill('Del');
  await page.getByTestId('to-option-del').click();
  await page.getByTestId('depart-day').nth(1).click();
  await page.getByTestId('passengers-toggle').click();
  await page.getByTestId('adults-inc').click();
  await page.locator('xpath=//button[normalize-space()="Done"]').click();
  await expect(page.getByTestId('passengers-summary')).toContainText('2 adults');
  await page.getByRole('combobox', { name: 'Cabin' }).selectOption('economy');
  await page.locator('xpath=//button[normalize-space()="Search flights"]').click();
  await expect(page).toHaveURL(/\/apps\/flights\/results/);
  await page.getByLabel('Non-stop').check();
  await page.getByRole('tab', { name: 'cheapest' }).click();
  await page.getByTestId('select-flight').nth(0).click();
  await page.getByTestId('fare-flex').click();
  await expect(page).toHaveURL(/\/apps\/flights\/passengers/);
  await page.locator('#pax-0-title').selectOption('mr');
  await page.locator('#pax-0-first').fill('Amir');
  await page.locator('#pax-0-last').fill('Khan');
  await page.locator('#pax-1-title').selectOption('ms');
  await page.locator('#pax-1-first').fill('Sara');
  await page.locator('#pax-1-last').fill('Khan');
  await page.getByLabel('Email').fill('amir@example.com');
  await page.getByLabel('Phone').fill('9876543210');
  await page.locator('xpath=//button[normalize-space()="Continue to seats"]').click();

  // Each pick auto-advances to the next traveller
  await page.locator('[data-testid^="seat-"][data-status="available"]').first().click();
  await page.locator('[data-testid^="seat-"][data-status="available"]').first().click();
  await page.getByRole('button', { name: 'Continue to payment' }).click();
  await page.getByLabel('Card number').fill('4242424242424242');
  await page.getByPlaceholder('MM/YY', { exact: true }).fill('1230');
  await page.getByLabel('CVV').fill('123');
  await page.getByTestId('pay-flight').click();
  await expect(page.getByTestId('ticket-confirmed')).toBeVisible();
  await expect(page.getByTestId('ticket-passengers')).toContainText('Sara Khan');
});

Practicing another tool?