VALIDATION LABMULTI-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.

Advanced

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.

1
2
3
4
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.

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-multi-step.spec.ts
ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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.

Idleform-multi-step.spec.ts
Failure demos
https://lab.hakdogan.com/practice/form-multi-step
playwright · headed · chromium0.00s
# awaiting Run Test · terminal scrolls automatically
Steps
  1. Attempt blocked Next
  2. Fill personal info
  3. Navigate to Address
  4. Go Back and preserve state
  5. Complete wizard
StatusIdle
BrowserChromium
FrameworkPlaywright + TypeScript
Elapsed (live)--
Specform-multi-step.spec.ts