VALIDATION LABPAYMENT FORM

Form Lab

Payment Form

Test a payment form with clearly labeled test card data only, covering success, declined card, authentication-required, expiry, CVV, ZIP, and save-card behavior.

Advanced

Scenario

Payment forms are high-risk. QA must never use real card data, and automated tests should cover card formatting, decline handling, authentication states, and success feedback using provider-approved test cards.

Live interactive form

Payment Form

Payment validation using provider-style test card numbers only.

Test card data only. Do not enter real card information.

Validation result panel

Awaiting submission.

Live widget · interact freely

Manual test checklist

  • 1Read the visible warning before typing card details
  • 2Submit an empty form and confirm required errors
  • 3Use 4242 4242 4242 4242 and confirm successful payment
  • 4Use 4000 0000 0000 0002 and confirm declined card error
  • 5Use 4000 0025 0000 3155 and confirm authentication required
  • 6Enter invalid CVV and expired date values and confirm validation errors

Expected result

Only test card numbers are accepted in the playground. Successful test payment shows a success message; declined and authentication-required cards show visible errors.

Automation challenge

Use only the labeled test card numbers. Fill card fields by label, use getByText() for the warning, assert declined-card errors, then assert the success toast for 4242.

Stable selectors

  • Payment form[data-testid="form-payment-form"]
  • Test card warning[data-testid="form-payment-test-card-warning"]
  • Cardholder Name[data-testid="form-payment-cardholder-name"]
  • Card Number[data-testid="form-payment-card-number"]
  • Expiry Date[data-testid="form-payment-expiry-date"]
  • CVV[data-testid="form-payment-cvv"]
  • Billing ZIP[data-testid="form-payment-billing-zip"]
  • Save card[data-testid="form-payment-save-card"]
  • Validation result[data-testid="form-payment-result"]

Locator strategy

Three levels from simple IDs to scoped Playwright locators. IDs and names are easy to learn but are not always the best long-term choice when labels change or components repeat.

Use simple IDs and names to understand how locating elements works. Form Lab pages expose predictable field ids and data-testid values for every control.

1
2
3
4
await page.goto('https://lab.hakdogan.com/practice/form-payment');

await page.locator('[data-testid="form-payment-form"]').waitFor();
await page.locator('[data-testid="form-payment-submit"]').click();

Avoid as primary strategies

XPath (unless there is no alternative), long CSS chains, Tailwind-style utility class selectors, generated or unstable IDs, and volatile framework internals break when layout, styling, or DOM structure shifts.

1
2
await page.locator('.w-full.rounded-xl.border.bg-blue-500').fill('demo@example.com');
await page.locator('//div[2]/form/div[1]/input').fill('demo@example.com');

Reference Playwright spec

form-payment.spec.ts
ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { test, expect } from '@playwright/test';

test.describe('form lab — payment form', () => {
  test('successful test card payment', async ({ page }) => {
    await page.goto('https://lab.hakdogan.com/practice/form-payment');

    await expect(page.getByText(/test card data only/i)).toBeVisible();
    await page.getByLabel('Cardholder Name').fill('QA Test');
    await page.getByLabel('Card Number').fill('4242 4242 4242 4242');
    await page.getByLabel('Expiry Date').fill('12/30');
    await page.getByLabel('CVV').fill('123');
    await page.getByLabel('Billing ZIP').fill('34000');
    await page.getByLabel(/save card/i).check();
    await page.getByRole('button', { name: /pay with test card/i }).click();

    await expect(page.getByText(/payment successful/i)).toBeVisible();
  });

  test('declined test card shows error', async ({ page }) => {
    await page.goto('https://lab.hakdogan.com/practice/form-payment');

    await page.getByLabel('Cardholder Name').fill('QA Test');
    await page.getByLabel('Card Number').fill('4000 0000 0000 0002');
    await page.getByLabel('Expiry Date').fill('12/30');
    await page.getByLabel('CVV').fill('123');
    await page.getByLabel('Billing ZIP').fill('34000');
    await page.getByRole('button', { name: /pay with test card/i }).click();

    await expect(page.getByText(/declined card/i)).toBeVisible();
  });
});

Headed Test Playback

Simulated headed-browser flow — no real browser is launched.

Automation-style playback (Playwright-shaped logs). No real browser; no commands run on your machine.

Idleform-payment.spec.ts
Failure demos
https://lab.hakdogan.com/practice/form-payment
playwright · headed · chromium0.00s
# awaiting Run Test · terminal scrolls automatically
Steps
  1. Read test-card warning
  2. Fill test card data
  3. Submit test payment
  4. Assert success or decline message
StatusIdle
BrowserChromium
FrameworkPlaywright + TypeScript
Elapsed (live)--
Specform-payment.spec.ts