Java tutorial
/* * Copyright 2005-2013 The Kuali Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ecl1.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kuali.kra.test.infrastructure; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.kuali.kra.infrastructure.Constants; import org.kuali.rice.krad.bo.BusinessObject; import org.openqa.selenium.Alert; import org.openqa.selenium.By; import org.openqa.selenium.NoAlertPresentException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.Clock; import org.openqa.selenium.support.ui.Select; import org.openqa.selenium.support.ui.SystemClock; import org.openqa.selenium.support.ui.Wait; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Function; public abstract class KcSeleniumHelper { protected static final String DEFAULT_USER = "quickstart"; private static final String RESEARCHER_TAB_TITLE = "Researcher"; private static final String UNIT_TAB_TITLE = "Unit"; private static final String CENTRAL_ADMIN_TAB_TITLE = "Central Admin"; private static final String MAINTENANCE_TAB_TITLE = "Maintenance"; private static final String SYSTEM_ADMIN_TAB_TITLE = "System Admin"; private static final String HELP_PAGE_TITLE = "KC"; private static final String CREATE_MAINTENANCE_DOCUMENT_LINK = "maintenance.do?businessObjectClassName=%s&methodToCall=start"; private static final String METHOD_TO_CALL_PREFIX = "methodToCall."; private static final String SHOW_ALL_TABS_BUTTON = METHOD_TO_CALL_PREFIX + "showAllTabs"; private static final String HIDE_ALL_TABS_BUTTON = METHOD_TO_CALL_PREFIX + "hideAllTabs"; private static final String TOGGLE_TAB_BUTTON = METHOD_TO_CALL_PREFIX + "toggleTab"; private static final String YES_BUTTON = "methodToCall.processAnswer.button0"; private static final String NO_BUTTON = "methodToCall.processAnswer.button1"; private static final String SAVE_BUTTON = METHOD_TO_CALL_PREFIX + "save"; private static final String RELOAD_BUTTON = METHOD_TO_CALL_PREFIX + "reload"; private static final String CLOSE_BUTTON = METHOD_TO_CALL_PREFIX + "close"; private static final String ROUTE_BUTTON = METHOD_TO_CALL_PREFIX + "route"; private static final String APPROVE_BUTTON = METHOD_TO_CALL_PREFIX + "approve"; private static final String BLANKET_APPROVE_BUTTON = METHOD_TO_CALL_PREFIX + "blanketApprove"; private static final String ERRORS_FOUND_ON_PAGE = "error(s) found on page"; private static final String SAVE_SUCCESS_MESSAGE = "Document was successfully saved"; private static final String RELOAD_SUCCESS_MESSAGE = "Document was successfully reloaded"; private static final String ROUTE_SUCCESS_MESSAGE = "Document was successfully submitted"; private static final String SUBMIT_SUCCESS_MESSAGE = "Document was successfully approved"; private static final Logger logger = LoggerFactory.getLogger(KcSeleniumHelper.class); private WebDriver driver; private enum TabCommand { OPEN, CLOSE; public boolean contains(String tabCommand) { boolean contains = StringUtils.equalsIgnoreCase(tabCommand, this.name()); for (TabCommand command : TabCommand.values()) { if (command != this) { contains |= !StringUtils.equalsIgnoreCase(tabCommand, command.name()); } } return contains; } } protected KcSeleniumHelper(WebDriver driver) { this.driver = driver; } /** * Checks for the Login web page and if it exists, logs in as the default user. */ public final void login() { if (StringUtils.equals(driver.getTitle(), "Login")) { logger.debug("Logging in as {}", DEFAULT_USER); set("__login_user", DEFAULT_USER); click("//input[@value='Login']"); logger.debug("Logged in as {}", DEFAULT_USER); } } /** * Logs in as the default backdoor user. */ public final void loginBackdoor() { loginBackdoor(DEFAULT_USER); } /** * Logs in as the backdoor user {@code loginUser}. */ public final void loginBackdoor(final String loginUser) { logger.debug("Logging in as {}", loginUser); clickResearcherTab(); set("backdoorId", loginUser); click("imageField"); logger.debug("Logged in as {}", loginUser); } /** * Clicks the Researcher tab. */ public final void clickResearcherTab() { click(RESEARCHER_TAB_TITLE); } /** * Clicks the Unit tab. */ public final void clickUnitTab() { click(UNIT_TAB_TITLE); } /** * Clicks the Central Admin tab. */ public final void clickCentralAdminTab() { click(CENTRAL_ADMIN_TAB_TITLE); } /** * Clicks the Maintenance tab. */ public final void clickMaintenanceTab() { click(MAINTENANCE_TAB_TITLE); } /** * Click the System Admin tab. */ public final void clickSystemAdminTab() { click(SYSTEM_ADMIN_TAB_TITLE); } /** * Click the Expand All button. */ public final void clickExpandAll() { if (findElement(SHOW_ALL_TABS_BUTTON, true)) { click(SHOW_ALL_TABS_BUTTON); } } /** * Click the Collapse All button. */ public final void clickCollapseAll() { if (findElement(HIDE_ALL_TABS_BUTTON, true)) { click(HIDE_ALL_TABS_BUTTON); } } /** * Gets the value of a control field. * * @param locator the id, partial name, partial title, or partial link name of the element to click on * @return the value of the element */ public final String get(final String locator) { return get(locator, false); } /** * Gets the value of a control field. * * @param locator the id, name, title, or link name of the element to click on depending on the value of {@code exact} * @param exact whether the locator should match exactly * @return the value of the element */ public final String get(final String locator, final boolean exact) { String value = Constants.EMPTY_STRING; WebElement element = getElement(locator, exact); String tagName = element.getTagName(); String elementType = element.getAttribute("type"); logger.debug("Getting the value of {} from the element which has a tag of {} and a type of {}", new String[] { value, tagName, elementType }); if (StringUtils.equals(tagName, "input") && StringUtils.equals(elementType, "checkbox")) { value = getCheckbox(element); } else if (StringUtils.equals(tagName, "input") && StringUtils.equals(elementType, "radio")) { value = getRadio(element); } else if (StringUtils.equals(tagName, "select")) { value = getSelect(element); } else { value = element.getAttribute("value"); } logger.debug("Got the value of {} from the element which has a tag of {} and a type of {}", new String[] { value, tagName, elementType }); return value; } /** * Gets the value of a checkbox. * * @param element the located parent element */ private final String getCheckbox(final WebElement element) { return BooleanUtils.toString(element.isSelected(), "on", "off"); } /** * Gets the value of a radio button. * * @param locator the id, name, title, or link name of the element to set depending on the value of {@code exact} * @param exact whether the locator should match exactly */ private final String getRadio(final WebElement element) { return BooleanUtils.toString(element.isSelected(), "on", "off"); } /** * Gets the value of a select. * * @param element the located parent element */ private String getSelect(final WebElement element) { Select select = new Select(element); return select.getFirstSelectedOption().getText(); } /** * Sets the value of a control field. * * @param locator the id, partial name, partial title, or partial link name of the element to set * @param value the new value of the element */ public final void set(final String locator, final String value) { set(locator, false, value); } /** * Sets the value of a control field. * * @param locator the id, name, title, or link name of the element to set depending on the value of {@code exact} * @param exact whether the locator should match exactly * @param value the new value of the element */ public final void set(final String locator, final boolean exact, final String value) { WebElement element = getElement(locator, exact); String tagName = element.getTagName(); String elementType = element.getAttribute("type"); logger.debug("Setting the value to {} of the element which has a tag of {} and a type of {}", new String[] { value, tagName, elementType }); if (StringUtils.equals(tagName, "input") && StringUtils.equals(elementType, "checkbox")) { setCheckbox(element, value); } else if (StringUtils.equals(tagName, "input") && StringUtils.equals(elementType, "file")) { setFile(element, value); } else if (StringUtils.equals(tagName, "input") && StringUtils.equals(elementType, "radio")) { setRadio(locator, exact, value); } else if (StringUtils.equals(tagName, "select")) { setSelect(element, value); } else { element.clear(); element.sendKeys(value); } logger.debug("Set the value to {} of the element which has a tag of {} and a type of {}", new String[] { value, tagName, elementType }); } /** * Sets the value of a checkbox. * * @param element the located parent element * @param value the new value of the element */ private final void setCheckbox(final WebElement element, final String value) { boolean booleanValue = BooleanUtils.toBoolean(value); if ((booleanValue && !element.isSelected()) || (!booleanValue && element.isSelected())) { element.click(); } } /** * Sets the value of a file upload. * * @param element the located parent element * @param value the new value of the element */ private final void setFile(final WebElement element, final String value) { element.sendKeys(value); } /** * Sets the value of a radio button. * * @param locator the id, name, title, or link name of the element to set depending on the value of {@code exact} * @param exact whether the locator should match exactly * @param value the new value of the element */ private final void setRadio(final String locator, final boolean exact, final String value) { WebElement radio = new ElementExistsWaiter(locator + " with value " + value + " not found") .until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { WebElement inputElement = null; for (WebElement radio : getElementsByName(locator, exact)) { String radioValue = radio.getAttribute("value"); if (StringUtils.equals(radioValue, value)) { inputElement = radio; break; } } return inputElement; } }); radio.click(); } /** * Sets the value of a select. * * @param element the located parent element * @param value the new value of the element */ private void setSelect(final WebElement element, final String value) { Select select = new Select(element); for (WebElement option : select.getOptions()) { String optionText = option.getText(); if (StringUtils.contains(optionText, value)) { option.click(); break; } } } /** * Clicks on an element in the web page. * <p> * Using any of the {@code click()} methods is the preferred way to click on an element due to the login process. If the login web page is * encountered, the user will be automatically logged in and the given button will be clicked. * * @param locator the id, partial name, partial title, or partial link name of the element to click on */ public final void click(final String locator) { click(locator, false, null); } /** * Clicks on an element in the web page. * <p> * Using any of the {@code click()} methods is the preferred way to click on an element due to the login process. If the login web page is * encountered, the user will be automatically logged in and the given button will be clicked. * * @param locator the id, name, title, or link name of the element to click on depending on the value of {@code exact} * @param exact whether the locator should match exactly */ public final void click(final String locator, final boolean exact) { click(locator, exact, null); } /** * Clicks on an element in the web page, asserting that the next page contains {@code nextPageTitle}. * <p> * Using any of the {@code click()} methods is the preferred way to click on an HTML element due to the login process. If the login web page is * encountered, the user will be automatically logged in and the given button will be clicked. * * @param locator the id, name, title, or link name of the element to click on depending on the value of {@code exact} * @param exact whether the locator should match exactly * @param nextPageTitle the expected title of the next web page (may be null) */ public final void click(final String locator, final boolean exact, final String nextPageTitle) { logger.debug("Clicking the element at {}", locator); getElement(locator, exact).click(); login(); if (nextPageTitle != null) { assertPageContains(nextPageTitle); } logger.debug("Clicked the element at {}", locator); } /** * Clicks on an element in the web page only if it exists. * * @param locator the id, partial name, partial title, or partial link name of the element to click on */ public final void toggle(final String locator) { toggle(locator, false); } /** * Clicks on an element in the web page only if it exists. * * @param locator the id, name, title, or link name of the element to click on depending on the value of {@code exact} * @param exact whether the locator should match exactly */ public final void toggle(final String locator, final boolean exact) { if (findElement(locator, exact)) { click(locator, exact); } } /** * Clicks on the Yes answer in the web page, if it exists. */ public final void clickYesAnswer() { if (findElement(YES_BUTTON, true)) { click(YES_BUTTON); } } /** * Clicks on the No answer in the web page, if it exists. */ public final void clickNoAnswer() { if (findElement(NO_BUTTON, true)) { click(NO_BUTTON); } } /** * Opens the tab with id containing {@code tabTitle} on the web page. The {@code tabTitle} is similar to the display text of the tab but has all non-word * characters removed. It is also used in the id of the element, where it is the text between "tab-" and "-imageToggle". For formatting purposes, * {@code tabTitle} can be separated with spaces which will be removed on search. * * @param tabTitle the title of the tab on the web page */ public final void openTab(final String tabTitle) { WebElement tab = new ElementExistsWaiter("Tab with title " + tabTitle + " not found on page") .until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { return getElementById(getTabId(tabTitle)); } }); clickTab(tab, TabCommand.OPEN); } /** * Opens the tab with index {@code index} on the web page. The {@code index} should be a number between {@code 0} and the number of active * tabs on the page. It does not count inactive hidden tabs on the page. * * @param index the index of the tab on the web page */ public final void openTab(final int index) { WebElement tab = new ElementExistsWaiter("Tab with index " + index + " not found on page") .until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { WebElement tab = null; List<WebElement> tabs = getElementsByName(TOGGLE_TAB_BUTTON, false); if (0 <= index && index < tabs.size()) { tab = tabs.get(index); } return tab; } }); clickTab(tab, TabCommand.OPEN); } /** * Closes the tab with id containing {@code tabTitle} on the web page. The {@code tabTitle} is similar to the display text of the tab but has all non-word * characters removed. It is also used in the id of the element, where it is the text between "tab-" and "-imageToggle". For formatting purposes, * {@code tabTitle} can be separated with spaces which will be removed on search. * * @param tabTitle the title of the tab on the web page */ public final void closeTab(final String tabTitle) { WebElement tab = new ElementExistsWaiter("Tab with title " + tabTitle + " not found on page") .until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { return getElementById(getTabId(tabTitle)); } }); clickTab(tab, TabCommand.CLOSE); } /** * Closes the tab with index {@code index} on the web page. The {@code index} should be a number between {@code 0} and the number of active * tabs on the page. It does not count inactive hidden tabs on the page. * * @param index the index of the tab on the web page */ public final void closeTab(final int index) { WebElement tab = new ElementExistsWaiter("Tab with index " + index + " not found on page") .until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { WebElement tab = null; List<WebElement> tabs = getElementsByName(TOGGLE_TAB_BUTTON, false); if (0 <= index && index < tabs.size()) { tab = tabs.get(index); } return tab; } }); clickTab(tab, TabCommand.CLOSE); } /** * Returns the generated id of a tab based on the {@code tabTitle}, which appears between "tab-" and "-imageToggle" and without whitespace. * * @param tabTitle the title of the tab on the web page */ private String getTabId(final String tabTitle) { return "tab-" + StringUtils.deleteWhitespace(tabTitle) + "-imageToggle"; } /** * Clicks the {@code tab} that contains the text {@code command} (typically 'open' or 'close'). * * @param tab the tab to click * @param command the instruction to either open or close the tab */ private void clickTab(final WebElement tab, final TabCommand command) { logger.debug("Clicking tab {} with requested command {}", tab.toString(), command.name()); String tabCommand = StringUtils.substringBefore(tab.getAttribute("title"), " "); if (command.contains(tabCommand)) { tab.click(); logger.debug("Clicked the tab {} with actual command {}", tab.toString(), tabCommand); } } /** * Gets the document number from a document's web page. * * @return the document's number */ public String getDocumentNumber() { final String locator = "//div[@id='headerarea']/div/table/tbody/tr[1]/td[1]"; return getDocumentNumber(locator); } /** * Gets the document number from a document's web page using the given XPath {@code locator}. * * @param locator the xpath string to locate the document number * @return the document's number */ protected final String getDocumentNumber(final String locator) { WebElement documentNumber = new ElementExistsWaiter(locator + " not found") .until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { return getElementByXPath(locator); } }); return documentNumber.getText(); } /** * Do a document search looking for the a specific document based upon its document number. The following occurs on a Document Search: * <ol> * <li>The Doc Search button is clicked on</li> * <li>In the Doc Search web page, the document number is filled in with the given value</li> * <li>The first item in the results is returned</li> * <li>The document number link is clicked on</li> * </ol> * * @param documentNumber the document number to search for */ public final void docSearch(final String documentNumber) { logger.debug("Searching for and opening document number {}", documentNumber); click("Document Search"); set("documentId", documentNumber); click("methodToCall.search"); click(documentNumber, true); logger.debug("Found and opening document number {}", documentNumber); } /** * Performs a single value Lookup. The following occurs on a Lookup: * <ol> * <li>The Lookup button is clicked on</li> * <li>In the Lookup web page, the search button is clicked on</li> * <li>The first item in the results is returned</li> * <li>The web page resulting from clicking on "Return Value" is returned</li> * </ol> * To find the Lookup button, the name attribute of all Lookup buttons are examined to see if it contains the given {@code tag}. Make sure to pick * a {@code tag} that is unique for that Lookup. * <p> * Using any of the {@code lookup()} methods is the preferred way to perform a lookup due to the login process. If the login web page is * encountered, the user will be automatically logged in and the lookup will be performed. * * @param tag identifies the Lookup button to click on */ public final void lookup(final String tag) { lookup(tag, null, null); } /** * Performs a single value Lookup. The following occurs on a Lookup: * <ol> * <li>The Lookup button is clicked on</li> * <li>In the Lookup web page, the given field is filled in with the given value</li> * <li>In the Lookup web page, the search button is clicked on</li> * <li>The first item in the results is returned</li> * </ol> * To find the Lookup button, the name attribute of all Lookup buttons are examined to see if it contains the given {@code tag}. Make sure to pick * a {@code tag} that is unique for that Lookup. * <p> * Using any of the {@code lookup()} methods is the preferred way to perform a lookup due to the login process. If the login web page is * encountered, the user will be automatically logged in and the lookup will be performed. * * @param tag identifies the Lookup button to click on * @param searchFieldId the id of the search field (may be null) * @param searchFieldValue the value to insert into the search field (may be null if id is null) */ public final void lookup(final String tag, final String searchFieldId, final String searchFieldValue) { logger.debug("Looking up single result for tag {} for search field {} with search value {}", new String[] { tag, searchFieldId, searchFieldValue }); clickLookup(tag); if (searchFieldId != null) { assertNotNull("searchValue is null", searchFieldValue); set(searchFieldId, searchFieldValue); } click("methodToCall.search"); assertTableCellValueContains("row", 0, 0, "return value"); click("return value", true); waitForFormLoad(); logger.debug("Looked up single result for tag {} for search field {} with search value {}", new String[] { tag, searchFieldId, searchFieldValue }); } /** * Performs a multiple value Lookup. The following occurs on a Lookup: * <ol> * <li>The Lookup icon is clicked on</li> * <li>In the Lookup web page, the search button is clicked on</li> * <li>The "Select All from All Pages" button is clicked on</li> * <li>The web page resulting from clicking on "Return Selected" is returned</li> * </ol> * To find the Lookup button, the name attribute of all Lookup buttons are examined to see if it contains the given {@code tag}. Make sure to pick * a {@code tag} that is unique for that Lookup. * <p> * Using any of the {@code multiLookup()} methods is the preferred way to perform a lookup due to the login process. If the login web page is * encountered, the user will be automatically logged in and the lookup will be performed. * * @param tag identifies the Lookup button to click on */ public final void multiLookup(final String tag) { multiLookup(tag, null, null); } /** * Performs a multiple value Lookup. The following occurs on a Lookup: * <ol> * <li>The Lookup icon is clicked on</li> * <li>The search field is filled in with the given search value.</li> * <li>In the Lookup web page, the search button is clicked on</li> * <li>The "Select All from All Pages" button is clicked on</li> * <li>The web page resulting from clicking on "Return Selected" is returned</li> * </ol> * To find the Lookup button, the name attribute of all Lookup buttons are examined to see if it contains the given {@code tag}. Make sure to pick * a {@code tag} that is unique for that Lookup. * <p> * Using any of the {@code multiLookup()} methods is the preferred way to perform a lookup due to the login process. If the login web page is * encountered, the user will be automatically logged in and the lookup will be performed. * * @param tag identifies the Lookup button to click on * @param searchFieldId the id of the search field (may be null) * @param searchFieldValue the value to insert into the search field (may be null if id is null) */ public final void multiLookup(final String tag, final String searchFieldId, final String searchFieldValue) { logger.debug("Looking up multiple results for tag {} for search field {} with search value {}", new String[] { tag, searchFieldId, searchFieldValue }); clickLookup(tag); if (searchFieldId != null) { assertNotNull("searchValue is null", searchFieldValue); set(searchFieldId, searchFieldValue); } click("methodToCall.search"); click("methodToCall.selectAll"); click("methodToCall.prepareToReturnSelectedResults"); logger.debug("Looked up multiple result for tag {} for search field {} with search value {}", new String[] { tag, searchFieldId, searchFieldValue }); } /** * Clicks a Lookup element that has a name attribute containing {@code tag}. * * @param tag identifies the Lookup button to click on */ private void clickLookup(final String tag) { final String locator = "//input[starts-with(@name,'methodToCall.performLookup') and contains(@name,'" + tag + "')]"; WebElement lookup = new ElementExistsWaiter(locator + " not found") .until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { return getElementByXPath(locator); } }); lookup.click(); login(); } /** * Creates a new maintenance document based on {@code className} and verifies that the next page has the title {@code nextPageTitle}. * * @param className the BO class name of this maintenance document * @param nextPageTitle the title of the maintenance document on the next page */ public final void createNewMaintenanceDocument(final String className, final String nextPageTitle) { final String locator = "//a[@href = '" + String.format(CREATE_MAINTENANCE_DOCUMENT_LINK, className) + "']"; WebElement createNewButton = new ElementExistsWaiter(locator + " not found") .until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { return getElementByXPath(locator); } }); createNewButton.click(); if (nextPageTitle != null) { assertTitleContains(nextPageTitle); } } /** * Edits an existing maintenance document based on {@code className} and {@code searchValues} and verifies that the next page has the title * {@code nextPageTitle}. * * @param className the BO class name of this maintenance document * @param searchValues the search values of the maintenance document to edit * @param nextPageTitle the title of the maintenance document on the next page */ public final void editExistingMaintenanceDocument(final String className, final Map<String, String> searchValues, final String nextPageTitle) { for (Entry<String, String> searchValue : searchValues.entrySet()) { set(searchValue.getKey(), searchValue.getValue()); } click("methodToCall.search"); click("edit"); if (nextPageTitle != null) { assertTitleContains(nextPageTitle); } } /** * Copies an existing maintenance document based on {@code className} and {@code searchValues} and verifies that the next page has the title * {@code nextPageTitle}. * * @param className the BO class name of this maintenance document * @param searchValues the search values of the maintenance document to copy * @param nextPageTitle the title of the maintenance document on the next page */ public final void copyExistingMaintenanceDocument(final String className, final Map<String, String> searchValues, final String nextPageTitle) { for (Entry<String, String> searchValue : searchValues.entrySet()) { set(searchValue.getKey(), searchValue.getValue()); } click("methodToCall.search"); click("copy"); if (nextPageTitle != null) { assertTitleContains(nextPageTitle); } } /** * Reload a document by clicking on the Reload button. */ public final void reloadDocument() { waitForFormLoad(); click(RELOAD_BUTTON); } /** * Save a document by clicking on the Save button. */ public final void saveDocument() { waitForFormLoad(); click(SAVE_BUTTON); } /** * Closes a document without saving. */ public final void closeDocument() { waitForFormLoad(); closeDocument(false); } /** * Closes a document, optionally saving if {@code save} is set to true. * * @param save whether or not the document should be saved before closing */ public final void closeDocument(boolean save) { if (save) { saveDocument(); } click(CLOSE_BUTTON); clickNoAnswer(); } /** * Saves and closes document and then performs a document search to retrieve the document. */ public final void closeAndSearchDocument() { String documentNumber = getDocumentNumber(); closeDocument(true); docSearch(documentNumber); } /** * Routes the document. */ public final void routeDocument() { waitForFormLoad(); click(ROUTE_BUTTON); } /** * Approves the document. */ public final void approveDocument() { waitForFormLoad(); click(APPROVE_BUTTON); } /** * Blanket approves the document. */ public final void blanketApproveDocument() { waitForFormLoad(); click(BLANKET_APPROVE_BUTTON); } /** * Asserts that the document has been reloaded. */ public final void assertReload() { assertPageContains(RELOAD_SUCCESS_MESSAGE); } /** * Asserts that the document has been saved with no errors. */ public final void assertSave() { assertPageDoesNotContain(ERRORS_FOUND_ON_PAGE); assertPageContains(SAVE_SUCCESS_MESSAGE); } /** * Asserts that the document has been routed with no errors. */ public final void assertRoute() { assertPageContains(ROUTE_SUCCESS_MESSAGE); } /** * Asserts that the document has been approved with no errors. */ public final void assertApprove() { assertPageContains(SUBMIT_SUCCESS_MESSAGE); } /** * Asserts that the element identified by {@code locator} exists. * * @param locator the id, partial name, partial title, or partial link name of the element to search for */ public final void assertElementExists(final String locator) { assertElementExists(locator, false); } /** * Asserts that the element identified by {@code locator} exists depending on the value of {@code exact}. * * @param locator the id, partial name, partial title, or partial link name of the element to search for * @param exact whether the locator should match exactly */ public final void assertElementExists(final String locator, final boolean exact) { clickExpandAll(); assertTrue("Element " + locator + " does not exist", findElement(locator, exact)); } /** * Asserts that the element identified by {@code locator} does not exist. * * @param locator the id, partial name, partial title, or partial link name of the element to search for */ public final void assertElementDoesNotExist(final String locator) { assertElementDoesNotExist(locator, false); } /** * Asserts that the element identified by {@code locator} does not exist depending on the value of {@code exact}. * * @param locator the id, partial name, partial title, or partial link name of the element to search for * @param exact whether the locator should match exactly */ public final void assertElementDoesNotExist(final String locator, final boolean exact) { clickExpandAll(); assertFalse("Element " + locator + " exists", findElement(locator, exact)); } /** * Asserts that the value of the element identified by {@code locator} contains {@code value}. * * @param locator the id, partial name, partial title, or partial link name of the element to search for * @param value the value to look for in the element */ public final void assertElementContains(final String locator, final String value) { assertElementContains(locator, false, value); } /** * Asserts that the value of the element identified by {@code locator} matches {@code value} depending on the value of {@code exact}. * * @param locator the id, name, title, or link name of the element to search for, exactness depending on the value of {@code exact} * @param exact whether the locator should match exactly * @param value the value to look for in the element */ public final void assertElementContains(final String locator, final boolean exact, final String value) { clickExpandAll(); assertTrue("Element " + locator + " does not contain " + value, StringUtils.contains(get(locator, exact), value)); } /** * Asserts that the value of the element identified by {@code locator} does <b>not</b> contain {@code value}. * * @param locator the id, partial name, partial title, or partial link name of the element to search for * @param value the value to look for in the element */ public final void assertElementDoesNotContain(final String locator, final String value) { assertElementDoesNotContain(locator, false, value); } /** * Asserts that the value of the element identified by {@code locator} does <b>not</b> match {@code value} depending on the value of {@code exact}. * * @param locator the id, name, title, or link name of the element to search for, exactness depending on the value of {@code exact} * @param exact whether the locator should match exactly * @param value the value to look for in the element */ public final void assertElementDoesNotContain(final String locator, final boolean exact, final String value) { clickExpandAll(); assertFalse("Element " + locator + " contains " + value, StringUtils.contains(get(locator, exact), value)); } /** * Asserts that the CSS selector identified by {@code cssSelector} contains {@code value}. * * @param cssSelector the CSS selector of element to search for * @param value the value to look for in the element */ public final void assertSelectorContains(final String cssSelector, final String value) { new ElementExistsWaiter("CSS selector " + cssSelector + " does not contain " + value) .until(new Function<WebDriver, Boolean>() { public Boolean apply(WebDriver driver) { boolean selectorContains = false; for (WebElement element : getElementsByCssSelector(cssSelector)) { logger.debug("Searching CSS selector {} with text {} for whether it contains value {}", new String[] { cssSelector, element.getText(), value }); if (StringUtils.contains(element.getText(), value)) { selectorContains = true; break; } } logger.debug( selectorContains ? "Found CSS selector {} contains value {}" : "Found CSS selector {} does not contain value {}", new String[] { cssSelector, value }); return selectorContains; } }); } /** * Asserts that the CSS selector identified by {@code cssSelector} does not contain {@code value}. * * @param cssSelector the CSS selector of element to search for * @param value the value to look for in the element */ public final void assertSelectorDoesNotContain(final String cssSelector, final String value) { new ElementDoesNotExistWaiter("CSS selector " + cssSelector + " contains " + value) .until(new Function<WebDriver, Boolean>() { public Boolean apply(WebDriver driver) { boolean selectorContains = false; for (WebElement element : getElementsByCssSelector(cssSelector)) { logger.debug("Searching CSS selector {} with text {} for whether it contains value {}", new String[] { cssSelector, element.getText(), value }); if (StringUtils.contains(element.getText(), value)) { selectorContains = true; break; } } logger.debug( selectorContains ? "Found CSS selector {} contains value {}" : "Found CSS selector {} does not contain value {}", new String[] { cssSelector, value }); return selectorContains; } }); } /** * Asserts that the web page contains {@code text}. * * @param text the string to look for in the web page. */ public final void assertPageContains(final String text) { clickExpandAll(); new ElementExistsWaiter("Page does not contain " + text).until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { return getElementByText(text); } }); } /** * Asserts that the web page does <b>not</b> contain {@code text}. * * @param text the string to look for in the web page. */ public final void assertPageDoesNotContain(final String text) { clickExpandAll(); new ElementDoesNotExistWaiter("Page contains " + text).until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { return getElementByText(text); } }); } /** * Asserts that the web page title contains {@code title}. * * @param title the title to look for in the web page. */ public final void assertTitleContains(final String title) { String pageSource = driver.getPageSource(); if (!StringUtils.contains(pageSource, title)) { if (switchToIFramePortlet()) { pageSource = driver.getPageSource(); } } assertTrue("Page does not contain " + title, StringUtils.contains(pageSource, title)); } /** * Asserts that the web page title does <b>not</b> contain {@code title}. * * @param title the title to look for in the web page. */ public final void assertTitleDoesNotContain(final String title) { String pageSource = driver.getPageSource(); if (StringUtils.contains(pageSource, title)) { if (switchToIFramePortlet()) { pageSource = driver.getPageSource(); } } assertFalse("Page contains" + title, StringUtils.contains(pageSource, title)); } /** * Assert that at least one of the elements in the list of options identified by {@code locator} contains {@code text}. * * @param locator the id, partial name, partial title, or partial link name of the element to search for * @param text the string to look for in the options */ public final void assertOptionsContain(final String locator, final String text) { assertOptionsContain(locator, false, text); } /** * Assert that at least one of the elements in the list of options identified by {@code locator} contains {@code text} depending on the value of {@code exact}. * * @param locator the id, name, title, or link name of the element to search for, exactness depending on the value of {@code exact}. * @param exact whether the locator should match exactly * @param text the string to look for in the options */ public final void assertOptionsContain(final String locator, final boolean exact, final String text) { logger.debug("Finding option values for select element {}", locator); Select select = new Select(getElement(locator, exact)); List<String> values = new ArrayList<String>(); for (WebElement option : select.getOptions()) { values.add(option.getText()); } logger.debug("Found option values for select element {} are {}", locator, values.toString()); assertTrue("Options for " + locator + " do not contain " + text, values.contains(text)); } /** * Assert that none of the elements in the list of options identified by {@code locator} contains {@code text}. * * @param locator the id, partial name, partial title, or partial link name of the element to search for * @param text the string to look for in the options */ public final void assertOptionsDoNotContain(final String locator, final String text) { assertOptionsDoNotContain(locator, false, text); } /** * Assert that none of the elements in the list of options identified by {@code locator} contains {@code text} depending on the value of {@code exact}. * * @param locator the id, name, title, or link name of the element to search for, exactness depending on the value of {@code exact}. * @param exact whether the locator should match exactly * @param text the string to look for in the options */ public final void assertOptionsDoNotContain(final String locator, final boolean exact, final String text) { logger.debug("Finding option values for select element {}", locator); Select select = new Select(getElement(locator, exact)); List<String> values = new ArrayList<String>(); for (WebElement option : select.getOptions()) { values.add(option.getText()); } logger.debug("Found option values for select element {} are {}", locator, values.toString()); assertFalse("Options for " + locator + " contains " + text, values.contains(text)); } /** * Assert that at least one of the selected elements in the list of options identified by {@code locator} contains {@code text}. * * @param locator the id, partial name, partial title, or partial link name of the element to search for * @param text the string to look for in the selected options */ public final void assertSelectedOptionsContain(final String locator, final String text) { assertSelectedOptionsContain(locator, false, text); } /** * Assert that at least one of the selected elements in the list of options identified by {@code locator} contains {@code text} depending on the value of {@code exact}. * * @param locator the id, name, title, or link name of the element to search for, exactness depending on the value of {@code exact}. * @param exact whether the locator should match exactly * @param text the string to look for in the selected options */ public final void assertSelectedOptionsContain(final String locator, final boolean exact, final String text) { logger.debug("Finding selected option values for select element {}", locator); Select select = new Select(getElement(locator, exact)); List<String> selectedValues = new ArrayList<String>(); for (WebElement option : select.getAllSelectedOptions()) { selectedValues.add(option.getText()); } logger.debug("Found selected option values for select element {} are {}", locator, selectedValues.toString()); assertTrue("Selected options for " + locator + " do not contain " + text, selectedValues.contains(text)); } /** * Assert that none of the selected elements in the list of options identified by {@code locator} contains {@code text}. * * @param locator the id, partial name, partial title, or partial link name of the element to search for * @param text the string to look for in the selected options */ public final void assertSelectedOptionsDoNotContain(final String locator, final String text) { assertSelectedOptionsDoNotContain(locator, false, text); } /** * Assert that none of the selected elements in the list of options identified by {@code locator} contains {@code text} depending on the value of {@code exact}. * * @param locator the id, name, title, or link name of the element to search for, exactness depending on the value of {@code exact}. * @param exact whether the locator should match exactly * @param text the string to look for in the selected options */ public final void assertSelectedOptionsDoNotContain(final String locator, final boolean exact, final String text) { logger.debug("Finding selected option values for select element {}", locator); Select select = new Select(getElement(locator, exact)); List<String> selectedValues = new ArrayList<String>(); for (WebElement option : select.getAllSelectedOptions()) { selectedValues.add(option.getText()); } logger.debug("Found selected option values for select element {} are {}", locator, selectedValues.toString()); assertFalse("Selected options for " + locator + " contains " + text, selectedValues.contains(text)); } public final void assertPopupWindowContains(final String popupWindowId, final String expectedText) { String parentWindowHandle = driver.getWindowHandle(); logger.debug("Switching to popup window {} out of parent window with handle {}", popupWindowId, parentWindowHandle); click(popupWindowId); switchToPopupWindow(parentWindowHandle); logger.debug("Switched to popup window {} out of parent window with handle {}", popupWindowId, parentWindowHandle); assertPageContains(expectedText); driver.close(); driver.switchTo().window(parentWindowHandle); logger.debug("Switched to parent window with handle {} from popup window {}", parentWindowHandle, popupWindowId); } /** * Asserts that the Expanded Text Area is providing a popup window in which to change its value. Verifies that the that this is working properly by * performing the following: * <ol> * <li>The text area is set to the {@code originalText} value</li> * <li>The pencil button is clicked on, opening in a popup window</li> * <li>The text in the popup window is examined to verify that it is equal to {@code originalText}</li> * <li>The popup window text area is changed to {@code expandedAreaText}</li> * <li>The "Continue" button is clicked on, closing the popup window</li> * <li>The resulting web page is examined to verify that the text area has changed to the value of {@code expandedAreaText}</li> * </ol> * * @param textAreaId identifies the text area * @param originalText the string to set the original text area to * @param expandedAreaText the string to set in the popup window text area */ public final void assertExpandedTextArea(final String textAreaId, final String originalText, final String expandedAreaText) { set(textAreaId, originalText); String parentWindowHandle = driver.getWindowHandle(); final String textAreaButtonLocator = "//input[starts-with(@name,'methodToCall.updateTextArea') and contains(@name, '" + textAreaId + "')]"; WebElement textAreaButton = new ElementExistsWaiter("Expand button for " + textAreaId + " not found") .until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { return getElementByXPath(textAreaButtonLocator); } }); textAreaButton.click(); switchToPopupWindow(parentWindowHandle); assertEquals(originalText, get(textAreaId)); set(textAreaId, expandedAreaText); final String continueButtonLocator = "methodToCall.postTextAreaToParent"; WebElement continueButton = new ElementExistsWaiter("Continue button for " + textAreaId + " not found") .until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { WebElement element = null; List<WebElement> elements = getActiveElementsByName(continueButtonLocator, false); if (!elements.isEmpty()) { element = elements.get(0); } return element; } }); continueButton.click(); driver.switchTo().window(parentWindowHandle); assertEquals(expandedAreaText, get(textAreaId)); } /** * Asserts that the help link is bringing up a page with the appropriate Help Page title. * * @param businessObjectClass the business object of the help link to click */ public final void assertHelpLink(Class<? extends BusinessObject> businessObjectClass) { String parentWindowHandle = driver.getWindowHandle(); String locator = "methodToCall=getBusinessObjectHelpText&businessObjectClassName=" + businessObjectClass.getName(); WebElement helpLink = getElementByXPath( "//node()[@target='helpWindow' and contains(@href, '" + locator + "')]"); helpLink.click(); switchToPopupWindow(parentWindowHandle); assertTitleContains(HELP_PAGE_TITLE); driver.close(); driver.switchTo().window(parentWindowHandle); } /** * Finds any available alert box (if it exists) and accepts it silently. */ public void acceptAlert() { try { Alert alert = driver.switchTo().alert(); alert.accept(); } catch (NoAlertPresentException nape) { logger.debug("Did not locate alert, ignoring..."); } } /** * Finds any available alert box (if it exists) and dismisses it silently. */ public void dismissAlert() { try { Alert alert = driver.switchTo().alert(); alert.dismiss(); } catch (NoAlertPresentException nape) { logger.debug("Did not locate alert, ignoring..."); } } /** * Returns the row count of the table identified by {@code id}. * * @param id identifies the table to search * @return the number of rows in the table */ public final int getTableRowCount(final String id) { final String locator = "//table[@id='" + id + "']/tbody/tr"; return new ElementExistsWaiter("Table with id " + id + " not found") .until(new Function<WebDriver, Integer>() { public Integer apply(WebDriver driver) { List<WebElement> rows = getElementsByXPath(locator); return rows.size(); } }); } /** * Returns the column count of the table identified by {@code id} at row {@code row}. * * @param id identifies the table to search * @param row the 0-valued row number to search * @return the number of columns in the table */ public final int getTableColumnCount(final String id, final int row) { String rowString = String.valueOf(row + 1); final String locator = "//table[@id='" + id + "']/tbody/tr[" + rowString + "]/td"; return new ElementExistsWaiter("Table with id " + id + " not found") .until(new Function<WebDriver, Integer>() { public Integer apply(WebDriver driver) { List<WebElement> columns = getElementsByXPath(locator); return columns.size(); } }); } /** * Asserts that the row count of the table identified by {@code id} matches {@code expectedRowCount}. * * @param id identifies the table to search * @param expectedRowCount the row count to verify */ public final void assertTableRowCount(final String id, final int expectedRowCount) { int actualRowCount = getTableRowCount(id); assertEquals("Actual row count of " + actualRowCount + " did not match the expected row count of " + expectedRowCount, expectedRowCount, actualRowCount); } /** * Returns the cell value of the table identified by {@code id} at row {@code row} and column {@code column}. * * @param id identifies the table to search * @param row the 0-valued row number to search * @param column the 0-valued column number to search * @return the cell value */ public final String getTableCellValue(final String id, final int row, final int column) { String rowString = String.valueOf(row + 1); String columnString = String.valueOf(column + 1); final String locator = "//table[@id='" + id + "']/tbody/tr[" + rowString + "]/td[" + columnString + "]"; return new ElementExistsWaiter("Cell value for table with id " + id + " at row " + rowString + " and column " + columnString + " not found").until(new Function<WebDriver, String>() { public String apply(WebDriver driver) { WebElement cell = getElementByXPath(locator); return cell == null ? Constants.EMPTY_STRING : normalize(cell.getText()); } }); } /** * Asserts that the text in the table identified by {@code id} at row {@code row} and column {@code column} contains {@code expectedText}. * * @param id identifies the table to search * @param row the 0-valued row number to search * @param column the 0-valued column number to search * @param expectedText the text to verify */ public final void assertTableCellValueContains(final String id, final int row, final int column, final String expectedText) { String actualText = getTableCellValue(id, row, column); assertTrue( "Actual cell text of " + actualText + " did not contain the expected cell text of " + expectedText, StringUtils.contains(actualText, expectedText)); } /** * Asserts that the text in the table identified by {@code id} at any particular column in any particular row contains {@code expectedText}. * * @param id identifies the table to search * @param expectedText the text to verify */ public final void assertTableCellValueContains(final String id, final String expectedText) { boolean tableContains = false; for (int row = 0; row < getTableRowCount(id); row++) { boolean tableRowContains = getTableRowContains(id, row, expectedText); if (tableRowContains) { tableContains = true; break; } } assertTrue("Cell text of " + expectedText + " not found", tableContains); } /** * Asserts that the text in the table identified by {@code id} at row {@code row} and column {@code column} does not contain {@code expectedText}. * * @param id identifies the table to search * @param row the 0-valued row number to search * @param column the 0-valued column number to search * @param expectedText the text to verify */ public final void assertTableCellValueDoesNotContain(final String id, final int row, final int column, final String expectedText) { String actualText = getTableCellValue(id, row, column); assertFalse("Actual cell text of " + actualText + " contains the expected cell text of " + expectedText, StringUtils.contains(actualText, expectedText)); } /** * Asserts that the text in the table identified by {@code id} at any particular column in any particular row does not contain {@code expectedText}. * * @param id identifies the table to search * @param expectedText the text to verify */ public final void assertTableCellValueDoesNotContain(final String id, final String expectedText) { boolean tableContains = false; for (int row = 0; row < getTableRowCount(id); row++) { boolean tableRowContains = getTableRowContains(id, row, expectedText); if (tableRowContains) { tableContains = true; break; } } assertFalse("Cell text of " + expectedText + " found", tableContains); } /** * Determines whether the text in the table identified by {@code id} at any particular column at row {@code row} contains {@code expectedText}. * * @param id identifies the table to search * @param row the 0-valued row number to search * @param expectedText the text to verify * @return true if row {@row} contains {@expectedText}, false otherwise */ private final boolean getTableRowContains(final String id, final int row, final String expectedText) { boolean tableRowContains = false; for (int column = 0; column < getTableColumnCount(id, row); column++) { logger.debug("Getting cell value from table {} at row {} and column {}", new Object[] { id, row, column }); String actualText = getTableCellValue(id, row, column); logger.debug("Found cell value from table {} at row {} and column {} to be {}", new Object[] { id, row, column, actualText }); if (StringUtils.contains(actualText, expectedText)) { tableRowContains = true; break; } } logger.debug( tableRowContains ? "Found table {} at row {} contains text {}" : "Found table {} at row {} does not contain text {}", new Object[] { id, row, expectedText }); return tableRowContains; } /** * Asserts that the page contains one or more errors. */ public final void assertPageErrors() { clickExpandAll(); new ElementExistsWaiter("Page does not contain errors").until(new Function<WebDriver, Boolean>() { public Boolean apply(WebDriver driver) { return getElementByText(ERRORS_FOUND_ON_PAGE) != null || getElementByText("Errors Found in Document") != null || getElementByText("Kuali :: Incident Report") != null; } }); } /** * Asserts that the page contains no errors. */ public final void assertNoPageErrors() { clickExpandAll(); new ElementDoesNotExistWaiter("Page contains errors").until(new Function<WebDriver, Boolean>() { public Boolean apply(WebDriver driver) { return getElementByText(ERRORS_FOUND_ON_PAGE) != null || getElementByText("Errors Found in Document") != null || getElementByText("Kuali :: Incident Report") != null; } }); } /** * Asserts that one or more of the errors contained in {@code panelId} contains {@code expectedText}. * * @param panelId the id attribute of the panel * @param text the string to look for in the errors */ public final void assertError(final String panelId, final String expectedText) { clickExpandAll(); boolean errorsContain = false; for (WebElement error : getErrors(panelId)) { if (StringUtils.contains(error.getText(), expectedText)) { errorsContain = true; break; } } assertTrue("Errors in " + panelId + " do not contain " + expectedText, errorsContain); } /** * Asserts that there are {@code expectedErrorCount} errors contained in {@code panelId}. * * @param panelId the id attribute of the panel * @param expectedErrorCount the number of errors expected on the page */ public final void assertErrorCount(final String panelId, final int expectedErrorCount) { clickExpandAll(); List<WebElement> errors = getErrors(panelId); assertEquals("Error count of " + errors.size() + " did not match the expected error count of " + expectedErrorCount, expectedErrorCount, errors.size()); } /** * Asserts that one or more of the warnings contained in {@code panelId} contains {@code expectedText}. * * @param panelId the id attribute of the panel * @param text the string to look for in the warnings */ public final void assertWarning(final String panelId, final String expectedText) { clickExpandAll(); boolean warningsContain = false; for (WebElement error : getWarnings(panelId)) { if (StringUtils.contains(error.getText(), expectedText)) { warningsContain = true; break; } } assertTrue("Warnings in " + panelId + " do not contain " + expectedText, warningsContain); } /** * Asserts that there are {@code expectedErrorCount} warnings contained in {@code panelId}. * * @param panelId the id attribute of the panel * @param expectedWarningCount the number of warnings expected on the page */ public final void assertWarningCount(final String panelId, final int expectedWarningCount) { clickExpandAll(); List<WebElement> warnings = getWarnings(panelId); assertEquals("Warning count of " + warnings.size() + " did not match the expected warning count of " + expectedWarningCount, expectedWarningCount, warnings.size()); } /** * Gets the absolute file path from the given Class {@code clazz}. * * @param clazz the class to get the file path from * @return the absolute file path of {@code clazz} */ public final String getAbsoluteFilePath(final Class<?> clazz) { URL fileUrl = getClass().getResource("/" + clazz.getCanonicalName().replaceAll("\\.", "/") + ".class"); assertNotNull(fileUrl); return new File(fileUrl.getFile()).getAbsolutePath(); } /** * Gets the simple file path from the given Class {@code clazz}. * * @param clazz the class to get the file path from * @return the simple file path of {@code clazz} */ public final String getSimpleFilePath(final Class<?> clazz) { String fileName = clazz.getSimpleName() + ".class"; assertNotNull(fileName); return fileName; } /** * Waits for the form to load by checking for the existence of "formComplete." */ private void waitForFormLoad() { new ElementExistsWaiter("Page did not load").until(new Function<WebDriver, Boolean>() { public Boolean apply(WebDriver driver) { boolean isFormComplete = false; List<WebElement> elements = driver.findElements(By.id("formComplete")); if (CollectionUtils.isNotEmpty(elements)) { isFormComplete = true; } return isFormComplete; } }); } /** * Finds an element in the web page and returns whether or not it exists. To find the element, the following algorithm is used: * <ol> * <li>Search for an active element with an {@code id} attribute that matches {@code locator}</li> * <li>If not found, search for the first active element with a {@code name} attribute that matches {@code locator} depending on the value of {@code exact}</li> * <li>If not found, search for the first active element with a {@code title} attribute that matches {@code locator} depending on the value of {@code exact}</li> * <li>If not found, search for the first active link element that matches {@code locator} depending on the value of {@code exact}</li> * </ol> * * @param locator the id, name, title, or link name of the element to search for * @param exact whether the name, title, or link name should match exactly * @return true if it exists, false otherwise */ private boolean findElement(final String locator, final boolean exact) { return new ElementExistenceFinderWaiter().until(locateElementByAll(locator, exact)); } /** * Gets an element in the web page. To find the element, the following algorithm is used: * <ol> * <li>Search for an active element with an {@code id} attribute that matches {@code locator}</li> * <li>If not found, search for the first active element with a {@code name} attribute that matches {@code locator} depending on the value of {@code exact}</li> * <li>If not found, search for the first active element with a {@code title} attribute that matches {@code locator} depending on the value of {@code exact}</li> * <li>If not found, search for the first active link element that matches {@code locator} depending on the value of {@code exact}</li> * </ol> * * @param locator the id, name, title, or link name of the element to search for * @param exact whether the name, title, or link name should match exactly * @return the first matching element */ private WebElement getElement(final String locator, final boolean exact) { return new ElementExistsWaiter(locator + " not found").until(locateElementByAll(locator, exact)); } /** * Returns a function that locates an element. To find the element, the following algorithm is used: * <ol> * <li>Search for an active element with an {@code id} attribute that matches {@code locator}</li> * <li>If not found, search for the first active element with a {@code name} attribute that matches {@code locator} depending on the value of {@code exact}</li> * <li>If not found, search for the first active element with a {@code title} attribute that matches {@code locator} depending on the value of {@code exact}</li> * <li>If not found, search for the first active link element that matches {@code locator} depending on the value of {@code exact}</li> * </ol> * * @param locator the id, name, title, or link name of the element to search for * @param exact whether the name, title, or link name should match exactly * @return a function that locates the first matching element */ private Function<WebDriver, WebElement> locateElementByAll(final String locator, final boolean exact) { return new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { logger.debug("Locating element {} by id", locator); WebElement element = getElementById(locator); if (element == null) { logger.debug("Locating element {} by name", locator); element = getElementByName(locator, exact); if (element == null) { logger.debug("Locating element {} by title", locator); element = getElementByTitle(locator, exact); if (element == null) { logger.debug("Locating element {} by link text", locator); element = getElementByLinkText(locator, exact); } } } logger.debug(element != null ? "Found element {}" : "Did not find element {}", locator); return element; } }; } /** * Gets the first active element in the web page with an {@code id} attribute that matches {@code id}. * * @param id the id of the element to search for * @return the first matching element */ private WebElement getElementById(final String id) { WebElement element = null; List<WebElement> elements = getElementsById(id); if (!elements.isEmpty()) { element = elements.get(0); } return element; } /** * Gets the first active element in the web page with a {@code name} attribute that matches {@code name} depending on the value of {@code exact}. * * @param name the name of the element to search for * @param exact whether the title should match exactly * @return the first matching element */ private WebElement getElementByName(final String name, final boolean exact) { WebElement element = null; List<WebElement> elements = getElementsByName(name, exact); if (!elements.isEmpty()) { element = elements.get(0); } return element; } /** * Gets the first active element in the web page with a {@code title} attribute that matches {@code title} depending on the value of {@code exact}. * * @param title the title of the element to search for * @param exact whether the title should match exactly * @return the first matching element */ private WebElement getElementByTitle(final String title, final boolean exact) { WebElement element = null; List<WebElement> elements = getElementsByTitle(title, exact); if (!elements.isEmpty()) { element = elements.get(0); } return element; } /** * Gets the first active element in the web page with link text that matches {@code linkText} depending on the value of {@code exact}. * * @param linkText the link text of the element to search for * @param exact whether the title should match exactly * @return the first matching element */ private WebElement getElementByLinkText(final String linkText, final boolean exact) { WebElement element = null; List<WebElement> elements = getElementsByLinkText(linkText, exact); if (!elements.isEmpty()) { element = elements.get(0); } return element; } /** * Gets the first active element in the web page that matches the XPath in {@code xPath}. * * @param xPath an XPath expression for the element to search for * @return the first matching element */ private WebElement getElementByXPath(final String xPath) { WebElement element = null; List<WebElement> elements = getElementsByXPath(xPath); if (!elements.isEmpty()) { element = elements.get(0); } return element; } /** * Gets the first active element in the web page that matches the CSS selector in {@code cssSelector}. * * @param cssSelector a CSS selector expression for the element to search for * @return the first matching element */ private WebElement getElementByCssSelector(final String cssSelector) { WebElement element = null; List<WebElement> elements = getElementsByCssSelector(cssSelector); if (!elements.isEmpty()) { element = elements.get(0); } return element; } /** * Gets the first active element in the web page with text that contains {@code text}. * * @param text the text in the element to search for * @return the first matching element */ private WebElement getElementByText(final String text) { WebElement element = null; List<WebElement> elements = getElementsByText(text); if (!elements.isEmpty()) { element = elements.get(0); } return element; } /** * Gets all active elements in the web page with an {@code id} attribute that matches {@code id}. * * @param id the id of the element to search for * @return a list of matching elements */ private List<WebElement> getElementsById(final String id) { driver.switchTo().defaultContent(); List<WebElement> elements = getActiveElementsById(id); if (elements.isEmpty()) { if (switchToIFramePortlet()) { elements.addAll(getActiveElementsById(id)); } } return elements; } /** * Gets all active elements in the current frame with an {@code id} attribute that matches {@code id}. * * @param id the id of the element to search for * @return a list of matching elements */ private List<WebElement> getActiveElementsById(final String id) { List<WebElement> elements = new ArrayList<WebElement>(); for (WebElement element : driver.findElements(By.id(id))) { if (element.isDisplayed()) { elements.add(element); } } return elements; } /** * Gets all active elements in the web page with a {@code name} attribute that matches {@code name} depending on the value of {@code exact}. * * @param name the partial name of the element to search for * @param exact whether the title should match exactly * @return a list of matching elements */ private List<WebElement> getElementsByName(final String name, final boolean exact) { driver.switchTo().defaultContent(); List<WebElement> elements = getActiveElementsByName(name, exact); if (elements.isEmpty()) { if (switchToIFramePortlet()) { elements.addAll(getActiveElementsByName(name, exact)); } } return elements; } /** * Gets all active elements in the current frame with a {@code name} attribute that matches {@code name} depending on the value of {@code exact}. * * @param name the name of the element to search for * @param exact whether the name should match exactly * @return a list of matching elements */ private List<WebElement> getActiveElementsByName(final String name, final boolean exact) { List<WebElement> activeElements = new ArrayList<WebElement>(); List<WebElement> elements = new ArrayList<WebElement>(); if (exact) { elements.addAll(driver.findElements(By.name(name))); } else { elements.addAll(driver.findElements(By.xpath(getAttributeContainsXPath("name", name)))); } for (WebElement element : elements) { if (element.isDisplayed()) { activeElements.add(element); } } return activeElements; } /** * Gets all active elements in the web page with a {@code title} attribute that matches {@code title} depending on the value of {@code exact}. * * @param title the title of the element to search for * @param exact whether the title should match exactly * @return a list of matching elements */ private List<WebElement> getElementsByTitle(final String title, final boolean exact) { driver.switchTo().defaultContent(); List<WebElement> elements = getActiveElementsByTitle(title, exact); if (elements.isEmpty()) { if (switchToIFramePortlet()) { elements.addAll(getActiveElementsByTitle(title, exact)); } } return elements; } /** * Gets all active elements in the current frame with a {@code title} attribute matches {@code title} depending on the value of {@code exact}. * * @param title the title of the element to search for * @param exact whether the title should match exactly * @return a list of matching elements */ private List<WebElement> getActiveElementsByTitle(final String title, final boolean exact) { List<WebElement> activeElements = new ArrayList<WebElement>(); List<WebElement> elements = new ArrayList<WebElement>(); if (exact) { elements.addAll(driver.findElements(By.xpath("//*[@title = '" + title + "']"))); } else { elements.addAll(driver.findElements(By.xpath(getAttributeContainsXPath("title", title)))); } for (WebElement element : elements) { if (element.isDisplayed()) { activeElements.add(element); } } return activeElements; } /** * Gets all active elements in the web page with link text that matches {@code linkText} depending on the value of {@code exact}. * * @param linkText the link text of the element to search for * @param exact whether the link text should match exactly * @return a list of matching elements */ private List<WebElement> getElementsByLinkText(final String linkText, final boolean exact) { driver.switchTo().defaultContent(); List<WebElement> elements = getActiveElementsByLinkText(linkText, exact); if (elements.isEmpty()) { if (switchToIFramePortlet()) { elements.addAll(getActiveElementsByLinkText(linkText, exact)); } } return elements; } /** * Gets all active elements in the current frame with link text that matches {@code linkText} depending on the value of {@code exact}. * * @param linkText the link text of the element to search for * @param exact whether the link text should match exactly * @return a list of matching elements */ private List<WebElement> getActiveElementsByLinkText(final String linkText, final boolean exact) { List<WebElement> activeElements = new ArrayList<WebElement>(); List<WebElement> elements = new ArrayList<WebElement>(); if (exact) { elements.addAll(driver.findElements(By.linkText(linkText))); } else { elements.addAll(driver.findElements(By.partialLinkText(linkText))); } for (WebElement element : elements) { if (element.isDisplayed()) { activeElements.add(element); } } return activeElements; } /** * Gets all active elements in the web page that match the XPath in {@code xPath}. * * @param xPath an XPath expression for the element to search for * @return a list of matching elements */ private List<WebElement> getElementsByXPath(final String xPath) { driver.switchTo().defaultContent(); List<WebElement> elements = getActiveElementsByXPath(xPath); if (elements.isEmpty()) { if (switchToIFramePortlet()) { elements.addAll(getActiveElementsByXPath(xPath)); } } return elements; } /** * Gets all active elements in the current frame that match the XPath in {@code xPath}. * * @param xPath an XPath expression for the element to search for * @return a list of matching elements */ private List<WebElement> getActiveElementsByXPath(final String xPath) { List<WebElement> elements = new ArrayList<WebElement>(); for (WebElement element : driver.findElements(By.xpath(xPath))) { if (element.isDisplayed()) { elements.add(element); } } return elements; } /** * Gets all active elements in the web page that match the CSS selector in {@code cssSelector}. * * @param cssSelector a CSS selector for the element to search for * @return a list of matching elements */ private List<WebElement> getElementsByCssSelector(final String cssSelector) { driver.switchTo().defaultContent(); List<WebElement> elements = getActiveElementsByCssSelector(cssSelector); if (elements.isEmpty()) { if (switchToIFramePortlet()) { elements.addAll(getActiveElementsByCssSelector(cssSelector)); } } return elements; } /** * Gets all active elements in the current frame that match the CSS selector in {@code cssSelector}. * * @param cssSelector a CSS selector for the element to search for * @return a list of matching elements */ private List<WebElement> getActiveElementsByCssSelector(final String cssSelector) { List<WebElement> elements = new ArrayList<WebElement>(); for (WebElement element : driver.findElements(By.cssSelector(cssSelector))) { if (element.isDisplayed()) { elements.add(element); } } return elements; } /** * Gets all active elements in the web page with text that contains {@code text}. * * @param text the text in the element to search for * @return a list of matching elements */ private List<WebElement> getElementsByText(final String text) { driver.switchTo().defaultContent(); List<WebElement> elements = getActiveElementsByText(text); if (elements.isEmpty()) { if (switchToIFramePortlet()) { elements.addAll(getActiveElementsByText(text)); } } return elements; } /** * Gets all active elements in the current frame with text that contains {@code text}. * * @param text the text in the element to search for * @return a list of matching elements */ private List<WebElement> getActiveElementsByText(final String text) { List<WebElement> elements = new ArrayList<WebElement>(); for (WebElement element : driver .findElements(By.xpath("//*[contains(normalize-space(text()), '" + text + "')]"))) { if (element.isDisplayed()) { elements.add(element); } } return elements; } /** * Attempts to switch to KC's inner frame, named 'iframeportlet'. * * @return true if the driver successfully switched to the inner frame, false otherwise */ private boolean switchToIFramePortlet() { boolean switchToIFramePortletSuccessful = true; try { driver.switchTo().frame("iframeportlet"); } catch (Exception e) { switchToIFramePortletSuccessful = false; } return switchToIFramePortletSuccessful; } /** * Attempts to switch to the latest popup window from the parent window. * * @param parentWindowHandle the handle of the parent window */ private void switchToPopupWindow(String parentWindowHandle) { for (String handle : driver.getWindowHandles()) { if (!StringUtils.equals(handle, parentWindowHandle)) { driver.switchTo().window(handle); break; } } } /** * Returns the XPath string that searches for elements that have an {@code attribute} that contains {@code text}. * * @param attribute the name of the attribute * @param text the text to search for in the attribute * @return an XPath expression for elements that have an {@code attribute} that contains {@code text} */ private String getAttributeContainsXPath(final String attribute, final String text) { return "//*[contains(@" + attribute + ", '" + text + "')]"; } /** * Returns the list of elements that contain errors in the element identified by {@code panelId}. * * @param panelId the id attribute of the panel * @return a list of errors contained in {@code panelId} */ private List<WebElement> getErrors(final String panelId) { final String locator = "div[id='" + panelId + "'] div[class='tab-container-error'] div div div"; return new ElementCountFinderWaiter().until(new Function<WebDriver, List<WebElement>>() { public List<WebElement> apply(WebDriver driver) { return getElementsByCssSelector(locator); } }); } /** * Returns the list of elements that contain warnings in the element identified by {@code panelId}. * * @param panelId the id attribute of the panel * @return a list of warnings contained in {@code panelId} */ private List<WebElement> getWarnings(final String panelId) { final String locator = "div[id='" + panelId + "'] div[class='tab-container'] div li"; return new ElementCountFinderWaiter().until(new Function<WebDriver, List<WebElement>>() { public List<WebElement> apply(WebDriver driver) { return getElementsByCssSelector(locator); } }); } /** * Returns a string that strips all multiple whitespaces out of the given {@code str) and replaces them with a single whitespace. * * @param str the string to normalize * @return the normalized string */ private String normalize(String str) { return (str == null) ? Constants.EMPTY_STRING : str.replaceAll("\\s+", " "); } /** * Implements a {@code Wait<WebDriver>} class for waiting for elements (especially Ajax elements) to appear on the page within a specified timeout. * Modified from {@code WebDriverWait} in order to integrate custom JUnit4 assertion messages. * * @see org.openqa.selenium.support.ui.Wait * @see org.openqa.selenium.support.ui.WebDriverWait */ private class ElementExistsWaiter implements Wait<WebDriver> { private final Clock clock = new SystemClock(); private final long testTimeOut = 10000; private final long sleepTimeOut = 500; private String message; protected ElementExistsWaiter(String message) { this.message = message; } /** * {@inheritDoc} * @see org.openqa.selenium.support.ui.Wait#until(com.google.common.base.Function) */ public <T> T until(Function<? super WebDriver, T> exists) { long end = clock.laterBy(testTimeOut); while (clock.isNowBefore(end)) { T value = exists.apply(driver); if (value != null) { if (Boolean.class.equals(value.getClass())) { if (Boolean.TRUE.equals(value)) { return value; } } else { return value; } } sleep(); } throw new AssertionError(message); } private void sleep() { try { Thread.sleep(sleepTimeOut); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } /** * Implements a {@code Wait<WebDriver>} class for waiting for elements (especially Ajax elements) to not appear on the page within a specified timeout. * Modified from {@code WebDriverWait} in order to integrate custom JUnit4 assertion messages and add the not condition. * * @see org.openqa.selenium.support.ui.Wait * @see org.openqa.selenium.support.ui.WebDriverWait */ private class ElementDoesNotExistWaiter implements Wait<WebDriver> { private final Clock clock = new SystemClock(); private final long testTimeOut = 1000; private final long sleepTimeOut = 500; private String message; protected ElementDoesNotExistWaiter(String message) { this.message = message; } /** * {@inheritDoc} * @see org.openqa.selenium.support.ui.Wait#until(com.google.common.base.Function) */ public <T> T until(Function<? super WebDriver, T> exists) { long end = clock.laterBy(testTimeOut); while (clock.isNowBefore(end)) { T value = exists.apply(driver); if (value != null) { if (Boolean.class.equals(value.getClass())) { if (Boolean.TRUE.equals(value)) { throw new AssertionError(message); } } else { throw new AssertionError(message); } } sleep(); } return null; } private void sleep() { try { Thread.sleep(sleepTimeOut); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } /** * Mimics an {@code ElementExistsWaiter} to determine whether an element exists on the page or not. * * @see org.openqa.selenium.support.ui.WebDriverWait */ private class ElementExistenceFinderWaiter { private final Clock clock = new SystemClock(); private final long testTimeOut = 1000; private final long sleepTimeOut = 500; /** * Mimics the {@code ElementExistsWaiter.until(..)} method and waits until the function evaluates to a value that is non-null. If the function becomes * non-null within a certain time, then this method will return true, otherwise, it will return false. * * @param exists the function to evaluate * * @see org.openqa.selenium.support.ui.Wait#until(com.google.common.base.Function) */ public <T> boolean until(Function<WebDriver, T> exists) { long end = clock.laterBy(testTimeOut); while (clock.isNowBefore(end)) { T value = exists.apply(driver); if (value != null) { return true; } sleep(); } return false; } private void sleep() { try { Thread.sleep(sleepTimeOut); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } /** * Mimics an {@code ElementExistsWaiter} to determine whether a list of elements exist on the page or not. * * @see org.openqa.selenium.support.ui.WebDriverWait */ private class ElementCountFinderWaiter { private final Clock clock = new SystemClock(); private final long testTimeOut = 1000; private final long sleepTimeOut = 500; /** * Mimics the {@code ElementExistsWaiter.until(..)} method and waits until the number of returned elements is greater than zero. If the number of * returned elements becomes greater than zero within a certain time, then this method will return the element list, otherwise, it will return an empty * list * * @param exists the function to evaluate * * @see org.openqa.selenium.support.ui.Wait#until(com.google.common.base.Function) */ public List<WebElement> until(Function<WebDriver, List<WebElement>> elements) { long end = clock.laterBy(testTimeOut); while (clock.isNowBefore(end)) { List<WebElement> values = elements.apply(driver); if (values != null && values.size() > 0) { return values; } sleep(); } return Collections.<WebElement>emptyList(); } private void sleep() { try { Thread.sleep(sleepTimeOut); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }