EODMS Katalon

From wiki
Revision as of 04:57, 15 October 2019 by William.mackinnon (talk | contribs)
Jump to navigation Jump to search

Types


import static com.kms.katalon.core.checkpoint.CheckpointFactory.findCheckpoint
import static com.kms.katalon.core.testcase.TestCaseFactory.findTestCase
import static com.kms.katalon.core.testdata.TestDataFactory.findTestData
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import com.kms.katalon.core.checkpoint.Checkpoint as Checkpoint
import com.kms.katalon.core.checkpoint.CheckpointFactory as CheckpointFactory
import com.kms.katalon.core.mobile.keyword.MobileBuiltInKeywords as MobileBuiltInKeywords
import com.kms.katalon.core.model.FailureHandling as FailureHandling
import com.kms.katalon.core.testcase.TestCase as TestCase
import com.kms.katalon.core.testcase.TestCaseFactory as TestCaseFactory
import com.kms.katalon.core.testdata.TestData as TestData
import com.kms.katalon.core.testdata.TestDataFactory as TestDataFactory
import com.kms.katalon.core.testobject.ObjectRepository as ObjectRepository
import com.kms.katalon.core.testobject.TestObject as TestObject
import com.kms.katalon.core.webservice.keyword.WSBuiltInKeywords as WSBuiltInKeywords
import com.kms.katalon.core.webui.driver.DriverFactory as DriverFactory
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUiBuiltInKeywords
import internal.GlobalVariable as GlobalVariable
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import com.kms.katalon.core.mobile.keyword.MobileBuiltInKeywords as Mobile
import com.kms.katalon.core.webservice.keyword.WSBuiltInKeywords as WS
import com.thoughtworks.selenium.Selenium
import org.openqa.selenium.firefox.FirefoxDriver
import org.openqa.selenium.WebDriver
import org.openqa.selenium.Keys
import org.openqa.selenium.StaleElementReferenceException
import org.openqa.selenium.WebElement
import org.openqa.selenium.interactions.Actions
import org.openqa.selenium.support.ui.ExpectedConditions
import org.openqa.selenium.support.ui.WebDriverWait
import org.openqa.selenium.support.ui.Select
import org.openqa.selenium.JavascriptExecutor
import org.junit.After
import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium
import static org.junit.Assert.*
import java.util.regex.Pattern
import static org.apache.commons.lang3.StringUtils.join
import java.text.SimpleDateFormat
import internal.GlobalVariable
import MobileBuiltInKeywords as Mobile
import WSBuiltInKeywords as WS
import WebUiBuiltInKeywords as WebUI

import java.util.HashMap
import java.util.UUID

/**
 * Holds the various search parameters which were entered into the EODMS interface by the user.
 * Is passed to the search() function to provide parameters for searches.
 * 
 * @author Kieran Moynihan
 */
public class SearchParameters {
	String region;
	String dateType;
	String[] dates;
	String[] selectedSatellites
	HashMap<String, String> textfieldValues;
	HashMap<String, Integer> radioSelected;
	HashMap<String, Integer> checkboxSelected;
	HashMap<String, String[]> selectboxSelectedOptionsText;

	public String getStartDate(){
		return dates[0]
	}

	public String getEndDate(){
		return dates[1]
	}

	public void setStartDate(String newStartDate){
		dates[0] = newStartDate
	}

	public void setEndDate(String newEndDate){
		dates[1] = newEndDate
	}

	/**
	 * 
	 * @param r		String						Name of Saved AOI region generated with UUID.
	 * @param d		String						Date type. Search option specifying Past 24 Hours, Any Time, Date Range, Seasonal Dates.
	 * @param dl	String[]					Dates. List of dates (0 or 2 dates) specified as boundaries of Date Range or Seasonal Dates.
	 * @param ss	String[]					Selected Satellites. List of satellites/data sources to get products from.
	 * @param tv	HashMap<String, String>		Text Field Values. List of all values which user had entered into text fields.
	 * @param rs	HashMap<String, Integer>	Radio buttons selected. Specifications for which radio buttons had been selected.
	 * @param cs	HashMap<String, Integer>	Check boxes selected. Specifications for which check boxes had been selected.
	 * @param sb	HashMap<String, String[]>	Select box options. List of each option selected from list of options in each select box.
	 */
	public SearchParameters(String r, String d, String[] dl, String[] ss,
	HashMap<String, String> tv, HashMap<String, Integer> rs,
	HashMap<String, Integer> cs, HashMap<String, String[]> sb) {
		this.region = r;
		this.dateType = d;
		this.dates = dl;
		this.selectedSatellites = ss;
		this.textfieldValues = tv;
		this.radioSelected = rs;
		this.checkboxSelected = cs;
		this.selectboxSelectedOptionsText = sb;
	}
}

/**
 * Provides functions used for manipulating and navigating the EODMS web page.
 *
 * @author Kieran Moynihan, Khang Nguyen
 */
public class WebBrowsing {
	// use these if selenium functions are not enough
	def driver = DriverFactory.getWebDriver()
	// Actions can help with focusing on and moving to element that might be hidden (need to scroll down a table to find them)
	Actions actions = new Actions(driver)
	// JavascriptExecutor can click on elements that are behind other elements by simply executing a click on the element, rather than simulating a user click
	JavascriptExecutor js = (JavascriptExecutor) driver

	private selenium
	private gui

	/**
	 * selClick with default duration 60 seconds
	 */
	public void selClick(String key){
		selClick(key, 60);
	}

	/**
	 * Attempts to click on an element multiple times until timeout occurs
	 */
	public void selClick(String key, int duration){
		for (int second = 0; second < duration; second++) {
			try {
				selenium.click(key)
				break;
			} catch (Exception except) {
				// prints the exception and fails on timeout
				if (second == duration-1) {
					println except
					fail('Timeout on click: '+key)
				}
			}
			Thread.sleep(1000);
		}
	}

	/**
	 * Enters dates into the dates tab.
	 */
	public void enterDates(String sDate, String eDate, String dateType) {
		// ensure in the date options tab
		selClick("id=Search")
		selClick("id=tab2")

		// row dependent on date search type
		String tRow = "";
		// rows correspond to table rows in the list of date options
		// row 5 contains text fields for DateRange option, not visible unless DateRange selected
		if (dateType == 'AnyTime') {
			tRow = '2'
		} else if (dateType == 'Past24Hours') {
			tRow = '3'
		} else if (dateType == 'DateRange') {
			tRow = "4"
		} else if (dateType == 'SeasonalDates') {
			tRow = "6"
		}
		// if AnyTime or Past24Hours selected, no further action required after selecting option
		if (tRow < 4) {
			selenium.click("//div[@id='panel2']/div/table/tbody/tr["+tRow+"]/td[2]/table/tbody/tr/td/span/label")
			return;
		}
		// get previous end date from date field
		// convert date YYYY-MM-DD -> YYYYMMDD
		String pEDate = String.join("", driver.findElementByXPath("//input[@id='"+dateType+"EndDate']").getAttribute("value").split("-"))
		// if there was a previous end date and the new start date is greater (later) than the previous end date, change the end date first (otherwise start date first)
		// this is because if you enter a start date that is later than the value in the end date field, an error will pop up and your previously entered start date will be cleared
		if (!(pEDate == "") && Integer.parseInt(String.join("", sDate.split("-"))) > Integer.parseInt(pEDate)) {
			// click end date field and enter end date
			selenium.click("id="+dateType+"EndDate")
			selenium.type("id="+dateType+"EndDate", eDate)
			// click date options tab again to remove popup calendar
			selenium.click("id=tab2")
			// click start date field and enter start date
			selenium.click("id="+dateType+"StartDate")
			selenium.type("id="+dateType+"StartDate", sDate)
			// click date options tab again to remove popup calendar
			selenium.click("id=tab2")
		} else {
			selenium.click("//div[@id='panel2']/div/table/tbody/tr["+tRow+"]/td[2]/table/tbody/tr/td/span/label")
			selenium.click("id="+dateType+"StartDate")
			selenium.type("id="+dateType+"StartDate", sDate)
			selenium.click("id=tab2")
			selenium.click("id="+dateType+"EndDate")
			selenium.type("id="+dateType+"EndDate", eDate)
			selenium.click("id=tab2")
		}
	}

	/**
	 * Logs user into EODMS from the main page.
	 */
	public void login(){
		// start login
		selClick("link=Login")
		// enter username
		selenium.click("id=usernameTextBox")
		selenium.type("id=usernameTextBox", gui.getUsername())
		// enter  password
		selenium.click("id=passwordTextBox")
		selenium.type("id=passwordTextBox", gui.getPassword())
		// press login button
		selenium.click("//*[@id='RootDockPanel']/div/div/div[1]/table/tbody/tr/td/div/table/tbody/tr[2]/td/div/div/div/div/table/tbody/tr/td/table/tbody/tr[4]/td/table/tbody/tr[2]/td/div/table/tbody/tr/td[2]/table/tbody/tr[3]/td/table/tbody/tr[2]/td/table/tbody/tr/td[2]/table/tbody/tr/td/table/tbody/tr[1]/td/div/table/tbody/tr/td[2]/table/tbody/tr/td/table/tbody/tr[2]/td/div/div/div/div/div/table/tbody/tr[2]/td[2]/table/tbody/tr/td[2]/div")
		Thread.sleep(1000);
		for(int halfsecond = 0; halfsecond <=60; halfsecond++){
			try {
				if (selenium.isVisible("link=Login")) {
					fail('Incorrect Login Information')
				}
			} catch (Exception e) {}
			try {
				if (selenium.isVisible("link=My Account")) break;
			} catch (Exception e) {
				if (halfsecond == 60) {
					fail('Timeout on login')
				}
				Thread.sleep(500)
			}
		}
		selClick("link=Search")
	}

	/**
	 * Gets the search parameters from the EODMS interface.
	 */
	public SearchParameters getSearch(){
		// ensure in search tab
		selClick("id=Search")
		// go to location panel
		selClick("id=tab1")
		// Open Save Your Area of Interest
		if (driver.findElementByXPath("//div[@id='panel1']/div/table/tbody/tr[16]/td/table").getAttribute("aria-hidden") == "true") {
			selClick("link=Save Your Area of Interest")
		}
		// Save the Area of Interest as ESR_[UUID]
		String tempAOI = 'ESR_'+UUID.randomUUID().toString()
		selClick("//div[@id='panel1']/div/table/tbody/tr[16]/td/table/tbody/tr/td/table/tbody/tr/td[2]/table/tbody/tr/td/input", 5)
		selenium.type("//div[@id='panel1']/div/table/tbody/tr[16]/td/table/tbody/tr/td/table/tbody/tr/td[2]/table/tbody/tr/td/input", tempAOI)
		selClick("//div[@id='panel1']/div/table/tbody/tr[16]/td/table/tbody/tr/td/table/tbody/tr/td[3]/div/div")
		Thread.sleep(500)
		for (int quartersecond = 0; quartersecond <= 120; quartersecond++){
			WebElement[] tables = driver.findElementsByXPath('//table[@class="resizableContentPanel"]')
			boolean exit = false
			for (WebElement table : tables) {
				if (table.getAttribute('aria-describedby') == 'saveAOIBoxDescription') {
					WebElement okBtn = table.findElementByXPath('./tbody/tr[2]/td/table/tbody/tr[2]/td/table/tbody/tr/td[2]/table/tbody/tr/td/div/div/div/div/div/table/tbody/tr[2]/td[2]/table/tbody/tr/td[2]/div')
					js.executeScript("arguments[0].click()", okBtn)
					exit = true
					break;
				}
			}
			if (exit) break;
		}
		// go to date options
		selClick("id=tab2")
		// list of Date Type buttons (Any Time, Past 24 Hours, Date Range, Seasonal Dates)
		WebElement[] datetypes = driver.findElementsByXPath("//input[@name='dates']")
		// Actual dateType selected
		String dateType = ""
		// for each dateType button
		for (int i = 0; i < datetypes.length; i++){
			WebElement radBut = datetypes[i]
			// if dateType button is selected
			if (radBut.isSelected()){
				// get the id of the button
				String id = radBut.getAttribute("id")
				// dateType is the value of the Label associated with this radio button
				dateType = selenium.getText("//label[@for='"+id+"']")
				break;
			}
		}
		// start and end dates
		String[] dates;
		// if is a dateType that uses date values
		if (dateType == 'Date Range' || dateType == 'Seasonal Dates') {
			// Remove spaces from dateType name
			if (dateType == 'Date Range') {
				dateType = 'DateRange'
			} else {
				dateType = 'SeasonalDates'
			}
			// retrieve start and end dates
			String startDate = driver.findElementByXPath("//input[@id='"+dateType+"StartDate']").getAttribute("value")
			String endDate = driver.findElementByXPath("//input[@id='"+dateType+"EndDate']").getAttribute("value")
			// set dates
			dates = [startDate, endDate];
			// if is a dateType that doesn't use date values
		} else {
			if (dateType == 'Any Time') {
				dateType = 'AnyTime'
			} else {
				dateType = 'Past24Hours'
			}
			// set default dates
			dates = ["", ""];
		}
		// Go to Data tab (sensors)
		selenium.click("id=tab3")

		// array of satellite options
		WebElement[] satOptions = driver.findElementsByXPath("//table[@id='panel3']/tbody/tr/td/div/div/div/div/table/tbody/tr[2]/td/div/div[2]/div/div/div/div/div")

		// holds satellite option names of satellites that are checked
		String[] satList = new String[satOptions.length];

		// for each satellite option in satOptions, if the option is checked, add the name of the option to satList
		int satListCount = 0;
		for (int i = 0; i < satOptions.length; i++) {
			if (!(satOptions[i].getAttribute("aria-label").endsWith("Not Checked")) && !(satOptions[i].getAttribute("aria-label").endsWith("Partially Checked"))) {
				satList[i] = driver.findElementByXPath("//div[@id='"+satOptions[i].getAttribute("id")+"']/table/tbody/tr/td[4]/table/tbody/tr[1]/td/table/tbody/tr/td[1]/div").getAttribute("innerText");
				satListCount++;
			} else {
				satList[i] = ""
			}
		}

		// satList with "" values removed
		String[] selectedSatellites = new String[satListCount];
		satListCount = 0;
		for (int i = 0; i < satList.length; i++) {
			if (satList[i] != "") {
				selectedSatellites[satListCount] = satList[i];
				satListCount++;
			}
		}
		// Go to Data options tab
		selenium.click("id=tab4")
		// retrieve all parameter web elements from the options tab
		WebElement optionsPanel = driver.findElementByXPath("//div[@id='panel4']")
		WebElement[] textfields = optionsPanel.findElementsByXPath(".//input[@type='text']")
		WebElement[] radiobuttons = optionsPanel.findElementsByXPath(".//input[@type='radio']")
		WebElement[] checkboxes = optionsPanel.findElementsByXPath(".//input[@type='checkbox']")
		WebElement[] selectboxes = optionsPanel.findElementsByXPath(".//select")

		// hash maps with keys of identifiers and values of values for each (set of) element(s)
		HashMap<String, String> textfieldValues = new HashMap<String, String>();
		HashMap<String, Integer> radioSelected = new HashMap<String, Integer>();
		HashMap<String, Integer> checkboxSelected = new HashMap<String, Integer>();
		HashMap<String, String[]> selectboxSelectedOptionsText = new HashMap<String, String[]>();

		// for each text field
		for (WebElement textfield : textfields) {
			// if the user entered text in the field
			if (textfield.getAttribute("title") != "") {
				// add the string to hash map with title of text field as key
				textfieldValues.put(textfield.getAttribute("title"), textfield.getAttribute("value"))
			}
		}
		// for each radio button
		for (WebElement radiobutton : radiobuttons) {
			// if the radio button was selected by the user
			if (radiobutton.isSelected()){
				String category = radiobutton.findElementByXPath("../../../../../../table").getAttribute("title")
				WebElement[] allButtons = radiobutton.findElementsByXPath("../../../td")
				int position = 0
				for (int i = 0; i < allButtons.length; i++){
					if (allButtons[i].findElementByXPath("./span/input").getAttribute("id") == radiobutton.getAttribute("id")) {
						position = i+1;
						break;
					}
				}
				// add the index in the group of the selected radio button to the hash map with the name of the group of radio buttons as the key
				radioSelected.put(category, position)
			}
		}
		// for each check box
		for (WebElement checkbox : checkboxes) {
			// if the check box was selected by the user
			if (checkbox.isSelected()){
				String category = checkbox.findElementByXPath("../../../../../../table").getAttribute("title")
				WebElement[] allButtons = checkbox.findElementsByXPath("../../../td")
				int position = 0
				for (int i = 0; i < allButtons.length; i++){
					if (allButtons[i].findElementByXPath("./span/input").getAttribute("id") == checkbox.getAttribute("id")) {
						position = i+1;
						break;
					}
				}
				// add the index in the group of the selected check box to the hash map with the name of the group of check boxes as the key
				checkboxSelected.put(category, position)
			}
		}
		// for each select box
		for (WebElement selectbox : selectboxes) {
			WebElement[] selOpt = new Select(selectbox).getAllSelectedOptions();
			String[] options = new String[selOpt.length]
			for (int i = 0; i < selOpt.length; i++) {
				options[i] = selOpt[i].getText()
			}
			// add the list of options selected within the select box to the hash map with the name of the hash map as the  key
			selectboxSelectedOptionsText.put(selectbox.getAttribute("title"), options)
		}
		// return the SearchParameters
		return new SearchParameters(tempAOI, dateType, dates, selectedSatellites, textfieldValues, radioSelected, checkboxSelected, selectboxSelectedOptionsText)
	}

	/**
	 * Enters the search parameters into the EODMS interface.
	 */
	public void search(SearchParameters SearchDetails){
		// wait for new page to load
		for (int quartersecond = 0; quartersecond < 60 ; quartersecond++) {
			try {
				if (selenium.isVisible("link=Use a Saved Area of Interest")) break;
			} catch (Exception e) { if (quartersecond == 60) fail("Can't see Saved Area of Interest link")}
			Thread.sleep(250);
		}
		// go to saved AOIs
		if (driver.findElementByXPath("//div[@id='panel1']/div/table/tbody/tr[12]/td/table").getAttribute("aria-hidden") == "true") {
			selClick("link=Use a Saved Area of Interest")
		}
		WebElement[] aois = driver.findElementsByXPath("//div[@id='panel1']/div/table/tbody/tr[12]/td/table/tbody/tr/td/table/tbody/tr/td[1]/a")
		for (WebElement aoi : aois){
			if (aoi.getAttribute('title') == SearchDetails.getRegion()){
				js.executeScript("arguments[0].click()", aoi)
				break;
			}
		}
		// go to date options
		selenium.click("id=tab2")
		// get dateType, start and end dates
		String dateType = SearchDetails.getDateType();
		String startDate = SearchDetails.getStartDate();
		String endDate = SearchDetails.getEndDate();
		if (dateType == 'AnyTime') {
			// click Any Time option
			selenium.click("//div[@id='panel2']/div/table/tbody/tr[2]/td[2]/table/tbody/tr/td/span/label")
		} else {
			// If SeasonalDates or DateRange, enter bounding dates
			// if SeasonalDates or DateRange but no dates were entered, fail
			if(startDate != "" && endDate != ""){
				enterDates(startDate, endDate, dateType)
				// catch is AnyTime
			}else{
				fail('Date Option is set to '+dateType+' but no dates were set.')
			}
		}
		// Go to Data tab (sensors)
		selenium.click("id=tab3")
		// select the specified satellites
		// get satellites to select from search details
		String[] selectedSatellites = SearchDetails.getSelectedSatellites()

		// list of satellite/data source options
		WebElement[] satOptions = driver.findElementsByXPath("//table[@id='panel3']/tbody/tr/td/div/div/div/div/table/tbody/tr[2]/td/div/div[2]/div/div/div/div/div")

		// holds xpath values for satellites that are selected
		String[] satelliteButtons = new String[selectedSatellites.length];

		// get and save xpath values into satelliteButtons
		for (int i = 0; i < satOptions.length; i++) {
			String satName = driver.findElementByXPath("//div[@id='"+satOptions[i].getAttribute("id")+"']/table/tbody/tr/td[4]/table/tbody/tr[1]/td/table/tbody/tr/td[1]/div").getAttribute("innerText");
			for (int j = 0; j  < selectedSatellites.length; j++) {
				if (selectedSatellites[j] == satName){
					satelliteButtons[j] = "//div[@id='"+satOptions[i].getAttribute("id")+"']/table/tbody/tr/td[2]/img"
				}
			}
		}
		// for each satellite to select
		for (String sat : satelliteButtons) {
			for (int decisecond = 0; true; decisecond++){
				// try to click the satellite until it works
				try {
					// unless it is already selected
					if (!(driver.findElementByXPath(sat).getAttribute("title") == "This node and all children nodes are selected.")){
						selenium.click(sat)
					}
					break;
				} catch (Exception e) {
					if (decisecond >= 100) {
						println e
						fail('Timeout looking for satellite buttons.')
					}
				}
				Thread.sleep(100)
			}

		}
		// Go to Data options tab
		selenium.click("id=tab4")
		// Get search parameters from SearchDetails
		HashMap<String, String> tfv = SearchDetails.getTextfieldValues()
		HashMap<String, Integer> rbs = SearchDetails.getRadioSelected()
		HashMap<String, Integer> cbs = SearchDetails.getCheckboxSelected()
		HashMap<String, String[]> sbo = SearchDetails.getSelectboxSelectedOptionsText()

		// wait for Date Options to load
		for (int decisecond = 0; true; decisecond++){
			try {
				if (selenium.isVisible("//div[@id='panel4']/table/tbody/tr/td/table/tbody/tr/td/div/div/table/tbody/tr[1]/td/div")) break;
			} catch (Exception e){
				if (decisecond >= 100) {
					println e
					fail('Timeout waiting for Data Options.')
				}
			}
			Thread.sleep(100);
		}

		for (int quartersecond = 0; true; quartersecond++) {
			try {
				// get the list of text fields on page
				WebElement[] textFields = driver.findElementsByXPath("//input[@type='text']")
				// for each text field in search parameters
				for (String textFieldTitle : tfv.keySet()) {
					// for each text field on page
					for (WebElement textField : textFields) {
						// if the text field on the page has the same title as from search parameters
						if (textField.getAttribute("title") == textFieldTitle) {
							// type the gathered value into the text field
							actions.moveToElement(textField)
							actions.click()
							actions.sendKeys(tfv.get(textFieldTitle))
						}
					}
				}
				actions.build().perform()
				break;
			} catch (Exception e) {
				if (quartersecond >= 12) {
					println e;
					fail('Timeout waiting to enter text field parameters.');
				}
				Thread.sleep(250)
			}
		}

		for (int quartersecond = 0; true; quartersecond++) {
			try {
				// for each selected radio button from search parameters
				for (String radioButtonCategory : rbs.keySet()) {
					// click on it
					actions.moveToElement(driver.findElementByXPath("//table[@title='"+radioButtonCategory+"']/tbody/tr/td["+rbs.get(radioButtonCategory).toString()+"]/span/input"))
					actions.click()
				}
				actions.build().perform()
				break;
			} catch (Exception e) {
				if (quartersecond >= 10) {
					println e;
					fail('Timeout waiting to enter radio button parameters.');
				}
				Thread.sleep(250)
			}
		}

		for (int quartersecond = 0; true; quartersecond++) {
			try {
				// for each selected check box from search parameters
				for (String checkboxCategory : cbs.keySet()) {
					// click on it
					actions.moveToElement(driver.findElementByXPath("//table[@title='"+checkboxCategory+"']/tbody/tr/td["+cbs.get(checkboxCategory).toString()+"]/span/input"))
					actions.click()
				}
				actions.build().perform()
				break;
			} catch (Exception e) {
				if (quartersecond >= 10) {
					println e;
					fail('Timeout waiting to enter checkbox parameters.');
				}
				Thread.sleep(250)
			}
		}

		for (int quartersecond = 0; true; quartersecond++) {
			try {
				// get the list of selectboxes current on the page
				WebElement[] selectBoxes = driver.findElementsByXPath("//select")
				// for each select box title that was gathered from search parameters
				for (String selectboxTitle : sbo.keySet()) {
					// for each select box on page
					for (WebElement selectBox : selectBoxes) {
						// if the select box on the page has the same title as we have, it should paramaterized
						if (selectBox.getAttribute("title") == selectboxTitle) {
							// get options selected from search parameters
							String[] selectedOptions = sbo.get(selectboxTitle)
							// if at least one option was selected
							if (selectedOptions.length > 0) {
								Select sel = new Select(selectBox)
								// deselect all previously selected options ("Any"/default option normally)
								sel.deselectAll()
								// select each option that was gathered from searhc parameters
								for (String option : selectedOptions){
									sel.selectByVisibleText(option)
								}
							}
						}
					}
				}
				break;
			} catch (Exception e) {
				if (quartersecond >= 10) {
					println e;
					fail('Timeout waiting to enter selectbox parameters.');
				}
				Thread.sleep(250)
			}
		}

		// go to submit search tab
		selenium.click("id=tab5")
		// specify the number of products per collection to the max (500)
		selenium.click("id=numSearchResults")
		selenium.click("//option[@value='500']")
	}

	/**
	 * Gets the number of products in the search from the search page.
	 */
	public String searchNumber(){
		// ensure in Search, Submit Search tab
		selClick("id=Search")
		selClick("id=tab5")
		// run the Get total result count link
		selClick("link=Get total result count")
		Thread.sleep(1000);
		// wait for count to finish
		for (int second = 0; true; second++) {
			try {
				if (selenium.isVisible("//img[@alt='Search Complete']")) break;
			} catch (Exception e) {
				if (second == 300) {
					fail('Timeout waiting for product count.')
				}
			}
			Thread.sleep(1000);
		}
		// get count from the popup
		String tmp = selenium.getText("//div/table/tbody/tr/td/table/tbody/tr/td/table/tbody/tr[2]/td/div/div[2]").split("\\s+")[1]
		// close the popup
		selClick("xpath=(.//*[normalize-space(text()) and normalize-space(.)='Loading'])[1]/following::img[14]")
		return tmp
	}

	/**
	 * Default value override of order()
	 * - Default max scenes per cart is 50
	 */
	public String[] order (String sDate, String eDate) {
		order(sDate, eDate, 50)
	}

	/**
	 * Manages the process of adding products/scenes to the EODMS cart
	 */
	public String[] order (String sDate, String eDate, int maxScenes) {
		// whether cart is full/maxScenes is reached
		boolean full = false;
		// max capacity of Cart
		int maxCart = -1;
		// date of last product looked at
		String lastDate = "";
		// XPath to table holding product lists
		String frame = "//div[@id='InitialSearchPanel']/table/tbody";
		// current page that is being looked at
		int pagenum = 0;
		// lowest product number on page
		int lowCount = -1;
		// highest product number on page
		int highCount = -1;
		// total number of products on all pages
		int maxCount = -1;
		// number of products that have been added to the cart
		int productCount = 0;
		// number of scenes that have been added to the cart
		int currentCart = 0;

		// ensure in search tab
		selClick("id=Search")
		Thread.sleep(500);
		// if a start and end date were provided
		if (!(sDate == "" && eDate == "")) {
			String dateType = ""
			// go to date options tab
			selenium.click("id=tab2")
			// wait and see whether DateRange or SeasonalDates are being used
			for (int second = 0;second <= 60; second++) {
				try {
					if (selenium.isVisible("id=DateRangeStartDate")) {
						dateType = 'DateRange'
						break;
					} else if (selenium.isVisible("id=SeasonalDatesStartDate")) {
						dateType = 'SeasonalDates'
						break;
					}
				} catch (Exception e) {}
				Thread.sleep(1000);
			}
			// enter the passed dates
			enterDates(sDate, eDate, dateType)
		}
		// go to Submit search tab
		selClick("id=tab5")
		// press search button
		selClick("//div[@id='panel5']/div/table/tbody/tr[6]/td[2]/div/div/div/div/div/table/tbody/tr[2]/td[2]/table/tbody/tr/td[2]/div")
		// wait for results to load
		for (int second = 0;second <= 60; second++) {
			try {
				if (selenium.isVisible(frame+"/tr[3]/td/table/tbody/tr/td[2]/table/tbody/tr/td[7]/div")) break;
			} catch (Exception e) {}
			Thread.sleep(1000);
		}
		// get number of pages
		int pages = Integer.parseInt(selenium.getText(frame+"/tr[3]/td/table/tbody/tr/td[2]/table/tbody/tr/td[7]/div"))
		// start adding products to cart until a limit is reached
		while (true) {
			try {
				// update page number
				pagenum++;
				// get the string containing the low, high, and max product counts
				String[] scenesInPage = selenium.getText(frame+"/tr[3]/td/table/tbody/tr/td[1]/div").split("\\s+")
				// get low and high product counts
				lowCount = Integer.parseInt(scenesInPage[1])
				highCount = Integer.parseInt(scenesInPage[3])
				// if no max count has been set yet
				if (maxCount == -1) {
					// if there is a max count value in string
					if (pages > 1) {
						maxCount = Integer.parseInt(scenesInPage[5])
						// if there is only one page, there won't be a max count value, only low to high (max count is same as high)
					} else {
						maxCount = highCount
					}
				}
				// for each product in the page
				for (int currentScene = 0; currentScene < ((highCount-lowCount)+1); currentScene++){
					// update product count
					productCount++;
					// row in the table that product is located in
					String productRow = Integer.toString(currentScene+3)
					// wait for product to be visible
					for (int second = 0;second <= 240; second++) {
						try {
							if (selenium.isVisible(frame+"/tr[2]/td/div/div/div/div[2]/table/tbody/tr["+productRow+"]/td[2]/div/span/input")) break;
						} catch (Exception e) {}
						Thread.sleep(250);
					}
					WebElement elem;
					// get the checkbox element and date of the product
					for (int i = 0; i < 100; i++){
						try {
							elem = driver.findElementByXPath(frame+"/tr[2]/td/div/div/div/div[2]/table/tbody/tr["+productRow+"]/td[2]/div/span/input")
							lastDate = driver.findElementByXPath(frame+"/tr[2]/td/div/div/div/div[2]/table/tbody/tr["+productRow+"]/td[4]/div").getText().split("\\s+")[0]
							break;
						} catch (StaleElementReferenceException e) {
							Thread.sleep(100)
							continue;
						}
					}
					// if the element has not already been added to the cart
					if (!driver.findElementByXPath(frame+"/tr[2]/td/div/div/div/div[2]/table/tbody/tr["+productRow+"]/td[2]/div/span/input").isSelected()) {
						// try to add the element to the cart
						selClick(frame+"/tr[2]/td/div/div/div/div[2]/table/tbody/tr["+productRow+"]/td[2]/div/span/input")
						boolean skip = false
						// wait for a popup to load
						for (int quartersecond = 0;quartersecond <= 300; quartersecond++) {
							try {
								// if the add to cart popup appears
								if (selenium.isVisible("xpath=(.//*[normalize-space(text()) and normalize-space(.)='Add to Cart'])[1]/preceding::input[1]")) break;
							} catch (Exception e) {}
							try{
								// if a popup indicating that the product has been ordered previously
								/**
								 // re-order the product
								 // Note: if this is re-enabled, then there will be duplicates requested. The current system starts a new search 
								 // from the end-date of the most recently looked at product. If there were multiple products on the same date, 
								 // those same objects will re-appear in the search for the next collection of products. This should be fixed
								 selenium.click("//div[(text() = 'Re-order' or . = 'Re-order')]")
								 */
								// do not re-order previously ordered products
								selenium.click("//div[(text() = 'Do not re-order' or . = 'Do not re-order')]")
								// skip (remove this if previously ordered products will be ordered again)
								skip = true
								break;
							} catch (Exception e){}
							try{
								// if a popup indicating that the product is not available in the archive
								// close the popup
								WebElement[] tables = driver.findElementsByXPath('//table[@class="resizableContentPanel"]')
								boolean exit = false
								for (WebElement table : tables) {
									if (table.getAttribute('aria-label') == 'Order Imagery Product') {
										WebElement closeBtn = table.findElementByXPath('./tbody/tr[2]/td/table/tbody/tr[2]/td/table/tbody/tr/td[3]/table/tbody/tr/td/div/div/div/div/div/table/tbody/tr[2]/td[2]/table/tbody/tr/td[2]/div')
										js.executeScript("arguments[0].click()", closeBtn)
										// and skip to the next product
										skip = true
										exit = true
										break;
									}
								}
								if (exit) break;
							} catch (Exception e){println e}
							Thread.sleep(250);
						}
						// if the product reached the add to cart popup
						if (skip == false) {
							// get the cart limits (current number in cart and max number in cart)
							String[] cartLimits = selenium.getText("//div[@class='addToCartLimitPanel']/div").split("\\s+")
							currentCart = Integer.parseInt(cartLimits[0])
							// update maxCart if no value yet
							if (maxCart == -1) {
								maxCart = Integer.parseInt(cartLimits[4])
								// cart max is the lowest of maxCart (max allowed by EODMS) and maxScenes (max passed by program)
								maxCart = (maxCart < maxScenes) ? maxCart : maxScenes
							}
							// get all checkboxes for scenes in product
							WebElement[] scanProducts = driver.findElementsByXPath("//table[@class='basicTable']/tbody/tr/td[6]/span/input")
							if (currentCart+scanProducts.length > maxCart) {
								// if not all scenes will fit in the cart
								// mark as cart being full
								full = true
							} else {
								// if all scenes will fit
								// click each scene checkbox
								for (WebElement scanProduct : scanProducts) {
									actions.moveToElement(scanProduct)
									actions.click()
								}
								actions.build().perform()
							}
							// click button to update cart
							selenium.click("//td[3]/table/tbody/tr/td/div/div/div/div/div/table/tbody/tr[2]/td[2]/table/tbody/tr/td[2]/div")
						}
					}
					// if the cart is full, stop adding
					if (full == true) break;
				}
				// if the last page has been completed or cart is full, stop adding
				if (pagenum >= pages || full == true) break;
				// otherwise, go to the next page
				WebElement elem = driver.findElementByXPath(frame+"/tr[3]/td/table/tbody/tr/td[2]/table/tbody/tr/td[8]/div")
				// JavascriptExecutor is used here because sometimes next-page button is hidden behind an invisible element
				js.executeScript("arguments[0].click()", elem)
			} catch (Exception e) {
				println e
				fail('Error in ordering scenes')
			}
		}
		// return informative string array to main (used for determining future carts)
		String[] output = [lastDate, productCount.toString(), full.toString(), currentCart.toString()]
		return output;
	}

	/**
	 * Submits an order once the cart is full (according to maxCart) or all products are collected
	 */
	public void finalOrder(){
		// go to the cart page
		selClick("//div[@id='Cart']/div/div/div/div/div/table/tbody/tr[2]/td[2]")
		Thread.sleep(1000);
		// click on the Submit Satellite Product Order button
		selClick("//tr[3]/td/table/tbody/tr/td/div/div/div/div/div/table/tbody/tr[2]/td[2]/table/tbody/tr/td[2]/div")
		// click on the Order button
		Thread.sleep(1000);
		for (int quartersecond = 0; quartersecond <= 240; quartersecond++) {
			try {
				WebElement elem = driver.findElementByXPath("//div[@title='Order']/div/div/div/div/table/tbody/tr[2]/td[2]")
				js.executeScript("arguments[0].click()", elem)
				break;
			} catch (Exception e){
				if (quartersecond == 240) {
					fail('Timed out trying to click Order button.')
				}
			}
			Thread.sleep(250)
		}
		// click on the OK button after order appears
		for (int quartersecond = 0; quartersecond <= 240; quartersecond++) {
			try {
				WebElement elem = driver.findElementByXPath("//div[@title='OK']/div/div/div/div/table/tbody/tr[2]/td[2]/table")
				js.executeScript("arguments[0].click()", elem)
				break;
			} catch (Exception e) {
				if (quartersecond == 240) {
					fail('Timed out waiting to press OK on order checkout.')
				}
			}
			Thread.sleep(250);
		}
		Thread.sleep(3000);
	}
	
	public boolean clearAOI(String region){
		try{
			selClick("id=Search")
			selClick("id=tab1")
			WebElement aoiTable = driver.findElementByXPath("//div[@id='panel1']/div/table/tbody/tr[12]/td/table")
			if (aoiTable.getAttribute("aria-hidden") == "true") {
				selClick("link=Use a Saved Area of Interest")
			}
			WebElement[] aois = aoiTable.findElementsByXPath("./tbody/tr/td/table/tbody/tr")
			for (WebElement aoi : aois) {
				if (aoi.findElementByXPath("./td[1]/a").getAttribute("title") == region) {
					js.executeScript("arguments[0].click()", aoi.findElementByXPath("./td[2]/a"))
					WebElement[] tables = driver.findElementsByXPath('//table[@class="resizableContentPanel"]')
					for (int quartersecond = 0; quartersecond <= 120; quartersecond++){
						boolean exit = false
						for (WebElement table : tables) {
							if (table.getAttribute('aria-label') == 'Are you sure you want to delete this Saved AOI?') {
								WebElement okBtn = table.findElementByXPath('./tbody/tr[2]/td/table/tbody/tr[2]/td/table/tbody/tr/td[2]/table/tbody/tr/td/div/div/div/div/div/table/tbody/tr[2]/td[2]/table/tbody/tr/td[2]/div')
								js.executeScript("arguments[0].click()", okBtn)
								break;
							}
						}
						return true
					}
				}
			}
			
		} catch (Exception e) {
			return false
		}
	}

	/**
	 * Logs user out of EODMS
	 */
	public void logout(){
		selClick("id=gwt-uid-2")
		selClick("id=gwt-uid-6")
		for (int second = 0;second <= 60; second++) {
			try {
				if (selenium.isVisible("link=Login")) break;
			} catch (Exception e) {}
			Thread.sleep(1000);
		}
		Thread.sleep(1000);
	}

	//reset selenium
	public void selen(WebDriverBackedSelenium selenium){
		this.selenium = selenium;
	}

	public WebBrowsing(WebDriverBackedSelenium selenium, StartGUI gui){
		this.selenium = selenium;
		this.gui = gui;
	}
}

Script


/**
 * Main control for EODMS Scene Retriever.
 * 
 * Takes user input for start of run and controls WebBrowsing functions.
 * 
 * @author Kieran Moynihan, Khang Nguyen
 */

import static com.kms.katalon.core.checkpoint.CheckpointFactory.findCheckpoint
import static com.kms.katalon.core.testcase.TestCaseFactory.findTestCase
import static com.kms.katalon.core.testdata.TestDataFactory.findTestData
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import com.kms.katalon.core.checkpoint.Checkpoint as Checkpoint
import com.kms.katalon.core.checkpoint.CheckpointFactory as CheckpointFactory
import com.kms.katalon.core.mobile.keyword.MobileBuiltInKeywords as MobileBuiltInKeywords
import com.kms.katalon.core.model.FailureHandling as FailureHandling
import com.kms.katalon.core.testcase.TestCase as TestCase
import com.kms.katalon.core.testcase.TestCaseFactory as TestCaseFactory
import com.kms.katalon.core.testdata.TestData as TestData
import com.kms.katalon.core.testdata.TestDataFactory as TestDataFactory
import com.kms.katalon.core.testobject.ObjectRepository as ObjectRepository
import com.kms.katalon.core.testobject.TestObject as TestObject
import com.kms.katalon.core.webservice.keyword.WSBuiltInKeywords as WSBuiltInKeywords
import com.kms.katalon.core.webui.driver.DriverFactory as DriverFactory
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUiBuiltInKeywords
import internal.GlobalVariable as GlobalVariable
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import com.kms.katalon.core.mobile.keyword.MobileBuiltInKeywords as Mobile
import com.kms.katalon.core.webservice.keyword.WSBuiltInKeywords as WS
import org.openqa.selenium.WebDriver
import org.openqa.selenium.WebElement
import org.openqa.selenium.JavascriptExecutor
import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium
import static org.junit.Assert.*
import java.util.regex.Pattern
import static org.apache.commons.lang3.StringUtils.join
import java.text.SimpleDateFormat

// launch startGUI
// StartGUI provides two input fields for user to enter a username and password for EODMS
StartGUI gui = new StartGUI();
// wait for user to finish entering login
while(gui.done() == false){
	Thread.sleep(250)
}
gui.dispose()

// open browser and go to URL
WebUI.openBrowser('https://www.eodms-sgdot.nrcan-rncan.gc.ca/index_en.jsp')
def driver = DriverFactory.getWebDriver()
String baseUrl = "https://www.eodms-sgdot.nrcan-rncan.gc.ca/index_en.jsp"
selenium = new WebDriverBackedSelenium(driver, baseUrl)
selenium.open("https://www.eodms-sgdot.nrcan-rncan.gc.ca/index_en.jsp")
WebBrowsing web = new WebBrowsing(selenium, gui);
// select the frame containing JavaScript for EODMS
selenium.selectFrame("index=0")
// wait for login button to load with the page
for (int quartersecond = 0;; quartersecond++) {
	if (quartersecond >= 240) fail("timeout");
	try { if (selenium.isVisible("link=Login")) break; } catch (Exception e) {}
	Thread.sleep(250);
}

//num will be the number of scans returned by the search
int num;

SearchParameters SearchDetails;

try{
	//log the user into EODMS using the provided login information
	web.login();
	
	//wait for the user to press the start button on the WaitGUI
	//the user is to enter the search parameters into the EODMS interface and then press start
	//This tool will read the webelements after user presses start in order to determine which options were selected
	WaitGUI gui2 = new WaitGUI();
	while (gui2.done() == false){
		Thread.sleep(250)
	}
	gui2.dispose()
	
	//get search parameters from EODMS interface
	SearchDetails = web.getSearch();
	dateType = SearchDetails.getDateType()
	startDate = SearchDetails.getStartDate()
	endDate = SearchDetails.getEndDate()
	
	//gets the number of products returned by the search
	println 'Getting number of scans...'
	num = Integer.parseInt(web.searchNumber());
	// List of years, used for separated SeasonalDates queries
	int[] yearList;
	// Maximum number of scenes to place in cart
	// Once this number of scenes is reached, the cart will be ordered 
	// and a new cart will be filled.
	int MAXSCENES = 50;
	// maxScenes is reset to MAXSCENES for each loop of cart filling
	int maxScenes = MAXSCENES;
	// Separated DateRange and SeasonalDates operations as SeasonalDates splits the dates entered into years and runs separately
	// This is necessary as the endDate must be decreased incrementally as carts are filled, and doing so with seasonal dates will affect 
	// all other years to be searched.
	// ex. 2014-04-01 -> 2016-09-30 vs. 2014-04-01 -> 2016-05-15 (in years 2014 and 2015, the search will only run to YYYY-05-15)
	if (dateType == 'SeasonalDates') {
		// get start and end years from date strings
		int startYear = Integer.parseInt(startDate.substring(0, 4))
		int endYear = Integer.parseInt(endDate.substring(0, 4))
		// determine the year gap and make a list of years
		int yearGap = endYear-startYear
		yearList = new int[(yearGap)+1]
		yearList[0] = startYear
		for (int i = 1; i < yearGap+1; i++) {
			yearList[i] = startYear+i;
		}
		// for each year, run the search over the specified range
		for (int year : yearList) {
			// create year string for the current year
			sDate = year.toString()+startDate.substring(4)
			eDate = year.toString()+endDate.substring(4)
			// go to the search tab
			for (int second = 0;second <= 60; second++) {
				try {
					selenium.click("id=Search")
					break;
				} catch (Exception e) {}
				Thread.sleep(1000);
			}
			Thread.sleep(250);
			// update the search dates to the current year
			web.enterDates(sDate, eDate, dateType);
			SearchDetails.setStartDate(sDate);
			SearchDetails.setEndDate(eDate);
			// get the number of products in the current year
			yearNum = Integer.parseInt(web.searchNumber());
			// if there are products for the year, get them (this is the same as dateRange)
			if (yearNum > 0) {
				while (true) {
					println 'Adding scans to cart...'
					web.logout()
					web.login()
					web.search(SearchDetails)
					// return from web.order:
					// [Last date that was seen from order (end next search here), 
					// number of products carted,
					// whether the cart was full,
					// number of scenes carted (can be multiple scenes per product)]
					String[] orderOut = web.order(sDate, eDate, maxScenes)
					// new end date
					eDate = orderOut[0]
					SearchDetails.setEndDate(eDate)
					// number of products remaining in year
					yearNum -= Integer.parseInt(orderOut[1])
					// number of scenes remaining before cart full
					maxScenes -= Integer.parseInt(orderOut[3])
					// if cart was full, assume there are more products
					if (Boolean.valueOf(orderOut[2]) == true) {
						println 'There are more products'
						// submit order
						web.finalOrder()
						// reset maxScenes
						maxScenes = MAXSCENES
					// if there are no more products in the year, finished searching year
					} else if (yearNum <= 0) {
						break;
					}
				}
			}
		}
	} else {
		while (true) {
			println 'Adding scans to cart...'
			web.logout()
			web.login()
			web.search(SearchDetails)
			// return from web.order:
			// [Last date that was seen from order (end next search here), 
			// number of products carted,
			// whether the cart was full,
			// number of scenes carted (can be multiple scenes per product)]
			String[] orderOut = web.order(startDate, endDate, maxScenes)
			// new end date
			endDate = orderOut[0]
			SearchDetails.setEndDate(endDate)
			// number of products remaining
			num -= Integer.parseInt(orderOut[1])
			// number of scenes remaining before cart full
			maxScenes -= Integer.parseInt(orderOut[3])
			// if cart was full, assume there are more products
			if (Boolean.valueOf(orderOut[2]) == true) {
				println 'There are more products'
				// submit order
				web.finalOrder()
				// reset maxScenes
				maxScenes = MAXSCENES
			// if there are no more products, finished searching
			} else if (num <= 0) {
				break;
			}
		}
	}
	// last submission
	println 'Got all products'
	web.finalOrder();
} catch (Exception e) {
	println e
} finally {
	if (!web.clearAOI(SearchDetails.getRegion())) {
		web.logout()
		web.login()
		web.clearAOI(SearchDetails.getRegion())
	}
	driver.quit()
}