PracticeQAAutomation logoPracticeQAAutomation
Pw

TypeScript · @playwright/test

NGO & donations with Playwright

Campaigns with progress bars, amount chips or a custom amount, one-time vs monthly, an optional fee top-up, PAN required at ₹2,000+ for an 80G certificate, anonymous and in-honour gifts, and pause/cancel for monthly giving.

App under test

💛 GiveHope

URL
/apps/donate
Minimum
₹100
PAN needed
₹2,000 and above
Fee cover
2.5%

Scenarios to automate

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

  1. 1

    Progress bars

    Beginner

    progress-<id> is role=progressbar; read aria-valuenow and compare with raised-<id> / goal.

  2. 2

    Chips vs custom

    Beginner

    Typing custom-amount clears the selected amount-* chip (aria-pressed=false) and vice versa.

  3. 3

    Threshold field

    Intermediate

    donor-pan and pan-required only appear once the total reaches ₹2,000; the PAN format is validated.

  4. 4

    Fee maths

    Intermediate

    cover-fee adds 2.5%: ₹2,500 → fee-amount ₹62.50, donation-total ₹2,562.50.

  5. 5

    Minimum amount

    Beginner

    A custom amount under ₹100 shows error-amount.

  6. 6

    Conditional field

    Beginner

    in-honour (switch) reveals honoree-name, which is then required.

  7. 7

    Recurring management

    Intermediate

    pause-recurring → recurring-status Paused → resume/cancel via cancel-recurring-modal; year-total updates.

  8. 8

    80G availability

    Intermediate

    Receipts with a PAN show download-80g; smaller gifts show no-80g.

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

// playwright.config.ts → use: { baseURL: 'https://www.practiceqaautomation.com' }
// Locators in this suite: getByRole ×3 · getByLabel ×4 · CSS ×3 · XPath ×2 · getByTestId ×13
test('sets up a monthly donation and pauses it', async ({ page }) => {
  await page.goto('/apps/donate');
  await page.getByRole('button', { name: 'education' }).click();
  await page.locator('xpath=//a[normalize-space()="Donate"]').click();
  await expect(page.getByTestId('campaign-title')).toContainText('Books for 500 rural classrooms');
  await page.getByRole('button', { name: 'Monthly' }).click();
  await page.getByTestId('amount-2500').click();
  await page.locator('#cover-fee').check();
  await expect(page.getByTestId('fee-amount')).toContainText('₹62.50');
  await expect(page.getByTestId('donation-total')).toContainText('₹2,562.50');
  await page.getByLabel('Full name').fill('Amir Khan');
  await page.getByLabel('Email').fill('amir@example.com');
  await page.locator('#donor-phone').fill('9876543210');
  await expect(page.getByTestId('pan-required')).toBeVisible();
  await page.locator('#donor-pan').fill('ABCDE1234F');
  await page.getByRole('switch', { name: 'Give in honour of someone' }).click();
  await page.getByLabel('In honour of', { exact: true }).fill('Grandma Fatima');
  await page.getByTestId('pay-upi').click();
  await page.getByLabel('UPI ID').fill('amir@okbank');
  await page.getByTestId('donate-now').click();
  await expect(page.getByTestId('receipt-amount')).toContainText('₹2,562.50/month');
  await expect(page.getByTestId('download-80g')).toBeVisible();
  await page.getByTestId('manage-giving').click();
  await expect(page.getByTestId('recurring-status')).toContainText('Active');
  await expect(page.getByTestId('year-total')).toContainText('₹30,750.00');
  await page.locator('xpath=//button[normalize-space()="Pause"]').click();
  await expect(page.getByTestId('recurring-status')).toContainText('Paused');
});

Practicing another tool?