Overview:
We already have seen automating below complex controls.
In this article, we will see how we could automate Calendar / DatePicker.
Goal:
To model a wrapper element for the DatePicker / Calendar. So that user can identify the datepicker field as any other field by the locator and select any given date without injecting Javascript.
Injecting Javascript might be very easy. But that is not right way to automate as it could potentially miss some issues. It might not trigger any associated events.
My use case would be to select any given date in the DatePicker/Calendar field as shown below.
DatePicker.setDate("01 Jan 2018");
DatePicker.setDate("29 Feb 2012");
JQuery DatePicker:
All the below elements make the DatePicker element.
Lets come up with a wrapper with all the WebElements which looks like Calendar.
DatePicker / Calendar Element:
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.LongStream;
public class DatePicker {
private static final String DATE_FORMAT = "dd MMMM yyyy";
private static final String DAY_FIRST = "01";
private static final String SPACE = " ";
private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern(DATE_FORMAT);
@FindBy(css = "a.ui-datepicker-prev")
private WebElement prev;
@FindBy(css = "a.ui-datepicker-next")
private WebElement next;
@FindBy(css = "div.ui-datepicker-title")
private WebElement curDate;
@FindBy(css = "a.ui-state-default")
private List< WebElement > dates;
public DatePicker(final WebDriver driver){
PageFactory.initElements(driver, this);
}
public void setDate(String date) {
long diff = this.getDateDifferenceInMonths(date);
int day = this.getDay(date);
WebElement arrow = (diff >= 0 ? next : prev);
diff = Math.abs(diff);
//click the arrows
LongStream.range(0, diff)
.forEach(i -> arrow.click());
//select the date
dates.stream()
.filter(ele -> Integer.parseInt(ele.getText()) == day)
.findFirst()
.ifPresent(ele -> ele.click());
}
private int getDay(String date) {
LocalDate dpToDate = LocalDate.parse(date, DTF);
return dpToDate.getDayOfMonth();
}
private long getDateDifferenceInMonths(String date) {
LocalDate dpCurDate = LocalDate.parse(DAY_FIRST + SPACE + this.getCurrentMonthFromDatePicker(), DTF);
LocalDate dpToDate = LocalDate.parse(date, DTF);
return YearMonth.from(dpCurDate).until(dpToDate, ChronoUnit.MONTHS);
}
private String getCurrentMonthFromDatePicker() {
return this.curDate.getText();
}
}
Page Object:
By using above DatePicker, I could find the date picker element – like locating a regular WebElement.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class DatePickerPage {
private final WebDriver driver;
@FindBy(id = "datepicker")
private WebElement dateField;
@FindBy(id = "ui-datepicker-div")
private DatePicker datePicker;
public DatePickerPage(final WebDriver driver){
this.driver = driver;
PageFactory.initElements(driver, this);
}
public void goTo() {
driver.get("https://jqueryui.com/datepicker/");
this.driver.switchTo().frame(0);
this.datePicker = new DatePicker(driver);
}
public DatePicker getDatePicker() {
dateField.click();
return datePicker;
}
public String getSelectedDate() {
return this.dateField.getAttribute("value");
}
}
TestNG Test:
This is my TestNG test to demo the data picker.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DatePickerTest {
private DatePickerPage datePickerPage;
private WebDriver driver;
@BeforeTest
public void setupDriver() {
this.driver = new ChromeDriver();
this.datePickerPage = new DatePickerPage(driver);
}
@Test(dataProvider = "inputDates")
public void dateTest(String ipDate, String opDate) {
datePickerPage.goTo();
datePickerPage.getDatePicker().setDate(ipDate);
Assert.assertEquals(datePickerPage.getSelectedDate(), opDate);
}
@DataProvider(name = "inputDates")
public static Object[][] getDates() {
return new Object[][]
{
{
"25 May 2017",
"05/25/2017"
},
{
"01 December 2016",
"12/01/2016"
},
{
"29 February 2016",
"02/29/2016"
},
{
"20 November 2020",
"11/20/2020"
}
};
}
}
Demo:
Summary:
By adding an abstraction layer, we control Calendar/DatePicker, a complex element, like a simple WebElement.
Happy Testing & Subscribe ?
Wonderful article. Thanks for sharing.
You are welcome!
Hi Sir,
I have not used this Arquillian Drone extension to selenium
How can i use the same without the above extension?
It would be very helpful if you could demo this only with page factory
You could achieve the same without arquillian – instead of auto injecting elements, you need to create the object yourself. But the logic for date manipulation would still be same!
Please, could you explain why do you use your DatePicker with FindBy annotation? FindBy annotation works only for webElement interface, but I don’t see that your DatePicker extends WebElement
Thanks
You are right. Actually it is an old article. I had used a different framework Arquillian for which you could find any custom object using FindBy. However I later modified this without arquillian – please ingore the @FindBy. everything else should be fine. Sorry for the confusion.
Anyway thank you for your solution. It helped me in 2019 🙂
Hello. Can you please show me the same date picker for selenium using python.
Hi Ashwini,
I am not familiar with python. My aim here is to show only a high level idea. Doing same logic in python should not be hard. Please try.
Thanks.