/** * ============================================================================= * * ORCID (R) Open Source * http://orcid.org * * Copyright (c) 2012-2014 ORCID, Inc. * Licensed under an MIT-Style License (MIT) * http://orcid.org/open-source-license * * This copyright and license information (including a link to the full license) * shall be included in its entirety in all copies or substantial portion of * the software. * * ============================================================================= */ package org.orcid.integration.blackbox.api; import static org.orcid.integration.blackbox.api.BlackBoxWebDriver.getWebDriver; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.junit.runner.RunWith; import org.openqa.selenium.By; import org.openqa.selenium.By.ByXPath; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.orcid.integration.api.helper.SystemPropertiesHelper; import org.orcid.integration.blackbox.web.SigninTest; import org.orcid.jaxb.model.common_rc2.Visibility; import org.orcid.pojo.ajaxForm.PojoUtil; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) public class BBBUtil { public static String getProperty(String key) { Properties prop = SystemPropertiesHelper.getProperties(); return prop.getProperty(key); } public static void logUserOut(String baseUrl, WebDriver webDriver) { webDriver.get(baseUrl + "/userStatus.json?logUserOut=true"); BBBUtil.extremeWaitFor(BBBUtil.documentReady(), webDriver); } public static void revokeApplicationsAccess(WebDriver webDriver) { List<String> clientIds = new ArrayList<String>(); Properties prop = SystemPropertiesHelper.getProperties(); String clientId1 = prop.getProperty("org.orcid.web.testClient1.clientId"); if (!PojoUtil.isEmpty(clientId1)) { clientIds.add(clientId1); } String clientId2 = prop.getProperty("org.orcid.web.testClient2.clientId"); if (!PojoUtil.isEmpty(clientId2)) { clientIds.add(clientId2); } String userName = prop.getProperty("org.orcid.web.testUser1.username"); String password = prop.getProperty("org.orcid.web.testUser1.password"); String baseUrl = "https://localhost:8443/orcid-web"; if (!PojoUtil.isEmpty(prop.getProperty("org.orcid.web.baseUri"))) { baseUrl = prop.getProperty("org.orcid.web.baseUri"); } webDriver.get(baseUrl + "/userStatus.json?logUserOut=true"); (new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.documentReady()); webDriver.get(baseUrl + "/my-orcid"); (new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.documentReady()); (new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.angularHasFinishedProcessing()); SigninTest.signIn(webDriver, userName, password); // Switch to accounts settings page By accountSettingsMenuLink = By.id("accountSettingMenuLink"); BBBUtil.extremeWaitFor(ExpectedConditions.presenceOfElementLocated(accountSettingsMenuLink), webDriver); webDriver.get(baseUrl + "/account"); BBBUtil.extremeWaitFor(BBBUtil.documentReady(), webDriver); BBBUtil.extremeWaitFor(BBBUtil.angularHasFinishedProcessing(), webDriver); try { boolean lookAgain = false; do { // Look for each revoke app button By revokeAppBtn = By.id("revokeAppBtn"); BBBUtil.extremeWaitFor(BBBUtil.angularHasFinishedProcessing(), webDriver); List<WebElement> appsToRevoke = webDriver.findElements(revokeAppBtn); boolean elementFound = false; // Iterate on them and delete the ones created by the specified // client id for (WebElement appElement : appsToRevoke) { String nameAttribute = appElement.getAttribute("name"); if (clientIds.contains(nameAttribute)) { BBBUtil.ngAwareClick(appElement, webDriver); (new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.angularHasFinishedProcessing()); // Wait for the revoke button By confirmRevokeAppBtn = By.id("confirmRevokeAppBtn"); BBBUtil.extremeWaitFor(ExpectedConditions.visibilityOfAllElementsLocatedBy(confirmRevokeAppBtn), webDriver); BBBUtil.ngAwareClick(webDriver.findElement(confirmRevokeAppBtn), webDriver); BBBUtil.extremeWaitFor(BBBUtil.angularHasFinishedProcessing(), webDriver); BBBUtil.noCboxOverlay(webDriver); BBBUtil.extremeWaitFor(BBBUtil.angularHasFinishedProcessing(), webDriver); // may need to put sleep back here elementFound = true; break; } } if (elementFound) { lookAgain = true; } else { lookAgain = false; } } while (lookAgain); } catch (Exception e) { // If it fail is because it couldn't find any other application } finally { logUserOut(baseUrl, webDriver); } } public static void ngAwareClick(WebElement webElement){ ngAwareClick(webElement, getWebDriver()); } public static void ngAwareClick(WebElement webElement, WebDriver webDriver) { waitForAngular(webDriver); Actions actions = new Actions(webDriver); actions.moveToElement(webElement).perform(); actions.click(webElement).perform(); waitForAngular(webDriver); } public static void ngAwareSendKeys(String keys, String id, WebDriver webDriver) { waitForAngular(); ((JavascriptExecutor) webDriver).executeScript( "" + "angular.element('#" + id + "').triggerHandler('focus');" + "angular.element('#" + id + "').val('" + keys + "');" + "angular.element('#" + id + "').triggerHandler('change');" + "angular.element('#" + id + "').triggerHandler('blur');" + "angular.element('#" + id + "').scope().$apply();"); BBBUtil.extremeWaitFor(BBBUtil.angularHasFinishedProcessing(), webDriver); } public static void noSpinners() { noSpinners(getWebDriver()); } public static void noSpinners(WebDriver webDriver) { (new WebDriverWait(webDriver, 20, 100)).until(ExpectedConditions.invisibilityOfElementLocated(By.cssSelector("i.glyphicon-refresh"))); } public static void waitForNoCboxOverlay() { noCboxOverlay(getWebDriver()); waitForAngular(); } public static void noCboxOverlay(WebDriver webDriver) { (new WebDriverWait(webDriver, 20, 100)).until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("//div[@id='cboxOverlay']"))); } public static void waitForCboxComplete(){ extremeWaitFor(cboxComplete(), getWebDriver()); waitForAngular(); } public static void shortWaitFor(ExpectedCondition<?> expectedCondition, WebDriver webDriver) { int wait = 5; int pollingInterval = 250; waitFor(wait, pollingInterval, expectedCondition, webDriver, false); } public static void extremeWaitFor(ExpectedCondition<?> expectedCondition, WebDriver webDriver) { int wait = 15; int pollingInterval = 250; waitFor(wait, pollingInterval, expectedCondition, webDriver, true); } private static void waitFor(int wait, int pollingInterval, ExpectedCondition<?> expectedCondition, WebDriver webDriver, boolean retry) { try { (new WebDriverWait(webDriver, wait, pollingInterval)).until(expectedCondition); } catch (Exception e) { if(retry) { ((JavascriptExecutor) webDriver).executeScript("$(window).trigger('resize');"); (new WebDriverWait(webDriver, wait, pollingInterval)).until(expectedCondition); } else { throw e; } } } public static void waitForAngular() { BBBUtil.extremeWaitFor(BBBUtil.angularHasFinishedProcessing(), getWebDriver()); } public static void waitForAngular(WebDriver webDriver) { BBBUtil.extremeWaitFor(BBBUtil.angularHasFinishedProcessing(), webDriver); } public static void waitForElementVisibility(By elementLocatedBy) { BBBUtil.extremeWaitFor(ExpectedConditions.visibilityOfElementLocated(elementLocatedBy), getWebDriver()); } public static void waitForElementPresence(By elementLocatedBy) { BBBUtil.extremeWaitFor(ExpectedConditions.presenceOfElementLocated(elementLocatedBy), getWebDriver()); } public static ExpectedCondition<Boolean> documentReady() { return new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver driver) { return Boolean.valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete")); } }; } public static ExpectedCondition<Boolean> angularHasFinishedProcessing() { /* * Getting complex. 1. We want to make sure Angular is done. So you call * the rootScope apply 2. We want to make sure the browser is done * rendering the DOM so we call $timeout * http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has- * finished-rendering/ 3. make sure there are no pending AJAX request, * if so start over */ return new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver driver) { ((JavascriptExecutor) driver).executeScript("" + "window._selenium_angular_done = false;" + "function _seleniumAngularDone() { " + " angular.element(document.documentElement).scope().$root.$apply(" + " function(){" + " setTimeout(function(){ " + " if ($.active > 0)" + " _seleniumAngularDone();" + " else" + " window._selenium_angular_done = true;" + " }, 0);" + " });" + "};" + "try { _seleniumAngularDone(); } catch(err) { /* do nothing */ }"); return Boolean.valueOf(((JavascriptExecutor) driver).executeScript("" + "return window._selenium_angular_done;").toString()); } }; } public static ExpectedCondition<Boolean> cboxComplete() { /* * Getting complex. 1. We want to make sure Angular is done. So you call * the rootScope apply 2. We want to make sure the browser is done * rendering the DOM so we call $timeout * http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has- * finished-rendering/ 3. make sure there are no pending AJAX request, * if so start over */ return new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver driver) { return Boolean.valueOf(((JavascriptExecutor) driver).executeScript("" + "return window.cbox_complete").toString()); } }; } public static boolean elementExists(By elementLocatedBy) { try { WebElement element = findElement(elementLocatedBy); return element != null; } catch (NoSuchElementException e) { return false; } } public static WebElement findElement(By elementLocatedBy) { return getWebDriver().findElement(elementLocatedBy); } public static WebElement findElementById(String id) { return findElement(By.id(id)); } public static WebElement findElementByXpath(String xpath) { return findElement(By.xpath(xpath)); } public static List<WebElement> findElements(By elementsLocatedBy) { return getWebDriver().findElements(elementsLocatedBy); } public static List<WebElement> findElementsByXpath(String xpath) { return getWebDriver().findElements(By.xpath(xpath)); } public static void getUrl(String url) { getWebDriver().get(url); } public static void getUrlAndWait(String url) { getWebDriver().get(url); documentReady(); waitForAngular(); noSpinners(); } public static String executeJavaScript(String javaScript, WebElement webElement, WebDriver driver) { JavascriptExecutor je = (JavascriptExecutor) driver; Object result = je.executeScript(javaScript, webElement); return result == null ? null : result.toString(); } public static String executeJavaScript(String javaScript, WebElement webElement) { JavascriptExecutor je = (JavascriptExecutor) getWebDriver(); Object result = je.executeScript(javaScript, webElement); return result == null ? null : result.toString(); } public static final int TIMEOUT_SECONDS = 10; public static final int SLEEP_MILLISECONDS = 100; /** * Assumes the user is already logged in and in the /my-orcid page * */ public static void changeDefaultUserVisibility(WebDriver webDriver, Visibility visibility) { BBBUtil.noSpinners(webDriver); BBBUtil.extremeWaitFor(BBBUtil.angularHasFinishedProcessing(), webDriver); By privacyPreferenceToggle = By.id("privacyPreferencesToggle"); BBBUtil.extremeWaitFor(ExpectedConditions.visibilityOfElementLocated(privacyPreferenceToggle), webDriver); WebElement toggle = webDriver.findElement(privacyPreferenceToggle); BBBUtil.ngAwareClick(toggle, webDriver); BBBUtil.extremeWaitFor(BBBUtil.angularHasFinishedProcessing(), webDriver); String clickXPathStr = "//div[@id='privacy-settings' and contains(text(),'By default, who should')]//a[contains(@ng-click,'" + visibility.value().toUpperCase() + "')]"; String clickWorkedStr = "//div[@id='privacy-settings' and contains(text(),'By default, who should ')]//li[@class='" +visibility.value().toLowerCase() + "Active']//a[contains(@ng-click,'" + visibility.value().toUpperCase() + "')]"; BBBUtil.extremeWaitFor(ExpectedConditions.visibilityOfElementLocated(ByXPath.xpath(clickXPathStr)), webDriver); BBBUtil.ngAwareClick(webDriver.findElement(ByXPath.xpath(clickXPathStr)), webDriver); BBBUtil.extremeWaitFor(BBBUtil.angularHasFinishedProcessing(), webDriver); BBBUtil.extremeWaitFor(ExpectedConditions.visibilityOfElementLocated(ByXPath.xpath(clickWorkedStr)), webDriver); // this is really evil, suggest JPA isn't flushing/persisting as quick as we would like try {Thread.sleep(500);} catch(Exception e) {}; } }