diff --git a/src/main/java/com/frameworkium/core/ui/ExtraExpectedConditions.java b/src/main/java/com/frameworkium/core/ui/ExtraExpectedConditions.java old mode 100644 new mode 100755 index 97f29202..d9f2d2f0 --- a/src/main/java/com/frameworkium/core/ui/ExtraExpectedConditions.java +++ b/src/main/java/com/frameworkium/core/ui/ExtraExpectedConditions.java @@ -102,4 +102,17 @@ public static ExpectedCondition> sizeLessThan( : null; } + /** + * Wait for the document ready state to equal 'complete'. + * Useful for javascript loading on page-load. + * + * @return a {@link ExpectedCondition} which returns false if the document + * isn't ready, and true if the document is ready + */ + public static ExpectedCondition documentBodyReady() { + + return driver -> + (Boolean) ((JavascriptExecutor) driver) + .executeScript("return document.readyState == 'complete';"); + } } diff --git a/src/main/java/com/frameworkium/core/ui/js/AbstractFramework.java b/src/main/java/com/frameworkium/core/ui/js/AbstractFramework.java new file mode 100755 index 00000000..15005abc --- /dev/null +++ b/src/main/java/com/frameworkium/core/ui/js/AbstractFramework.java @@ -0,0 +1,13 @@ +package com.frameworkium.core.ui.js; + +import org.openqa.selenium.JavascriptExecutor; + +public interface AbstractFramework { + + /** {@inheritDoc} **/ + boolean isPresent(JavascriptExecutor javascriptExecutor); + + /** {@inheritDoc} **/ + void waitToBeReady(JavascriptExecutor javascriptExecutor); + +} diff --git a/src/main/java/com/frameworkium/core/ui/js/JavascriptWait.java b/src/main/java/com/frameworkium/core/ui/js/JavascriptWait.java new file mode 100755 index 00000000..1352f53c --- /dev/null +++ b/src/main/java/com/frameworkium/core/ui/js/JavascriptWait.java @@ -0,0 +1,85 @@ +package com.frameworkium.core.ui.js; + +import com.frameworkium.core.ui.ExtraExpectedConditions; +import com.frameworkium.core.ui.js.framework.Angular; +import com.frameworkium.core.ui.js.framework.AngularTwo; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.support.ui.Wait; + +import java.util.Arrays; + +/** + * Frameworkium implementation of waiting for JS events on page-load + */ +public class JavascriptWait { + + private final Wait wait; + private final JavascriptExecutor javascriptExecutor; + + private SupportedFramework detectedFramework; + + public JavascriptWait(WebDriver driver, Wait wait) { + this.wait = wait; + this.javascriptExecutor = (JavascriptExecutor) driver; + } + + /** + * Default entry to {@link JavascriptWait} + * + * Following actions are waited on: + *
    + *
  • Document state to be ready
  • + *
  • If page is using a supported JS framework, it will detect & wait
  • + *
+ */ + public void waitForJavascriptEventsOnLoad() { + waitForDocumentReady(); + detectFramework(); + waitForJavascriptFramework(); + } + + /** + * If a page is using a supported JS framework, it will wait until it's ready + */ + public void waitForJavascriptFramework() { + if (detectedFramework != null) getFrameworkClass(detectedFramework).waitToBeReady(javascriptExecutor); + } + + private void waitForDocumentReady() { + wait.until(ExtraExpectedConditions.documentBodyReady()); + } + + private void detectFramework() { + detectedFramework = Arrays.stream(SupportedFramework.values()) + .filter(supportedFramework -> getFrameworkClass(supportedFramework).isPresent(javascriptExecutor)) + .findFirst() + .orElse(null); + } + + private AbstractFramework getFrameworkClass(SupportedFramework supportedFramework) { + try { + return ((AbstractFramework) supportedFramework.getFrameworkClass().newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Supported JS Frameworks within Frameworkium and their class which implement {@link AbstractFramework} + */ + private enum SupportedFramework { + ANGULAR(Angular.class), + ANGULAR_TWO(AngularTwo.class); + + private Class frameworkClass; + + SupportedFramework(Class frameworkClass) { + this.frameworkClass = frameworkClass; + } + + public Class getFrameworkClass() { + return this.frameworkClass; + } + } +} diff --git a/src/main/java/com/frameworkium/core/ui/js/framework/Angular.java b/src/main/java/com/frameworkium/core/ui/js/framework/Angular.java new file mode 100755 index 00000000..e7f7baae --- /dev/null +++ b/src/main/java/com/frameworkium/core/ui/js/framework/Angular.java @@ -0,0 +1,18 @@ +package com.frameworkium.core.ui.js.framework; + +import com.frameworkium.core.ui.js.AbstractFramework; +import com.paulhammant.ngwebdriver.NgWebDriver; +import org.openqa.selenium.JavascriptExecutor; + +public class Angular implements AbstractFramework { + + @Override + public boolean isPresent(JavascriptExecutor javascriptExecutor) { + return (Boolean) javascriptExecutor.executeScript("return typeof angular == 'object';"); + } + + @Override + public void waitToBeReady(JavascriptExecutor javascriptExecutor) { + new NgWebDriver(javascriptExecutor).waitForAngularRequestsToFinish(); + } +} diff --git a/src/main/java/com/frameworkium/core/ui/js/framework/AngularTwo.java b/src/main/java/com/frameworkium/core/ui/js/framework/AngularTwo.java new file mode 100755 index 00000000..65d0e800 --- /dev/null +++ b/src/main/java/com/frameworkium/core/ui/js/framework/AngularTwo.java @@ -0,0 +1,18 @@ +package com.frameworkium.core.ui.js.framework; + +import com.frameworkium.core.ui.js.AbstractFramework; +import com.paulhammant.ngwebdriver.NgWebDriver; +import org.openqa.selenium.JavascriptExecutor; + +public class AngularTwo implements AbstractFramework { + + @Override + public boolean isPresent(JavascriptExecutor javascriptExecutor) { + return (Boolean) javascriptExecutor.executeScript("return typeof ng == 'object';"); + } + + @Override + public void waitToBeReady(JavascriptExecutor javascriptExecutor) { + new NgWebDriver(javascriptExecutor).waitForAngular2RequestsToFinish(); + } +} diff --git a/src/main/java/com/frameworkium/core/ui/pages/BasePage.java b/src/main/java/com/frameworkium/core/ui/pages/BasePage.java old mode 100644 new mode 100755 index 2ada3f63..9e9d9da5 --- a/src/main/java/com/frameworkium/core/ui/pages/BasePage.java +++ b/src/main/java/com/frameworkium/core/ui/pages/BasePage.java @@ -4,8 +4,8 @@ import com.frameworkium.core.common.reporting.allure.AllureLogger; import com.frameworkium.core.ui.annotations.Visible; import com.frameworkium.core.ui.capture.model.Command; +import com.frameworkium.core.ui.js.JavascriptWait; import com.frameworkium.core.ui.tests.BaseTest; -import com.paulhammant.ngwebdriver.NgWebDriver; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.openqa.selenium.JavascriptExecutor; @@ -13,8 +13,6 @@ import org.openqa.selenium.support.ui.Wait; import ru.yandex.qatools.htmlelements.loader.HtmlElementLoader; -import java.util.Objects; - public abstract class BasePage> { protected final Logger logger = LogManager.getLogger(this); @@ -22,15 +20,14 @@ public abstract class BasePage> { protected Wait wait; /** Visibility with current page wait and driver */ protected Visibility visibility; - - private NgWebDriver ngDriver; + protected JavascriptWait javascriptWait; /** Constructor, initialises all things useful. */ public BasePage() { driver = BaseTest.getDriver(); wait = BaseTest.getWait(); visibility = new Visibility(wait, BaseTest.getDriver()); - ngDriver = new NgWebDriver(BaseTest.getDriver()); + javascriptWait = new JavascriptWait(driver, wait); } /** @@ -89,7 +86,7 @@ public T get(long timeout) { *

*

    *
  • Initialises fields with lazy proxies
  • - *
  • Waits for AngularJS requests to finish loading, if present
  • + *
  • Waits for Javascript events including document ready & JS frameworks (if applicable)
  • *
  • Processes Frameworkium visibility annotations e.g. {@link Visible}
  • *
  • Log page load to Allure and Capture
  • *
@@ -100,15 +97,14 @@ public T get(long timeout) { @SuppressWarnings("unchecked") public T get() { + //Populate Page Object HtmlElementLoader.populatePageObject(this, driver); - // wait for page to load - if (isPageAngularJS()) { - waitForAngularRequestsToFinish(); - } + //Wait for Elements & JS visibility.waitForAnnotatedElementVisibility(this); + javascriptWait.waitForJavascriptEventsOnLoad(); - // log page load + //Log takePageLoadedScreenshotAndSendToCapture(); logPageLoadToAllure(); @@ -135,8 +131,11 @@ private void takePageLoadedScreenshotAndSendToCapture() { } } - private boolean isPageAngularJS() { - return Objects.equals(executeJS("return typeof angular;"), "object"); + /** + * Waits for all JS framework requests to finish on page + */ + protected void waitForJavascriptFrameworkToFinish() { + javascriptWait.waitForJavascriptFramework(); } /** @@ -181,11 +180,6 @@ protected Object executeAsyncJS(String javascript) { return returnObj; } - /** Method to wait for AngularJS requests to finish on the page */ - protected void waitForAngularRequestsToFinish() { - ngDriver.waitForAngularRequestsToFinish(); - } - /** @return Returns the title of the web page */ public String getTitle() { return driver.getTitle(); diff --git a/src/main/java/com/frameworkium/core/ui/tests/BaseTest.java b/src/main/java/com/frameworkium/core/ui/tests/BaseTest.java old mode 100644 new mode 100755 index 2927799a..e6163fad --- a/src/main/java/com/frameworkium/core/ui/tests/BaseTest.java +++ b/src/main/java/com/frameworkium/core/ui/tests/BaseTest.java @@ -1,12 +1,18 @@ package com.frameworkium.core.ui.tests; -import com.frameworkium.core.common.listeners.*; +import com.frameworkium.core.common.listeners.MethodInterceptor; +import com.frameworkium.core.common.listeners.ResultLoggerListener; +import com.frameworkium.core.common.listeners.TestListener; import com.frameworkium.core.common.reporting.TestIdUtils; import com.frameworkium.core.common.reporting.allure.AllureLogger; import com.frameworkium.core.common.reporting.allure.AllureProperties; import com.frameworkium.core.ui.capture.ScreenshotCapture; -import com.frameworkium.core.ui.driver.*; -import com.frameworkium.core.ui.listeners.*; +import com.frameworkium.core.ui.driver.Driver; +import com.frameworkium.core.ui.driver.DriverSetup; +import com.frameworkium.core.ui.driver.WebDriverWrapper; +import com.frameworkium.core.ui.listeners.CaptureListener; +import com.frameworkium.core.ui.listeners.SauceLabsListener; +import com.frameworkium.core.ui.listeners.ScreenshotListener; import com.saucelabs.common.SauceOnDemandAuthentication; import com.saucelabs.common.SauceOnDemandSessionIdProvider; import com.saucelabs.testng.SauceOnDemandAuthenticationProvider; @@ -14,16 +20,21 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.*; +import org.openqa.selenium.StaleElementReferenceException; +import org.openqa.selenium.WebDriver; import org.openqa.selenium.remote.SessionId; import org.openqa.selenium.support.ui.FluentWait; import org.openqa.selenium.support.ui.Wait; import org.testng.annotations.*; import java.lang.reflect.Method; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Optional; -import java.util.concurrent.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import static java.util.Objects.isNull; @@ -166,7 +177,6 @@ public static WebDriverWrapper getDriver() { return driver.get().getDriver(); } - /** Loops through all active driver types and tears them down */ @AfterSuite(alwaysRun = true) public static void tearDownRemainingDrivers() { diff --git a/src/test/java/com/frameworkium/integration/angularjs/pages/web/DeveloperGuidePage.java b/src/test/java/com/frameworkium/integration/angularjs/pages/web/DeveloperGuidePage.java old mode 100644 new mode 100755 index 895e222e..2493ef22 --- a/src/test/java/com/frameworkium/integration/angularjs/pages/web/DeveloperGuidePage.java +++ b/src/test/java/com/frameworkium/integration/angularjs/pages/web/DeveloperGuidePage.java @@ -36,7 +36,7 @@ public DeveloperGuidePage searchDeveloperGuide(String inputText) { public DeveloperGuidePage clickBootstrapSearchItem() { bootstrapSearchItem.click(); - waitForAngularRequestsToFinish(); + waitForJavascriptFrameworkToFinish(); return this; }