VALIDATION LAB•MULTI-STEP FORM
Form Lab
Multi-Step Form
Test a five-step wizard with personal info, address, preferences, review, submit, Next/Back controls, step validation, and progress indicator.
Scenario
Multi-step forms fail at boundaries: skipped validation, lost state, broken progress, and review pages that submit stale data. QA should assert each transition, not only the final button.
Live interactive form
Multi-Step Form
Step 1 of 5: Personal Info
Validation result panel
Complete required fields before continuing.
Live widget · interact freely
Manual test checklist
- 1Try Next on step 1 with empty fields and confirm it is blocked
- 2Fill Personal Info and proceed to Address
- 3Use Back and confirm data is preserved
- 4Complete Address and Preferences and verify progress updates
- 5Review the summary before submitting
- 6Submit and confirm the success toast
Expected result
Next is blocked when required fields are invalid; Back preserves previous data; final submit shows a success toast after all steps are valid.
Automation challenge
Assert the progress indicator, blocked Next behavior, Back preservation, and final success using getByLabel(), getByRole(), getByTestId(), check(), and selectOption().
Stable selectors
- Wizard
[data-testid="form-multi-step-form"] - Progress
[data-testid="form-multi-step-progress"] - Next
[data-testid="form-multi-step-next"] - Back
[data-testid="form-multi-step-back"] - Submit
[data-testid="form-multi-step-submit"] - Validation result
[data-testid="form-multi-step-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.
1234
await page.goto('https://lab.hakdogan.com/practice/form-multi-step');
await page.locator('[data-testid="form-multi-step-form"]').waitFor();
await page.locator('[data-testid="form-multi-step-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.
12
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
12345678910111213141516
import { test, expect } from '@playwright/test';
test('multi-step form validates each step', async ({ page }) => {
await page.goto('https://lab.hakdogan.com/practice/form-multi-step');
await page.getByRole('button', { name: /next/i }).click();
await expect(page.getByText(/first name is required/i)).toBeVisible();
await page.getByLabel('First Name').fill('Hasan');
await page.getByLabel('Email').fill('hasan@lab.dev');
await page.getByRole('button', { name: /next/i }).click();
await expect(page.getByTestId('form-multi-step-progress')).toContainText('Step 2 of 5');
await page.getByRole('button', { name: /back/i }).click();
await expect(page.getByLabel('First Name')).toHaveValue('Hasan');
});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.
form-multi-step.spec.ts- Attempt blocked Next
- Fill personal info
- Navigate to Address
- Go Back and preserve state
- Complete wizard