[Java] Working with Date picker control.

The Date picker is a very common control that we find on many websites, especially on booking services. There are also many ways to implement Date Picker – some allow the user to enter the date into the Textbox to choose the date, while others require the user to click on the calendar. This post shows how to work with Date picker when clicking on the calendar is mandatory using Selenium with Java.


The example we use today is the Date Picker on flighthub.com

There are a few things that we need to be aware of before working with this control:
+ We cannot search for any past day.
+ We can only search from current day.
+ We can search until the last day of this month of next year, but we cannot search further that time.
=> So let’s say we go to this website today – as of this blog, it is April 24, 2019 or 24/04/2019, the period we can search is from 24/04/2019 – 30/4/2020.

We use these variables to track for elements in this blog:
dpFromMonth is the month in the calendar on the left side
dpFromYear is the year in the calendar on the left side
expectedDay is the day input by the user
expectedMonth is the month input by the user
expectedYear is the year input by the user

First, when we click on the Departure Textbox, the Date picker control appears. Depends on your approach, there are different ways to interact with this control. For us, we focus on the arrows that we can move to next / previous month, the label that shows the month and year of the calendar, and the day to be clicked. So we grabs these elements as below:

Starting from the div root that contains the whole control
 
By byCalendarArrow = By.cssSelector("a[data-handler='next']");	
By byLeftTable = By.cssSelector("div[class='ui-datepicker-group ui-datepicker-group-first']");							
By byCalendarBody = By.cssSelector("div[class='ui-datepicker-group ui-datepicker-group-first'] >  table[class='ui-datepicker-calendar'] > tbody");

From the structure above, we can see that the control is divided into 2 sections
“ui-datepicker-group ui-datepicker-group-first” contains all elements for the left section of the control, and
“ui-datepicker-group ui-datepicker-group-last” contains the right section. Since we are interest in the label that contains the month and the year, we only need to specify these div as the root, and we can then find their children element later.

Second, since we know that we can only search from today until this month next year, so we always start working from the left side of this control. If we works on the right side, we cannot search for any date in this month.

The month and year within the two span below
We are interested in the last 2 span classes that have Month, Year values

Starting at the root of the left table that we have above, we can find those two span elements using the code below:

 	
WebElement dpTable = driver.findElement(byLeftTable);		
WebElement dpTitle = dpTable.findElement(By.className("ui-datepicker-title"));
WebElement dpFromMonth = dpTitle.findElement(By.className("ui-datepicker-month"));
WebElement dpFromYear = dpTitle.findElement(By.className("ui-datepicker-year"));	

Now we got the month and the year that shows in the Calendar in dpFromMonth and dpFromYear, we are going to compare it with the date that the user going to input.

For the sake of simplicity, we assume the user uses an input date as of “24/06/2019”. We are going to parse this date into separated variables in order to compare with the date in the Calendar. Let’s say the “24/06/2019” is passed to the method by a parameter as String, then we can use the SimpleDateFormat from Java library to parse this string, and get the day, month, year using the Calendar class.

 
String expectedDay = GetDayFromDate(date);
String expectedMonth = GetMonthFromDate(date);
String expectedYear = GetYearFromDate(date); 
private Calendar ParseDate(String date) throws ParseException
	{
		SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");			
		Date temporary = dateFormat.parse(date);
		Calendar c = Calendar.getInstance();
		c.setTime(temporary);	
		return c;
	}
	
	private String GetDayFromDate(String date) throws ParseException
	{
		Calendar c = ParseDate(date);
		return String.valueOf(c.get(Calendar.DATE));		
	}
	
	private String GetMonthFromDate(String date) throws ParseException
	{
		Calendar c = ParseDate(date);
		return c.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
	}
	
	private String GetYearFromDate(String date) throws ParseException
	{
		Calendar c = ParseDate(date);
		return String.valueOf(c.get(Calendar.YEAR));		
	}

From the code above, we get expectedDay = 24, expectedMonth = June, expectedYear = 2019. Please note that the getDisPlayName will return the name of the month, instead of an integer value.

Now we are going to compare this expectedMonth and expectedYear with dpFromMonth and dpFromYear, if they are not match, we will keep clicking the calendar arrow until we have all of them equal.

// loop to find match month and year			 
// == cannot be used to compare string, == mean having the same memory storing location
while(!dpFromMonth.getText().equals(String.valueOf(expectedMonth)) || !dpFromYear.getText().equals(String.valueOf(expectedYear))) 
{	
	//loop clicking arrow
	calendarArrow = driver.findElement(byCalendarArrow);				
	calendarArrow.click();
	
	//find the whole calendar month and year again, the date picker element is stale / reset
	dpTable = driver.findElement(byLeftTable);	
	dpTitle = dpTable.findElement(By.className("ui-datepicker-title"));
	dpFromYear = dpTitle.findElement(By.className("ui-datepicker-year"));
	dpFromMonth = dpTitle.findElement(By.className("ui-datepicker-month"));		 
} 			

In this loop, first we click on the arrow to go to next month, and we have to get the dpFromMonth, dpFromYear elements again. Why? Since we click on the arrow, even though the calendar stays the same to human eyes, the actual elements on the web that we got before this step become stale or no longer attach to the DOM. Here is the result if we don’t find those elements again.

After the loop, now the calendar is at a state where expectedMonth is the same with dpFromMonth, and expectedYear is the same with dpFromYear. We are going to click on the day on the calendar based on expectedDay variable.

Now we find the cell that has 24 as text

How does this calendar look like?

It is a collection of <tr> and <td> tag inside <tbody>

We are going to use the By element byCalendarBody that we defined near the beginning of this post. From this root, we try to find all <tr> tags, we loop through every single <td> inside each tr, and compare the text with expectedDay. If they match, we click on that element.

WebElement calendarBody = driver.findElement(byCalendarBody);		
List days;
WebElement dayTobeClicked = null;
			
List allRows = calendarBody.findElements(By.tagName("tr"));

Boolean found = false;
for (WebElement temp : allRows) {    
	days = temp.findElements(By.tagName("a")); // we have to find the text inside each <a> tag
	for (WebElement temp2 : days) {          // day within rows
		 if(temp2.getText().equals(expectedDay)) 
		 {
			 dayTobeClicked = temp2;
			 found = true;
			 break;
		 }
	}
	if(found == true)
		break;
}

dayTobeClicked.click();		

It works for most time periods, except one case when the user inputs any day of this month of next year. The reason why it does not work is once we reach to this month of next year, the day is on the right side of the control.

At the last period, there is no arrow on the right side

To get it works, we put a hack into our loop in the comparison of dpFromMonth and dpFromYear by checking for the arrow. If we do a test manually, when we are at that state, the arrow disappears and the user cannot search further than that time, so we put a check on the arrow if the element is not exist, we exit the loop.

if(driver.findElements(byCalendarArrow).size() <1)
	break;

Then we try to find the day on the right side of the calendar, not the left side.

if(!dpFromMonth.getText().equals(expectedMonth))
{
	byCalendarBody = By.cssSelector("div[class='ui-datepicker-group ui-datepicker-group-last'] >  table[class='ui-datepicker-calendar'] > tbody");	
}

Notice that we use the div with class = “ui-datepicker-group ui-datepicker-group-last

The source can be found in my github.
https://github.com/quan612/TestPlaneTicket/blob/master/src/test/java/seleniumTests/CommonRepository.java

[C#] Recording test results on TestLink using TestLinkAPI and Description attribute

The purpose is to record the result of the test to TestLink. If we are using NUnit 3, there are two ways to get the result per execution – either using TestContext.CurrentContext.Result.Outcome.Status, or using ITestListener to parse the xml result. This blog post just concentrates on the former only.

The result of the case is determined whether there are exceptions when the test runs, or a condition failed the assertion. Otherwise, the result is assumed “Pass” when the test reaches [TearDown] attribute. So to record the result, the call to Test Link API should be put in Test TearDown.

First, we can go to nuget, get this package:

Please note that this package contains most of Test Link models to interact with the API, but it still lacks the logic to select a particular test case in general.

Assuming the project on Test Link, we already have a Test Project, a Test Plan, and Test cases assigned to the Test Plan. This is important as it does not matter how many test cases we created under Test Specifications, we still have to add them to a Test Plan.

Second, we add the property to TestFixture. Since we use TestFixture to run one whole test suite, so the property suits this requirement perfectly.

[TestFixture]
[Property("Test Project", your test project")]
[Property("Test Suite", "your test suite under specification")]
[Property("Test Plan", "your test plan")]

Third, we create a new instance of TestLink, and loop through all test cases added to this test plan.

var testPlanRepository = _testLink.getTestPlanByName(testProject, testPlan);
var testSuites = _testLink.GetTestSuitesForTestPlan(testPlanRepository.id);
var testSuiteId = GetTestSuiteByName(testSuites, testSuiteName);
var testCases = _testLink.GetTestCasesForTestSuite(testSuiteId, false);
var testCaseRepository = GetIndividualTestCaseFromListOfTestCases(testCases, testContext);

+ Get the test plan using the getTestPlanByName from the api.
+ Get all test suites added into the test plan above.
+ Get test suite id based on test suite name defined in the Property above.
+ Get all test cases added to this test suite. Please note that if we don’t add the test cases into test plan manually on Test Link, the result at this step will be 0.
+ Get the test case we are going to record the result, based on the testContext. The testContext is a parameter passed in from method called, using TestContext.CurrentContext

Finally, the result can be recorded using the API provided based on the package.

if (testCaseRepository != null)
                _testLink.ReportTCResult(
                    testCaseRepository.id,
                    testPlanRepository.id,
                    testCaseStatus, 
                    1,  // platform, does not affect anything
                    "your environment", 
                    false, //whether to overwrite the result
                    true,
                    executionNotes);

Notes: There are two things we have to define here that the package above does not provide (as of this blog is posted)
GetTestSuiteByName(testSuites, testSuiteName);

private int GetTestSuiteByName(List testSuites, string testSuiteName)
        {
            var suite = testSuites.Where(a => a.name == testSuiteName).ToList().FirstOrDefault();
            if (suite != null)
                return suite.id;
            throw new Exception("Cannot find test suite");
        }

GetIndividualTestCaseFromListOfTestCases(testCases, testContext);

 private TestCaseFromTestSuite GetIndividualTestCaseFromListOfTestCases(
            List testCases, TestContext testContext)
        {
            var testCaseRepository = testCases.Where(a => a.name == testContext.Test.Properties.Get("Description").ToString())
                .ToList().FirstOrDefault();
          // here is how the case is found
            if (testCaseRepository != null)
                return testCaseRepository;
            throw new Exception("Cannot find test case");
        }

The full working code can be found in my github.

[C#] Automation structure with NUnit 3+

I make this blog post because the original project was using NUnit 2 for several months, and not until later when we try to incorporate other open source library into the existing project, we decided to move to version 3. We soon realized that migrating from version 2 to version 3 was not like a 5-minute refactoring, since we had to recompile some of our old libraries with the new NUnit, as well as the changes of the NUnit itself.

This is a very basic structure of the test using NUnit Attribute, so that the user can control the test flow – setting up global variables, using external 3rd party library, dispose of objects after running.

Considering two simple cases to test login using Selenium.
+ Login using a user name and a correct password for that user.
+ Login using this user and use a password from another user.

We can use various attributes inside TestFixture attribute to set things up. In the [Setup], we can take care of how the webdriver being instantiated. and get the test data for those particular test case from the excel or any kinds of data driven, since this method runs before any test. Then the [TearDown] to dispose of web driver since it runs after any test. For the whole test suite, [OneTimeSetup] and [OneTimeTearDown] do the same things, but on the test suite level – it runs once for all the test inside this [TestFixture]

The test fixture class:

[TestFixture]
public class LoginTestPage
{
	[OneTimeSetup]
	public void FixtureSetup()
	{ 
		TestContext.Progress.WriteLine("one time fixture 1 set up");
	}
	
	[OneTimeTearDown]
	public void FixtureTearDown()
	{ 
		TestContext.Progress.WriteLine("one time fixture 1 tear down");
	}
	
	[Setup]
	public void Setup()
	{ 
		TestContext.Progress.WriteLine("Individual test set up");
	}
	
	[TearDown]
	public void TearDown()
	{ 
		TestContext.Progress.WriteLine("Individual test tear down");
	}
	
	[Test]
	public void TestLogin()
	{
		Login(properUserName, goodPassWord);
	}
	
	[Test]
	public void TestLoginWithBadPassWord()
	{
		Login(properUserName, wrongPassWord);
	}
	
	private void Login(string userName, string passWord)
	{
		//your login logic
	}
}

With NUnit 3, we can put the OneTimeSetup, OneTimeTearDown outside of any class and under [SetupFixture], so the methods are considered to run on the assembly level (run one for the whole assembly). This set up is useful when we want to integrate external library report, or executing particular scripts for the whole project.

 
[SetupFixture]
public class BaseTest
{
	[OnceTimeSetup]
	public void OneTimeSetup()
	{ 
		TestContext.Progress.WriteLine("One time set up for the whole test");
	}

	[OneTimeTearDown] 
	public void OneTimeTearDown()
	{ 
		TestContext.Progress.WriteLine("One time tear down for the whole test");
	} 
}

To test the flow, we can use TestContext.Progress.WriteLine(“”) to see the order of the attributes run in real time.