001/* 002 * (C) Copyright 2011-2015 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Sun Seng David TAN 016 * Florent Guillaume 017 * Benoit Delbosc 018 * Antoine Taillefer 019 * Anahide Tchertchian 020 * Guillaume Renard 021 * Mathieu Guillaume 022 * Julien Carsique 023 */ 024package org.nuxeo.functionaltests; 025 026import static org.junit.Assert.assertEquals; 027import static org.junit.Assert.assertTrue; 028import static org.junit.Assert.fail; 029 030import java.io.File; 031import java.io.IOException; 032import java.lang.reflect.Constructor; 033import java.lang.reflect.Field; 034import java.lang.reflect.Method; 035import java.net.MalformedURLException; 036import java.net.URI; 037import java.net.URL; 038import java.net.URLClassLoader; 039import java.util.ArrayList; 040import java.util.Arrays; 041import java.util.List; 042import java.util.concurrent.TimeUnit; 043import java.util.jar.Attributes; 044import java.util.jar.JarFile; 045 046import net.jsourcerer.webdriver.jserrorcollector.JavaScriptError; 047 048import org.apache.commons.lang.SystemUtils; 049import org.apache.commons.logging.Log; 050import org.apache.commons.logging.LogFactory; 051import org.browsermob.proxy.ProxyServer; 052import org.junit.After; 053import org.junit.AfterClass; 054import org.junit.Before; 055import org.junit.BeforeClass; 056import org.junit.Rule; 057import org.junit.rules.MethodRule; 058import org.nuxeo.common.Environment; 059import org.nuxeo.common.utils.FileUtils; 060import org.nuxeo.functionaltests.fragment.WebFragment; 061import org.nuxeo.functionaltests.pages.AbstractPage; 062import org.nuxeo.functionaltests.pages.DocumentBasePage; 063import org.nuxeo.functionaltests.pages.DocumentBasePage.UserNotConnectedException; 064import org.nuxeo.functionaltests.pages.FileDocumentBasePage; 065import org.nuxeo.functionaltests.pages.LoginPage; 066import org.nuxeo.functionaltests.pages.NoteDocumentBasePage; 067import org.nuxeo.functionaltests.pages.forms.CollectionCreationFormPage; 068import org.nuxeo.functionaltests.pages.forms.DublinCoreCreationDocumentFormPage; 069import org.nuxeo.functionaltests.pages.forms.FileCreationFormPage; 070import org.nuxeo.functionaltests.pages.forms.NoteCreationFormPage; 071import org.nuxeo.functionaltests.pages.forms.WorkspaceFormPage; 072import org.nuxeo.functionaltests.pages.tabs.CollectionContentTabSubPage; 073import org.openqa.selenium.By; 074import org.openqa.selenium.JavascriptExecutor; 075import org.openqa.selenium.NoSuchElementException; 076import org.openqa.selenium.Proxy; 077import org.openqa.selenium.TimeoutException; 078import org.openqa.selenium.WebDriver; 079import org.openqa.selenium.WebElement; 080import org.openqa.selenium.chrome.ChromeDriver; 081import org.openqa.selenium.chrome.ChromeOptions; 082import org.openqa.selenium.firefox.FirefoxDriver; 083import org.openqa.selenium.firefox.FirefoxProfile; 084import org.openqa.selenium.internal.WrapsElement; 085import org.openqa.selenium.remote.CapabilityType; 086import org.openqa.selenium.remote.Command; 087import org.openqa.selenium.remote.DesiredCapabilities; 088import org.openqa.selenium.remote.DriverCommand; 089import org.openqa.selenium.remote.RemoteWebDriver; 090import org.openqa.selenium.support.PageFactory; 091import org.openqa.selenium.support.ui.FluentWait; 092import org.openqa.selenium.support.ui.Wait; 093 094import com.google.common.base.Function; 095import com.google.common.collect.ImmutableMap; 096 097/** 098 * Base functions for all pages. 099 */ 100public abstract class AbstractTest { 101 102 /** 103 * @since 5.9.2 104 */ 105 public final static String TEST_USERNAME = "jdoe"; 106 107 /** 108 * @since 5.9.2 109 */ 110 public final static String TEST_PASSWORD = "test"; 111 112 /** 113 * Polling frequency in milliseconds. 114 * 115 * @since 5.9.2 116 */ 117 public static final int POLLING_FREQUENCY_MILLISECONDS = 100; 118 119 /** 120 * Page Load timeout in seconds. 121 * 122 * @since 5.9.2 123 */ 124 public static final int PAGE_LOAD_TIME_OUT_SECONDS = 60; 125 126 /** 127 * @since 5.7 128 */ 129 public static final String CHROME_DRIVER_DEFAULT_PATH_LINUX = "/usr/bin/chromedriver"; 130 131 /** 132 * @since 5.7 "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" doesn't work 133 */ 134 public static final String CHROME_DRIVER_DEFAULT_PATH_MAC = "/Applications/chromedriver"; 135 136 /** 137 * @since 5.7 138 */ 139 public static final String CHROME_DRIVER_DEFAULT_PATH_WINVISTA = SystemUtils.getUserHome().getPath() 140 + "\\AppData\\Local\\Google\\Chrome\\Application\\chromedriver.exe"; 141 142 /** 143 * @since 5.7 144 */ 145 public static final String CHROME_DRIVER_DEFAULT_PATH_WINXP = SystemUtils.getUserHome().getPath() 146 + "\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chromedriver.exe"; 147 148 /** 149 * @since 5.7 150 */ 151 public static final String CHROME_DRIVER_DEFAULT_EXECUTABLE_NAME = "chromedriver"; 152 153 /** 154 * @since 5.7 155 */ 156 public static final String CHROME_DRIVER_WINDOWS_EXECUTABLE_NAME = "chromedriver.exe"; 157 158 static final Log log = LogFactory.getLog(AbstractTest.class); 159 160 public static final String NUXEO_URL = System.getProperty("nuxeoURL", "http://localhost:8080/nuxeo").replaceAll( 161 "/$", ""); 162 163 public static final int LOAD_TIMEOUT_SECONDS = 30; 164 165 public static final int LOAD_SHORT_TIMEOUT_SECONDS = 2; 166 167 public static final int AJAX_TIMEOUT_SECONDS = 10; 168 169 public static final int AJAX_SHORT_TIMEOUT_SECONDS = 2; 170 171 public static final int POLLING_FREQUENCY_SECONDS = 1; 172 173 private static final String FIREBUG_XPI = "firebug-1.6.2-fx.xpi"; 174 175 private static final String FIREBUG_VERSION = "1.6.2"; 176 177 private static final String FIREBUG_M2 = "firebug/firebug/1.6.2-fx"; 178 179 private static final int PROXY_PORT = 4444; 180 181 private static final String HAR_NAME = "http-headers.json"; 182 183 public static final String SYSPROP_CHROME_DRIVER_PATH = "webdriver.chrome.driver"; 184 185 public static RemoteWebDriver driver; 186 187 protected static File tmp_firebug_xpi; 188 189 protected static ProxyServer proxyServer = null; 190 191 /** 192 * Logger method to follow what's being run on server logs and take a screenshot of the last page in case of failure 193 */ 194 @Rule 195 public MethodRule watchman = new LogTestWatchman(driver, NUXEO_URL); 196 197 /** 198 * This method will be executed before any method registered with JUnit After annotation. 199 * 200 * @since 5.8 201 */ 202 public void runBeforeAfters() { 203 ((LogTestWatchman) watchman).runBeforeAfters(); 204 } 205 206 @BeforeClass 207 public static void initDriver() throws Exception { 208 String browser = System.getProperty("browser", "firefox"); 209 // Use the same strings as command-line Selenium 210 if (browser.equals("chrome") || browser.equals("firefox")) { 211 initFirefoxDriver(); 212 } else if (browser.equals("googlechrome")) { 213 initChromeDriver(); 214 } else { 215 throw new RuntimeException("Browser not supported: " + browser); 216 } 217 driver.manage().timeouts().pageLoadTimeout(PAGE_LOAD_TIME_OUT_SECONDS, TimeUnit.SECONDS); 218 } 219 220 protected static void initFirefoxDriver() throws Exception { 221 DesiredCapabilities dc = DesiredCapabilities.firefox(); 222 FirefoxProfile profile = new FirefoxProfile(); 223 // Disable native events (makes things break on Windows) 224 profile.setEnableNativeEvents(false); 225 // Set English as default language 226 profile.setPreference("general.useragent.locale", "en"); 227 profile.setPreference("intl.accept_languages", "en"); 228 // Set other confs to speed up FF 229 230 // Speed up firefox by pipelining requests on a single connection 231 profile.setPreference("network.http.keep-alive", true); 232 profile.setPreference("network.http.pipelining", true); 233 profile.setPreference("network.http.proxy.pipelining", true); 234 profile.setPreference("network.http.pipelining.maxrequests", 8); 235 236 // Try to use less memory 237 profile.setPreference("browser.sessionhistory.max_entries", 10); 238 profile.setPreference("browser.sessionhistory.max_total_viewers", 4); 239 profile.setPreference("browser.sessionstore.max_tabs_undo", 4); 240 profile.setPreference("browser.sessionstore.interval", 1800000); 241 242 // disable unresponsive script alerts 243 profile.setPreference("dom.max_script_run_time", 0); 244 profile.setPreference("dom.max_chrome_script_run_time", 0); 245 246 // don't skip proxy for localhost 247 profile.setPreference("network.proxy.no_proxies_on", ""); 248 249 // prevent different kinds of popups/alerts 250 profile.setPreference("browser.tabs.warnOnClose", false); 251 profile.setPreference("browser.tabs.warnOnOpen", false); 252 profile.setPreference("extensions.newAddons", false); 253 profile.setPreference("extensions.update.notifyUser", false); 254 255 // disable autoscrolling 256 profile.setPreference("browser.urlbar.autocomplete.enabled", false); 257 258 // downloads conf 259 profile.setPreference("browser.download.useDownloadDir", false); 260 261 // prevent FF from running in offline mode when there's no network 262 // connection 263 profile.setPreference("toolkit.networkmanager.disable", true); 264 265 // prevent FF from giving health reports 266 profile.setPreference("datareporting.policy.dataSubmissionEnabled", false); 267 profile.setPreference("datareporting.healthreport.uploadEnabled", false); 268 profile.setPreference("datareporting.healthreport.service.firstRun", false); 269 profile.setPreference("datareporting.healthreport.service.enabled", false); 270 profile.setPreference("datareporting.healthreport.logging.consoleEnabled", false); 271 272 // start page conf to speed up FF 273 profile.setPreference("browser.startup.homepage", "about:blank"); 274 profile.setPreference("pref.browser.homepage.disable_button.bookmark_page", false); 275 profile.setPreference("pref.browser.homepage.disable_button.restore_default", false); 276 277 // misc confs to avoid useless updates 278 profile.setPreference("browser.search.update", false); 279 profile.setPreference("browser.bookmarks.restore_default_bookmarks", false); 280 281 // misc confs to speed up FF 282 profile.setPreference("extensions.ui.dictionary.hidden", true); 283 profile.setPreference("layout.spellcheckDefault", 0); 284 285 // webdriver logging 286 if (Boolean.TRUE.equals(Boolean.valueOf(System.getenv("nuxeo.log.webriver")))) { 287 String location = System.getProperty("basedir") + File.separator + "target"; 288 File outputFolder = new File(location); 289 if (!outputFolder.exists() || !outputFolder.isDirectory()) { 290 outputFolder = null; 291 } 292 File webdriverlogFile = File.createTempFile("webdriver", ".log", outputFolder); 293 profile.setPreference("webdriver.log.file", webdriverlogFile.getAbsolutePath()); 294 log.warn("Webdriver logs saved in " + webdriverlogFile); 295 } 296 297 addFireBug(profile); 298 JavaScriptError.addExtension(profile); 299 Proxy proxy = startProxy(); 300 if (proxy != null) { 301 // Does not work, but leave code for when it does 302 // Workaround: use 127.0.0.2 303 proxy.setNoProxy(""); 304 // setProxyPreferences method does not exist with selenium version 2.43.0 305 profile.setProxyPreferences(proxy); 306 // FIXME Should be dc.setCapability(CapabilityType.PROXY, proxy); 307 } 308 dc.setCapability(FirefoxDriver.PROFILE, profile); 309 driver = new FirefoxDriver(dc); 310 } 311 312 protected static void initChromeDriver() throws Exception { 313 if (System.getProperty(SYSPROP_CHROME_DRIVER_PATH) == null) { 314 String chromeDriverDefaultPath = null; 315 String chromeDriverExecutableName = CHROME_DRIVER_DEFAULT_EXECUTABLE_NAME; 316 if (SystemUtils.IS_OS_LINUX) { 317 chromeDriverDefaultPath = CHROME_DRIVER_DEFAULT_PATH_LINUX; 318 } else if (SystemUtils.IS_OS_MAC) { 319 chromeDriverDefaultPath = CHROME_DRIVER_DEFAULT_PATH_MAC; 320 } else if (SystemUtils.IS_OS_WINDOWS_XP) { 321 chromeDriverDefaultPath = CHROME_DRIVER_DEFAULT_PATH_WINXP; 322 chromeDriverExecutableName = CHROME_DRIVER_WINDOWS_EXECUTABLE_NAME; 323 } else if (SystemUtils.IS_OS_WINDOWS_VISTA) { 324 chromeDriverDefaultPath = CHROME_DRIVER_DEFAULT_PATH_WINVISTA; 325 chromeDriverExecutableName = CHROME_DRIVER_WINDOWS_EXECUTABLE_NAME; 326 } else if (SystemUtils.IS_OS_WINDOWS) { 327 // Unknown default path on other Windows OS. To be completed. 328 chromeDriverExecutableName = CHROME_DRIVER_WINDOWS_EXECUTABLE_NAME; 329 } 330 331 if (chromeDriverDefaultPath != null && new File(chromeDriverDefaultPath).exists()) { 332 log.warn(String.format("Missing property %s but found %s. Using it...", SYSPROP_CHROME_DRIVER_PATH, 333 chromeDriverDefaultPath)); 334 System.setProperty(SYSPROP_CHROME_DRIVER_PATH, chromeDriverDefaultPath); 335 } else { 336 // Can't find chromedriver in default location, check system 337 // path 338 File chromeDriverExecutable = findExecutableOnPath(chromeDriverExecutableName); 339 if ((chromeDriverExecutable != null) && (chromeDriverExecutable.exists())) { 340 log.warn(String.format("Missing property %s but found %s. Using it...", SYSPROP_CHROME_DRIVER_PATH, 341 chromeDriverExecutable.getCanonicalPath())); 342 System.setProperty(SYSPROP_CHROME_DRIVER_PATH, chromeDriverExecutable.getCanonicalPath()); 343 } else { 344 log.error(String.format("Could not find the Chrome driver looking at %s or system path." 345 + " Download it from %s and set its path with " + "the System property %s.", 346 chromeDriverDefaultPath, "http://code.google.com/p/chromedriver/downloads/list", 347 SYSPROP_CHROME_DRIVER_PATH)); 348 } 349 } 350 } 351 DesiredCapabilities dc = DesiredCapabilities.chrome(); 352 ChromeOptions options = new ChromeOptions(); 353 options.addArguments(Arrays.asList("--ignore-certificate-errors")); 354 Proxy proxy = startProxy(); 355 if (proxy != null) { 356 proxy.setNoProxy(""); 357 dc.setCapability(CapabilityType.PROXY, proxy); 358 } 359 dc.setCapability(ChromeOptions.CAPABILITY, options); 360 driver = new ChromeDriver(dc); 361 } 362 363 /** 364 * @since 5.7 365 */ 366 protected static File findExecutableOnPath(String executableName) { 367 String systemPath = System.getenv("PATH"); 368 String[] pathDirs = systemPath.split(File.pathSeparator); 369 File fullyQualifiedExecutable = null; 370 for (String pathDir : pathDirs) { 371 File file = new File(pathDir, executableName); 372 if (file.isFile()) { 373 fullyQualifiedExecutable = file; 374 break; 375 } 376 } 377 return fullyQualifiedExecutable; 378 } 379 380 /** 381 * @since 7.1 382 */ 383 @After 384 public void checkJavascriptError() { 385 if (driver != null) { 386 List<JavaScriptError> jsErrors = JavaScriptError.readErrors(driver); 387 if (jsErrors != null && !jsErrors.isEmpty()) { 388 StringBuilder msg = new StringBuilder(); 389 msg.append(jsErrors.size()).append(" Javascript error(s) detected: "); 390 msg.append("["); 391 int i = 0; 392 for (JavaScriptError jsError : jsErrors) { 393 if (i != 0) { 394 msg.append(", "); 395 } 396 i++; 397 msg.append("\"").append(jsError.getErrorMessage()).append("\""); 398 msg.append(" at ").append(jsError.getSourceName()); 399 msg.append(" line ").append(jsError.getLineNumber()); 400 } 401 msg.append("]"); 402 fail(msg.toString()); 403 } 404 } 405 } 406 407 @AfterClass 408 public static void quitDriver() { 409 if (driver != null) { 410 driver.quit(); 411 driver = null; 412 } 413 removeFireBug(); 414 415 try { 416 stopProxy(); 417 } catch (Exception e) { 418 log.error("Could not stop proxy: " + e.getMessage()); 419 } 420 } 421 422 /** 423 * Introspects the classpath and returns the list of files in it. FIXME: should use 424 * HarnessRuntime#getClassLoaderFiles that returns the same thing 425 * 426 * @throws Exception 427 */ 428 protected static List<String> getClassLoaderFiles() throws Exception { 429 ClassLoader cl = AbstractTest.class.getClassLoader(); 430 URL[] urls = null; 431 if (cl instanceof URLClassLoader) { 432 urls = ((URLClassLoader) cl).getURLs(); 433 } else if (cl.getClass().getName().equals("org.apache.tools.ant.AntClassLoader")) { 434 Method method = cl.getClass().getMethod("getClasspath"); 435 String cp = (String) method.invoke(cl); 436 String[] paths = cp.split(File.pathSeparator); 437 urls = new URL[paths.length]; 438 for (int i = 0; i < paths.length; i++) { 439 urls[i] = new URL("file:" + paths[i]); 440 } 441 } else { 442 System.err.println("Unknown classloader type: " + cl.getClass().getName()); 443 return null; 444 } 445 446 JarFile surefirebooterJar = null; 447 for (URL url : urls) { 448 URI uri = url.toURI(); 449 if (uri.getPath().matches(".*/nuxeo-runtime-[^/]*\\.jar")) { 450 break; 451 } else if (uri.getScheme().equals("file") && uri.getPath().contains("surefirebooter")) { 452 surefirebooterJar = new JarFile(new File(uri)); 453 } 454 } 455 456 // special case for maven surefire with useManifestOnlyJar 457 if (surefirebooterJar != null) { 458 try { 459 try { 460 String cp = surefirebooterJar.getManifest() 461 .getMainAttributes() 462 .getValue(Attributes.Name.CLASS_PATH); 463 if (cp != null) { 464 String[] cpe = cp.split(" "); 465 URL[] newUrls = new URL[cpe.length]; 466 for (int i = 0; i < cpe.length; i++) { 467 // Don't need to add 'file:' with maven 468 // surefire >= 2.4.2 469 String newUrl = cpe[i].startsWith("file:") ? cpe[i] : "file:" + cpe[i]; 470 newUrls[i] = new URL(newUrl); 471 } 472 urls = newUrls; 473 } 474 } finally { 475 surefirebooterJar.close(); 476 } 477 } catch (Exception e) { 478 // skip 479 } 480 } 481 // turn into files 482 List<String> files = new ArrayList<>(urls.length); 483 for (URL url : urls) { 484 files.add(url.toURI().getPath()); 485 } 486 return files; 487 } 488 489 private static final String M2_REPO = "repository/"; 490 491 protected static void addFireBug(FirefoxProfile profile) throws Exception { 492 // this is preventing from running tests in eclipse 493 // profile.addExtension(AbstractTest.class, "/firebug.xpi"); 494 495 File xpi = null; 496 List<String> clf = getClassLoaderFiles(); 497 for (String f : clf) { 498 if (f.endsWith("/" + FIREBUG_XPI)) { 499 xpi = new File(f); 500 } 501 } 502 if (xpi == null) { 503 String customM2Repo = System.getProperty("M2_REPO", M2_REPO).replaceAll("/$", ""); 504 // try to guess the location in the M2 repo 505 for (String f : clf) { 506 if (f.contains(customM2Repo)) { 507 String m2 = f.substring(0, f.indexOf(customM2Repo) + customM2Repo.length()); 508 xpi = new File(m2 + "/" + FIREBUG_M2 + "/" + FIREBUG_XPI); 509 break; 510 } 511 } 512 } 513 if (xpi == null || !xpi.exists()) { 514 log.warn(FIREBUG_XPI + " not found in classloader or local M2 repository"); 515 return; 516 } 517 profile.addExtension(xpi); 518 519 // avoid "first run" page 520 profile.setPreference("extensions.firebug.currentVersion", FIREBUG_VERSION); 521 } 522 523 protected static void removeFireBug() { 524 if (tmp_firebug_xpi != null) { 525 tmp_firebug_xpi.delete(); 526 tmp_firebug_xpi.getParentFile().delete(); 527 } 528 } 529 530 protected static Proxy startProxy() throws Exception { 531 if (Boolean.TRUE.equals(Boolean.valueOf(System.getProperty("useProxy", "false")))) { 532 proxyServer = new ProxyServer(PROXY_PORT); 533 proxyServer.start(); 534 proxyServer.setCaptureHeaders(true); 535 // Block access to tracking sites 536 proxyServer.blacklistRequests("https?://www\\.nuxeo\\.com/embedded/wizard.*", 410); 537 proxyServer.blacklistRequests("https?://.*\\.mktoresp\\.com/.*", 410); 538 proxyServer.blacklistRequests(".*_mchId.*", 410); 539 proxyServer.blacklistRequests("https?://.*\\.google-analytics\\.com/.*", 410); 540 proxyServer.newHar("webdriver-test"); 541 Proxy proxy = proxyServer.seleniumProxy(); 542 return proxy; 543 } else { 544 return null; 545 } 546 } 547 548 protected static void stopProxy() throws Exception { 549 if (proxyServer != null) { 550 String target = System.getProperty(Environment.NUXEO_LOG_DIR); 551 File harFile; 552 if (target == null) { 553 harFile = new File(HAR_NAME); 554 } else { 555 harFile = new File(target, HAR_NAME); 556 } 557 proxyServer.getHar().writeTo(harFile); 558 proxyServer.stop(); 559 } 560 } 561 562 @Before 563 public void setUpAbstract() { 564 if (driver != null) { 565 driver.get(NUXEO_URL + "/wro/api/v1/resource/bundle/nuxeo_includes.js"); 566 } 567 } 568 569 public static <T> T get(String url, Class<T> pageClassToProxy) { 570 if (driver != null) { 571 List<JavaScriptError> jsErrors = JavaScriptError.readErrors(driver); 572 if (jsErrors != null && !jsErrors.isEmpty()) { 573 StringBuilder msg = new StringBuilder(); 574 msg.append(jsErrors.size()).append(" Javascript error(s) detected: "); 575 msg.append("["); 576 int i = 0; 577 for (JavaScriptError jsError : jsErrors) { 578 if (i != 0) { 579 msg.append(", "); 580 } 581 i++; 582 msg.append("\"").append(jsError.getErrorMessage()).append("\""); 583 msg.append(" at ").append(jsError.getSourceName()); 584 msg.append(" line ").append(jsError.getLineNumber()); 585 } 586 msg.append("]"); 587 if (driver != null) { 588 ScreenshotTaker taker = new ScreenshotTaker(); 589 taker.takeScreenshot(driver, "NXP-17647-"); 590 taker.dumpPageSource(driver, "NXP-17647-"); 591 driver.get(NUXEO_URL + "/wro/api/v1/resource/bundle/nuxeo_includes.js"); 592 taker.dumpPageSource(driver, "NXP-17647-includes-js"); 593 } 594 fail(msg.toString()); 595 } 596 } 597 driver.get(url); 598 return asPage(pageClassToProxy); 599 } 600 601 /** 602 * Do not wait for page load. Do not handle error. Do not give explicit error in case of failure. This is a very raw 603 * get. 604 * 605 * @since 6.0 606 */ 607 public static <T> T getWithoutErrorHandler(String url, Class<T> pageClassToProxy) throws IOException { 608 Command command = new Command(AbstractTest.driver.getSessionId(), DriverCommand.GET, 609 ImmutableMap.of("url", url)); 610 AbstractTest.driver.getCommandExecutor().execute(command); 611 return asPage(pageClassToProxy); 612 } 613 614 public static WebDriver getPopup() { 615 String currentWindow = driver.getWindowHandle(); 616 for (String popup : driver.getWindowHandles()) { 617 if (popup.equals(currentWindow)) { 618 continue; 619 } 620 return driver.switchTo().window(popup); 621 } 622 return null; 623 } 624 625 public static <T> T asPage(Class<T> pageClassToProxy) { 626 T page = instantiatePage(pageClassToProxy); 627 return fillElement(pageClassToProxy, page); 628 } 629 630 public static <T extends WebFragment> T getWebFragment(By by, Class<T> webFragmentClass) { 631 WebElement element = Locator.findElementWithTimeout(by); 632 return getWebFragment(element, webFragmentClass); 633 } 634 635 public static <T extends WebFragment> T getWebFragment(WebElement element, Class<T> webFragmentClass) { 636 T webFragment = instantiateWebFragment(element, webFragmentClass); 637 webFragment = fillElement(webFragmentClass, webFragment); 638 // fillElement somehow overwrite the 'element' field, reset it. 639 webFragment.setElement(element); 640 return webFragment; 641 } 642 643 /** 644 * Fills an instantiated page/form/widget attributes 645 * 646 * @since 5.7 647 */ 648 public static <T> T fillElement(Class<T> pageClassToProxy, T page) { 649 PageFactory.initElements(new VariableElementLocatorFactory(driver, AJAX_TIMEOUT_SECONDS), page); 650 // check all required WebElements on the page and wait for their 651 // loading 652 final List<String> fieldNames = new ArrayList<>(); 653 final List<WrapsElement> elements = new ArrayList<>(); 654 for (Field field : pageClassToProxy.getDeclaredFields()) { 655 if (field.getAnnotation(Required.class) != null) { 656 try { 657 field.setAccessible(true); 658 fieldNames.add(field.getName()); 659 elements.add((WrapsElement) field.get(page)); 660 } catch (Exception e) { 661 throw new RuntimeException(e); 662 } 663 } 664 } 665 666 Wait<T> wait = new FluentWait<>(page).withTimeout(LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS).pollingEvery( 667 POLLING_FREQUENCY_MILLISECONDS, TimeUnit.MILLISECONDS); 668 try { 669 return wait.until(new Function<T, T>() { 670 @Override 671 public T apply(T page) { 672 String notLoaded = anyElementNotLoaded(elements, fieldNames); 673 if (notLoaded == null) { 674 return page; 675 } else { 676 return null; 677 } 678 } 679 }); 680 } catch (TimeoutException e) { 681 throw new TimeoutException("not loaded: " + anyElementNotLoaded(elements, fieldNames), e); 682 } 683 } 684 685 protected static String anyElementNotLoaded(List<WrapsElement> proxies, List<String> fieldNames) { 686 for (int i = 0; i < proxies.size(); i++) { 687 WrapsElement proxy = proxies.get(i); 688 try { 689 // method implemented in LocatingElementHandler 690 proxy.getWrappedElement(); 691 } catch (NoSuchElementException e) { 692 return fieldNames.get(i); 693 } 694 } 695 return null; 696 } 697 698 // private in PageFactory... 699 protected static <T> T instantiatePage(Class<T> pageClassToProxy) { 700 try { 701 try { 702 Constructor<T> constructor = pageClassToProxy.getConstructor(WebDriver.class); 703 return constructor.newInstance(driver); 704 } catch (NoSuchMethodException e) { 705 return pageClassToProxy.newInstance(); 706 } 707 } catch (RuntimeException e) { 708 throw e; 709 } catch (Exception e) { 710 throw new RuntimeException(e); 711 } 712 } 713 714 protected static <T extends WebFragment> T instantiateWebFragment(WebElement element, Class<T> webFragmentClass) { 715 try { 716 try { 717 Constructor<T> constructor = webFragmentClass.getConstructor(WebDriver.class, WebElement.class); 718 return constructor.newInstance(driver, element); 719 } catch (NoSuchMethodException e) { 720 return webFragmentClass.newInstance(); 721 } 722 } catch (RuntimeException e) { 723 throw e; 724 } catch (Exception e) { 725 throw new RuntimeException(e); 726 } 727 } 728 729 public LoginPage getLoginPage() { 730 return get(NUXEO_URL + "/logout", LoginPage.class); 731 } 732 733 public LoginPage logout() { 734 return getLoginPage(); 735 } 736 737 /** 738 * navigate to a link text. wait until the link is available and click on it. 739 */ 740 public <T extends AbstractPage> T nav(Class<T> pageClass, String linkText) { 741 WebElement link = Locator.findElementWithTimeout(By.linkText(linkText)); 742 if (link == null) { 743 return null; 744 } 745 link.click(); 746 return asPage(pageClass); 747 } 748 749 /** 750 * Navigate to a specified url 751 * 752 * @param urlString url 753 * @throws MalformedURLException 754 */ 755 public void navToUrl(String urlString) throws MalformedURLException { 756 URL url = new URL(urlString); 757 driver.navigate().to(url); 758 } 759 760 /** 761 * Login as Administrator 762 * 763 * @return the Document base page (by default returned by CAP) 764 * @throws UserNotConnectedException 765 */ 766 public DocumentBasePage login() throws UserNotConnectedException { 767 return login("Administrator", "Administrator"); 768 } 769 770 public DocumentBasePage login(String username, String password) throws UserNotConnectedException { 771 DocumentBasePage documentBasePage = getLoginPage().login(username, password, DocumentBasePage.class); 772 documentBasePage.checkUserConnected(username); 773 return documentBasePage; 774 } 775 776 /** 777 * Login as default test user. 778 * 779 * @since 5.9.2 780 */ 781 public DocumentBasePage loginAsTestUser() throws UserNotConnectedException { 782 return login(TEST_USERNAME, TEST_PASSWORD); 783 } 784 785 /** 786 * Login using an invalid credential. 787 * 788 * @param username 789 * @param password 790 */ 791 public LoginPage loginInvalid(String username, String password) { 792 LoginPage loginPage = getLoginPage().login(username, password, LoginPage.class); 793 return loginPage; 794 } 795 796 /** 797 * Init the repository with a test Workspace form the {@code currentPage}. 798 * 799 * @param currentPage the current page 800 * @return the created Workspace page 801 * @throws Exception if initializing repository fails 802 */ 803 protected DocumentBasePage initRepository(DocumentBasePage currentPage) throws Exception { 804 return createWorkspace(currentPage, "Test Workspace", "Test Workspace for my dear WebDriver."); 805 } 806 807 /** 808 * Cleans the repository (delete the test Workspace) from the {@code currentPage}. 809 * 810 * @param currentPage the current page 811 * @throws Exception if cleaning repository fails 812 */ 813 protected void cleanRepository(DocumentBasePage currentPage) throws Exception { 814 deleteWorkspace(currentPage, "Test Workspace"); 815 } 816 817 /** 818 * Creates a Workspace form the {@code currentPage}. 819 * 820 * @param currentPage the current page 821 * @param workspaceTitle the workspace title 822 * @param workspaceDescription the workspace description 823 * @return the created Workspace page 824 */ 825 protected DocumentBasePage createWorkspace(DocumentBasePage currentPage, String workspaceTitle, 826 String workspaceDescription) { 827 // Go to Workspaces 828 DocumentBasePage workspacesPage = currentPage.getNavigationSubPage().goToDocument("Workspaces"); 829 // Get Workspace creation form page 830 WorkspaceFormPage workspaceCreationFormPage = workspacesPage.getWorkspacesContentTab().getWorkspaceCreatePage(); 831 // Create Workspace 832 DocumentBasePage workspacePage = workspaceCreationFormPage.createNewWorkspace(workspaceTitle, 833 workspaceDescription); 834 return workspacePage; 835 } 836 837 /** 838 * Deletes the Workspace with title {@code workspaceTitle} from the {@code currentPage}. 839 * 840 * @param currentPage the current page 841 * @param workspaceTitle the workspace title 842 */ 843 protected void deleteWorkspace(DocumentBasePage currentPage, String workspaceTitle) { 844 // Go to Workspaces 845 DocumentBasePage workspacesPage = currentPage.getNavigationSubPage().goToDocument("Workspaces"); 846 // Delete the Workspace 847 workspacesPage.getContentTab().removeDocument(workspaceTitle); 848 } 849 850 /** 851 * Creates a File form the {@code currentPage}. 852 * 853 * @param currentPage the current page 854 * @param fileTitle the file title 855 * @param fileDescription the file description 856 * @param uploadBlob true if a blob needs to be uploaded (temporary file created for this purpose) 857 * @param filePrefix the file prefix 858 * @param fileSuffix the file suffix 859 * @param fileContent the file content 860 * @return the created File page 861 * @throws IOException if temporary file creation fails 862 */ 863 protected FileDocumentBasePage createFile(DocumentBasePage currentPage, String fileTitle, String fileDescription, 864 boolean uploadBlob, String filePrefix, String fileSuffix, String fileContent) throws IOException { 865 // Get File creation form page 866 FileCreationFormPage fileCreationFormPage = currentPage.getContentTab().getDocumentCreatePage("File", 867 FileCreationFormPage.class); 868 // Create File 869 FileDocumentBasePage filePage = fileCreationFormPage.createFileDocument(fileTitle, fileDescription, uploadBlob, 870 filePrefix, fileSuffix, fileDescription); 871 return filePage; 872 } 873 874 /** 875 * Creates a Collections container form the {@code currentPage}. 876 * 877 * @param currentPage the current page 878 * @param collectionsTitle the Collections container title 879 * @param collectionsDescription the collections description 880 * @return the created Collections page 881 * @throws IOException if temporary file creation fails 882 */ 883 protected DocumentBasePage createCollections(DocumentBasePage currentPage, String collectionsTitle, 884 String fileDescription) { 885 DublinCoreCreationDocumentFormPage dublinCoreDocumentFormPage = currentPage.getContentTab() 886 .getDocumentCreatePage( 887 "Collections", 888 DublinCoreCreationDocumentFormPage.class); 889 // Create File 890 DocumentBasePage documentBasePage = dublinCoreDocumentFormPage.createDocument(collectionsTitle, fileDescription); 891 return documentBasePage; 892 } 893 894 /** 895 * Creates a Collection form the {@code currentPage}. 896 * 897 * @param currentPage the current page 898 * @param collectionsTitle the Collections container title 899 * @param fileDescription the collection description 900 * @return the created Collections page 901 */ 902 protected CollectionContentTabSubPage createCollection(DocumentBasePage currentPage, String collectionsTitle, 903 String fileDescription) { 904 CollectionCreationFormPage collectionCreationFormPage = currentPage.getContentTab().getDocumentCreatePage( 905 "Collection", CollectionCreationFormPage.class); 906 // Create File 907 CollectionContentTabSubPage documentBasePage = collectionCreationFormPage.createDocument(collectionsTitle, 908 fileDescription); 909 return documentBasePage; 910 } 911 912 /** 913 * Creates a temporary file and returns its absolute path. 914 * 915 * @param filePrefix the file prefix 916 * @param fileSuffix the file suffix 917 * @param fileContent the file content 918 * @return the temporary file to upload path 919 * @throws IOException if temporary file creation fails 920 * @since 5.9.3 921 */ 922 public static String getTmpFileToUploadPath(String filePrefix, String fileSuffix, String fileContent) 923 throws IOException { 924 // Create tmp file, deleted on exit 925 File tmpFile = File.createTempFile(filePrefix, fileSuffix); 926 tmpFile.deleteOnExit(); 927 FileUtils.writeFile(tmpFile, fileContent); 928 assertTrue(tmpFile.exists()); 929 930 // Check file URI protocol 931 assertEquals("file", tmpFile.toURI().toURL().getProtocol()); 932 933 // Return file absolute path 934 return tmpFile.getAbsolutePath(); 935 } 936 937 /** 938 * Get the current document id stored in the javascript ctx.currentDocument variable of the current page. 939 * 940 * @return the current document id 941 * @since 5.7 942 */ 943 protected String getCurrentDocumentId() { 944 return (String) ((JavascriptExecutor) driver).executeScript(String.format("return ctx.currentDocument;")); 945 } 946 947 /** 948 * Creates a Note form the {@code currentPage}. 949 * 950 * @param currentPage the current page 951 * @param noteTitle the note title 952 * @param noteDescription the note description 953 * @param defineNote true if the content of the note needs to be defined 954 * @param noteContent the content of the note 955 * @return the created note page. 956 * @throws IOException 957 * @since 5.9.4 958 */ 959 protected NoteDocumentBasePage createNote(DocumentBasePage currentPage, String noteTitle, String noteDescription, 960 boolean defineNote, String noteContent) throws IOException { 961 // Get the Note creation form 962 NoteCreationFormPage noteCreationPage = currentPage.getContentTab().getDocumentCreatePage("Note", 963 NoteCreationFormPage.class); 964 // Create a Note 965 NoteDocumentBasePage notePage = noteCreationPage.createNoteDocument(noteTitle, noteDescription, defineNote, 966 noteContent); 967 return notePage; 968 } 969 970}