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
Progress bars
Beginnerprogress-<id> is role=progressbar; read aria-valuenow and compare with raised-<id> / goal.
- 2
Chips vs custom
BeginnerTyping custom-amount clears the selected amount-* chip (aria-pressed=false) and vice versa.
- 3
Threshold field
Intermediatedonor-pan and pan-required only appear once the total reaches ₹2,000; the PAN format is validated.
- 4
Fee maths
Intermediatecover-fee adds 2.5%: ₹2,500 → fee-amount ₹62.50, donation-total ₹2,562.50.
- 5
Minimum amount
BeginnerA custom amount under ₹100 shows error-amount.
- 6
Conditional field
Beginnerin-honour (switch) reveals honoree-name, which is then required.
- 7
Recurring management
Intermediatepause-recurring → recurring-status Paused → resume/cancel via cancel-recurring-modal; year-total updates.
- 8
80G availability
IntermediateReceipts 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.
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');
});