VALIDATION LAB•DATE RANGE PICKER
Date & Files
Date Range Picker
Set start/end times, pick day span in two taps, and validate the pill shows `HH:MM - DD/MM/YYYY → …` including auto-swap.
Scenario
Booking, analytics filters, payroll periods. Bugs hide in: the inclusive/exclusive end day, the leap-year boundary, the swap-on-pick logic, and the empty 'reset' state.
Pick date & time range
Live widget · interact freely
Manual test checklist
- 1Pick days 5 and 12 with default clocks — pill shows `HH:MM - 05/MM/YYYY → HH:MM - 12/MM/YYYY`
- 2Flip end earlier than start (tap 12 then 5) — times stay attached but days swap logically
- 3Change only `range-start-time` — left side updates, calendar selection unchanged until you pick again
- 4Reset wipes both anchors but keeps spinner defaults intact
Expected result
First tap is start, second is end; each side carries its clock. If second day < first, endpoints swap.
Automation challenge
Hardcode `range-start-time` / `range-end-time` inputs, tap `range-cell-12` then `range-cell-5`, assert the chip regex shows `/05\/\d{2}\/\d{4}.*→.*12\/` DD segments after swap.
Stable selectors
- Open range
[data-testid="range-open"] - Start time
[data-testid="range-start-time"] - End time
[data-testid="range-end-time"] - Range chip
[data-testid="range-chip"] - Range cell template
[data-testid="range-cell-{n}"] - Reset
[data-testid="range-reset"]
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.
12345
await page.goto('https://lab.hakdogan.com/practice/date-range');
await page.locator('#range-open').click();
await page.locator('#range-cell-12').click();
await page.locator('#range-cell-5').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
12345678910111213
import { test, expect } from '@playwright/test';
test('date range auto-swaps when end < start', async ({ page }) => {
await page.goto('https://lab.hakdogan.com/practice/date-range');
await page.getByTestId('range-open').click();
await page.getByTestId('range-cell-12').click();
await page.getByTestId('range-cell-5').click();
await expect(page.getByTestId('range-chip')).toHaveText(
/\d{2}:\d{2} - 05\/\d{2}\/\d{4}\s*→\s*\d{2}:\d{2} - 12\/\d{2}\/\d{4}/,
);
});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.
date-range.spec.ts- Interacting with date range picker widget
- Awaiting deterministic render
- Asserting expected state
- Cleaning up context
- Pass