PracticeQAAutomation logoPracticeQAAutomation
Pw

TypeScript · @playwright/test

Ride hailing with Playwright

Pickup and drop autocomplete with swap, fares per vehicle computed from distance, a promo, then a live ride that moves from searching to completed — with OTP, a cancel fee rule, rating and tip.

App under test

🚕 RideGo

URL
/apps/ride
Promo
FIRSTRIDE (50%, max ₹75)
Stages
2s · 5s · 8s · 11s
Cancel fee
₹30 after arrival

Scenarios to automate

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

  1. 1

    Autocomplete

    Beginner

    Type in pickup-input / drop-input and choose *-option-<place>; the same place can't be both.

  2. 2

    Swap & shortcut

    Beginner

    use-home fills pickup; swap-locations exchanges both inputs and redraws ride-map.

  3. 3

    Fare maths

    Intermediate

    fare-<vehicle> = base + per-km × distance + per-min × time; check against trip-distance.

  4. 4

    Promo cap

    Intermediate

    FIRSTRIDE halves the fare but caps at ₹75 — fare-payable reflects the cap.

  5. 5

    Live status

    Advanced

    ride-status walks through all five stages on a timer — chain text waits, don't sleep.

  6. 6

    Cancel rules

    Advanced

    Cancel before arrival is free; after “Driver arrived” cancel-fee-warning shows ₹30 and cancel-fee records it.

  7. 7

    Moving marker

    Intermediate

    During the trip, map-car moves along the route (SVG text element).

  8. 8

    Rate & tip

    Beginner

    After completion pick rate-driver-* and tip-*; trip-total includes the tip.

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

// playwright.config.ts → use: { baseURL: 'https://www.practiceqaautomation.com' }
// Locators in this suite: getByRole ×2 · getByLabel ×1 · getByPlaceholder ×1 · CSS ×1 · XPath ×2 · getByTestId ×14
test('books a cab and completes the trip', async ({ page }) => {
  await page.goto('/apps/ride');
  await page.getByLabel('Pickup').fill('Tech');
  await page.getByTestId('pickup-option-office').click();
  await page.getByPlaceholder('Where to?', { exact: true }).fill('air');
  await page.getByTestId('drop-option-airport').click();
  await expect(page.getByTestId('trip-distance')).toContainText('24.7 km');
  await page.getByTestId('ride-mini').click();
  await page.locator('#ride-promo').fill('FIRSTRIDE');
  await page.getByRole('button', { name: 'Apply' }).click();
  await expect(page.getByTestId('fare-payable')).toContainText('₹432');
  await page.getByRole('combobox', { name: 'Payment' }).selectOption('upi');
  await page.locator('xpath=//button[normalize-space()="Book Mini"]').click();

  // The ride progresses on its own — wait for each status
  await expect(page.getByTestId('ride-status')).toContainText('Driver assigned');
  await expect(page.getByTestId('ride-otp')).toBeVisible();
  await expect(page.getByTestId('ride-status')).toContainText('Driver arrived');
  await expect(page.getByTestId('ride-status')).toContainText('On trip');
  await expect(page.getByTestId('ride-status')).toContainText('Completed');
  await page.getByTestId('rate-driver-5').click();
  await page.getByTestId('tip-20').click();
  await page.locator('xpath=//button[normalize-space()="Submit"]').click();
  await expect(page.getByTestId('rating-saved')).toBeVisible();
  await expect(page.getByTestId('trip-total')).toContainText('₹452');
});

Practicing another tool?