Power Your Software Testing with AI Agents and Cloud

The Native AI-Agentic Cloud Platform to Supercharge Quality Engineering. Test Intelligently and Ship Faster.

Testing

Playwright tutorial: Automating Date Pickers

Read this tutorial to learn how to automate date pickers or calendars in the Playwright automation framework.

Last Updated on:

Date pickers are one essential UI for many websites where we pick data by writing or selecting. And if it does not function well, data can get fumbled; you don't want problems like someone’s birth year registered on your website being 2024 while 2022 is ongoing, right? Automating date pickers can be tricky however, Playwright frameworks provide various methods to automate date pickers for web automation easily.

Playwright is an automation tool for an end to end testing of websites on browsers such as Chromium, Firefox, and WebKit. Playwright automation framework is gaining more and more popularity in the testing domain. One survey by the State of JS shows that Playwright’s awareness has gone up to 34% in a mere span of 2 years since it was made live by Microsoft in 2020. It is because Playwright is faster than Selenium or other frameworks, Supports various languages such as JavaScript, Python, and .NET, makes it more favorable to developers, and is deployed by Microsoft, which gives unshaken credibility.

In this Playwright tutorial, you will learn two different methods for automating date pickers or calendars using the Playwright automation framework and with the help of TestMu AI’s Selenium Playground.

TL;DR

  • Some date pickers accept typed text, while others are calendar widgets a test has to click through.
  • Playwright can fill a date input directly when the field accepts text in a known format.
  • Clicking through a calendar means locating the month and year controls, then the day cell.
  • Moment.js compares the target month with the one the calendar shows to decide whether to click previous or next.
  • A loop over the month and year header text reaches any month without hardcoding how many clicks it takes.
  • Running the same date picker suite in parallel on a cloud grid covers browsers you do not have locally.

Automating date pickers with the fill method

fill() command in Playwright is used to fill the field with some test data. It is used for inputting values in inboxes, text areas, and other elements of web pages. You can add a locator of an element, and it will prompt an input event with given test data.

TestMu AI Selenium playground provides a date picker demo with two calendars, both using the "dd/mm/yyyy" date format. The first is a single date field and the second is a start and end date range. Both open a Bootstrap calendar that you operate with clicks, which the next section covers.

Bootstrap Date Pickers Demo page on the TestMu AI Selenium playground with a Select Date field and a Select Date Range with Start date and End date fields

fill() works when the field itself holds the date as text, such as a native date input (<input type="date">). It does not work on the two demo calendars. The first field is read-only, so fill() times out on it. The range fields accept typed text, but the widget listens for key presses, which fill() does not send, so a filled value disappears when the calendar closes. The sample below uses page.setContent() to build a page with one native date input, so it runs without any site.

Automating date pickers with the fill() command is an easier and short method. All you have to do is pass the date with the fill() function. Here are the steps for this method.

Test scenario:

  • Load a page that has a native date input.
  • Add a date in the inbox and pass the text.

Here’s the test script for automating the date picker with the fill() command.


import { test, expect } from "@playwright/test";

test("Calendar demo using fill function", async ({ page }) => {
  await page.setContent('<label for="birthday">Birthday:</label> <input type="date" id="birthday">');
  const date = "2026-01-13";

  const birthday = page.getByLabel("Birthday");
  await birthday.fill(date);
  await expect(birthday).toHaveValue(date);
});

Here are step-by-step explanations of how it has been done.

Step 1: Import test and expect from Playwright. Create a test block and load the page. Here page.setContent() writes a page with one native date input.

Step 2: Store the test data in a variable. The value is a string in the "YYYY-MM-DD" format.


test("Calendar demo using fill function", async ({ page }) => {
  await page.setContent('<label for="birthday">Birthday:</label> <input type="date" id="birthday">');
  const date = "2026-01-13";

Step 3: Now, to add that data to an inbox, locate the field by its label and use the fill() command to pass the test data. Then assert the value with toHaveValue() so the test fails if the date did not go in.


  const birthday = page.getByLabel("Birthday");
  await birthday.fill(date);
  await expect(birthday).toHaveValue(date);
});

Note: You can get a failure while testing because date formation is not similar to the expected format.

To search the expected format for the date in a webpage, go to the console log and type this:


document.getElementById("birthday").value

It will give you value that is present in a particular ID.

In this example, our date formation is set as "YYYY-MM-DD." A native date input always returns its value in that format, whatever format the browser displays, so use the same formation while adding test data.

Run the test with the --headed flag to watch the browser, or with --debug to step through it in the Playwright Inspector.

Test across 3000+ browser and OS environments with TestMu AI

Automating date pickers with Playwright and Moment.js

Moment.js is an open and free JavaScript tool that helps to tackle issues with date and time. It is a package of native JavaScript date objects which helps to manipulate or display date/time in JavaScript easily. The Moment.js documentation now describes it as a legacy project in maintenance mode. It still does the month comparison this tutorial needs.

The fill method is easier, where we just inserted data in the inbox, and we got results. However, to create a perfect test case for date pickers, there is another method where we will use the previous and next buttons to select months and years and perform a clicking action on a date. Here we will understand, step by step, how to pick dates from the current month, the previous month, and the next month.

Test scenario:

  • Go to the Bootstrap Date Pickers page on TestMu AI Selenium Playground.
  • Open the calendar with click action on the Start date field.
  • Find a date in the calendar for that month.

Test script for automating date pickers with the moment method:


import { test, expect } from "@playwright/test";

test("Pick a date from the previous month", async ({ page }) => {
  await page.clock.setFixedTime(new Date("2026-01-13T10:00:00"));
  await page.goto("https://www.testmuai.com/selenium-playground/bootstrap-date-picker-demo");

  const startDate = page.getByPlaceholder("Start date");
  const calendar = page.locator(".datepicker-days");

  await expect(async () => {
    await startDate.blur();
    await startDate.click();
    await expect(calendar).toBeVisible({ timeout: 1000 });
  }).toPass();

  const mmYY = calendar.locator(".datepicker-switch");
  const prev = calendar.locator(".prev");
  const next = calendar.locator(".next");

  await prev.click();
  await calendar.locator("td.day:not(.old):not(.new)").filter({ hasText: /^4$/ }).click();

  await expect(startDate).toHaveValue("04/12/2025");
});

Here are the steps for performing the test for the previous month:

Step 1: Define the URL with the help of the goto command. Before that, the sample fixes the browser's date at 13 January 2026 with page.clock.setFixedTime(). The calendar opens on today's month, so without a fixed date the previous month depends on the day you run the test, and the test fails whenever the 4th of that month is a Sunday, because this calendar disables Sundays.

Step 2: Now, for picking the date, you need first to perform a click action on the inbox. Find its element from inspect and perform a click action to open the calendar. The page attaches the calendar with a script that loads after the HTML, and the calendar opens only when the field gains focus. A click that lands before the script is ready focuses the field and opens nothing. So the sample puts three steps inside expect().toPass(), which runs them again until all three pass: remove focus, click, and check that the calendar is visible.


const startDate = page.getByPlaceholder("Start date");
const calendar = page.locator(".datepicker-days");

await expect(async () => {
  await startDate.blur();
  await startDate.click();
  await expect(calendar).toBeVisible({ timeout: 1000 });
}).toPass();

Step 3: When the calendar opens, select the month. For that, first, grab elements for the “previous button,” “next button,” and “date picker switch”.

Elements for date picker switch:

Inspect the date picker switch because there are five different date picker buttons. The calendar has one view each for days, months, years, decades and centuries, and every view has its own switch. Start from the days view, .datepicker-days, so the locator picks the specific switch you are testing on.


const mmYY = calendar.locator(".datepicker-switch");

Here the calendar locator defines the days view where the button is, and the chained locator defines the button.

Elements for previous/next buttons:

Elements for the previous and next buttons are in the same table; simply make changes in class for the previous and next buttons.


const prev = calendar.locator(".prev");
const next = calendar.locator(".next");

Step 4: Now, for example, you want to select the 4th date of the previous month. For that, you have to perform the click action on the previous button and then click for the date 4th.


await prev.click();
await calendar.locator("td.day:not(.old):not(.new)").filter({ hasText: /^4$/ }).click();

await expect(startDate).toHaveValue("04/12/2025");

Note: While finding the cell for a date, there is a text "4" for every month, but their "class" will be different: for the displayed month it will be "day", for the previous month "old day", and for the next month "new day". We are focusing on the displayed month, so the locator leaves out "old" and "new". Today's cell has the class "today day" and a selected cell also carries "active", so an exact match on the whole class attribute misses both. This calendar also disables Sundays, and a click on a disabled cell selects nothing. The sample asserts the value of the field to catch that.

This one was a basic idea of this test, to check it ran the test.

Picking dates for the other months or years

In the previous example, you picked a date from the previous month. Now let's move ahead and write a test script for picking a date from another month.

Test scenario:

  • Go to the Bootstrap Date Picker Page on TestMu AI Selenium Playground.
  • Find “May 2019” by clicking the action on the previous button.
  • Find the date once you get the month.

You can continue with the same test because the defined elements for buttons are the same.


import { test, expect } from "@playwright/test";

test("Pick a date from May 2019", async ({ page }) => {
  await page.goto("https://www.testmuai.com/selenium-playground/bootstrap-date-picker-demo");

  const startDate = page.getByPlaceholder("Start date");
  const calendar = page.locator(".datepicker-days");

  await expect(async () => {
    await startDate.blur();
    await startDate.click();
    await expect(calendar).toBeVisible({ timeout: 1000 });
  }).toPass();

  const mmYY = calendar.locator(".datepicker-switch");
  const prev = calendar.locator(".prev");
  const next = calendar.locator(".next");

  const dateToSelect: string = "May 2019";

  while ((await mmYY.textContent()) !== dateToSelect) {
    await prev.click();
  }

  await calendar.locator("td.day:not(.old):not(.new)").filter({ hasText: /^4$/ }).click();

  await expect(startDate).toHaveValue("04/05/2019");
});

Here are additional changes needed for this test.

  • Add a string and change its name to “dateToSelect.”
  • Now in a normal way, you have to keep pressing the previous button until we dont get “May 2019” in the date picker switch, which is not the right way; instead, use the while condition to create a loop.

const dateToSelect: string = "May 2019";

while ((await mmYY.textContent()) !== dateToSelect) {
  await prev.click();
}

Here is the explanation:

  • mmYY is a locator for the date picker switch, and we used textContent with it, which will fetch the text from that element.
  • It will try to receive a text from the element and check if it is similar to the “dateToSelect” string.
  • When it is not similar to the string, it will perform a click action on the “previous” button. Run a test to verify that condition is true.

With date pickers, locating UI elements can be challenging due to dynamic attributes and varying structures.

Playwright’s robust locators and loops simplify handling such elements, ensuring accurate interactions. Follow the video below to learn how to handle UI elements in Playwright.

Youtube thumbnail

Picking dates for the next month or year

After writing the test case for the previous month, you have to make certain additional changes to make this test script eligible for testing on next month's date or on any date. Also, you will need to call the moment.js library to execute tests on date pickers for different months or years.

Test scenario:

  • Go to the Bootstrap Date Pickers page on TestMu AI Selenium Playground.
  • Find months given in the data set by clicking action on the previous or next button.
  • Find the date in that month and click.

Here is the modified test script required to perform this test.


import { test, expect } from "@playwright/test";
import moment from "moment";

test("Pick a date from any month", async ({ page }) => {
  await page.goto("https://www.testmuai.com/selenium-playground/bootstrap-date-picker-demo");

  const startDate = page.getByPlaceholder("Start date");
  const calendar = page.locator(".datepicker-days");

  await expect(async () => {
    await startDate.blur();
    await startDate.click();
    await expect(calendar).toBeVisible({ timeout: 1000 });
  }).toPass();

  const mmYY = calendar.locator(".datepicker-switch");
  const prev = calendar.locator(".prev");
  const next = calendar.locator(".next");

  const dateToSelect: string = "December 2030";
  const shownMonth = moment(await mmYY.innerText(), "MMMM YYYY");
  const isEarlier = moment(dateToSelect, "MMMM YYYY").isBefore(shownMonth);
  console.log("Target month is earlier? " + isEarlier);
  while ((await mmYY.textContent()) !== dateToSelect) {
    if (isEarlier) {
      await prev.click();
    } else {
      await next.click();
    }
  }

  await calendar.locator("td.day:not(.old):not(.new)").filter({ hasText: /^4$/ }).click();

  await expect(startDate).toHaveValue("04/12/2030");
});

Let's continue with a similar test case and add some steps:

  • Install Moment.js with npm install moment, then import the "moment" package from the moment library.

  • import { test, expect } from "@playwright/test";
    import moment from "moment";
  • Now you have to format a date. Some calendars have a month like “Mar” for march, and some have a full word. If the calendar has full-length spelling, use “MMMM”; if it uses only the first three letters, then use “MMM.”
  • After deciding the format, use the “is before” function, which returns the boolean. Pass it the month shown in the date picker switch, and it tells you whether the given data is from a previous month or a next month.
    • If data is from the previous month, it will show true.
    • And if it's from next month, it will show false.

    • const shownMonth = moment(await mmYY.innerText(), "MMMM YYYY");
      const isEarlier = moment(dateToSelect, "MMMM YYYY").isBefore(shownMonth);
      console.log("Target month is earlier? " + isEarlier);
  • Now “is before” added true and false condition, with the help of it, while loop can decide where to perform click action:
    • So when a condition is false, it shows that the given data is in the next month so that it will perform a click action for the “next” button.
    • If a condition is true, data belongs to previous months so that it will perform a click action for the “previous” button.
    • If data is from the month already on screen, it will be false, and the while loop won't run because textContent will be equal to dateToSelect.

    • while ((await mmYY.textContent()) !== dateToSelect) {
        if (isEarlier) {
          await prev.click();
        } else {
          await next.click();
        }
      }
      
  • This script will help you to reach any specific month. Add click action for any date like before and run the test.

  •   await calendar.locator("td.day:not(.old):not(.new)").filter({ hasText: /^4$/ }).click();
    
      await expect(startDate).toHaveValue("04/12/2030");
    });
    

Making reusable date-picking function

Here, with the help of the previous script, you can select any year, month, or date, but it's not organized. Month and year at another place while the date is at another command line. Create one new function which is organized and reusable. Here are the following steps:


import { test, expect } from "@playwright/test";
import moment from "moment";

test("Pick dates with a reusable function", async ({ page }) => {
  await page.goto("https://www.testmuai.com/selenium-playground/bootstrap-date-picker-demo");

  const startDate = page.getByPlaceholder("Start date");
  const calendar = page.locator(".datepicker-days");

  async function selectDate(date: number, dateToSelect: string) {
    await expect(async () => {
      await startDate.blur();
      await startDate.click();
      await expect(calendar).toBeVisible({ timeout: 1000 });
    }).toPass();

    const mmYY = calendar.locator(".datepicker-switch");
    const prev = calendar.locator(".prev");
    const next = calendar.locator(".next");

    // const dateToSelect: string = "December 2030";
    const shownMonth = moment(await mmYY.innerText(), "MMMM YYYY");
    const isEarlier = moment(dateToSelect, "MMMM YYYY").isBefore(shownMonth);
    console.log("Target month is earlier? " + isEarlier);
    while ((await mmYY.textContent()) !== dateToSelect) {
      if (isEarlier) {
        await prev.click();
      } else {
        await next.click();
      }
    }
    await calendar
      .locator("td.day:not(.old):not(.new)")
      .filter({ hasText: new RegExp(`^${date}$`) })
      .click();
  }

  await selectDate(4, "December 2030");
  await expect(startDate).toHaveValue("04/12/2030");

  await selectDate(4, "May 2019");
  await expect(startDate).toHaveValue("04/05/2019");
});

Test execution

Step 1: Select the whole script; once you select it, you will find the bulb icon from where you can create a new function. Give it the name “selectDate”.

Step 2: This “new function” will sync with the selected script.

Step 3: Before making changes for month and year, comment on the previous code line for month and year.


// const dateToSelect: string = "December 2030";

Step 4: Add the dateToSelect string in a new function. So when you can add the value of month and year in a new function, It will put the value in the “while” loop, and the rest of the process will be the same as the previous test.


async function selectDate(date: number, dateToSelect: string) {

Step 5: Code line where you have defined the element for a date, make changes, and add “date” with $ sign to connect with the “selectDate” function.


await calendar
  .locator("td.day:not(.old):not(.new)")
  .filter({ hasText: new RegExp(`^${date}$`) })
  .click();

Reporting

Finally, you are ready to launch your new test script to automate date pickers.

Scale Playwright Testing with the TestMu AI Cloud

TestMu AI is a continuous quality cloud testing platform with an automation testing grid of 3,000+ browser and OS combinations that help you scale your Playwright test execution effortlessly. With TestMu AI, you can.

  • Debug Playwright tests from various logs, such as network logs, command logs, and full video recordings.
  • Execute Playwright tests 70% faster than any testing cloud by leveraging HyperExecute, the fastest end-to-end test orchestration cloud by TestMu AI.
  • Run Playwright tests in parallel on multiple configurations and trim down your test execution by multiple folds.
  • Organize your test analytics by creating a custom dashboard and widgets with TestMu AI Test Analytics.
  • Integrate seamlessly with your CI/CD pipelines.
  • Apart from Playwright, TestMu AI’s automation testing cloud supports all major browser testing frameworks such as Selenium, Cypress, Puppeteer, etc.

You can execute the Playwright test on the TestMu AI platform with desired capabilities such as browser name, version, OS, etc., and some advanced capabilities such as command logs and network logs, etc. Here is the config.ts file for executing the Playwright test on the TestMu AI platform.

You can access all those tests from the Playwright tutorial from this GitHub repo.

Conclusion

So that’s it, folks! After compiling this tutorial, I hope you get a good insight into automating date pickers and calendars. Also, you learned about the moment library and how to use it to simplify automation in Playwright. We will continue our learning in the next tutorial.

The Playwright automation framework is fast, reliable, and easy compared to other frameworks and supports all major browsers. That's why its popularity propelled over time. To learn Playwright automation testing, follow the TestMu AI YouTube channel.

Author

...

Mehul Gadhiya

Blogs: 6

  • Twitter
  • Linkedin

Mehul Gadhiya is a Community Contributor with experience in creating content and strategies around software testing, automation, and emerging technologies. At TestMu AI, he has worked on initiatives spanning product messaging, sales enablement, and lifecycle campaigns, supporting testing solutions like KaneAI and HyperExecute. He has also contributed to community-focused efforts, including the Testμ Conference, building awareness and collaboration across the QA and developer ecosystem

Reviewer

...

Parth Mistry

Reviewer

  • Linkedin

Parth Mistry is a Member of Technical Staff at TestMu AI, building SmartUI, the visual regression testing product. He developed and owns the SmartUI CLI, a modular TypeScript tool built on Playwright for multi-browser automation, and built the Storybook CLI for visual regression of UI components. He maintains cross-language SDKs in Python, Java, Ruby, C#, and Node.js, and engineered a Node-based visual rendering service on Kafka, Redis, MySQL, and S3. His migration of that service to an event-driven, KEDA-autoscaled architecture improved execution speed by 60% and cut annual infrastructure cost by $9,600. He also built the end-to-end SmartUI integration with KaneAI. Parth is a Google Summer of Code 2024 contributor and an alumnus of IIT Jodhpur.

Date Pickers in Playwright FAQs

Did you find this page helpful?

More Related Learning Hubs

TestMu AI forEnterprise

Get access to solutions built on Enterprise
grade security, privacy, & compliance

  • Advanced access controls
  • Advanced data retention rules
  • Advanced Local Testing
  • Premium Support options
  • Early access to beta features
  • Private Slack Channel
  • Unlimited Manual Accessibility DevTools Tests