How to Handle Date Pickers in Selenium and Playwright
Practice date picker automation — open calendars, select dates, navigate months, handle date ranges, constraints, and keyboard entry in Playwright, Selenium & Cypress.
Interactive Scenarios
Min: 2025-06-01 · Max: 2025-12-31
For your next visit
Expected return visit
The date below is rendered dynamically. Locate it without data-testid.
| ID | Scenario | Type | Priority | |
|---|---|---|---|---|
| DP_001 | Verify date can be typed into a native date input Expected: Input value equals the typed date string. ✅ positivehigh | positive | high | |
| DP_002 | Verify calendar opens on trigger click Expected: Calendar grid becomes visible after clicking the trigger. ✅ positivehigh | positive | high | |
| DP_003 | Verify a specific day can be selected from the calendar Expected: Clicking a day cell updates the displayed selected date. ✅ positivehigh | positive | high | |
| DP_004 | Verify next-month navigation updates the calendar header Expected: Month heading increments by one after clicking next. ✅ positivemedium | positive | medium | |
| DP_005 | Verify previous-month navigation updates the calendar header Expected: Month heading decrements by one after clicking previous. ✅ positivemedium | positive | medium | |
| DP_006 | Verify date range start and end inputs accept valid dates Expected: Both inputs hold the correct date strings and the range summary updates. ✅ positivehigh | positive | high | |
| DP_007 | Verify constrained input rejects dates before min Expected: Input is invalid or shows browser validation error when value < min. ❌ negativehigh | negative | high | |
| DP_008 | Verify constrained input rejects dates after max Expected: Input is invalid when value > max. ❌ negativehigh | negative | high | |
| DP_009 | Verify sibling date field located via XPath ancestor Expected: Input fills correctly when located through a label sibling. ✅ positivemedium | positive | medium | |
| DP_010 | Verify scoped booking card date button click Expected: Clicking Book inside the Morning card produces the correct result text. ✅ positivemedium | positive | medium | |
| DP_011 | Verify dynamic date display shows today's date Expected: The displayed date matches the current local date. ✅ positivemedium | positive | medium | |
| DP_012 | Verify date input is accessible via keyboard Tab navigation Expected: Date input receives focus when tabbed to. ✅ positivemedium | positive | medium | |
| DP_013 | Verify calendar day buttons have correct aria-label attributes Expected: Each day cell announces its full date to screen readers. ✅ positivelow | positive | low | |
| DP_014 | Verify clearing a date input resets the display Expected: After clearing, the result shows the empty/reset state. ⚠️ edgelow | edge | low |
Date pickers come in two forms: a plain HTML <input type="date"> and a custom calendar widget built with divs or dialog. The automation strategy differs significantly between the two — always inspect the DOM first to identify which type you are working with.
Native date inputs accept ISO-format strings (YYYY-MM-DD) directly via fill() or sendKeys(). They expose min, max, and value attributes that are easy to assert. Avoid using keyboard arrow keys to navigate — just set the value string directly.
// Fill a native date input
await page.getByTestId('dp-basic-input').fill('2025-06-15');
// Assert value
const val = await page.getByTestId('dp-basic-input').inputValue();
expect(val).toBe('2025-06-15');
// Clear
await page.getByTestId('dp-basic-input').fill('');Custom calendar widgets require click-based interaction: open the picker, navigate months with next/previous buttons, then click the target day cell. Scope all selectors inside the open calendar container to avoid matching hidden instances.
// Open the calendar
await page.getByTestId('dp-calendar-trigger').click();
// Navigate to next month
await page.getByTestId('dp-next-month').click();
// Select a specific day using data-date attribute
await page.locator('[data-testid="dp-day-btn"][data-date="2025-07-20"]').click();
// Assert selected date
await expect(page.locator('#result-s02')).toContainText('2025-07-20');Date range pickers expose two inputs — start and end. Fill them in order; some implementations reset the end date if you set it before the start. Assert the visual summary element after both inputs are filled.
// Fill date range
await page.getByTestId('dp-range-start').fill('2025-08-01');
await page.getByTestId('dp-range-end').fill('2025-08-15');
// Assert range summary
await expect(page.locator('#result-s04')).toContainText('2025-08-01');
await expect(page.locator('#result-s04')).toContainText('2025-08-15');Constrained inputs have min and max attributes that browsers enforce natively. Test the boundary: a date one day before min and one day after max should produce an invalid state. Use checkValidity() or assert aria-invalid.
// Assert min / max attributes
const min = await page.getByTestId('dp-constrained-input').getAttribute('min');
const max = await page.getByTestId('dp-constrained-input').getAttribute('max');
expect(min).toBeTruthy();
expect(max).toBeTruthy();
// Fill out-of-range date and check validity
await page.getByTestId('dp-constrained-input').fill('2020-01-01');
const isValid = await page.getByTestId('dp-constrained-input').evaluate(
(el: HTMLInputElement) => el.checkValidity()
);
expect(isValid).toBe(false);| Action | Selenium | Playwright JS | Playwright PY | Cypress |
|---|---|---|---|---|
| Fill date | sendKeys("YYYY-MM-DD") | fill("YYYY-MM-DD") | fill("YYYY-MM-DD") | .type("YYYY-MM-DD") |
| Get value | getAttribute("value") | inputValue() | input_value() | .invoke('val') |
| Get min | getAttribute("min") | getAttribute("min") | get_attribute("min") | .invoke('attr', 'min') |
| Get max | getAttribute("max") | getAttribute("max") | get_attribute("max") | .invoke('attr', 'max') |
| Clear input | clear() | fill("") | fill("") | .clear() |