PracticeQAAutomation logoPracticeQAAutomation
Pw

TypeScript · @playwright/test

Event ticketing with Playwright

Filter events, pick quantities across ticket tiers (sold out, only-N-left, max 5 per order), fill details per attendee with unique emails, apply a promo, pay and get QR tickets — or join a waitlist.

App under test

🎟️ EventHive

URL
/apps/events
Promo
EARLY10 (10%)
Booking fee
5%
Max per order
5 tickets

Scenarios to automate

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

  1. 1

    Filters & switch

    Beginner

    category-*, event-city, event-range and free-only combine; assert event-count.

  2. 2

    Stock & order cap

    Intermediate

    tier-*-inc disables at the tier's stock (VIP has 3) and when the order reaches 5 tickets.

  3. 3

    Sold-out states

    Beginner

    tier-sold-out-early shows on TestCon; Design Systems Meetup is fully sold out and shows the waitlist.

  4. 4

    Dynamic attendee forms

    Intermediate

    One attendee-N block per ticket; first-is-buyer copies your details into ticket 1.

  5. 5

    Unique emails

    Intermediate

    Two attendees with the same email show error-unique-emails.

  6. 6

    Totals maths

    Advanced

    Check ticket-subtotal, event-discount (10%), event-fees (5% after discount) and event-total.

  7. 7

    Free registration

    Beginner

    The free workshop skips card fields; the button says Register.

  8. 8

    Waitlist position

    Intermediate

    join-waitlist validates the email and shows waitlist-joined with your position.

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
eventhive.spec.ts
import { test, expect } from '@playwright/test';

// playwright.config.ts → use: { baseURL: 'https://www.practiceqaautomation.com' }
// Locators in this suite: getByRole ×2 · getByLabel ×4 · getByPlaceholder ×2 · CSS ×6 · XPath ×2 · getByTestId ×10
test('buys tickets for three attendees', async ({ page }) => {
  await page.goto('/apps/events');
  await page.getByRole('button', { name: 'tech' }).click();
  await expect(page.getByTestId('event-count')).toContainText('2 events');
  await page.getByTestId('open-event-e1').click();
  await expect(page.getByTestId('tier-sold-out-early')).toBeVisible();
  await page.getByTestId('tier-regular-inc').click();
  await page.getByTestId('tier-regular-inc').click();
  await page.getByTestId('tier-vip-inc').click();
  await expect(page.getByTestId('ticket-count')).toContainText('3');
  await expect(page.getByTestId('ticket-subtotal')).toContainText('₹10,997');
  await page.locator('xpath=//button[normalize-space()="Continue"]').click();
  await page.getByLabel('Full name').fill('Amir Khan');
  await page.locator('#buyer-email').fill('amir@example.com');
  await page.getByLabel('Phone').fill('9876543210');
  await page.locator('#first-is-buyer').check();
  await page.locator('#attendee-1-name').fill('Sara Khan');
  await page.locator('#attendee-1-email').fill('sara@example.com');
  await page.locator('#attendee-2-name').fill('Dev Patel');
  await page.locator('#attendee-2-email').fill('dev@example.com');
  await page.getByPlaceholder('Promo code', { exact: true }).fill('EARLY10');
  await page.getByRole('button', { name: 'Apply' }).click();
  await expect(page.getByTestId('event-total')).toContainText('₹10,392');
  await page.getByLabel('Card number').fill('4242424242424242');
  await page.getByPlaceholder('MM/YY', { exact: true }).fill('1230');
  await page.getByLabel('CVV').fill('123');
  await page.locator('xpath=//button[normalize-space()="Pay & get tickets"]').click();
  await expect(page.getByTestId('tickets-issued')).toBeVisible();
  await expect(page.getByTestId('ticket-holder').nth(2)).toContainText('Dev Patel');
});

Practicing another tool?