/* $Id$ */ /** * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache 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 * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>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.apache.manifoldcf.core.tests; import org.hamcrest.CoreMatchers; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.openqa.selenium.Alert; import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.SearchContext; import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.ie.InternetExplorerDriver; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.Select; import org.openqa.selenium.support.ui.WebDriverWait; import java.util.List; public class SeleniumTester { protected WebDriver driver = null; protected WebDriverWait wait = null; private final long defaultTimeOutInSeconds = 15; public enum BrowserType { CHROME, FIREFOX, IE } /** * Constructor. Create a test sequence object. */ public SeleniumTester() { } /** * Set up for all tests. Basically this grabs the necessary stuff out of resources and writes it * to the current directory. */ @Before public void setup() throws Exception { driver = null; wait = null; } public void start(final BrowserType browserType, final String language, final String startURL) { //Download Chrome Driver for Linux from here (https://chromedriver.storage.googleapis.com/index.html?path=2.28/) switch (browserType) { case CHROME: if (System.getProperty("webdriver.chrome.driver") == null || System.getProperty("webdriver.chrome.driver").length() == 0) throw new IllegalStateException("Please configure your SL_CHROME_DRIVER environment variable to point to the Selenium Google Chrome Driver"); //Create a new instance of Chrome driver ChromeOptions options = new ChromeOptions(); options.addArguments("--start-maximized", "--lang=" + language); driver = new ChromeDriver(options); break; case FIREFOX: if(System.getProperty("webdriver.gecko.driver") == null || System.getProperty("webdriver.gecko.driver").length() == 0) throw new IllegalStateException( "Please configure your SL_FIREFOX_DRIVER environment variable to point to the Mozilla Firefox Driver"); //Create a new instance of Firefox driver driver = new FirefoxDriver(); break; case IE: if(System.getProperty("webdriver.ie.driver") == null || System.getProperty("webdriver.ie.driver").length() == 0) throw new IllegalStateException( "Please configure your SL_IE_DRIVER environment variable to point to the Internet Explorer Driver"); //For more info, on how to configure IE driver, plese read https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver driver = new InternetExplorerDriver(); break; default: throw new IllegalArgumentException("Unknown browser type"); } wait = new WebDriverWait(driver, defaultTimeOutInSeconds); driver.get(startURL); } public WebDriver getDriver() { return driver; } public WebDriverWait getWait() { return wait; } public WebElement findElementById(String id) { return driver.findElement(By.id(id)); } /** * Verify that we land in a correct page based on display title * @param expected */ public void verifyHeader(String expected) { WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("heading"))); Assert.assertThat(element.getText(), CoreMatchers.is(CoreMatchers.equalTo(expected))); } /** * Verify that we land in a correct page based on display title substring * @param expected */ public void verifyHeaderContains(String expected) { WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("heading"))); Assert.assertThat(element.getText(), CoreMatchers.containsString(expected)); } /** * Verify that we don't land in an error page */ public void verifyThereIsNoError() { WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("heading"))); Assert.assertNotEquals("Error!", element.getText()); } /** * Navigate to a page based on sidebar link alt text * @param lintAlt */ public void navigateTo(String lintAlt) { //Identify the link WebElement ele = driver.findElement( By.cssSelector(".sidebar-menu .treeview-menu a[alt=\"" + lintAlt + "\"]")); //Expand the menu group, so that the element gets visible String js = "return $(arguments[0]).closest('.treeview').get(0)"; WebElement parent = (WebElement)((JavascriptExecutor)driver).executeScript(js, ele); if (!hasClass(parent, "active")) { js = "$(arguments[0]).closest('.treeview').find('a:first-child').click();"; ((JavascriptExecutor)driver).executeScript(js, ele); //waitUntilAnimationIsDone(".sidebar-menu .treeview .treeview-menu"); //Wait for a second for the animation to complete. try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } //Wait until the menu is link is visible wait.until(ExpectedConditions.elementToBeClickable(ele)).click(); //waitForAjax(); waitForAjaxAndDocumentReady(); } /** * Check if a element is present in DOM * @param selector * @return true, if the element exists else false */ private boolean exists(By selector) { return driver.findElements(selector).size() != 0; } /** * Find an element by waiting we find it based on its visibility * @param selector * @return */ public WebElement waitFindElement(By selector) { return wait.until(ExpectedConditions.visibilityOfElementLocated(selector)); } /** * Find an element by waiting until it becomes clickable * @param selector * @return */ public WebElement waitElementClickable(By selector) { return wait.until(ExpectedConditions.elementToBeClickable(selector)); } /** * Find an element by waiting until we find it's presence in dom * @param selector * @return */ public WebElement waitUntilPresenceOfElementLocated(By selector) { return wait.until(ExpectedConditions.presenceOfElementLocated(selector)); } /** * Find an element by it's name * @param name */ public void waitForElementWithName(String name) { waitFor(By.name(name)); } public void waitForPresenceById(String id) { waitForPresence(By.id(id)); } public void waitForPresence(By selector) { wait.until(ExpectedConditions.presenceOfElementLocated(selector)); } public void waitFor(By selector) { wait.until(ExpectedConditions.visibilityOfElementLocated(selector)); } /** * Click a tab by it's name * @param tabName the name of the tab */ public void clickTab(String tabName) { WebElement element = waitElementClickable(By.cssSelector("a[data-toggle=\"tab\"][alt=\"" + tabName + " tab\"]")); element.click(); waitForAjaxAndDocumentReady(); } /** * Click a button based on title, button created using anchor tag and has title attribute set. * @param title */ public void clickButtonByTitle(String title) { WebElement element = waitElementClickable( By.xpath( "//a[contains(@class,'btn') and contains(@data-original-title,'" + title + "')]")); element.click(); if (!isAlertPresent()) { waitForAjaxAndDocumentReady(); } } public void clickButton(String text) throws Exception { clickButton(text, defaultTimeOutInSeconds); } /** * Clicks a button based on visible text, this type of button is created using anchor tag with * .btn class * @param text */ public void clickButton(String text, long timeOutInSeconds) throws Exception { /*WebElement element = waitElementClickable( By.xpath("//a[contains(@class,'btn') and normalize-space()='" + text + "']")); element.click();*/ boolean found = false; List<WebElement> elements = driver.findElements(By.xpath("//a[contains(concat(' ',@class,' '), ' btn ')] | //button[contains(concat(' ',@class,' '), ' btn ')]")); for (int i = 0; i < elements.size(); i++) { WebElement element = elements.get(i); System.out.println(getRenderedSource(element)); wait.until(ExpectedConditions.elementToBeClickable(element)); String actualText = element.getText(); if (actualText != null && actualText.length() > 0) { actualText = actualText.trim(); } if (actualText.equals(text)) { element.click(); found = true; break; } } if (!found) { throw new Exception("Button not found with text - " + text); } if (!isAlertPresent()) { waitForAjaxAndDocumentReady(timeOutInSeconds); } } /** * Click a button created using <input type="button"/> * @param buttonText * @param islegacy */ public void clickButton(String buttonText, boolean islegacy) throws Exception { if (!islegacy) { clickButton(buttonText); } else { waitFindElement(By.cssSelector("[type=\"button\"][value=\"" + buttonText + "\"]")).click(); } } /** * Click on a radio button with a specific value * @param name * @param value */ public void clickRadioButton(String name, String value) { waitElementClickable(By.xpath("//input[@type='radio'][@name='" + name + "'][@value='" + value + "']")).click(); } /** * Click a checkbox with the specified name * @param name */ public void clickCheckbox(String name) { waitElementClickable(By.xpath("//input[@type='checkbox'][@name='" + name + "']")).click(); } /** * Check if a alert box appeared in the browser.s * @return */ public boolean isAlertPresent() { boolean foundAlert = false; WebDriverWait wait = new WebDriverWait(driver, 0 /*timeout in seconds*/); try { wait.until(ExpectedConditions.alertIsPresent()); foundAlert = true; } catch (TimeoutException eTO) { foundAlert = false; } return foundAlert; } /** * Accepts the alert box */ public void acceptAlert() { wait.until(ExpectedConditions.alertIsPresent()); Alert alert = driver.switchTo().alert(); alert.accept(); } /** * Set value of an element with name * @param name * @param value */ public void setValue(String name, String value) { setValue(driver, name, value); } /** * Set value of an element with name by searching in another element * @param context * @param name * @param value */ public void setValue(SearchContext context, String name, String value) { setValue(context, By.name(name), value); } public void setValue(SearchContext context, By selector, String value) { WebElement element = context.findElement(selector); //Make sure, there is no default text in the input. element.clear(); element.sendKeys(value); } /** * Select value of a custom select box using javascript. * @param name * @param value */ public void selectValue(String name, String value) { WebElement element = waitUntilPresenceOfElementLocated(By.name(name)); System.out.println(element.toString()); if (hasClass(element, "selectpicker")) { String js = "$(arguments[0]).selectpicker('val','" + value + "')"; ((JavascriptExecutor)driver).executeScript(js, element); } else { Select select = new Select(element); select.selectByValue(value); } } /** * Executes javascript in browser * @param element * @param method * @param params */ public void executeJquery(WebElement element, String method, String params) { String js = "$(arguments[0])." + method + "(" + params + ")"; System.out.println("JavaScript to be executed: " + js); ((JavascriptExecutor)driver).executeScript(js, element); } /** * Check if an element has a class * @param element * @param className * @return */ private boolean hasClass(WebElement element, String className) { if (element.getAttribute("class") != null) return element.getAttribute("class").contains(className); return false; } /** * Get the attribute value of an element * @param id * @param attribute * @return */ public String getAttributeValueById(String id, String attribute) { WebElement element = driver.findElement(By.id(id)); return element.getAttribute(attribute); } // Macro operations for job management /** * Perform an action (Start, Start minimal, Pause, Restart, Restart minimal, Abort) on a specified * job (English version). * @param jobID * @param action */ public void performJobActionEN(String jobID, String action) { //Navigate to Status and Job management navigateTo("Manage jobs"); waitForElementWithName("liststatuses"); waitElementClickable( By.xpath( "//tr[@job-id=" + jobID + "]//a[contains(@class,'btn') and text()='" + action + "']")) .click(); } /** * Wait until the status of an job become as mentioned (English version) * @param jobID is the jobID * @param jobStatus is the desired job status (e.g. 'Done') * @param timeoutAmount is the maximum time until the status is expected * @throws Exception */ public void waitForJobStatusEN(final String jobID, final String jobStatus, final int timeoutAmount) throws Exception { waitForJobStatus(jobID, jobStatus, timeoutAmount, "Manage jobs", "liststatuses", "Refresh"); } /** * Wait until the status of an job become as mentioned (generic version) * @param jobID is the jobID * @param jobStatus is the desired job status (e.g. 'Done') * @param timeoutAmount is the maximum time until the status is expected * @param manageJobsPage is the 'manage jobs' page * @param listStatusesElement is the 'list statuses' element * @param refreshButton is the 'Refresh" button * @throws Exception */ public void waitForJobStatus(final String jobID, final String jobStatus, int timeoutAmount, final String manageJobsPage, final String listStatusesElement, final String refreshButton) throws Exception { //Navigate to Status and Job management navigateTo(manageJobsPage); waitForElementWithName(listStatusesElement); while (true) { if (!exists(By.xpath("//tr[@job-id='" + jobID + "']"))) { throw new Exception("Job " + jobID + " not found"); } if (exists(By.xpath("//tr[@job-id='" + jobID + "' and @job-status-name='" + jobStatus + "']"))) { break; } if (timeoutAmount == 0) { throw new Exception("Timed out waiting for job " + jobID + " to acheive status '" + jobStatus + "'"); } clickButton(refreshButton); waitForElementWithName(listStatusesElement); //Let us wait for a second. Thread.sleep(1000L); timeoutAmount--; } } /** * Obtain a given job's status (English version). * @param jobID is the job ID. * @return the job status, if found, */ public String getJobStatusEN(final String jobID) throws Exception { return getJobStatus(jobID, "Manage jobs", "liststatuses"); } /** * Obtain a given job's status (generic version). * @param jobID is the job ID. * @param manageJobsPage is the 'manage jobs' page * @param listStatusesElement is the 'list statuses' element * @return the job status, if found, */ public String getJobStatus(final String jobID, final String manageJobsPage, final String listStatusesElement) throws Exception { //Navigate to Status and Job management navigateTo(manageJobsPage); waitForElementWithName(listStatusesElement); final WebElement element = driver.findElement(By.xpath("//tr[@job-id=" + jobID + "]")); if (element == null) { throw new Exception("Can't find job " + jobID); } return element.getAttribute("job-status-name"); } /** * Wait for a specified job to go away after being deleted (English version). * @param jobID * @param timeoutAmount * @throws Exception */ public void waitForJobDeleteEN(final String jobID, int timeoutAmount) throws Exception { waitForJobDelete(jobID, timeoutAmount, "Manage jobs", "liststatuses", "Refresh"); } /** * Wait for a specified job to go away after being deleted (generic version). * @param jobID * @param timeoutAmount * @param manageJobsPage is the 'manage jobs' page * @param listStatusesElement is the 'list statuses' element * @param refreshButton is the 'Refresh" button * @throws Exception */ public void waitForJobDelete(final String jobID, int timeoutAmount, final String manageJobsPage, final String listStatusesElement, final String refreshButton) throws Exception { navigateTo(manageJobsPage); waitForElementWithName(listStatusesElement); while (exists(By.xpath("//tr[@job-id=\"" + jobID + "\"]"))) { if (timeoutAmount == 0) { throw new Exception("Timed out waiting for job " + jobID + " to go away"); } clickButton(refreshButton); waitForElementWithName(listStatusesElement); //Let us wait for a second. Thread.sleep(1000L); timeoutAmount--; } } public boolean waitForAjaxAndDocumentReady() { return waitForAjaxAndDocumentReady(defaultTimeOutInSeconds); } public boolean waitForAjaxAndDocumentReady(long timeOutInSeconds) { WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds); // wait for jQuery to load ExpectedCondition<Boolean> jQueryLoad = new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver driver) { try { return ((Long) ((JavascriptExecutor)getDriver()).executeScript("return jQuery.active") == 0); } catch (Exception e) { // no jQuery present return true; } } }; // wait for Javascript to load ExpectedCondition<Boolean> jsLoad = new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver driver) { return ((JavascriptExecutor)getDriver()) .executeScript("return document.readyState") .toString() .equals("complete"); } }; return wait.until(jQueryLoad) && wait.until(jsLoad); } public void waitUntilAnimationIsDone(final String selector) { waitUntilAnimationIsDone(selector, defaultTimeOutInSeconds); } public void waitUntilAnimationIsDone(final String selector, final long timeOutInSeconds) { WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds); ExpectedCondition<Boolean> expectation = new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver driver) { String temp = ((JavascriptExecutor)driver) .executeScript("return jQuery('" + selector + "').is(':animated')") .toString(); return temp.equalsIgnoreCase("false"); } }; try { wait.until(expectation); } catch (TimeoutException e) { throw new AssertionError("Element animation is not finished in time. selector: " + selector); } } /** * Get the source of the html document * @return */ public String getRenderedSource() { return getRenderedSource(By.tagName("html")); } /** * Get the source of an element by find it in DOM * @param selector * @return */ public String getRenderedSource(By selector) { return getRenderedSource(driver.findElement(selector)); } /** * Get the source of an element * @param element * @return */ public String getRenderedSource(WebElement element) { return (String) ((JavascriptExecutor)driver).executeScript("return arguments[0].innerHTML", element); } private long tick() { long TICKS_AT_EPOCH = 621355968000000000L; return System.currentTimeMillis() * 10000 + TICKS_AT_EPOCH; } /** * Clean up the files we created. */ @After public void teardown() throws Exception { if (driver != null) { driver.close(); driver.quit(); driver = null; wait = null; } } }