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