├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── chromeDeps.gradle ├── docs ├── BuildingPageObjects.md ├── ConfiguringProjectSettings.md ├── CustomizingCapabilities.md ├── DevelopmentEnvironment.md ├── JUnit4Support.md ├── JavaScriptEnhancements.md ├── LocalGridConfiguration.md ├── PageComponents.md ├── Selenium2.53.1VersionMatrix.md ├── TargetPlatformFeature.md ├── TestNGSupport.md ├── TransitionErrorDetection.md ├── WelcomeToSeleniumFoundation.md ├── example │ ├── ExamplePage.md │ ├── FrameComponent.md │ ├── ModelTest.md │ ├── ShadowRootComponent.md │ ├── TableComponent.md │ └── TableRowComponent.md └── images │ └── META-INF.png ├── edgeDeps.gradle ├── espressoDeps.gradle ├── firefoxDeps.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── htmlunitDeps.gradle ├── mac2Deps.gradle ├── operaDeps.gradle ├── phantomjsDeps.gradle ├── safariDeps.gradle ├── selenium3Deps.gradle ├── selenium4Deps.gradle ├── settings.gradle ├── src ├── main │ ├── java │ │ └── com │ │ │ └── nordstrom │ │ │ └── automation │ │ │ └── selenium │ │ │ ├── AbstractSeleniumConfig.java │ │ │ ├── DriverPlugin.java │ │ │ ├── annotations │ │ │ ├── InitialPage.java │ │ │ ├── NoDriver.java │ │ │ └── PageUrl.java │ │ │ ├── core │ │ │ ├── ByType.java │ │ │ ├── DriverManager.java │ │ │ ├── GridUtility.java │ │ │ ├── JsUtility.java │ │ │ ├── SeleniumGrid.java │ │ │ ├── ServerPidFinder.java │ │ │ └── TestBase.java │ │ │ ├── examples │ │ │ ├── ExamplePage.java │ │ │ ├── FormComponent.java │ │ │ ├── FrameComponent.java │ │ │ ├── IOSPage.java │ │ │ ├── JUnitRoot.java │ │ │ ├── JUnitTargetRoot.java │ │ │ ├── MacPage.java │ │ │ ├── QuickStart.java │ │ │ ├── ServletFlags.java │ │ │ ├── ShadowRootComponent.java │ │ │ ├── TableComponent.java │ │ │ ├── TableRowComponent.java │ │ │ ├── TestNgRoot.java │ │ │ ├── TestNgTargetRoot.java │ │ │ └── WindowsPage.java │ │ │ ├── exceptions │ │ │ ├── ComponentStillDisplayedTimeoutException.java │ │ │ ├── ComponentStillInvisibleTimeoutException.java │ │ │ ├── ConditionStillInvalidTimeoutException.java │ │ │ ├── ConditionStillValidTimeoutException.java │ │ │ ├── ContainerVacatedException.java │ │ │ ├── DocumentNotReadyTimeoutException.java │ │ │ ├── DriverExecutableNotFoundException.java │ │ │ ├── DriverNotAvailableException.java │ │ │ ├── ElementAbsentOrHiddenTimeoutException.java │ │ │ ├── ElementAttributeTimeoutException.java │ │ │ ├── ElementNotClickableTimeoutException.java │ │ │ ├── ElementNotPresentTimeoutException.java │ │ │ ├── ElementReferenceRefreshFailureException.java │ │ │ ├── ElementSelectionStateTimeoutException.java │ │ │ ├── ElementStillFreshTimeoutException.java │ │ │ ├── ElementStillVisibleTimeoutException.java │ │ │ ├── ElementTextContentTimeoutException.java │ │ │ ├── GridServerLaunchFailedException.java │ │ │ ├── InitialPageNotSpecifiedException.java │ │ │ ├── InvalidGridHostException.java │ │ │ ├── LandingPageMismatchException.java │ │ │ ├── NoWindowAppearedTimeoutException.java │ │ │ ├── OptionalElementNotAcquiredException.java │ │ │ ├── PageLoadRendererTimeoutException.java │ │ │ ├── PageNotLoadedException.java │ │ │ ├── PageTransitionRefreshTimeoutException.java │ │ │ ├── PlatformActivationFailedException.java │ │ │ ├── ShadowRootContextException.java │ │ │ ├── TransitionErrorException.java │ │ │ ├── UnknownGridHostException.java │ │ │ ├── VacationStackTrace.java │ │ │ └── WindowStillExistsTimeoutException.java │ │ │ ├── interfaces │ │ │ ├── DetectsLoadCompletion.java │ │ │ ├── DriverProvider.java │ │ │ ├── TransitionErrorDetector.java │ │ │ └── WrapsContext.java │ │ │ ├── junit │ │ │ ├── DriverListener.java │ │ │ ├── DriverWatcher.java │ │ │ ├── JUnitBase.java │ │ │ ├── JUnitPlatformBase.java │ │ │ ├── JUnitTargetBase.java │ │ │ ├── PageSourceArtifact.java │ │ │ ├── PageSourceCapture.java │ │ │ ├── RetryAnalyzer.java │ │ │ ├── ScreenshotArtifact.java │ │ │ └── ScreenshotCapture.java │ │ │ ├── listeners │ │ │ ├── DriverListener.java │ │ │ ├── PageSourceArtifact.java │ │ │ ├── PageSourceCapture.java │ │ │ ├── PlatformInterceptor.java │ │ │ ├── ScreenshotArtifact.java │ │ │ └── ScreenshotCapture.java │ │ │ ├── model │ │ │ ├── ComponentContainer.java │ │ │ ├── ComponentList.java │ │ │ ├── ComponentMap.java │ │ │ ├── ContainerList.java │ │ │ ├── ContainerMap.java │ │ │ ├── ContainerMethodInterceptor.java │ │ │ ├── Enhanceable.java │ │ │ ├── Enhanced.java │ │ │ ├── FirefoxShadowRoot.java │ │ │ ├── Frame.java │ │ │ ├── FrameList.java │ │ │ ├── FrameMap.java │ │ │ ├── Page.java │ │ │ ├── PageComponent.java │ │ │ ├── ReferenceFetcher.java │ │ │ ├── RobustElementFactory.java │ │ │ ├── RobustElementWrapper.java │ │ │ ├── RobustJavascriptExecutor.java │ │ │ ├── RobustWebElement.java │ │ │ ├── ShadowRoot.java │ │ │ ├── ShadowRootList.java │ │ │ └── ShadowRootMap.java │ │ │ ├── platform │ │ │ ├── PlatformEnum.java │ │ │ ├── PlatformTargetable.java │ │ │ ├── TargetPlatform.java │ │ │ ├── TargetPlatformHandler.java │ │ │ ├── TargetPlatformRule.java │ │ │ ├── TargetType.java │ │ │ └── TargetTypeName.java │ │ │ ├── plugins │ │ │ ├── ChromeCaps.java │ │ │ ├── EdgeCaps.java │ │ │ ├── EspressoPlugin.java │ │ │ ├── FirefoxCaps.java │ │ │ ├── HtmlUnitCaps.java │ │ │ ├── InternetExplorerCaps.java │ │ │ ├── Mac2Plugin.java │ │ │ ├── OperaCaps.java │ │ │ ├── PhantomJsCaps.java │ │ │ ├── PluginUtils.java │ │ │ ├── RemoteWebDriverPlugin.java │ │ │ ├── SafariCaps.java │ │ │ ├── UiAutomator2Plugin.java │ │ │ ├── WindowsPlugin.java │ │ │ └── XCUITestPlugin.java │ │ │ ├── servlet │ │ │ └── ExamplePageLauncher.java │ │ │ ├── support │ │ │ ├── Coordinator.java │ │ │ ├── Coordinators.java │ │ │ ├── RetryAnalyzer.java │ │ │ ├── SearchContextWait.java │ │ │ ├── TestNgBase.java │ │ │ ├── TestNgPlatformBase.java │ │ │ └── TestNgTargetBase.java │ │ │ └── utility │ │ │ ├── PageSourceUtils.java │ │ │ ├── ReflectUtils.java │ │ │ ├── ScreenshotUtils.java │ │ │ └── SearchContextUtils.java │ └── resources │ │ ├── ExamplePage.html │ │ ├── checkShadowHost.js │ │ ├── createScriptNode.js │ │ ├── documentReady.js │ │ ├── frame_a.html │ │ ├── frame_b.html │ │ ├── frame_c.html │ │ ├── frame_d.html │ │ ├── hubConfig-s3.json │ │ ├── hubConfig-s4.json │ │ ├── javaGlueLib.js │ │ ├── locateByXpath.js │ │ ├── locateEveryByCss.format │ │ ├── locateEveryByXpath.format │ │ ├── locateFirstByCss.format │ │ ├── locateFirstByXpath.format │ │ ├── locateIndexByCss.format │ │ ├── locateIndexByXpath.format │ │ ├── nodeConfig-s3.json │ │ ├── nodeConfig-s4.json │ │ └── operaChromiumVersions.json ├── selenium3 │ └── java │ │ └── com │ │ └── nordstrom │ │ └── automation │ │ └── selenium │ │ ├── SeleniumConfig.java │ │ ├── api │ │ └── GridProxyResponse.java │ │ ├── core │ │ ├── FoundationSlotMatcher.java │ │ ├── GridServer.java │ │ ├── LocalSeleniumGrid.java │ │ ├── ServerProcessKiller.java │ │ └── WebDriverUtils.java │ │ ├── examples │ │ ├── AndroidPage.java │ │ └── ServletContainer.java │ │ ├── plugins │ │ ├── AbstractAppiumPlugin.java │ │ ├── ChromePlugin.java │ │ ├── EdgePlugin.java │ │ ├── FirefoxPlugin.java │ │ ├── HtmlUnitPlugin.java │ │ ├── InternetExplorerPlugin.java │ │ ├── OperaPlugin.java │ │ ├── PhantomJsPlugin.java │ │ └── SafariPlugin.java │ │ ├── servlet │ │ └── ExamplePageServlet.java │ │ └── utility │ │ ├── BinaryFinder.java │ │ ├── DataUtils.java │ │ ├── DefaultNetworkInterfaceProviderV4.java │ │ ├── HostIdentifierV4.java │ │ └── HostUtils.java ├── selenium4 │ └── java │ │ └── com │ │ └── nordstrom │ │ └── automation │ │ └── selenium │ │ ├── SeleniumConfig.java │ │ ├── core │ │ ├── FoundationSlotMatcher.java │ │ ├── GridServer.java │ │ ├── LocalSeleniumGrid.java │ │ ├── NodeStatus.java │ │ ├── Nodes.java │ │ ├── ServerProcessKiller.java │ │ └── WebDriverUtils.java │ │ ├── examples │ │ ├── AndroidPage.java │ │ └── ServletContainer.java │ │ ├── plugins │ │ ├── AbstractAppiumPlugin.java │ │ ├── ChromePlugin.java │ │ ├── EdgePlugin.java │ │ ├── FirefoxPlugin.java │ │ ├── HtmlUnitPlugin.java │ │ ├── InternetExplorerPlugin.java │ │ ├── OperaPlugin.java │ │ ├── PhantomJsPlugin.java │ │ └── SafariPlugin.java │ │ ├── servlet │ │ └── ExamplePageServlet.java │ │ └── utility │ │ ├── BinaryFinder.java │ │ ├── DataUtils.java │ │ └── HostUtils.java └── test │ ├── java │ └── com │ │ └── nordstrom │ │ └── automation │ │ └── selenium │ │ ├── SeleniumConfigTest.java │ │ ├── android │ │ └── AndroidTest.java │ │ ├── core │ │ ├── ByTypeTest.java │ │ ├── GridUtilityTest.java │ │ ├── JsUtilityTest.java │ │ ├── ModelTestCore.java │ │ └── WebDriverUtilsTest.java │ │ ├── ios │ │ └── IOSTest.java │ │ ├── junit │ │ ├── JUnitModelTest.java │ │ ├── JUnitPlatformTest.java │ │ ├── PageSourceCaptureTest.java │ │ └── ScreenshotCaptureTest.java │ │ ├── listeners │ │ ├── DriverManagerTest.java │ │ ├── NoDriverManagerTest.java │ │ ├── PageSourceCaptureTest.java │ │ └── ScreenshotCaptureTest.java │ │ ├── mac │ │ └── MacTest.java │ │ ├── model │ │ ├── ComponentContainerTest.java │ │ └── ModelTest.java │ │ ├── platform │ │ ├── PhaseName.java │ │ └── Transition.java │ │ ├── support │ │ └── TestNgPlatformTest.java │ │ └── windows │ │ └── WindowsTest.java │ └── resources │ ├── META-INF │ └── services │ │ ├── com.nordstrom.automation.junit.JUnitRetryAnalyzer │ │ ├── com.nordstrom.automation.junit.JUnitWatcher │ │ ├── com.nordstrom.automation.testng.TestNGRetryAnalyzer │ │ └── org.testng.ITestNGListener │ ├── logback.xml │ ├── requireMetaTagByName.js │ └── toggleOptionalNode.js ├── uiautomator2Deps.gradle ├── windowsDeps.gradle └── xcuitestDeps.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .settings 2 | target/ 3 | target-*/ 4 | .classpath 5 | .project 6 | *~ 7 | *.tmproj 8 | *.iml 9 | .idea/ 10 | *.md.html 11 | logs/ 12 | logs-*/ 13 | .gradle/ 14 | build/ 15 | build-*/ 16 | gradle/ 17 | gradlew 18 | gradlew.bat 19 | /bin/ 20 | test-output/ 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /chromeDeps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.ChromePlugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.name', 'chrome') 4 | System.setProperty('selenium.context.platform', 'web-app') 5 | dependencies { 6 | testImplementation('org.seleniumhq.selenium:selenium-chrome-driver') { 7 | exclude module: 'selenium-remote-driver' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /docs/DevelopmentEnvironment.md: -------------------------------------------------------------------------------- 1 | 2 | # Introduction 3 | 4 | Building the **Selenium Foundation** project requires a few standard tools and a few basic configurations. For executing tests in **Eclipse**, there are also configuration options you'll need to adjust for **TestNG** and **JUnit**. The information on this page should have you up and running in short order. 5 | 6 | ## Developer Tools 7 | 8 | The majority of the environment needed to build **Selenium Foundation** is comprised of standard tools installed with default settings: 9 | 10 | | Utility / System | Details | 11 | |---:|:---| 12 | | Package manager | Chocolatey 0.10.8 | 13 | | Version control | git version 2.27 | 14 | | Command shell | GNU bash, version 4.4.23(1) | 15 | | Gradle (build tool) | version 6.5 | 16 | | Eclipse (IDE) | 2020-12 (4.18.0) | 17 | | BuildShip Plug-In | 3.1.5.v20210113-0904 | 18 | | TestNG Plug-In | 7.3.0.202011271758 | 19 | 20 | ## Java Versions 21 | 22 | To build **Selenium Foundation**, you'll need a Java 8 software development kit: 23 | 24 | * Java 8: version 1.8.0_311 25 | 26 | ## Gradle Configuration: 27 | 28 | The Gradle project file for **Selenium Foundation** includes a **selenium3Deps** sub-file. This reflects the past (and probable future) support for multiple versions of the Selenium API. 29 | 30 | ### JDK Toolchains 31 | 32 | To build the **Selenium Foundation** project, you'll also need to add a **Maven** `toolchain` specification to your system configuration. This configuration allows **Gradle** to auto-discover the JDK installations on your machine: 33 | 34 | ###### C:\\Users\\<username>\\.m2\\toolchains.xml 35 | ```xml 36 | 37 | 38 | 39 | 40 | jdk 41 | 42 | 8 43 | 44 | 45 | C:\Program Files\Java\jdk1.8.0_251 46 | 47 | 48 | 49 | ``` 50 | 51 | ## Environment Variables 52 | 53 | | Variable | Target | 54 | |:---|:---| 55 | | **`GRADLE_HOME`** | Root folder of Gradle installation | 56 | | **`JDK8_HOME`** | Root folder of JDK 8 installation | 57 | 58 | ## JUnit Run Configuration 59 | 60 | The JUnit support provided by **Selenium Foundation** relies on [event notifications](JUnit4Support.md#outline-of-required-elements) published by **JUnit Foundation**. Notifications are enabled by a Java agent, which uses bytecode enhancement to install hooks on test and configuration methods. 61 | 62 | To run or debug **JUnit 4** tests in **Eclipse**, specify the `-javaagent` option in the `VM arguments` field on the `Arguments` tab of the **JUnit** run configuration: 63 | 64 | > -javaagent:<maven-repo>/repository/com/nordstrom/tools/junit-foundation/16.0.1/junit-foundation-16.0.1.jar 65 | 66 | > Written with [StackEdit](https://stackedit.io/). 67 | -------------------------------------------------------------------------------- /docs/example/FrameComponent.md: -------------------------------------------------------------------------------- 1 | | [ModelTest.java](ModelTest.md) | [ExamplePage.java](ExamplePage.md) | [TableComponent.java](TableComponent.md) | [TableRowComponent.java](TableRowComponent.md) | **FrameComponent.java** | [ShadowRootComponent.java](ShadowRootComponent.md) | 2 | 3 | # Sample Code 4 | 5 | ###### FrameComponent.java 6 | ```java 7 | package com.nordstrom.automation.selenium.model; 8 | 9 | import org.openqa.selenium.By; 10 | import org.openqa.selenium.SearchContext; 11 | import org.openqa.selenium.WebDriver; 12 | 13 | public class FrameComponent extends Frame { 14 | 15 | public FrameComponent(By locator, ComponentContainer parent) { 16 | super(locator, parent); 17 | } 18 | 19 | public FrameComponent(By locator, int index, ComponentContainer parent) { 20 | super(locator, index, parent); 21 | } 22 | 23 | public FrameComponent(RobustWebElement element, ComponentContainer parent) { 24 | super(element, parent); 25 | } 26 | 27 | public FrameComponent(int index, ComponentContainer parent) { 28 | super(index, parent); 29 | } 30 | 31 | public FrameComponent(String nameOrId, ComponentContainer parent) { 32 | super(nameOrId, parent); 33 | } 34 | 35 | private enum Using implements ByEnum { 36 | HEADING(By.cssSelector("h1")); 37 | 38 | private final By locator; 39 | 40 | Using(By locator) { 41 | this.locator = locator; 42 | } 43 | 44 | @Override 45 | public By locator() { 46 | return locator; 47 | } 48 | } 49 | 50 | public String getPageContent() { 51 | return findElement(Using.HEADING).getText(); 52 | } 53 | 54 | public static Object getKey(SearchContext context) { 55 | RobustWebElement element = (RobustWebElement) context; 56 | WebDriver driver = element.getWrappedDriver().switchTo().frame(element); 57 | Object key = driver.findElement(Using.HEADING.locator).getText(); 58 | switchToParentFrame(element); 59 | return key; 60 | } 61 | } 62 | ``` -------------------------------------------------------------------------------- /docs/example/ShadowRootComponent.md: -------------------------------------------------------------------------------- 1 | | [ModelTest.java](ModelTest.md) | [ExamplePage.java](ExamplePage.md) | [TableComponent.java](TableComponent.md) | [TableRowComponent.java](TableRowComponent.md) | [FrameComponent.java](FrameComponent.md) | **ShadowRootComponent** | 2 | 3 | # Sample Code 4 | 5 | ###### ShadowRootComponent.java 6 | ```java 7 | package com.nordstrom.automation.selenium.model; 8 | 9 | import org.openqa.selenium.By; 10 | import org.openqa.selenium.SearchContext; 11 | import org.openqa.selenium.WebDriver; 12 | 13 | public class ShadowRootComponent extends ShadowRoot { 14 | 15 | public ShadowRootComponent(final By locator, final ComponentContainer parent) { 16 | super(locator, parent); 17 | } 18 | 19 | public ShadowRootComponent(final By locator, final int index, final ComponentContainer parent) { 20 | super(locator, index, parent); 21 | } 22 | 23 | public ShadowRootComponent(final RobustWebElement element, final ComponentContainer parent) { 24 | super(element, parent); 25 | } 26 | 27 | private enum Using implements ByEnum { 28 | HEADING(By.cssSelector("h1")); 29 | 30 | private final By locator; 31 | 32 | Using(By locator) { 33 | this.locator = locator; 34 | } 35 | 36 | @Override 37 | public By locator() { 38 | return locator; 39 | } 40 | } 41 | 42 | public String getContent() { 43 | return findElement(Using.HEADING).getText(); 44 | } 45 | 46 | public static Object getKey(SearchContext context) { 47 | RobustWebElement element = (RobustWebElement) context; 48 | WebDriver driver = element.getWrappedDriver(); 49 | Object key = driver.findElement(Using.HEADING.locator).getText(); 50 | return key; 51 | } 52 | } 53 | ``` -------------------------------------------------------------------------------- /docs/example/TableComponent.md: -------------------------------------------------------------------------------- 1 | | [ModelTest.java](ModelTest.md) | [ExamplePage.java](ExamplePage.md) | **TableComponent.java** | [TableRowComponent.java](TableRowComponent.md) | [FrameComponent.java](FrameComponent.md) | [ShadowRootComponent.java](ShadowRootComponent.md) | 2 | 3 | # Sample Code 4 | 5 | ###### TableComponent.java 6 | ```java 7 | package com.nordstrom.automation.selenium.model; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import org.openqa.selenium.By; 13 | import org.openqa.selenium.SearchContext; 14 | import org.openqa.selenium.WebElement; 15 | 16 | public class TableComponent extends PageComponent { 17 | 18 | public TableComponent(By locator, ComponentContainer parent) { 19 | super(locator, parent); 20 | } 21 | 22 | public TableComponent(RobustWebElement element, ComponentContainer parent) { 23 | super(element, parent); 24 | } 25 | 26 | private TableRowComponent tableHdr; 27 | private List tableRows; 28 | 29 | protected enum Using implements ByEnum { 30 | HDR_ROW(By.cssSelector("tr[id*='-h']")), 31 | TBL_ROW(By.cssSelector("tr[id*='-r']")); 32 | 33 | private final By locator; 34 | 35 | Using(By locator) { 36 | this.locator = locator; 37 | } 38 | 39 | @Override 40 | public By locator() { 41 | return locator; 42 | } 43 | } 44 | 45 | public List getHeadings() { 46 | return getTableHdr().getContent(); 47 | } 48 | 49 | public List> getContent() { 50 | List> result = new ArrayList<>(); 51 | for (TableRowComponent row : getTableRows()) { 52 | result.add(row.getContent()); 53 | } 54 | return result; 55 | } 56 | 57 | private TableRowComponent getTableHdr() { 58 | if (tableHdr == null) { 59 | tableHdr = new TableRowComponent(Using.HDR_ROW.locator, this); 60 | } 61 | return tableHdr; 62 | } 63 | 64 | private List getTableRows() { 65 | if (tableRows == null) { 66 | tableRows = newComponentList(TableRowComponent.class, Using.TBL_ROW.locator); 67 | } 68 | return tableRows; 69 | } 70 | 71 | public static Object getKey(SearchContext context) { 72 | return ((WebElement) context).getAttribute("id"); 73 | } 74 | } 75 | ``` -------------------------------------------------------------------------------- /docs/example/TableRowComponent.md: -------------------------------------------------------------------------------- 1 | | [ModelTest.java](ModelTest.md) | [ExamplePage.java](ExamplePage.md) | [TableComponent.java](TableComponent.md) | **TableRowComponent.java** | [FrameComponent.java](FrameComponent.md) | [ShadowRootComponent.java](ShadowRootComponent.md) | 2 | 3 | # Sample Code 4 | 5 | ###### TableRowComponent.java 6 | ```java 7 | package com.nordstrom.automation.selenium.model; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | import org.openqa.selenium.By; 13 | import org.openqa.selenium.SearchContext; 14 | import org.openqa.selenium.WebElement; 15 | 16 | public class TableRowComponent extends PageComponent { 17 | 18 | public TableRowComponent(By locator, ComponentContainer parent) { 19 | super(locator, parent); 20 | } 21 | 22 | public TableRowComponent(RobustWebElement element, ComponentContainer parent) { 23 | super(element, parent); 24 | } 25 | 26 | protected enum Using implements ByEnum { 27 | TBL_CELL(By.cssSelector("th,td")); 28 | 29 | private final By locator; 30 | 31 | Using(By locator) { 32 | this.locator = locator; 33 | } 34 | 35 | @Override 36 | public By locator() { 37 | return locator; 38 | } 39 | } 40 | 41 | private List cells; 42 | 43 | public List getContent() { 44 | List cells = getCells(); 45 | return Arrays.asList(cells.get(0).getText(), cells.get(1).getText(), cells.get(2).getText()); 46 | } 47 | 48 | private List getCells() { 49 | if (cells == null) { 50 | cells = findElements(Using.TBL_CELL); 51 | } 52 | return cells; 53 | } 54 | } 55 | ``` -------------------------------------------------------------------------------- /docs/images/META-INF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbabcoc/Selenium-Foundation/018533d900f1ab87c5de2c575cac2d95dfeafd05/docs/images/META-INF.png -------------------------------------------------------------------------------- /edgeDeps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.EdgePlugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.name', 'MicrosoftEdge') 4 | System.setProperty('selenium.context.platform', 'web-app') 5 | dependencies { 6 | testImplementation('org.seleniumhq.selenium:selenium-edge-driver') { 7 | exclude module: 'selenium-remote-driver' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /espressoDeps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.EspressoPlugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.caps', '{"platformName":"Android","appium:automationName":"Espresso","appium:forceEspressoRebuild":true,"appium:showGradleLog":true,"appium:app":"https://github.com/appium/appium/raw/master/packages/appium/sample-code/apps/ApiDemos-debug.apk"}') 4 | System.setProperty('selenium.context.platform', 'android') 5 | System.setProperty('selenium.grid.examples', 'false') 6 | System.setProperty('appium.with.pm2', 'true') 7 | dependencies { 8 | testImplementation('io.appium:java-client') { 9 | exclude group: 'org.seleniumhq.selenium', module: 'selenium-java' 10 | exclude group: 'org.seleniumhq.selenium', module: 'selenium-support' 11 | exclude group: 'org.slf4j', module: 'slf4j-api' 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /firefoxDeps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.FirefoxPlugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.name', 'firefox') 4 | System.setProperty('selenium.context.platform', 'web-app') 5 | dependencies { 6 | testImplementation('org.seleniumhq.selenium:selenium-firefox-driver') { 7 | exclude module: 'selenium-remote-driver' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | profile=selenium4 2 | version=28.0.0-SNAPSHOT 3 | browsers=htmlunit 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbabcoc/Selenium-Foundation/018533d900f1ab87c5de2c575cac2d95dfeafd05/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /htmlunitDeps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.HtmlUnitPlugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.name', 'htmlunit') 4 | System.setProperty('selenium.context.platform', 'web-app') 5 | dependencies { 6 | if ("${profile}" == "selenium4") { 7 | testImplementation 'com.nordstrom.ui-tools:htmlunit-remote' 8 | } else { 9 | testImplementation('org.seleniumhq.selenium:htmlunit-driver') { 10 | exclude module: 'selenium-support' 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /mac2Deps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.Mac2Plugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.caps', '{"platformName":"Mac","appium:automationName":"Mac2","appium:bundleId":"com.apple.TextEdit"}') 4 | System.setProperty('selenium.context.platform', 'mac-app') 5 | System.setProperty('selenium.grid.examples', 'false') 6 | System.setProperty('appium.with.pm2', 'true') 7 | dependencies { 8 | testImplementation('io.appium:java-client') { 9 | exclude group: 'org.seleniumhq.selenium', module: 'selenium-java' 10 | exclude group: 'org.seleniumhq.selenium', module: 'selenium-support' 11 | exclude group: 'org.slf4j', module: 'slf4j-api' 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /operaDeps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.OperaPlugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.name', 'opera') 4 | System.setProperty('selenium.context.platform', 'web-app') 5 | dependencies { 6 | testImplementation('org.seleniumhq.selenium:selenium-opera-driver') { 7 | exclude module: 'selenium-remote-driver' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /phantomjsDeps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.PhantomJsPlugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.name', 'phantomjs') 4 | System.setProperty('selenium.context.platform', 'web-app') 5 | dependencies { 6 | testImplementation('com.codeborne:phantomjsdriver') { 7 | exclude group: 'org.seleniumhq.selenium', module: 'selenium-remote-driver' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /safariDeps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.SafariPlugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.name', 'safari') 4 | System.setProperty('selenium.context.platform', 'web-app') 5 | dependencies { 6 | testImplementation('org.seleniumhq.selenium:selenium-safari-driver') { 7 | exclude module: 'selenium-remote-driver' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /selenium3Deps.gradle: -------------------------------------------------------------------------------- 1 | ext.buildRoot = file('build-s3') 2 | ext.libsDir = new File(buildRoot, 'libs') 3 | 4 | java { 5 | toolchain { 6 | languageVersion = JavaLanguageVersion.of(8) 7 | } 8 | } 9 | 10 | sourceSets { 11 | main { 12 | java { 13 | srcDirs = [ 'src/main/java', 'src/selenium3/java' ] 14 | destinationDirectory = new File(buildRoot, 'classes') 15 | } 16 | output.resourcesDir = "${buildRoot}/classes" 17 | } 18 | test { 19 | java { 20 | destinationDirectory = new File(buildRoot, 'test-classes') 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | constraints { 27 | api 'com.nordstrom.tools:testng-foundation:5.2.1-j8' 28 | api 'org.seleniumhq.selenium:selenium-server:3.141.59' 29 | api 'org.seleniumhq.selenium:selenium-support:3.141.59' 30 | api 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' 31 | api 'org.seleniumhq.selenium:selenium-edge-driver:3.141.59' 32 | api 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' 33 | api 'org.seleniumhq.selenium:selenium-opera-driver:3.141.59' 34 | api 'org.seleniumhq.selenium:selenium-safari-driver:3.141.59' 35 | api 'org.seleniumhq.selenium:htmlunit-driver:2.70.0' 36 | api 'com.codeborne:phantomjsdriver:1.4.4' 37 | api 'io.github.bonigarcia:webdrivermanager:6.0.0' 38 | api 'org.apache.httpcomponents:httpclient:4.5.14' 39 | api 'org.jsoup:jsoup:1.19.1' 40 | api 'org.apache.commons:commons-lang3:3.17.0' 41 | api 'com.beust:jcommander:1.82' 42 | api 'com.squareup.okhttp3:okhttp:4.12.0' 43 | api 'com.squareup.okio:okio:3.10.2' 44 | api 'org.eclipse.jetty.websocket:websocket-client:9.4.57.v20241219' 45 | api 'org.jetbrains.kotlin:kotlin-stdlib:2.1.20' 46 | api 'org.jetbrains.kotlin:kotlin-stdlib-common:2.1.20' 47 | testImplementation 'io.appium:java-client:7.6.0' 48 | testImplementation 'org.mockito:mockito-core:4.11.0' 49 | } 50 | api 'com.nordstrom.tools:testng-foundation' 51 | api 'org.seleniumhq.selenium:selenium-server' 52 | api 'org.seleniumhq.selenium:selenium-support' 53 | api('io.github.bonigarcia:webdrivermanager') { 54 | exclude group: 'org.slf4j', module: 'slf4j-api' 55 | } 56 | api 'org.apache.httpcomponents:httpclient' 57 | api 'org.jsoup:jsoup' 58 | api 'org.apache.commons:commons-lang3' 59 | api 'com.beust:jcommander' 60 | api 'com.squareup.okhttp3:okhttp' 61 | api 'com.squareup.okio:okio' 62 | api 'org.eclipse.jetty.websocket:websocket-client' 63 | testImplementation 'org.mockito:mockito-core' 64 | } 65 | -------------------------------------------------------------------------------- /selenium4Deps.gradle: -------------------------------------------------------------------------------- 1 | ext.buildRoot = file('build-s4') 2 | ext.libsDir = new File(buildRoot, 'libs') 3 | 4 | java { 5 | toolchain { 6 | languageVersion = JavaLanguageVersion.of(17) 7 | } 8 | } 9 | 10 | sourceSets { 11 | main { 12 | java { 13 | srcDirs = [ 'src/main/java', 'src/selenium4/java' ] 14 | destinationDirectory = new File(buildRoot, 'classes') 15 | } 16 | output.resourcesDir = "${buildRoot}/classes" 17 | } 18 | test { 19 | java { 20 | destinationDirectory = new File(buildRoot, 'test-classes') 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | constraints { 27 | api 'com.nordstrom.tools:testng-foundation:5.2.1-j11' 28 | api 'org.seleniumhq.selenium:selenium-grid:4.32.0' 29 | api 'org.seleniumhq.selenium:selenium-support:4.32.0' 30 | api 'org.seleniumhq.selenium:selenium-chrome-driver:4.32.0' 31 | api 'org.seleniumhq.selenium:selenium-edge-driver:4.32.0' 32 | api 'org.seleniumhq.selenium:selenium-firefox-driver:4.32.0' 33 | api 'org.seleniumhq.selenium:selenium-opera-driver:4.4.0' 34 | api 'org.seleniumhq.selenium:selenium-safari-driver:4.32.0' 35 | api 'com.nordstrom.ui-tools:htmlunit-remote:4.32.0' 36 | api 'org.seleniumhq.selenium:htmlunit3-driver:4.32.0' 37 | api 'org.htmlunit:htmlunit:4.12.0' 38 | api 'com.codeborne:phantomjsdriver:1.5.0' 39 | api 'org.apache.httpcomponents:httpclient:4.5.14' 40 | api 'org.eclipse.jetty:jetty-servlet:9.4.57.v20241219' 41 | api 'org.jsoup:jsoup:1.19.1' 42 | api 'org.apache.commons:commons-lang3:3.17.0' 43 | api 'com.beust:jcommander:1.82' 44 | api 'io.netty:netty-transport-native-epoll:4.1.119.Final' 45 | api 'io.netty:netty-transport-native-kqueue:4.1.119.Final' 46 | testImplementation 'io.appium:java-client:9.4.0' 47 | testImplementation 'org.mockito:mockito-core:4.11.0' 48 | } 49 | api 'com.nordstrom.tools:testng-foundation' 50 | api 'org.seleniumhq.selenium:selenium-grid' 51 | api 'org.seleniumhq.selenium:selenium-support' 52 | api 'org.apache.httpcomponents:httpclient' 53 | api 'org.eclipse.jetty:jetty-servlet' 54 | api 'org.jsoup:jsoup' 55 | api 'org.apache.commons:commons-lang3' 56 | api 'com.beust:jcommander' 57 | testImplementation 'org.mockito:mockito-core' 58 | } 59 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'selenium-foundation' 2 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/annotations/InitialPage.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.annotations; 2 | 3 | import static java.lang.annotation.ElementType.METHOD; 4 | import static java.lang.annotation.ElementType.TYPE; 5 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 6 | 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.Target; 9 | 10 | import com.nordstrom.automation.selenium.model.Page; 11 | 12 | /** 13 | * This annotation enables you to specify an initial page that should be loaded after instantiating the driver, on 14 | * either individual test methods or for an entire test class. Note that any page class specified as an initial page 15 | * must declare its associated URL via the {@link PageUrl} annotation. 16 | */ 17 | @Retention(RUNTIME) 18 | @Target({TYPE, METHOD}) 19 | public @interface InitialPage { 20 | /** 21 | * Get the class of the initial page. 22 | * 23 | * @return initial page class 24 | */ 25 | Class value() default Page.class; 26 | 27 | /** 28 | * Get the URL of the initial page. 29 | * 30 | * @return initial page URL 31 | */ 32 | PageUrl pageUrl() default @PageUrl(); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/annotations/NoDriver.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.annotations; 2 | 3 | import static java.lang.annotation.ElementType.METHOD; 4 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * This annotation enables you to decline automatic driver instantiation for an individual test method. 11 | */ 12 | @Retention(RUNTIME) 13 | @Target(METHOD) 14 | public @interface NoDriver { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/core/ServerPidFinder.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.core; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.util.Optional; 7 | 8 | import com.nordstrom.common.file.OSInfo; 9 | 10 | /** 11 | * This class uses OS utilities to identify the ID of the server process listening to the specified port. 12 | */ 13 | public class ServerPidFinder { 14 | 15 | private enum PidFinder { 16 | WINDOWS("cmd.exe", "/c", "for /f \"tokens=5\" %%a in ('netstat -ano ^| findstr :%d ^| findstr LISTENING') do @echo %%a"), 17 | MAC_UNIX("sh", "-c", "lsof -i :%d -sTCP:LISTEN -t"); 18 | 19 | private String executable; 20 | private String commandOption; 21 | private String commandFormat; 22 | 23 | PidFinder(String execuable, String commandOption, String commandFormat) { 24 | this.executable = execuable; 25 | this.commandOption = commandOption; 26 | this.commandFormat = commandFormat; 27 | } 28 | 29 | String getExecutable() { 30 | return executable; 31 | } 32 | 33 | String getOption() { 34 | return commandOption; 35 | } 36 | 37 | String getCommand(int port) { 38 | return String.format(commandFormat, port); 39 | } 40 | } 41 | 42 | /** 43 | * Private constructor to prevent instantiation. 44 | */ 45 | private ServerPidFinder() { 46 | throw new AssertionError("ServerPidFinder is a static utility class that cannot be instantiated"); 47 | } 48 | 49 | /** 50 | * Get the process ID of the server listening to the specified port. 51 | * 52 | * @param port {@code localhost} port to check 53 | * @return if found, ID of listening process; otherwise {@code null} 54 | */ 55 | public static String getPidOfServerAt(int port) { 56 | String pid = null; 57 | 58 | try { 59 | PidFinder finder = 60 | OSInfo.getDefault().getType() == OSInfo.OSType.WINDOWS ? PidFinder.WINDOWS : PidFinder.MAC_UNIX; 61 | 62 | ProcessBuilder pb = new ProcessBuilder(finder.getExecutable(), finder.getOption(), finder.getCommand(port)); 63 | Process process = pb.start(); 64 | 65 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { 66 | pid = Optional.ofNullable(reader.readLine()).filter(s -> s != null && !s.matches("\\s*")).map(String::trim).orElse(null); 67 | } 68 | 69 | process.waitFor(); 70 | } catch (IOException e) { 71 | // nothing to do here; 72 | } catch (InterruptedException e) { 73 | Thread.currentThread().interrupt(); 74 | } 75 | 76 | return pid; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/examples/IOSPage.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.examples; 2 | 3 | import org.openqa.selenium.By; 4 | import org.openqa.selenium.WebDriver; 5 | 6 | import com.nordstrom.automation.selenium.model.Page; 7 | 8 | /** 9 | * This class is the model for the target view of the sample application used by the Appium iOS unit test. 10 | */ 11 | public class IOSPage extends Page { 12 | 13 | /** 14 | * Constructor for main view context. 15 | * 16 | * @param driver driver object 17 | */ 18 | public IOSPage(WebDriver driver) { 19 | super(driver); 20 | } 21 | 22 | /** 23 | * This enumeration defines element locator constants. 24 | */ 25 | protected enum Using implements ByEnum { 26 | /** text field */ 27 | TEXT_FIELD(By.className("XCUIElementTypeTextField")); 28 | 29 | private final By locator; 30 | 31 | Using(By locator) { 32 | this.locator = locator; 33 | } 34 | 35 | @Override 36 | public By locator() { 37 | return locator; 38 | } 39 | } 40 | 41 | /** 42 | * Populate text field with the specified string. 43 | * 44 | * @param keys string to type into text field 45 | */ 46 | public void modifyField(String keys) { 47 | findElement(Using.TEXT_FIELD).click(); 48 | findElement(Using.TEXT_FIELD).sendKeys(keys); 49 | } 50 | 51 | /** 52 | * Get text field content. 53 | * 54 | * @return text field content 55 | */ 56 | public String accessField() { 57 | return findElement(Using.TEXT_FIELD).getText(); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/examples/JUnitRoot.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.examples; 2 | 3 | import org.junit.BeforeClass; 4 | 5 | import com.nordstrom.automation.selenium.junit.JUnitBase; 6 | 7 | /** 8 | * This class provides a base for JUnit test classes with methods that target features of {@link ExamplePage}. 9 | */ 10 | public class JUnitRoot extends JUnitBase { 11 | 12 | /** 13 | * This BeforeClass method configures Selenium Foundation to target {@link ExamplePage}. 14 | */ 15 | @BeforeClass 16 | public static void beforeClass() { 17 | ExamplePage.setHubAsTarget(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/examples/JUnitTargetRoot.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.examples; 2 | 3 | import org.junit.BeforeClass; 4 | 5 | import com.nordstrom.automation.selenium.junit.JUnitTargetBase; 6 | import com.nordstrom.automation.selenium.platform.TargetType; 7 | 8 | /** 9 | * This class provides a base for JUnit test classes with methods that target features of {@link ExamplePage}. 10 | */ 11 | public class JUnitTargetRoot extends JUnitTargetBase { 12 | 13 | /** 14 | * This BeforeClass method configures Selenium Foundation to target {@link ExamplePage}. 15 | */ 16 | @BeforeClass 17 | public static void beforeClass() { 18 | ExamplePage.setHubAsTarget(); 19 | } 20 | 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | @Override 25 | public TargetType getDefaultPlatform() { 26 | return TargetType.WEB_APP; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/examples/MacPage.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.examples; 2 | 3 | import org.openqa.selenium.By; 4 | import org.openqa.selenium.WebDriver; 5 | 6 | import com.nordstrom.automation.selenium.model.Page; 7 | 8 | /** 9 | * This class is the model for the target view of the sample application used by the Appium Macintosh unit test. 10 | */ 11 | public class MacPage extends Page { 12 | 13 | /** 14 | * Constructor for main view context. 15 | * 16 | * @param driver driver object 17 | */ 18 | public MacPage(WebDriver driver) { 19 | super(driver); 20 | } 21 | 22 | /** 23 | * This enumeration defines element locator constants. 24 | */ 25 | protected enum Using implements ByEnum { 26 | /** text field */ 27 | EDIT_FIELD(By.className("XCUIElementTypeTextView")); 28 | 29 | private final By locator; 30 | 31 | Using(By locator) { 32 | this.locator = locator; 33 | } 34 | 35 | @Override 36 | public By locator() { 37 | return locator; 38 | } 39 | } 40 | 41 | /** 42 | * Populate text field with the specified string. 43 | * 44 | * @param keys string to type into text field 45 | */ 46 | public void modifyDocument(String keys) { 47 | findElement(Using.EDIT_FIELD).sendKeys(keys); 48 | } 49 | 50 | /** 51 | * Get text field content. 52 | * 53 | * @return text field content 54 | */ 55 | public String accessDocument() { 56 | return findElement(Using.EDIT_FIELD).getText(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/examples/ServletFlags.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.examples; 2 | 3 | import java.util.List; 4 | 5 | import com.beust.jcommander.Parameter; 6 | 7 | /** 8 | * This class defines the options supported by the {@link ServletContainer} command line utility. 9 | */ 10 | public class ServletFlags { 11 | 12 | /** servlet container port */ 13 | @Parameter( 14 | names = {"-p", "--port"}, 15 | description = "Port to listen on. (default: 8080)") 16 | private int port = 8080; 17 | 18 | /** hosted servlet classes */ 19 | @Parameter( 20 | names = "--servlet", 21 | description = "Fully qualified servlet class name (may be specified more than once)", 22 | required = true) 23 | private List servlets; 24 | 25 | /** 26 | * Get servlet container port. 27 | * 28 | * @return servlet container port 29 | */ 30 | public int getPort() { 31 | return port; 32 | } 33 | 34 | /** 35 | * Get hosted servlet classes. 36 | * 37 | * @return hosted servlet classes 38 | */ 39 | public List getServlets() { 40 | if ( ! (servlets == null || servlets.isEmpty())) { 41 | return servlets; 42 | } 43 | throw new IllegalStateException("At least one servlet class must be specified"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/examples/TestNgRoot.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.examples; 2 | 3 | import org.testng.annotations.BeforeClass; 4 | 5 | import com.nordstrom.automation.selenium.support.TestNgBase; 6 | 7 | /** 8 | * This class provides a base for TestNG test classes with methods that target features of {@link ExamplePage}. 9 | */ 10 | public class TestNgRoot extends TestNgBase { 11 | 12 | /** 13 | * This BeforeClass method configures Selenium Foundation to target {@link ExamplePage}. 14 | */ 15 | @BeforeClass 16 | public void beforeClass() { 17 | ExamplePage.setHubAsTarget(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/examples/TestNgTargetRoot.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.examples; 2 | 3 | import org.testng.annotations.BeforeClass; 4 | 5 | import com.nordstrom.automation.selenium.platform.TargetType; 6 | import com.nordstrom.automation.selenium.support.TestNgTargetBase; 7 | 8 | /** 9 | * This class provides a base for TestNG test classes with methods that target features of {@link ExamplePage}. 10 | */ 11 | public class TestNgTargetRoot extends TestNgTargetBase { 12 | 13 | /** 14 | * This BeforeClass method configures Selenium Foundation to target {@link ExamplePage}. 15 | */ 16 | @BeforeClass 17 | public void beforeClass() { 18 | ExamplePage.setHubAsTarget(); 19 | } 20 | 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | @Override 25 | public TargetType getDefaultPlatform() { 26 | return TargetType.WEB_APP; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/examples/WindowsPage.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.examples; 2 | 3 | import org.openqa.selenium.By; 4 | import org.openqa.selenium.WebDriver; 5 | import com.nordstrom.automation.selenium.model.Page; 6 | 7 | /** 8 | * This class is the model for the target view of the sample application used by the Appium Windows unit test. 9 | */ 10 | public class WindowsPage extends Page { 11 | 12 | /** 13 | * Constructor for main view context. 14 | * 15 | * @param driver driver object 16 | */ 17 | public WindowsPage(WebDriver driver) { 18 | super(driver); 19 | } 20 | 21 | /** 22 | * This enumeration defines element locator constants. 23 | */ 24 | protected enum Using implements ByEnum { 25 | /** text field */ 26 | EDIT_FIELD(By.className("Edit")); 27 | 28 | private final By locator; 29 | 30 | Using(By locator) { 31 | this.locator = locator; 32 | } 33 | 34 | @Override 35 | public By locator() { 36 | return locator; 37 | } 38 | } 39 | 40 | /** 41 | * Populate text field with the specified string. 42 | * 43 | * @param keys string to type into text field 44 | */ 45 | public void modifyDocument(String keys) { 46 | findElement(Using.EDIT_FIELD).sendKeys(keys); 47 | } 48 | 49 | /** 50 | * Get document content. 51 | * 52 | * @return document content 53 | */ 54 | public String getDocument() { 55 | return findElement(Using.EDIT_FIELD).getText(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ComponentStillDisplayedTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.model.PageComponent; 6 | 7 | /** 8 | * This exception is associated with the {@link PageComponent#componentIsHidden()} condition and indicates that the 9 | * indicated page component was still displayed when the timeout interval expired. 10 | */ 11 | public class ComponentStillDisplayedTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = 5397614393701035129L; 14 | 15 | /** 16 | * Constructor for a new "component still displayed" timeout exception with 17 | * the specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public ComponentStillDisplayedTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ComponentStillInvisibleTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.model.PageComponent; 6 | 7 | /** 8 | * This exception is associated with the {@link PageComponent#componentIsVisible()} condition and indicates that the 9 | * indicated page component was still invisible when the timeout interval expired. 10 | */ 11 | public class ComponentStillInvisibleTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = 7779370358344583623L; 14 | 15 | /** 16 | * Constructor for a new "component still invisible" timeout exception with 17 | * the specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public ComponentStillInvisibleTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ConditionStillInvalidTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the {@link Coordinators#has(java.util.function.Function)} wrapper method 9 | * and indicates that the wrapped condition was still returning a 'negative' result when the timeout interval expired. 10 | */ 11 | public class ConditionStillInvalidTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = -5012103332012897882L; 14 | 15 | /** 16 | * Constructor for a new "component still invalid" timeout exception with 17 | * the specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public ConditionStillInvalidTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ConditionStillValidTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the {@link Coordinators#not(java.util.function.Function)} wrapper method 9 | * and indicates that the wrapped condition was still returning a 'positive' result when the timeout interval expired. 10 | */ 11 | public class ConditionStillValidTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = -1194280527172574112L; 14 | 15 | /** 16 | * Constructor for a new "component still valid" timeout exception with 17 | * the specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public ConditionStillValidTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ContainerVacatedException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | /** 4 | * This exception is thrown when a client calls a method of a container object that's no longer valid. 5 | */ 6 | public class ContainerVacatedException extends RuntimeException { 7 | 8 | private static final long serialVersionUID = -7653982501901130765L; 9 | 10 | /** 11 | * Constructor for a new "component vacated" exception with the specified 12 | * stack trace. 13 | * 14 | * @param stackTrace execution stack trace for the point at which the 15 | * associated container became invalid 16 | */ 17 | public ContainerVacatedException(VacationStackTrace stackTrace) { 18 | super(stackTrace); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/DocumentNotReadyTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.core.JsUtility; 6 | 7 | /** 8 | * This exception is associated with the {@link JsUtility#documentIsReady()} condition and indicates that the 9 | * current document failed to become ready within the timeout interval. 10 | */ 11 | public class DocumentNotReadyTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = 3611395001046784941L; 14 | 15 | /** 16 | * Constructor for a new "document not ready" timeout exception with the 17 | * specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public DocumentNotReadyTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/DriverExecutableNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import com.nordstrom.automation.selenium.utility.BinaryFinder; 4 | 5 | /** 6 | * This exception is associated with the {@link BinaryFinder#findDriver(String)} method and indicates that a 7 | * driver matching the specified capabilities could not be acquired. 8 | */ 9 | public class DriverExecutableNotFoundException extends RuntimeException { 10 | 11 | private static final long serialVersionUID = -5718589545720652315L; 12 | 13 | private static final String PATH_DEFINED = "Driver executable neither found at '%s' (specified by [%s]) nor on the PATH"; 14 | private static final String PATH_OMITTED = "Driver executable not found on the PATH; add it or specify location in [%s]"; 15 | 16 | /** 17 | * Constructor for a new "driver executable not found" exception with 18 | * hints from the specified driver path property. 19 | * 20 | * @param driverPathProp driver path property (may be {@code null} 21 | */ 22 | public DriverExecutableNotFoundException(String driverPathProp) { 23 | super(getMessage(driverPathProp)); 24 | } 25 | 26 | /** 27 | * Build the exception message with hints from the specified driver path property. 28 | * 29 | * @param driverPathProp driver path property (may be {@code null} 30 | * @return exception message string 31 | */ 32 | private static String getMessage(final String driverPathProp) { 33 | String driverPath = System.getProperty(driverPathProp); 34 | if (driverPath != null) { 35 | return String.format(PATH_DEFINED, driverPath, driverPathProp); 36 | } else { 37 | return String.format(PATH_OMITTED, driverPathProp); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/DriverNotAvailableException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import com.nordstrom.automation.selenium.core.TestBase; 4 | 5 | /** 6 | * This exception is thrown by {@link TestBase#getDriver} when no driver is available. 7 | */ 8 | public class DriverNotAvailableException extends RuntimeException { 9 | 10 | private static final long serialVersionUID = 657965846077748022L; 11 | 12 | /** 13 | * Constructor for "driver not available" exception with default message. 14 | */ 15 | public DriverNotAvailableException() { 16 | super("No driver was found in the current test context"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ElementAbsentOrHiddenTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the {@link Coordinators#visibilityOf(org.openqa.selenium.WebElement)}, 9 | * {@link Coordinators#visibilityOfElementLocated(org.openqa.selenium.By)}, and 10 | * {@link Coordinators#visibilityOfAnyElementLocated(org.openqa.selenium.By)} conditions and indicates that the 11 | * specified element was still absent or hidden when the timeout interval expired. 12 | */ 13 | public class ElementAbsentOrHiddenTimeoutException extends TimeoutException { 14 | 15 | private static final long serialVersionUID = -2295630523192380636L; 16 | 17 | /** 18 | * Constructor for a new "element absent or hidden" timeout exception with 19 | * the specified message and cause. 20 | * 21 | * @param message the detail message (which is saved for later retrieval 22 | * by the {@link #getMessage()} method). 23 | * @param cause the cause (which is saved for later retrieval by the 24 | * {@link #getCause()} method). (A {@code null} value is 25 | * permitted, and indicates that the cause is nonexistent or 26 | * unknown.) 27 | */ 28 | public ElementAbsentOrHiddenTimeoutException(String message, Throwable cause) { 29 | super(message, cause); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ElementAttributeTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the 9 | * {@link Coordinators#elementToHaveAttributeValue(org.openqa.selenium.By, String, String)} and 10 | * and {@link Coordinators#textToBePresentInElementValue(org.openqa.selenium.By, String)} conditions and indicates 11 | * that the indicated attribute of the specified element failed to attain the specified value within the timeout 12 | * interval. 13 | */ 14 | public class ElementAttributeTimeoutException extends TimeoutException { 15 | 16 | private static final long serialVersionUID = 7422856432865870480L; 17 | 18 | /** 19 | * Constructor for a new "element attribute" timeout exception with 20 | * the specified message and cause. 21 | * 22 | * @param message the detail message (which is saved for later retrieval 23 | * by the {@link #getMessage()} method). 24 | * @param cause the cause (which is saved for later retrieval by the 25 | * {@link #getCause()} method). (A {@code null} value is 26 | * permitted, and indicates that the cause is nonexistent or 27 | * unknown.) 28 | */ 29 | public ElementAttributeTimeoutException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ElementNotClickableTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the {@link Coordinators#elementToBeClickable(org.openqa.selenium.By)} and 9 | * {@link Coordinators#elementToBeClickable(org.openqa.selenium.WebElement)} conditions and indicates that the 10 | * specified element failed to become click-able within the timeout interval. 11 | */ 12 | public class ElementNotClickableTimeoutException extends TimeoutException { 13 | 14 | private static final long serialVersionUID = -8475358618203763123L; 15 | 16 | /** 17 | * Constructor for a new "element not clickable" timeout exception with 18 | * the specified message and cause. 19 | * 20 | * @param message the detail message (which is saved for later retrieval 21 | * by the {@link #getMessage()} method). 22 | * @param cause the cause (which is saved for later retrieval by the 23 | * {@link #getCause()} method). (A {@code null} value is 24 | * permitted, and indicates that the cause is nonexistent or 25 | * unknown.) 26 | */ 27 | public ElementNotClickableTimeoutException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ElementNotPresentTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the {@link Coordinators#presenceOfElementLocated(org.openqa.selenium.By)} 9 | * condition and indicates that no elements matching the specified locator were found within the timeout interval. 10 | */ 11 | public class ElementNotPresentTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = 7039645286156391657L; 14 | 15 | /** 16 | * Constructor for a new "element not present" timeout exception with 17 | * the specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public ElementNotPresentTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ElementReferenceRefreshFailureException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.StaleElementReferenceException; 4 | 5 | import com.nordstrom.automation.selenium.model.RobustElementWrapper; 6 | 7 | /** 8 | * This exception is associated with 9 | * {@link RobustElementWrapper#refreshReference(org.openqa.selenium.StaleElementReferenceException)} 10 | * and indicates that the attempt to refresh a stale element reference was unsuccessful. 11 | */ 12 | public class ElementReferenceRefreshFailureException extends StaleElementReferenceException { 13 | 14 | private static final long serialVersionUID = 417132799562814181L; 15 | 16 | /** 17 | * Constructor for a new "element reference refresh failure" exception with the 18 | * specified message and cause. 19 | * 20 | * @param message the detail message (which is saved for later retrieval 21 | * by the {@link #getMessage()} method). 22 | * @param cause the cause (which is saved for later retrieval by the 23 | * {@link #getCause()} method). (A {@code null} value is 24 | * permitted, and indicates that the cause is nonexistent or 25 | * unknown.) 26 | */ 27 | public ElementReferenceRefreshFailureException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ElementSelectionStateTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the 9 | * {@link Coordinators#elementSelectionStateToBe(org.openqa.selenium.By, boolean)} condition and indicates that the 10 | * specified element failed to attain the indicated selection state within the timeout interval. 11 | */ 12 | public class ElementSelectionStateTimeoutException extends TimeoutException { 13 | 14 | private static final long serialVersionUID = 2150778933322672061L; 15 | 16 | /** 17 | * Constructor for a new "element selection state" timeout exception with 18 | * the specified message and cause. 19 | * 20 | * @param message the detail message (which is saved for later retrieval 21 | * by the {@link #getMessage()} method). 22 | * @param cause the cause (which is saved for later retrieval by the 23 | * {@link #getCause()} method). (A {@code null} value is 24 | * permitted, and indicates that the cause is nonexistent or 25 | * unknown.) 26 | */ 27 | public ElementSelectionStateTimeoutException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ElementStillFreshTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the {@link Coordinators#stalenessOf(org.openqa.selenium.WebElement)} condition 9 | * and indicates that the specified element reference failed to go "stale" within the timeout interval. 10 | */ 11 | public class ElementStillFreshTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = -3082528281757446744L; 14 | 15 | /** 16 | * Constructor for a new "element still fresh" timeout exception with 17 | * the specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public ElementStillFreshTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ElementStillVisibleTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the {@link Coordinators#invisibilityOfElementLocated(org.openqa.selenium.By)} 9 | * condition and indicates that the specified element was still visible when the timeout interval expired. 10 | */ 11 | public class ElementStillVisibleTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = -3777087787464228714L; 14 | 15 | /** 16 | * Constructor for a new "element still visible" timeout exception with 17 | * the specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public ElementStillVisibleTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ElementTextContentTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the 9 | * {@link Coordinators#textToBePresentInElementLocated(org.openqa.selenium.By, String)} and 10 | * {@link Coordinators#textToNotBeEmptyInElementLocated(org.openqa.selenium.By)} conditions and indicates that the 11 | * specified element failed to attain the indicated text content within the timeout interval. 12 | */ 13 | public class ElementTextContentTimeoutException extends TimeoutException { 14 | 15 | private static final long serialVersionUID = -2893297898946904937L; 16 | 17 | /** 18 | * Constructor for a new "element text content" timeout exception with 19 | * the specified message and cause. 20 | * 21 | * @param message the detail message (which is saved for later retrieval 22 | * by the {@link #getMessage()} method). 23 | * @param cause the cause (which is saved for later retrieval by the 24 | * {@link #getCause()} method). (A {@code null} value is 25 | * permitted, and indicates that the cause is nonexistent or 26 | * unknown.) 27 | */ 28 | public ElementTextContentTimeoutException(String message, Throwable cause) { 29 | super(message, cause); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/GridServerLaunchFailedException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | /** 4 | * Thrown if a Grid component process failed to start. 5 | */ 6 | public class GridServerLaunchFailedException extends RuntimeException { 7 | 8 | private static final long serialVersionUID = 5186366410431999078L; 9 | private static final String TEMPLATE = "Failed to start grid %s process"; 10 | 11 | /** 12 | * Constructor for {@code launch failed} exception with the specified server role. 13 | * 14 | * @param role Grid server role specifier ({@code hub} or {@code node}) 15 | * @param cause the cause of this exception 16 | */ 17 | public GridServerLaunchFailedException(final String role, final Throwable cause) { 18 | super(getMessage(role), cause); 19 | } 20 | 21 | /** 22 | * Get exception message for the specified server role. 23 | * 24 | * @param role Grid server role specifier ({@code hub} or {@code node}) 25 | * @return exception message for the specified server role 26 | */ 27 | private static String getMessage(final String role) { 28 | return String.format(TEMPLATE, role); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/InitialPageNotSpecifiedException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | /** 4 | * This exception is thrown if no initial page was specified. 5 | */ 6 | public class InitialPageNotSpecifiedException extends RuntimeException { 7 | 8 | private static final long serialVersionUID = -6182879162513331011L; 9 | 10 | /** 11 | * Constructor for "initial page not specified" exception with default message. 12 | */ 13 | public InitialPageNotSpecifiedException() { 14 | super("No initial page has been specified"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/InvalidGridHostException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import java.net.MalformedURLException; 4 | 5 | import org.apache.http.HttpHost; 6 | 7 | /** 8 | * This exception is thrown to indicate that the Selenium Grid host specification is malformed. 9 | */ 10 | public class InvalidGridHostException extends RuntimeException { 11 | 12 | private static final long serialVersionUID = -3037697283479571401L; 13 | private static final String TEMPLATE = "Specified Selenium Grid %s host URI '%s' is malformed"; 14 | 15 | /** 16 | * Constructor for {@code invalid host} exception with the specified role and host. 17 | * 18 | * @param role Grid server role specifier ({@code hub} or {@code node}) 19 | * @param host Grid server host specifier 20 | * @param cause the cause of this exception 21 | */ 22 | public InvalidGridHostException(final String role, final HttpHost host, final MalformedURLException cause) { 23 | super(getMessage(role, host), cause); 24 | } 25 | 26 | /** 27 | * Get exception message for the specified role and host. 28 | * 29 | * @param role Grid server role specifier ({@code hub} or {@code node}) 30 | * @param host Grid server host specifier 31 | * @return exception message for the specified role and host 32 | */ 33 | private static String getMessage(final String role, final HttpHost host) { 34 | return String.format(TEMPLATE, role, host.toURI()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/NoWindowAppearedTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the {@link Coordinators#newWindowIsOpened(java.util.Set)} condition and 9 | * indicates that no new browser window appeared within the timeout interval. 10 | */ 11 | public class NoWindowAppearedTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = 1178667313414119377L; 14 | 15 | /** 16 | * Constructor for a new "no window appeared" timeout exception with 17 | * the specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public NoWindowAppearedTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/OptionalElementNotAcquiredException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.NoSuchElementException; 4 | import org.openqa.selenium.WebElement; 5 | 6 | /** 7 | * This exception is thrown upon failing to acquire the reference for an optional element prior to invoking a 8 | * {@link WebElement} method. 9 | */ 10 | public class OptionalElementNotAcquiredException extends NoSuchElementException { 11 | 12 | private static final long serialVersionUID = -1817241270199904930L; 13 | 14 | /** 15 | * Constructor for {@code optional element not acquired} exception. 16 | * 17 | * @param cause the cause of this exception 18 | */ 19 | public OptionalElementNotAcquiredException(final NoSuchElementException cause) { 20 | super("Unable to acquire reference for optional element", cause); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/PageLoadRendererTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.model.ContainerMethodInterceptor; 6 | 7 | /** 8 | * This exception is associated with {@link ContainerMethodInterceptor#intercept(Object, Method, Object[], Callable)} 9 | * and indicates that the browser timed out waiting for web page resources to be processed and rendered. 10 | */ 11 | public class PageLoadRendererTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = 2076867125637147698L; 14 | 15 | /** 16 | * Constructor for a new "page load renderer" timeout exception with the 17 | * specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public PageLoadRendererTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/PageNotLoadedException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.concurrent.Callable; 5 | 6 | import org.openqa.selenium.NoSuchElementException; 7 | import org.openqa.selenium.NotFoundException; 8 | 9 | import com.nordstrom.automation.selenium.interfaces.DetectsLoadCompletion; 10 | import com.nordstrom.automation.selenium.model.ComponentContainer; 11 | import com.nordstrom.automation.selenium.model.ContainerMethodInterceptor; 12 | import com.nordstrom.automation.selenium.model.Page; 13 | import com.nordstrom.automation.selenium.support.Coordinator; 14 | 15 | /** 16 | * This exception is thrown by implementations of {@link DetectsLoadCompletion#isLoadComplete()} to indicate that 17 | * loading of the page is not yet complete. The determination of page-load conditions is scenario-specific and may 18 | * include the use of {@link ComponentContainer#checkPageLoadCondition(Coordinator, String)}, which facilitates 19 | * the use of predefined condition evaluation functions for page-load checking.
20 | *
21 | * The condition-polling mechanism employed by 22 | * {@link ContainerMethodInterceptor#intercept(Object, Method, Object[], Callable)} records instances of this 23 | * exception, but will continue to poll until {@link DetectsLoadCompletion#isLoadComplete()} completes without 24 | * exception or time runs out. Note that it's not necessary to wrap instances of {@link NotFoundException} 25 | * (e.g. - {@link NoSuchElementException}) with this exception, as these are automatically handled by 26 | * {@link ComponentContainer#waitForLandingPage(Page)}. 27 | */ 28 | public class PageNotLoadedException extends RuntimeException { 29 | private static final long serialVersionUID = -8491929915611599716L; 30 | 31 | /** 32 | * Constructor for {@code page not loaded} exception with specified detail message. 33 | * 34 | * @param message detail message 35 | */ 36 | public PageNotLoadedException(final String message) { 37 | super(message); 38 | } 39 | 40 | /** 41 | * Constructor for {@code page not loaded} exception with specified cause. 42 | * 43 | * @param cause cause of this exception 44 | */ 45 | public PageNotLoadedException(final Throwable cause) { 46 | super(cause); 47 | } 48 | 49 | /** 50 | * Constructor for {@code page not loaded} exception with specified detail message and cause. 51 | * 52 | * @param message detail message 53 | * @param cause cause of this exception 54 | */ 55 | public PageNotLoadedException(final String message, final Throwable cause) { 56 | super(message, cause); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/PageTransitionRefreshTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.model.ContainerMethodInterceptor; 6 | 7 | /** 8 | * This exception is associated with {@link ContainerMethodInterceptor#intercept(Object, Method, Object[], Callable)} 9 | * and indicates that the parent page reference element failed to go "stale" within the timeout interval. 10 | */ 11 | public class PageTransitionRefreshTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = 6396536840195276179L; 14 | 15 | /** 16 | * Constructor for a new "page transition refresh" timeout exception with 17 | * the specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public PageTransitionRefreshTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/PlatformActivationFailedException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import com.nordstrom.automation.selenium.platform.PlatformEnum; 6 | import com.nordstrom.automation.selenium.platform.PlatformTargetable; 7 | 8 | /** 9 | * This exception is thrown by implementation of the {@link PlatformTargetable#activatePlatform activatePlatform} 10 | * method to indicate that platform activation failed. 11 | * 12 | */ 13 | public class PlatformActivationFailedException extends RuntimeException { 14 | 15 | private static final long serialVersionUID = 7336291801605667538L; 16 | private static final String TEMPLATE = "Failed to activate target platform '%s'"; 17 | 18 | /** 19 | * Constructor for {@code platform activation failed} exception with the specified platform and optional details. 20 | * 21 | * @param platform platform to be activated 22 | * @param details [optional] message details 23 | */ 24 | public PlatformActivationFailedException(final PlatformEnum platform, final String... details) { 25 | super(getMessage(platform, details)); 26 | } 27 | 28 | /** 29 | * Constructor for {@code platform activation failed} exception with the specified platform, underlying cause, and 30 | * optional details. 31 | * 32 | * @param platform platform to be activated 33 | * @param cause underlying cause for activation failure 34 | * @param details [optional] message details 35 | */ 36 | public PlatformActivationFailedException( 37 | final PlatformEnum platform, final Throwable cause, final String... details) { 38 | super(getMessage(platform, details), cause); 39 | } 40 | 41 | /** 42 | * Get exception message to the specified platform with optional details. 43 | * 44 | * @param platform platform to be activated 45 | * @param details [optional] message details 46 | * @return exception message to the specified platform 47 | */ 48 | private static String getMessage(final PlatformEnum platform, String... details) { 49 | String appendix = (details.length == 0) ? "" : "\n" + StringUtils.join(details, "\n"); 50 | return String.format(TEMPLATE, platform.getName()) + appendix; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/ShadowRootContextException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import com.nordstrom.automation.selenium.model.ShadowRoot; 4 | 5 | /** 6 | * This exception is throw during instantiation of {@link ShadowRoot} page components if the indicated root 7 | * element is not a shadow host or has 'closed' shadow-DOM mode. 8 | */ 9 | public class ShadowRootContextException extends RuntimeException { 10 | 11 | private static final long serialVersionUID = -2655316241833901377L; 12 | 13 | /** 14 | * Constructor for {@code shadow root context} exception. 15 | */ 16 | public ShadowRootContextException() { 17 | super("Context is not a shadow host or has 'closed' shadow-DOM mode"); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/TransitionErrorException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.SearchContext; 4 | import org.openqa.selenium.WebDriver; 5 | 6 | import com.nordstrom.automation.selenium.interfaces.TransitionErrorDetector; 7 | import com.nordstrom.automation.selenium.model.ComponentContainer; 8 | import com.nordstrom.automation.selenium.model.ContainerMethodInterceptor; 9 | import com.nordstrom.automation.selenium.model.Enhanceable; 10 | 11 | /** 12 | * This exception is thrown by {@link ContainerMethodInterceptor#scanForErrors(SearchContext)} when a registered 13 | * {@link TransitionErrorDetector} service provider detects an error. 14 | */ 15 | public class TransitionErrorException extends IllegalStateException { 16 | 17 | /** exception error message */ 18 | private final String errorMessage; 19 | private static final long serialVersionUID = -2969607575378647073L; 20 | 21 | /** 22 | * Constructor for {@code transition error} exception. 23 | * 24 | * @param context container context in which the error was detected 25 | * @param errorMessage error message 26 | */ 27 | public TransitionErrorException(ComponentContainer context, String errorMessage) { 28 | super(buildMessage(context, errorMessage)); 29 | this.errorMessage = errorMessage; 30 | } 31 | 32 | /** 33 | * Get message for this transition error. 34 | * 35 | * @return transition error message 36 | */ 37 | public String getErrorMessage() { 38 | return errorMessage; 39 | } 40 | 41 | /** 42 | * Build the message for this transition error exception. 43 | * 44 | * @param context container context in which the error was detected 45 | * @param errorMessage error message 46 | * @return transition error exception message 47 | */ 48 | private static String buildMessage(ComponentContainer context, String errorMessage) { 49 | StringBuilder builder = new StringBuilder("Transition error detected: ").append(errorMessage); 50 | builder.append("\nContainer: ").append(Enhanceable.getContainerClass(context).getName()); 51 | WebDriver driver = context.getWrappedDriver(); 52 | if (driver != null) { 53 | String pageUrl = driver.getCurrentUrl(); 54 | if (pageUrl != null) { 55 | builder.append("\nPage URL: ").append(pageUrl); 56 | } 57 | String pageTitle = driver.getTitle(); 58 | if (pageTitle != null) { 59 | builder.append("\nPage title: ").append(pageTitle); 60 | } 61 | } 62 | return builder.toString(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/UnknownGridHostException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import java.net.UnknownHostException; 4 | 5 | /** 6 | * This exception indicates that the configured Selenium Grid server host name couldn't be resolved to an IP address. 7 | */ 8 | public class UnknownGridHostException extends RuntimeException { 9 | 10 | private static final long serialVersionUID = -3037697283479571401L; 11 | private static final String TEMPLATE = "Specified Selenium Grid %s host '%s' was not found"; 12 | 13 | /** 14 | * Constructor for {@code unknown host} exception with the specified role and host name. 15 | * 16 | * @param role Grid server role specifier ({@code hub} or {@code node}) 17 | * @param hostName Grid server host name 18 | * @param cause the cause of this exception 19 | */ 20 | public UnknownGridHostException(final String role, final String hostName, final UnknownHostException cause) { 21 | super(getMessage(role, hostName), cause); 22 | } 23 | 24 | /** 25 | * Get exception message to the specified role and host name. 26 | * 27 | * @param role Grid server role specifier ({@code hub} or {@code node}) 28 | * @param hostName Grid server host name 29 | * @return exception message to the specified role and host name 30 | */ 31 | private static String getMessage(final String role, final String hostName) { 32 | return String.format(TEMPLATE, role, hostName); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/VacationStackTrace.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | import com.nordstrom.automation.selenium.utility.ReflectUtils; 6 | import com.nordstrom.common.base.StackTrace; 7 | 8 | /** 9 | * This exception is used to record the execution stack trace for the point at which the 10 | * associated container became invalid. 11 | * 12 | * @see ContainerVacatedException 13 | */ 14 | public class VacationStackTrace extends StackTrace { 15 | 16 | private static final long serialVersionUID = -512001372372827847L; 17 | 18 | private final transient Method vacater; 19 | private final transient String reason; 20 | private static final String PREAMBLE = "Container object was vacated by invocation of method: "; 21 | 22 | /** 23 | * Constructs a new {@code container vacated} exception with the specified vacater. 24 | * 25 | * @param vacater method that caused the container to be vacated 26 | */ 27 | public VacationStackTrace(final Method vacater) { 28 | this(vacater, null); 29 | } 30 | 31 | /** 32 | * Constructs a new {@code container vacated} exception with the specified vacater. 33 | * 34 | * @param vacater method that caused the container to be vacated 35 | * @param reason for vacating the target object 36 | */ 37 | public VacationStackTrace(final Method vacater, final String reason) { 38 | super(getMessage(vacater, reason)); 39 | this.vacater = vacater; 40 | this.reason = reason; 41 | } 42 | 43 | /** 44 | * Get the reason that the affected container object to be vacated. 45 | * 46 | * @return reason for vacating the target object 47 | */ 48 | public String getReason() { 49 | return reason; 50 | } 51 | 52 | /** 53 | * Get the method that caused the affected container object to be vacated. 54 | * 55 | * @return method that vacated the target object 56 | */ 57 | public Method getVacater() { 58 | return vacater; 59 | } 60 | 61 | /** 62 | * Assemble the message for this exception. 63 | * 64 | * @param method method that vacated the target object. 65 | * @param reason for vacating the target object 66 | * @return message for this exception 67 | */ 68 | private static String getMessage(final Method method, final String reason) { 69 | String className = method.getDeclaringClass().getSimpleName(); 70 | String signature = ReflectUtils.getSignature(method); 71 | String suffix = (reason != null) ? "\n" + reason : ""; 72 | return PREAMBLE + className + ":" + signature + suffix; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/exceptions/WindowStillExistsTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.exceptions; 2 | 3 | import org.openqa.selenium.TimeoutException; 4 | 5 | import com.nordstrom.automation.selenium.support.Coordinators; 6 | 7 | /** 8 | * This exception is associated with the {@link Coordinators#windowIsClosed(String)} condition and indicates that the 9 | * browser window with the specified window handle was still present when the timeout interval expired. 10 | */ 11 | public class WindowStillExistsTimeoutException extends TimeoutException { 12 | 13 | private static final long serialVersionUID = 1228040448300937511L; 14 | 15 | /** 16 | * Constructor for a new "window still exists" timeout exception with 17 | * the specified message and cause. 18 | * 19 | * @param message the detail message (which is saved for later retrieval 20 | * by the {@link #getMessage()} method). 21 | * @param cause the cause (which is saved for later retrieval by the 22 | * {@link #getCause()} method). (A {@code null} value is 23 | * permitted, and indicates that the cause is nonexistent or 24 | * unknown.) 25 | */ 26 | public WindowStillExistsTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/interfaces/DetectsLoadCompletion.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.interfaces; 2 | 3 | import org.openqa.selenium.SearchContext; 4 | 5 | import com.nordstrom.automation.selenium.model.ComponentContainer; 6 | 7 | /** 8 | * Page classes that model pages with complex loading behavior implement this interface to provide scenario-specific 9 | * detection of page load completion. This is typically required for single-page applications or more conventional 10 | * multi-page applications that use dynamic load techniques (e.g. - AJAX). 11 | */ 12 | public interface DetectsLoadCompletion { 13 | 14 | /** 15 | * Determine if the page has finished loading. 16 | * 17 | * @return 'true' if the page has finished loading; otherwise 'false' 18 | */ 19 | boolean isLoadComplete(); 20 | 21 | /** 22 | * Get the container search context 23 | *

24 | * NOTE: This method is lifted from the {@link ComponentContainer} class. 25 | * 26 | * @return container search context 27 | */ 28 | SearchContext getContext(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/interfaces/DriverProvider.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.interfaces; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | import org.openqa.selenium.WebDriver; 6 | 7 | /** 8 | * Test classes with non-standard driver configurations implement this interface, which enables the driver manager 9 | * to obtain a driver from the {@link #provideDriver(Method)} method of test class instance. 10 | */ 11 | public interface DriverProvider { 12 | 13 | /** 14 | * Acquire a driver object for the specified method. 15 | * @param method the method being invoked 16 | * 17 | * @return driver object 18 | */ 19 | WebDriver provideDriver(Method method); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/interfaces/TransitionErrorDetector.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.interfaces; 2 | 3 | import com.nordstrom.automation.selenium.model.ComponentContainer; 4 | 5 | /** 6 | * This interface defines the method implemented by Selenium Foundation transition error detectors. These detectors are 7 | * registered via a ServiceLoader provider configuration file. Registered detectors are notified whenever a container 8 | * method returns a new container object. 9 | */ 10 | public interface TransitionErrorDetector { 11 | 12 | /** 13 | * Scan the specified container context for transition errors. 14 | * 15 | * @param context container context to scan for errors 16 | * @return error message string; {@code null} if no errors are detected 17 | */ 18 | String scanForErrors(ComponentContainer context); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/interfaces/WrapsContext.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.interfaces; 2 | 3 | import org.openqa.selenium.SearchContext; 4 | import org.openqa.selenium.WrapsDriver; 5 | 6 | /** 7 | * Classes implement this interface to provide access to their underlying native Selenium 8 | * {@link SearchContext}. This interface also defines methods related to automatic recovery from 9 | * {@link org.openqa.selenium.StaleElementReferenceException StaleElementReferenceException} 10 | * failures. 11 | */ 12 | public interface WrapsContext extends WrapsDriver { 13 | 14 | /** 15 | * Switch the driver to the context that underlies this object. 16 | * 17 | * @return this object's underlying search context 18 | */ 19 | SearchContext switchTo(); 20 | 21 | /** 22 | * Get the underlying search context for this object. 23 | * 24 | * @return object search context 25 | */ 26 | SearchContext getWrappedContext(); 27 | 28 | /** 29 | * Refresh the underlying search context for this object. 30 | * 31 | * @param expiration expiration time of context chain 32 | * @return object search context 33 | */ 34 | SearchContext refreshContext(long expiration); 35 | 36 | /** 37 | * Determine when the underlying search context for this object was acquired. 38 | * 39 | * @return search context acquisition time (from {@link System#currentTimeMillis()}) 40 | */ 41 | long acquiredAt(); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/junit/DriverListener.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.junit; 2 | 3 | import com.nordstrom.automation.junit.ShutdownListener; 4 | import com.nordstrom.automation.selenium.core.DriverManager; 5 | 6 | /** 7 | * This class implements a driver shutdown listener. 8 | */ 9 | public class DriverListener implements ShutdownListener { 10 | 11 | /** 12 | * {@inheritDoc} 13 | */ 14 | @Override 15 | public void onShutdown() { 16 | DriverManager.onFinish(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/junit/JUnitTargetBase.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.junit; 2 | 3 | import com.nordstrom.automation.selenium.platform.TargetType; 4 | 5 | /** 6 | * This class is a concrete subclass of {@link JUnitPlatformBase} specifying {@link TargetType} as the platform. 7 | */ 8 | public class JUnitTargetBase extends JUnitPlatformBase { 9 | 10 | /** 11 | * Constructor for JUnit tests classes that support the {@link TargetType} platform. 12 | */ 13 | public JUnitTargetBase() { 14 | super(TargetType.class); 15 | } 16 | 17 | /** 18 | * {@inheritDoc} 19 | */ 20 | @Override 21 | public TargetType getDefaultPlatform() { 22 | return TargetType.SUPPORT; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/junit/PageSourceArtifact.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.junit; 2 | 3 | import java.nio.file.Path; 4 | import java.util.Optional; 5 | 6 | import org.openqa.selenium.WebDriver; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import com.nordstrom.automation.selenium.core.DriverManager; 10 | import com.nordstrom.automation.selenium.utility.PageSourceUtils; 11 | import com.nordstrom.automation.junit.ArtifactType; 12 | 13 | /** 14 | * This class implements the artifact type for screenshot capture. 15 | */ 16 | public class PageSourceArtifact extends ArtifactType { 17 | 18 | private static final String ARTIFACT_PATH = "page-source"; 19 | private static final String EXTENSION = "html"; 20 | private static final Logger LOGGER = LoggerFactory.getLogger(PageSourceArtifact.class); 21 | 22 | /** 23 | * {@inheritDoc} 24 | */ 25 | @Override 26 | public boolean canGetArtifact(final Object instance) { 27 | Optional optDriver = DriverManager.nabDriver(instance); 28 | return PageSourceUtils.canGetArtifact(optDriver, LOGGER); 29 | } 30 | 31 | /** 32 | * {@inheritDoc} 33 | */ 34 | @Override 35 | public byte[] getArtifact(final Object instance, final Throwable reason) { 36 | Optional optDriver = DriverManager.nabDriver(instance); 37 | return PageSourceUtils.getArtifact(optDriver, reason, LOGGER).getBytes(); 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | @Override 44 | public Path getArtifactPath(final Object instance) { 45 | return super.getArtifactPath(instance).resolve(ARTIFACT_PATH); 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | @Override 52 | public String getArtifactExtension() { 53 | return EXTENSION; 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | @Override 60 | public Logger getLogger() { 61 | return LOGGER; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/junit/PageSourceCapture.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.junit; 2 | 3 | import com.nordstrom.automation.junit.ArtifactCollector; 4 | 5 | /** 6 | * This class uses the {@link ArtifactCollector} to implement a page source capturing test watcher. 7 | */ 8 | public class PageSourceCapture extends ArtifactCollector { 9 | 10 | /** 11 | * This constructor provides a {@link PageSourceArtifact} object to the {@link ArtifactCollector}. 12 | * 13 | * @param instance JUnit test class instance 14 | */ 15 | public PageSourceCapture(final Object instance) { 16 | super(instance, new PageSourceArtifact()); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/junit/RetryAnalyzer.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.junit; 2 | 3 | import org.junit.runners.model.FrameworkMethod; 4 | import org.openqa.selenium.WebDriverException; 5 | 6 | import com.nordstrom.automation.junit.JUnitRetryAnalyzer; 7 | 8 | /** 9 | * This class implements a Selenium-specific JUnit retry analyzer. 10 | */ 11 | public class RetryAnalyzer implements JUnitRetryAnalyzer { 12 | 13 | /** 14 | * {@inheritDoc} 15 | *

16 | * This implementation deems that a test that fails with an instance of {@link WebDriverException} is re-triable. 17 | */ 18 | @Override 19 | public boolean retry(FrameworkMethod method, Throwable thrown) { 20 | return (thrown instanceof WebDriverException); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/junit/ScreenshotArtifact.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.junit; 2 | 3 | import java.nio.file.Path; 4 | import java.util.Optional; 5 | 6 | import org.openqa.selenium.WebDriver; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import com.nordstrom.automation.selenium.core.DriverManager; 10 | import com.nordstrom.automation.selenium.utility.ScreenshotUtils; 11 | import com.nordstrom.automation.junit.ArtifactType; 12 | 13 | /** 14 | * This class implements the artifact type for screenshot capture. 15 | */ 16 | public class ScreenshotArtifact extends ArtifactType { 17 | 18 | private static final String ARTIFACT_PATH = "screenshots"; 19 | private static final String EXTENSION = "png"; 20 | private static final Logger LOGGER = LoggerFactory.getLogger(ScreenshotArtifact.class); 21 | 22 | /** 23 | * {@inheritDoc} 24 | */ 25 | @Override 26 | public boolean canGetArtifact(final Object instance) { 27 | Optional optDriver = DriverManager.nabDriver(instance); 28 | return ScreenshotUtils.canGetArtifact(optDriver, LOGGER); 29 | } 30 | 31 | /** 32 | * {@inheritDoc} 33 | */ 34 | @Override 35 | public byte[] getArtifact(final Object instance, final Throwable reason) { 36 | Optional optDriver = DriverManager.nabDriver(instance); 37 | return ScreenshotUtils.getArtifact(optDriver, reason, LOGGER); 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | @Override 44 | public Path getArtifactPath(final Object instance) { 45 | return super.getArtifactPath(instance).resolve(ARTIFACT_PATH); 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | @Override 52 | public String getArtifactExtension() { 53 | return EXTENSION; 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | @Override 60 | public Logger getLogger() { 61 | return LOGGER; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/junit/ScreenshotCapture.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.junit; 2 | 3 | import com.nordstrom.automation.junit.ArtifactCollector; 4 | 5 | /** 6 | * This class uses the {@link ArtifactCollector} to implement a screenshot capturing test watcher. 7 | */ 8 | public class ScreenshotCapture extends ArtifactCollector { 9 | 10 | /** 11 | * This constructor provides a {@link ScreenshotArtifact} object to the {@link ArtifactCollector}. 12 | * 13 | * @param instance JUnit test class instance 14 | */ 15 | public ScreenshotCapture(final Object instance) { 16 | super(instance, new ScreenshotArtifact()); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/listeners/PageSourceArtifact.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.listeners; 2 | 3 | import java.nio.file.Path; 4 | import java.util.Optional; 5 | 6 | import org.openqa.selenium.WebDriver; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.testng.ITestResult; 10 | import org.testng.Reporter; 11 | 12 | import com.nordstrom.automation.selenium.core.DriverManager; 13 | import com.nordstrom.automation.selenium.utility.PageSourceUtils; 14 | import com.nordstrom.automation.testng.ArtifactType; 15 | 16 | /** 17 | * This class implements the artifact type for screenshot capture. 18 | */ 19 | public class PageSourceArtifact extends ArtifactType { 20 | 21 | private static final String ARTIFACT_PATH = "page-source"; 22 | private static final String EXTENSION = "html"; 23 | private static final Logger LOGGER = LoggerFactory.getLogger(PageSourceArtifact.class); 24 | 25 | /** 26 | * {@inheritDoc} 27 | */ 28 | @Override 29 | public boolean canGetArtifact(final ITestResult result) { 30 | // ensure current test result is set 31 | Reporter.setCurrentTestResult(result); 32 | Optional optDriver = DriverManager.nabDriver(result.getInstance()); 33 | return PageSourceUtils.canGetArtifact(optDriver, LOGGER); 34 | } 35 | 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | @Override 40 | public byte[] getArtifact(final ITestResult result) { 41 | // ensure current test result is set 42 | Reporter.setCurrentTestResult(result); 43 | Optional optDriver = DriverManager.nabDriver(result.getInstance()); 44 | return PageSourceUtils.getArtifact(optDriver, result.getThrowable(), LOGGER).getBytes(); 45 | } 46 | 47 | /** 48 | * {@inheritDoc} 49 | */ 50 | @Override 51 | public Path getArtifactPath(final ITestResult result) { 52 | return super.getArtifactPath(result).resolve(ARTIFACT_PATH); 53 | } 54 | 55 | /** 56 | * {@inheritDoc} 57 | */ 58 | @Override 59 | public String getArtifactExtension() { 60 | return EXTENSION; 61 | } 62 | 63 | /** 64 | * {@inheritDoc} 65 | */ 66 | @Override 67 | public Logger getLogger() { 68 | return LOGGER; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/listeners/PageSourceCapture.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.listeners; 2 | 3 | import com.nordstrom.automation.testng.ArtifactCollector; 4 | 5 | /** 6 | * This class uses the {@link ArtifactCollector} to implement a page source capturing listener. 7 | */ 8 | public class PageSourceCapture extends ArtifactCollector { 9 | 10 | /** 11 | * This constructor provides a {@link PageSourceArtifact} object to the {@link ArtifactCollector}. 12 | */ 13 | public PageSourceCapture() { 14 | super(new PageSourceArtifact()); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/listeners/ScreenshotArtifact.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.listeners; 2 | 3 | import java.nio.file.Path; 4 | import java.util.Optional; 5 | 6 | import org.openqa.selenium.WebDriver; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.testng.ITestResult; 10 | import org.testng.Reporter; 11 | 12 | import com.nordstrom.automation.selenium.core.DriverManager; 13 | import com.nordstrom.automation.selenium.utility.ScreenshotUtils; 14 | import com.nordstrom.automation.testng.ArtifactType; 15 | 16 | /** 17 | * This class implements the artifact type for screenshot capture. 18 | */ 19 | public class ScreenshotArtifact extends ArtifactType { 20 | 21 | private static final String ARTIFACT_PATH = "screenshots"; 22 | private static final String EXTENSION = "png"; 23 | private static final Logger LOGGER = LoggerFactory.getLogger(ScreenshotArtifact.class); 24 | 25 | /** 26 | * {@inheritDoc} 27 | */ 28 | @Override 29 | public boolean canGetArtifact(final ITestResult result) { 30 | // ensure current test result is set 31 | Reporter.setCurrentTestResult(result); 32 | Optional optDriver = DriverManager.nabDriver(result.getInstance()); 33 | return ScreenshotUtils.canGetArtifact(optDriver, LOGGER); 34 | } 35 | 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | @Override 40 | public byte[] getArtifact(final ITestResult result) { 41 | // ensure current test result is set 42 | Reporter.setCurrentTestResult(result); 43 | Optional optDriver = DriverManager.nabDriver(result.getInstance()); 44 | return ScreenshotUtils.getArtifact(optDriver, result.getThrowable(), LOGGER); 45 | } 46 | 47 | /** 48 | * {@inheritDoc} 49 | */ 50 | @Override 51 | public Path getArtifactPath(final ITestResult result) { 52 | return super.getArtifactPath(result).resolve(ARTIFACT_PATH); 53 | } 54 | 55 | /** 56 | * {@inheritDoc} 57 | */ 58 | @Override 59 | public String getArtifactExtension() { 60 | return EXTENSION; 61 | } 62 | 63 | /** 64 | * {@inheritDoc} 65 | */ 66 | @Override 67 | public Logger getLogger() { 68 | return LOGGER; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/listeners/ScreenshotCapture.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.listeners; 2 | 3 | import com.nordstrom.automation.testng.ArtifactCollector; 4 | 5 | /** 6 | * This class uses the {@link ArtifactCollector} to implement a screenshot capturing listener. 7 | */ 8 | public class ScreenshotCapture extends ArtifactCollector { 9 | 10 | /** 11 | * This constructor provides a {@link ScreenshotArtifact} object to the {@link ArtifactCollector}. 12 | */ 13 | public ScreenshotCapture() { 14 | super(new ScreenshotArtifact()); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/model/ComponentList.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.model; 2 | 3 | import org.openqa.selenium.By; 4 | 5 | /** 6 | * This class defines a list for Selenium Foundation page component objects. 7 | *

8 | * NOTE: This class implements a read-only list; all methods that would alter the composition of the collection 9 | * (e.g. - {@link #add(Object)}) result in {@link UnsupportedOperationException}. 10 | * 11 | * @param the class of page component objects collected by this list 12 | */ 13 | public class ComponentList extends ContainerList { 14 | 15 | /** 16 | * Constructor for component list with parent, type, and locator 17 | * 18 | * @param parent parent container 19 | * @param containerType container type 20 | * @param locator container context element locator 21 | */ 22 | ComponentList(final ComponentContainer parent, final Class componentType, final By locator) { 23 | super(parent, componentType, locator); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/model/ComponentMap.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.model; 2 | 3 | import org.openqa.selenium.By; 4 | 5 | /** 6 | * This class defines a map for Selenium Foundation page component objects. 7 | *

8 | * NOTE: This class implements a read-only map; all methods that would alter the composition of the collection 9 | * (e.g. - {@link #put(Object, Object)}) result in {@link UnsupportedOperationException}. 10 | * 11 | * @param the class of page component objects collected by this map 12 | */ 13 | public class ComponentMap extends ContainerMap { 14 | 15 | /** 16 | * Constructor for component map with parent, type, and locator 17 | * 18 | * @param parent parent container 19 | * @param containerType container type 20 | * @param locator container context element locator 21 | */ 22 | ComponentMap(final ComponentContainer parent, final Class containerType, final By locator) { 23 | super(parent, containerType, locator); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/model/Enhanced.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.model; 2 | 3 | /** 4 | * This marker interface is added to dynamically created classes for future identification 5 | */ 6 | public interface Enhanced { } 7 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/model/FrameList.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.model; 2 | 3 | import org.openqa.selenium.By; 4 | 5 | /** 6 | * This class defines a list for Selenium Foundation frame objects. 7 | *

8 | * NOTE: This class implements a read-only list; all methods that would alter the composition of the collection 9 | * (e.g. - {@link #add(Object)}) result in {@link UnsupportedOperationException}. 10 | * 11 | * @param the class of frame objects collected by this list 12 | */ 13 | public class FrameList extends ContainerList { 14 | 15 | /** 16 | * Constructor for frame list with parent, type, and locator 17 | * 18 | * @param parent parent container 19 | * @param containerType container type 20 | * @param locator container context element locator 21 | */ 22 | FrameList(final ComponentContainer parent, final Class containerType, final By locator) { 23 | super(parent, containerType, locator); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/model/FrameMap.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.model; 2 | 3 | import org.openqa.selenium.By; 4 | 5 | /** 6 | * This class defines a map for Selenium Foundation frame objects. 7 | *

8 | * NOTE: This class implements a read-only map; all methods that would alter the composition of the collection 9 | * (e.g. - {@link #put(Object, Object)}) result in {@link UnsupportedOperationException}. 10 | * 11 | * @param the class of frame objects collected by this map 12 | */ 13 | public class FrameMap extends ContainerMap { 14 | 15 | /** 16 | * Constructor for frame map with parent, type, and locator 17 | * 18 | * @param parent parent container 19 | * @param containerType container type 20 | * @param locator container context element locator 21 | */ 22 | FrameMap(final ComponentContainer parent, final Class containerType, final By locator) { 23 | super(parent, containerType, locator); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/model/ReferenceFetcher.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.model; 2 | 3 | import org.openqa.selenium.By; 4 | import org.openqa.selenium.SearchContext; 5 | import org.openqa.selenium.StaleElementReferenceException; 6 | import org.openqa.selenium.WebElement; 7 | import org.openqa.selenium.WrapsElement; 8 | 9 | import com.nordstrom.automation.selenium.interfaces.WrapsContext; 10 | 11 | /** 12 | * This interface defines the methods that are added to {@link WebElement} to create {@link RobustElementWrapper}. 13 | */ 14 | public interface ReferenceFetcher extends SearchContext, WrapsElement, WrapsContext, Enhanced { 15 | 16 | /** 17 | * Get a wrapped reference to the first element matching the specified locator. 18 | *

19 | * NOTE: Use {@link ReferenceFetcher#hasReference()} to determine if a valid reference was acquired. 20 | * 21 | * @param by the locating mechanism 22 | * @return robust web element 23 | */ 24 | WebElement findOptional(By by); 25 | 26 | /** 27 | * Determine if this robust element wraps a valid reference. 28 | * 29 | * @return 'true' if reference was acquired; otherwise 'false' 30 | */ 31 | boolean hasReference(); 32 | 33 | /** 34 | * Get the search context for this element. 35 | * 36 | * @return element search context 37 | */ 38 | WrapsContext getContext(); 39 | 40 | /** 41 | * Get the locator for this element. 42 | * 43 | * @return element locator 44 | */ 45 | By getLocator(); 46 | 47 | /** 48 | * Get the element index. 49 | *

50 | * NOTE: {@link RobustElementWrapper#CARDINAL CARDINAL} = 1st matched reference; 51 | * {@link RobustElementWrapper#OPTIONAL OPTIONAL} = an optional reference 52 | * 53 | * @return element index (see NOTE) 54 | */ 55 | int getIndex(); 56 | 57 | /** 58 | * Refresh the wrapped element reference. 59 | * 60 | * @param refreshTrigger {@link StaleElementReferenceException} that necessitates reference refresh 61 | * @return this robust element wrapper with refreshed reference 62 | */ 63 | RobustElementWrapper refreshReference(StaleElementReferenceException refreshTrigger); 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/model/RobustWebElement.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.model; 2 | 3 | import org.openqa.selenium.WebElement; 4 | 5 | /** 6 | * This interface declares the public API for "robust" web elements, adding the reference-refreshing methods of the 7 | * {@link ReferenceFetcher} interface to the standard {@link WebElement} interface. 8 | */ 9 | public interface RobustWebElement extends WebElement, ReferenceFetcher { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/model/ShadowRootList.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.model; 2 | 3 | import org.openqa.selenium.By; 4 | 5 | /** 6 | * This class defines a list for Selenium Foundation shadow root objects. 7 | *

8 | * NOTE: This class implements a read-only list; all methods that would alter the composition of the collection 9 | * (e.g. - {@link #add(Object)}) result in {@link UnsupportedOperationException}. 10 | * 11 | * @param the class of shadow root objects collected by this list 12 | */ 13 | public class ShadowRootList extends ContainerList { 14 | 15 | /** 16 | * Constructor for shadow root list with parent, type, and locator 17 | * 18 | * @param parent parent container 19 | * @param containerType container type 20 | * @param locator container context element locator 21 | */ 22 | ShadowRootList(final ComponentContainer parent, final Class containerType, final By locator) { 23 | super(parent, containerType, locator); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/model/ShadowRootMap.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.model; 2 | 3 | import org.openqa.selenium.By; 4 | 5 | /** 6 | * This class defines a map for Selenium Foundation shadow root objects. 7 | *

8 | * NOTE: This class implements a read-only map; all methods that would alter the composition of the collection 9 | * (e.g. - {@link #put(Object, Object)}) result in {@link UnsupportedOperationException}. 10 | * 11 | * @param the class of shadow root objects collected by this map 12 | */ 13 | public class ShadowRootMap extends ContainerMap { 14 | 15 | /** 16 | * Constructor for shadow root map with parent, type, and locator 17 | * 18 | * @param parent parent container 19 | * @param containerType container type 20 | * @param locator container context element locator 21 | */ 22 | ShadowRootMap(final ComponentContainer parent, final Class containerType, final By locator) { 23 | super(parent, containerType, locator); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/platform/PlatformEnum.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.platform; 2 | 3 | /** 4 | * This interface provides common methods for collections of platform constants. 5 | */ 6 | public interface PlatformEnum { 7 | 8 | /** 9 | * Get name of platform constant. 10 | * 11 | * @return platform constant name 12 | */ 13 | String getName(); 14 | 15 | /** 16 | * Determine if the specified context platform matches this constant. 17 | * 18 | * @param contextPlatform active context platform 19 | * @return 'true' if this constant matches the specified context platform; otherwise 'false' 20 | */ 21 | boolean matches(String contextPlatform); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/platform/PlatformTargetable.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.platform; 2 | 3 | import org.openqa.selenium.WebDriver; 4 | 5 | import com.nordstrom.automation.selenium.exceptions.PlatformActivationFailedException; 6 | import com.nordstrom.common.file.PathUtils.PathModifier; 7 | 8 | /** 9 | * Test classes that implement this interface are afforded the ability to specify the platform(s) supported by each 10 | * of its test methods. Each test method can specify one or more target platform on which it should run. At run-time, 11 | * the test flow controller determines which test methods should run on the current target platform, only including 12 | * those test methods that support that platform. 13 | * 14 | * @param

platform specifier 15 | */ 16 | public interface PlatformTargetable

& PlatformEnum> extends PathModifier { 17 | 18 | /** 19 | * Get the target platform for this test class instance. 20 | * 21 | * @return target platform for this instance 22 | */ 23 | P getTargetPlatform(); 24 | 25 | /** 26 | * Activate the specified target platform. 27 | * 28 | * @param driver WebDriver object (may be {@code null}) 29 | * @param platform platform to be activated 30 | * @throws PlatformActivationFailedException if platform activation fails 31 | */ 32 | void activatePlatform(WebDriver driver, P platform) throws PlatformActivationFailedException; 33 | 34 | /** 35 | * Get the collection of valid platforms. 36 | * 37 | * @return array of valid platform constants 38 | */ 39 | P[] getValidPlatforms(); 40 | 41 | /** 42 | * Get the default platform specifier. 43 | * 44 | * @return default platform constant 45 | */ 46 | P getDefaultPlatform(); 47 | 48 | /** 49 | * Convert the specified platform name to the corresponding constant. 50 | * 51 | * @param name platform name 52 | * @return platform constant; 'null' for unsupported names 53 | */ 54 | P platformFromString(String name); 55 | 56 | /** 57 | * Get data type of platform enumeration. 58 | * 59 | * @return data type of platform enumeration 60 | */ 61 | Class

getPlatformType(); 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/platform/TargetPlatform.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.platform; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * This annotation is used to describe what target platform the test will be run against. 10 | */ 11 | @Target(ElementType.METHOD) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface TargetPlatform { 14 | /** 15 | * Get platform name. 16 | * 17 | * @return platform name 18 | */ 19 | String value(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/platform/TargetPlatformHandler.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.platform; 2 | 3 | /** 4 | * This class contains support methods used by our target platform implementation. 5 | */ 6 | public class TargetPlatformHandler { 7 | 8 | /** 9 | * Resolve the target platform constant for the associated method. 10 | * 11 | * @param

target platform enumeration 12 | * @param testObject test class object 13 | * @param targetPlatform {@link TargetPlatform} annotation for the current method (may be 'null') 14 | * @return target platform constant; 'null' if test class object is not {@link PlatformTargetable} 15 | */ 16 | @SuppressWarnings("unchecked") 17 | public static

& PlatformEnum> P resolveTargetPlatform(Object testObject, TargetPlatform targetPlatform) { 18 | P platform = null; 19 | 20 | if (testObject instanceof PlatformTargetable) { 21 | if (targetPlatform == null) { 22 | platform = ((PlatformTargetable

) testObject).getDefaultPlatform(); 23 | } else { 24 | platform = ((PlatformTargetable

) testObject).platformFromString(targetPlatform.value()); 25 | } 26 | } 27 | 28 | return platform; 29 | } 30 | 31 | /** 32 | * Determine if the associated method should run on the specified target platform. 33 | *

34 | * NOTE: The method is runnable if the test class implements {@link PlatformTargetable} and the method 35 | * supports the specified target platform. If the method has no {@link TargetPlatform @TargetPlatform} annotation, 36 | * it is assumed to support the implementation-defined 'default' platform. If the test class doesn't implement 37 | * PlatformTargetable, the method is runnable if the CONTEXT_PLATFORM setting matches the specified target 38 | * platform. 39 | * 40 | * @param contextPlatform active context platform 41 | * @param platformConstant {@link PlatformEnum} constant for the current method 42 | * @return 'true' if the associated method should run on the specified target platform; otherwise 'false' 43 | */ 44 | public static boolean shouldRun(String contextPlatform, PlatformEnum platformConstant) { 45 | if ((contextPlatform != null) && (platformConstant != null)) { 46 | return platformConstant.matches(contextPlatform); 47 | } 48 | return true; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/platform/TargetPlatformRule.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.platform; 2 | 3 | import org.junit.AssumptionViolatedException; 4 | import org.junit.rules.TestRule; 5 | import org.junit.runner.Description; 6 | import org.junit.runners.model.Statement; 7 | 8 | import com.nordstrom.automation.selenium.SeleniumConfig; 9 | 10 | /** 11 | * This class implements a JUnit test rule that performs target platform method filtering. 12 | * 13 | * @param

platform specifier 14 | */ 15 | public class TargetPlatformRule

& PlatformEnum> implements TestRule { 16 | 17 | private Object testObject; 18 | private P platform; 19 | 20 | /** 21 | * Constructor for target platform method rule objects. 22 | * 23 | * @param testObject test class instance 24 | */ 25 | public TargetPlatformRule(Object testObject) { 26 | this.testObject = testObject; 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | */ 32 | @Override 33 | public Statement apply(final Statement base, final Description description) { 34 | if (!description.isTest()) return base; 35 | 36 | final String contextPlatform = SeleniumConfig.getConfig().getContextPlatform(); 37 | final TargetPlatform targetPlatform = description.getAnnotation(TargetPlatform.class); 38 | 39 | platform = TargetPlatformHandler.resolveTargetPlatform(testObject, targetPlatform); 40 | 41 | if (TargetPlatformHandler.shouldRun(contextPlatform, platform)) { 42 | return base; 43 | } else { 44 | return new Statement() { 45 | @Override 46 | public void evaluate() throws Throwable { 47 | String message = String.format("%s.%s() doesn't specify platform '%s'", 48 | description.getClassName(), description.getMethodName(), contextPlatform); 49 | throw new AssumptionViolatedException(message); 50 | } 51 | }; 52 | } 53 | } 54 | 55 | /** 56 | * Get platform specifier for this test rule. 57 | * 58 | * @return platform specified 59 | */ 60 | public P getPlatform() { 61 | return platform; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/platform/TargetTypeName.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.platform; 2 | 3 | import com.nordstrom.automation.selenium.plugins.EspressoPlugin; 4 | import com.nordstrom.automation.selenium.plugins.Mac2Plugin; 5 | import com.nordstrom.automation.selenium.plugins.RemoteWebDriverPlugin; 6 | import com.nordstrom.automation.selenium.plugins.UiAutomator2Plugin; 7 | import com.nordstrom.automation.selenium.plugins.WindowsPlugin; 8 | import com.nordstrom.automation.selenium.plugins.XCUITestPlugin; 9 | 10 | /** 11 | * This interface defines the names of the platforms supported by the Selenium Foundation unit tests. 12 | */ 13 | public interface TargetTypeName extends PlatformEnum { 14 | /** 15 | * target: support feature
16 | * driver: (not-applicable) 17 | */ 18 | String SUPPORT_NAME = "support"; 19 | 20 | /** 21 | * target: web application
22 | * driver: {@link RemoteWebDriverPlugin} 23 | */ 24 | String WEB_APP_NAME = "web-app"; 25 | 26 | /** 27 | * target: Android application
28 | * driver: {@link UiAutomator2Plugin}, {@link EspressoPlugin} 29 | */ 30 | String ANDROID_NAME = "android"; 31 | 32 | /** 33 | * target: iOS application
34 | * driver: {@link XCUITestPlugin} 35 | */ 36 | String IOS_APP_NAME = "ios-app"; 37 | 38 | /** 39 | * target: Macintosh application
40 | * driver: {@link Mac2Plugin} 41 | */ 42 | String MAC_APP_NAME = "mac-app"; 43 | 44 | /** 45 | * target: Windows application
46 | * driver: {@link WindowsPlugin} 47 | */ 48 | String WINDOWS_NAME = "windows"; 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/plugins/EspressoPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import com.nordstrom.automation.selenium.SeleniumConfig; 8 | 9 | /** 10 | * This class is the plug-in for the Espresso engine of Appium 11 | */ 12 | public class EspressoPlugin extends AbstractAppiumPlugin { 13 | 14 | /** driver name */ 15 | public static final String DRIVER_NAME = "Espresso"; 16 | 17 | /** 18 | * Constructor for EspressoPlugin objects. 19 | */ 20 | public EspressoPlugin() { 21 | super(DRIVER_NAME); 22 | } 23 | 24 | private static final String CAPABILITIES = 25 | "{\"appium:automationName\":\"Espresso\",\"platformName\":\"Android\"}"; 26 | 27 | private static final String BASELINE = 28 | "{\"appium:automationName\":\"Espresso\",\"platformName\":\"Android\"," + 29 | "\"nord:options\":{\"personality\":\"Espresso\"," + 30 | "\"pluginClass\":\"com.nordstrom.automation.selenium.plugins.EspressoPlugin\"}}"; 31 | 32 | private static final Map PERSONALITIES; 33 | 34 | private static final String DRIVER_CLASS_NAME = "io.appium.java_client.android.AndroidDriver"; 35 | 36 | static { 37 | Map personalities = new HashMap<>(); 38 | personalities.put(DRIVER_NAME, BASELINE); 39 | PERSONALITIES = Collections.unmodifiableMap(personalities); 40 | } 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | @Override 46 | public String getCapabilities(SeleniumConfig config) { 47 | return addNordOptions(config, CAPABILITIES); 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | @Override 54 | public Map getPersonalities() { 55 | return PERSONALITIES; 56 | } 57 | 58 | /** 59 | * {@inheritDoc} 60 | */ 61 | @Override 62 | public String getDriverClassName() { 63 | return DRIVER_CLASS_NAME; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/plugins/Mac2Plugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import com.nordstrom.automation.selenium.SeleniumConfig; 8 | 9 | /** 10 | * This class is the plug-in for the Mac2 engine of Appium 11 | */ 12 | public class Mac2Plugin extends AbstractAppiumPlugin { 13 | 14 | /** driver name */ 15 | public static final String DRIVER_NAME = "Mac2"; 16 | 17 | /** 18 | * Constructor for Mac2Plugin objects. 19 | */ 20 | public Mac2Plugin() { 21 | super(DRIVER_NAME); 22 | } 23 | 24 | private static final String CAPABILITIES = 25 | "{\"appium:automationName\":\"Mac2\",\"platformName\":\"Mac\"}"; 26 | 27 | private static final String BASELINE = 28 | "{\"appium:automationName\":\"Mac2\",\"platformName\":\"Mac\"," + 29 | "\"nord:options\":{\"personality\":\"Mac2\"," + 30 | "\"pluginClass\":\"com.nordstrom.automation.selenium.plugins.Mac2Plugin\"}}"; 31 | 32 | private static final Map PERSONALITIES; 33 | 34 | private static final String DRIVER_CLASS_NAME = "io.appium.java_client.mac.Mac2Driver"; 35 | 36 | static { 37 | Map personalities = new HashMap<>(); 38 | personalities.put(DRIVER_NAME, BASELINE); 39 | PERSONALITIES = Collections.unmodifiableMap(personalities); 40 | } 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | @Override 46 | public String getCapabilities(SeleniumConfig config) { 47 | return addNordOptions(config, CAPABILITIES); 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | @Override 54 | public Map getPersonalities() { 55 | return PERSONALITIES; 56 | } 57 | 58 | /** 59 | * {@inheritDoc} 60 | */ 61 | @Override 62 | public String getDriverClassName() { 63 | return DRIVER_CLASS_NAME; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/plugins/UiAutomator2Plugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import com.nordstrom.automation.selenium.SeleniumConfig; 8 | 9 | /** 10 | * This class is the plug-in for the UiAutomator2 engine of Appium 11 | */ 12 | public class UiAutomator2Plugin extends AbstractAppiumPlugin { 13 | 14 | /** driver name */ 15 | public static final String DRIVER_NAME = "UiAutomator2"; 16 | 17 | /** 18 | * Constructor for UiAutomator2Plugin objects. 19 | */ 20 | public UiAutomator2Plugin() { 21 | super(DRIVER_NAME); 22 | } 23 | 24 | private static final String CAPABILITIES = 25 | "{\"appium:automationName\":\"UiAutomator2\",\"platformName\":\"Android\"}," + 26 | "{\"appium:automationName\":\"UiAutomator2\",\"platformName\":\"Android\",\"browserName\":\"chrome\"}"; 27 | 28 | private static final String BASELINE = 29 | "{\"appium:automationName\":\"UiAutomator2\",\"platformName\":\"Android\"," + 30 | "\"nord:options\":{\"personality\":\"UiAutomator2\"," + 31 | "\"pluginClass\":\"com.nordstrom.automation.selenium.plugins.UiAutomator2Plugin\"}}"; 32 | 33 | private static final String CHROME = 34 | "{\"appium:automationName\":\"UiAutomator2\",\"platformName\":\"Android\",\"browserName\":\"chrome\"," + 35 | "\"nord:options\":{\"personality\":\"UiAutomator2.chrome\"," + 36 | "\"pluginClass\":\"com.nordstrom.automation.selenium.plugins.UiAutomator2Plugin\"}}"; 37 | 38 | private static final Map PERSONALITIES; 39 | 40 | private static final String DRIVER_CLASS_NAME = "io.appium.java_client.android.AndroidDriver"; 41 | 42 | static { 43 | Map personalities = new HashMap<>(); 44 | personalities.put(DRIVER_NAME, BASELINE); 45 | personalities.put(DRIVER_NAME + ".chrome", CHROME); 46 | PERSONALITIES = Collections.unmodifiableMap(personalities); 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public String getCapabilities(SeleniumConfig config) { 54 | return addNordOptions(config, CAPABILITIES); 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public Map getPersonalities() { 62 | return PERSONALITIES; 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | @Override 69 | public String getDriverClassName() { 70 | return DRIVER_CLASS_NAME; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/plugins/WindowsPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import com.nordstrom.automation.selenium.SeleniumConfig; 8 | 9 | /** 10 | * This class is the plug-in for the Windows engine of Appium 11 | */ 12 | public class WindowsPlugin extends AbstractAppiumPlugin { 13 | 14 | /** driver name */ 15 | public static final String DRIVER_NAME = "Windows"; 16 | 17 | /** 18 | * Constructor for WindowsPlugin objects. 19 | */ 20 | public WindowsPlugin() { 21 | super(DRIVER_NAME); 22 | } 23 | 24 | private static final String CAPABILITIES = 25 | "{\"appium:automationName\":\"Windows\",\"platformName\":\"Windows\"}"; 26 | 27 | private static final String BASELINE = 28 | "{\"appium:automationName\":\"Windows\",\"platformName\":\"Windows\"," + 29 | "\"nord:options\":{\"personality\":\"Windows\"," + 30 | "\"pluginClass\":\"com.nordstrom.automation.selenium.plugins.WindowsPlugin\"}}"; 31 | 32 | private static final Map PERSONALITIES; 33 | 34 | private static final String DRIVER_CLASS_NAME = "io.appium.java_client.windows.WindowsDriver"; 35 | 36 | static { 37 | Map personalities = new HashMap<>(); 38 | personalities.put(DRIVER_NAME, BASELINE); 39 | PERSONALITIES = Collections.unmodifiableMap(personalities); 40 | } 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | @Override 46 | public String getCapabilities(SeleniumConfig config) { 47 | return addNordOptions(config, CAPABILITIES); 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | @Override 54 | public Map getPersonalities() { 55 | return PERSONALITIES; 56 | } 57 | 58 | /** 59 | * {@inheritDoc} 60 | */ 61 | @Override 62 | public String getDriverClassName() { 63 | return DRIVER_CLASS_NAME; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/plugins/XCUITestPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import com.nordstrom.automation.selenium.SeleniumConfig; 8 | 9 | /** 10 | * This class is the plug-in for the XCUITest engine of Appium 11 | */ 12 | public class XCUITestPlugin extends AbstractAppiumPlugin { 13 | 14 | /** driver name */ 15 | public static final String DRIVER_NAME = "XCUITest"; 16 | 17 | /** 18 | * Constructor for XCUITestPlugin objects. 19 | */ 20 | public XCUITestPlugin() { 21 | super(DRIVER_NAME); 22 | } 23 | 24 | private static final String CAPABILITIES = 25 | "{\"appium:automationName\":\"XCUITest\",\"platformName\":\"iOS\",\"browserName\":\"Safari\"," + 26 | "\"appium:deviceName\":\"iPhone Simulator\"}"; 27 | 28 | private static final String BASELINE = 29 | "{\"appium:automationName\":\"XCUITest\",\"platformName\":\"iOS\"," + 30 | "\"nord:options\":{\"personality\":\"XCUITest\"," + 31 | "\"pluginClass\":\"com.nordstrom.automation.selenium.plugins.XCUITestPlugin\"}}"; 32 | 33 | private static final Map PERSONALITIES; 34 | 35 | private static final String DRIVER_CLASS_NAME = "io.appium.java_client.ios.IOSDriver"; 36 | 37 | static { 38 | Map personalities = new HashMap<>(); 39 | personalities.put(DRIVER_NAME, BASELINE); 40 | PERSONALITIES = Collections.unmodifiableMap(personalities); 41 | } 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | @Override 47 | public String getCapabilities(SeleniumConfig config) { 48 | return addNordOptions(config, CAPABILITIES); 49 | } 50 | 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | @Override 55 | public Map getPersonalities() { 56 | return PERSONALITIES; 57 | } 58 | 59 | /** 60 | * {@inheritDoc} 61 | */ 62 | @Override 63 | public String getDriverClassName() { 64 | return DRIVER_CLASS_NAME; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/support/Coordinator.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.support; 2 | 3 | import java.util.function.Function; 4 | 5 | import org.openqa.selenium.SearchContext; 6 | import org.openqa.selenium.TimeoutException; 7 | 8 | /** 9 | * Models a condition that might reasonably be expected to eventually evaluate to something that is 10 | * neither null nor false. Examples would include determining if a web page has loaded or that an 11 | * element is visible. 12 | *

13 | * Note that implementations of the Coordinator interface are expected to be idempotent. They will 14 | * be called in a loop by {@link SearchContextWait} and any modification of the state of the application 15 | * under test may have unexpected side-effects. 16 | * 17 | * @param The return type 18 | */ 19 | public abstract class Coordinator implements Function { 20 | 21 | /** 22 | * This method can be overridden by implementations of {@link Coordinator} to provide a context-specific 23 | * timeout exception associated with the implemented condition. 24 | * 25 | * @param e The original {@link TimeoutException} object thrown by the framework "wait" implementation 26 | * @return an instance of a context-specific sub-class of {@link TimeoutException} 27 | */ 28 | public TimeoutException differentiateTimeout(TimeoutException e) { 29 | return e; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/support/RetryAnalyzer.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.support; 2 | 3 | import org.openqa.selenium.WebDriverException; 4 | import org.testng.ITestResult; 5 | 6 | import com.nordstrom.automation.testng.TestNGRetryAnalyzer; 7 | 8 | /** 9 | * This class implements a Selenium-specific TestNG retry analyzer. 10 | */ 11 | public class RetryAnalyzer implements TestNGRetryAnalyzer { 12 | 13 | /** 14 | * {@inheritDoc} 15 | *

16 | * This implementation deems that a test that fails with an instance of {@link WebDriverException} is re-triable. 17 | */ 18 | @Override 19 | public boolean retry(ITestResult result) { 20 | return (result.getThrowable() instanceof WebDriverException); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/nordstrom/automation/selenium/support/TestNgTargetBase.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.support; 2 | 3 | import com.nordstrom.automation.selenium.platform.TargetType; 4 | 5 | /** 6 | * This class is a concrete subclass of {@link TestNgPlatformBase} specifying {@link TargetType} as the platform. 7 | */ 8 | public class TestNgTargetBase extends TestNgPlatformBase { 9 | 10 | /** 11 | * Constructor for TestNG tests classes that support the {@link TargetType} platform. 12 | */ 13 | public TestNgTargetBase() { 14 | super(TargetType.class); 15 | } 16 | 17 | /** 18 | * {@inheritDoc} 19 | */ 20 | @Override 21 | public TargetType getDefaultPlatform() { 22 | return TargetType.SUPPORT; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/checkShadowHost.js: -------------------------------------------------------------------------------- 1 | var shadow = arguments[0].shadowRoot; 2 | if (shadow == null) throw "invalid shadow host"; 3 | -------------------------------------------------------------------------------- /src/main/resources/createScriptNode.js: -------------------------------------------------------------------------------- 1 | var head = document.getElementsByTagName('head')[0]; 2 | var script = document.createElement('script'); 3 | script.textContent = arguments[0]; 4 | head.appendChild(script); 5 | -------------------------------------------------------------------------------- /src/main/resources/documentReady.js: -------------------------------------------------------------------------------- 1 | return ((typeof window.jQuery == 'undefined') || ((window.jQuery.active == 0) && ($(':animated').length == 0)) && (document.readyState == 'complete')); -------------------------------------------------------------------------------- /src/main/resources/frame_a.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frame A Title 5 | 6 | 7 | 8 |

Frame A

9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/frame_b.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frame B Title 5 | 6 | 7 | 8 |

Frame B

9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/frame_c.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frame C Title 5 | 6 | 7 | 8 |

Frame C

9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/frame_d.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frame D Title 5 | 6 | 7 | 8 |

Frame D

9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/hubConfig-s3.json: -------------------------------------------------------------------------------- 1 | { 2 | "newSessionWaitTimeout": -1, 3 | "capabilityMatcher": "com.nordstrom.automation.selenium.core.FoundationSlotMatcher", 4 | "throwOnCapabilityNotPresent": true, 5 | "cleanUpCycle": 5000, 6 | "role": "hub", 7 | "debug": false, 8 | "browserTimeout": 0, 9 | "timeout": 300000 10 | } -------------------------------------------------------------------------------- /src/main/resources/hubConfig-s4.json: -------------------------------------------------------------------------------- 1 | { 2 | "distributor": { 3 | "slot-matcher": "com.nordstrom.automation.selenium.core.FoundationSlotMatcher", 4 | "reject-unsupported-caps": true 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/locateByXpath.js: -------------------------------------------------------------------------------- 1 | var context = (arguments[0].length) ? arguments[0][0] : document; 2 | return document.evaluate(arguments[1], context, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; 3 | -------------------------------------------------------------------------------- /src/main/resources/locateEveryByCss.format: -------------------------------------------------------------------------------- 1 | return %s.querySelectorAll(%s); 2 | -------------------------------------------------------------------------------- /src/main/resources/locateEveryByXpath.format: -------------------------------------------------------------------------------- 1 | return document.evaluate(%2$s, %1$s, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null).singleNodeValue; 2 | -------------------------------------------------------------------------------- /src/main/resources/locateFirstByCss.format: -------------------------------------------------------------------------------- 1 | return %s.querySelector(%s); 2 | -------------------------------------------------------------------------------- /src/main/resources/locateFirstByXpath.format: -------------------------------------------------------------------------------- 1 | return document.evaluate(%2$s, %1$s, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; 2 | -------------------------------------------------------------------------------- /src/main/resources/locateIndexByCss.format: -------------------------------------------------------------------------------- 1 | return %s.querySelectorAll(%s)[%d]; 2 | -------------------------------------------------------------------------------- /src/main/resources/locateIndexByXpath.format: -------------------------------------------------------------------------------- 1 | return document.evaluate(%2$s[%3$d], %1$s, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; 2 | -------------------------------------------------------------------------------- /src/main/resources/nodeConfig-s3.json: -------------------------------------------------------------------------------- 1 | { 2 | "capabilities": [ ], 3 | "proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy", 4 | "maxSession": 5, 5 | "register": true, 6 | "registerCycle": 5000, 7 | "nodeStatusCheckTimeout": 5000, 8 | "nodePolling": 5000, 9 | "role": "node", 10 | "unregisterIfStillDownAfter": 60000, 11 | "downPollingLimit": 2, 12 | "debug": false 13 | } -------------------------------------------------------------------------------- /src/main/resources/nodeConfig-s4.json: -------------------------------------------------------------------------------- 1 | { 2 | "node": { 3 | "detect-drivers": false 4 | } 5 | } -------------------------------------------------------------------------------- /src/main/resources/operaChromiumVersions.json: -------------------------------------------------------------------------------- 1 | { 2 | "109": "123", 3 | "110": "124", 4 | "111": "125", 5 | "112": "126", 6 | "113": "127", 7 | "114": "128", 8 | "115": "130", 9 | "116": "131", 10 | "117": "132", 11 | "118": "133", 12 | "119": "134" 13 | } 14 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/api/GridProxyResponse.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.api; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.stream.Collectors; 7 | 8 | import org.openqa.selenium.Capabilities; 9 | import org.openqa.selenium.MutableCapabilities; 10 | 11 | public class GridProxyResponse { 12 | 13 | @SuppressWarnings("unchecked") 14 | public static List fromJson(Object obj) { 15 | try { 16 | Map input = (Map) obj; 17 | Map request = (Map) input.get("request"); 18 | Map configuration = (Map) request.get("configuration"); 19 | List> capabilities = (List>) configuration.get("capabilities"); 20 | return capabilities.stream().map(MutableCapabilities::new).collect(Collectors.toList()); 21 | } catch (NullPointerException | ClassCastException eaten) { 22 | // nothing to do here 23 | } 24 | 25 | return Collections.emptyList(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/examples/AndroidPage.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.examples; 2 | 3 | import org.openqa.selenium.By; 4 | import org.openqa.selenium.WebDriver; 5 | import org.openqa.selenium.WebElement; 6 | import org.openqa.selenium.support.ui.ExpectedConditions; 7 | import org.openqa.selenium.support.ui.WebDriverWait; 8 | 9 | import com.nordstrom.automation.selenium.annotations.PageUrl; 10 | import com.nordstrom.automation.selenium.model.Page; 11 | 12 | /** 13 | * This class is the model for the "Invoke Search" view of the Android API Demos app. 14 | */ 15 | @PageUrl(appPackage="io.appium.android.apis", value=".app.SearchInvoke") 16 | public class AndroidPage extends Page { 17 | 18 | /** 19 | * Constructor for main view context. 20 | * 21 | * @param driver driver object 22 | */ 23 | public AndroidPage(WebDriver driver) { 24 | super(driver); 25 | } 26 | 27 | /** 28 | * This enumeration defines element locator constants. 29 | */ 30 | protected enum Using implements ByEnum { 31 | /** search query "prefill" field */ 32 | QUERY_PREFILL(By.id("txt_query_prefill")), 33 | /** 'onSearchRequested' button */ 34 | ACTIVATE_SEARCH(By.id("btn_start_search")), 35 | /** search query input field */ 36 | QUERY_INPUT_FIELD(By.id("android:id/search_src_text")); 37 | 38 | private final By locator; 39 | 40 | Using(By locator) { 41 | this.locator = locator; 42 | } 43 | 44 | @Override 45 | public By locator() { 46 | return locator; 47 | } 48 | } 49 | 50 | /** 51 | * Submit the specified search query. 52 | * 53 | * @param query search query 54 | */ 55 | public void submitSearchQuery(String query) { 56 | findElement(Using.QUERY_PREFILL).sendKeys(query); 57 | findElement(Using.ACTIVATE_SEARCH).click(); 58 | } 59 | 60 | /** 61 | * Get result of submitted search query. 62 | * 63 | * @return search result 64 | */ 65 | public String getSearchResult() { 66 | WebDriverWait wait = new WebDriverWait(driver, 30); 67 | WebElement searchResult = wait.until(ExpectedConditions.visibilityOfElementLocated(Using.QUERY_INPUT_FIELD.locator)); 68 | return searchResult.getText(); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/plugins/ChromePlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for ChromeDriver. 9 | */ 10 | public class ChromePlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for ChromePlugin objects. 14 | */ 15 | public ChromePlugin() { 16 | super(ChromeCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.chrome.ChromeDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>org.seleniumhq.selenium</groupId>
24 |      *  <artifactId>selenium-chrome-driver</artifactId>
25 |      *  <version>3.141.59</version>
26 |      *</dependency>
27 | * 28 | * net.bytebuddy.matcher.ElementMatcher 29 | * 30 | *
<dependency>
31 |      *  <groupId>net.bytebuddy</groupId>
32 |      *  <artifactId>byte-buddy</artifactId>
33 |      *  <version>1.17.5</version>
34 |      *</dependency>
35 | */ 36 | private static final String[] DEPENDENCY_CONTEXTS = { 37 | "org.openqa.selenium.chrome.ChromeDriver", 38 | "net.bytebuddy.matcher.ElementMatcher" 39 | }; 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | @Override 45 | public String[] getDependencyContexts() { 46 | return DEPENDENCY_CONTEXTS; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public String getCapabilities(SeleniumConfig config) { 54 | return ChromeCaps.getCapabilities(); 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public Map getPersonalities() { 62 | return ChromeCaps.getPersonalities(); 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | @Override 69 | public String[] getPropertyNames(String capabilities) { 70 | return ChromeCaps.getPropertyNames(capabilities); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/plugins/EdgePlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for EdgeDriver. 9 | */ 10 | public class EdgePlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for EdgePlugin objects. 14 | */ 15 | public EdgePlugin() { 16 | super(EdgeCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.edge.EdgeDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>org.seleniumhq.selenium</groupId>
24 |      *  <artifactId>selenium-edge-driver</artifactId>
25 |      *  <version>3.141.59</version>
26 |      *</dependency>
27 | */ 28 | private static final String[] DEPENDENCY_CONTEXTS = { 29 | "org.openqa.selenium.edge.EdgeDriver" 30 | }; 31 | 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | @Override 36 | public String[] getDependencyContexts() { 37 | return DEPENDENCY_CONTEXTS; 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | @Override 44 | public String getCapabilities(SeleniumConfig config) { 45 | return EdgeCaps.getCapabilities(); 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | @Override 52 | public Map getPersonalities() { 53 | return EdgeCaps.getPersonalities(); 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | @Override 60 | public String[] getPropertyNames(String capabilities) { 61 | return EdgeCaps.getPropertyNames(capabilities); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/plugins/FirefoxPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for FirefoxDriver. 9 | */ 10 | public class FirefoxPlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for FirefoxPlugin objects. 14 | */ 15 | public FirefoxPlugin() { 16 | super(FirefoxCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.firefox.FirefoxDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>org.seleniumhq.selenium</groupId>
24 |      *  <artifactId>selenium-firefox-driver</artifactId>
25 |      *  <version>3.141.59</version>
26 |      *</dependency>
27 | * 28 | * net.bytebuddy.matcher.ElementMatcher 29 | * 30 | *
<dependency>
31 |      *  <groupId>net.bytebuddy</groupId>
32 |      *  <artifactId>byte-buddy</artifactId>
33 |      *  <version>1.17.5</version>
34 |      *</dependency>
35 | */ 36 | private static final String[] DEPENDENCY_CONTEXTS = { 37 | "org.openqa.selenium.firefox.FirefoxDriver", 38 | "net.bytebuddy.matcher.ElementMatcher" 39 | }; 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | @Override 45 | public String[] getDependencyContexts() { 46 | return DEPENDENCY_CONTEXTS; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public String getCapabilities(SeleniumConfig config) { 54 | return FirefoxCaps.getCapabilities(); 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public Map getPersonalities() { 62 | return FirefoxCaps.getPersonalities(); 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | @Override 69 | public String[] getPropertyNames(String capabilities) { 70 | return FirefoxCaps.getPropertyNames(capabilities); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/plugins/InternetExplorerPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for InternetExplorerDriver. 9 | */ 10 | public class InternetExplorerPlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for InternetExplorerPlugin objects. 14 | */ 15 | public InternetExplorerPlugin() { 16 | super(InternetExplorerCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.ie.InternetExplorerDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>org.seleniumhq.selenium</groupId>
24 |      *  <artifactId>selenium-ie-driver</artifactId>
25 |      *  <version>3.141.59</version>
26 |      *</dependency>
27 | * 28 | * net.bytebuddy.matcher.ElementMatcher 29 | * 30 | *
<dependency>
31 |      *  <groupId>net.bytebuddy</groupId>
32 |      *  <artifactId>byte-buddy</artifactId>
33 |      *  <version>1.17.5</version>
34 |      *</dependency>
35 | */ 36 | private static final String[] DEPENDENCY_CONTEXTS = { 37 | "org.openqa.selenium.ie.InternetExplorerDriver", 38 | "net.bytebuddy.matcher.ElementMatcher" 39 | }; 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | @Override 45 | public String[] getDependencyContexts() { 46 | return DEPENDENCY_CONTEXTS; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public String getCapabilities(SeleniumConfig config) { 54 | return InternetExplorerCaps.getCapabilities(); 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public Map getPersonalities() { 62 | return InternetExplorerCaps.getPersonalities(); 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | @Override 69 | public String[] getPropertyNames(String capabilities) { 70 | return InternetExplorerCaps.getPropertyNames(capabilities); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/plugins/OperaPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for OperaDriver. 9 | */ 10 | public class OperaPlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for OperaPlugin objects. 14 | */ 15 | public OperaPlugin() { 16 | super(OperaCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.opera.OperaDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>org.seleniumhq.selenium</groupId>
24 |      *  <artifactId>selenium-opera-driver</artifactId>
25 |      *  <version>3.141.59</version>
26 |      *</dependency>
27 | * 28 | * net.bytebuddy.matcher.ElementMatcher 29 | * 30 | *
<dependency>
31 |      *  <groupId>net.bytebuddy</groupId>
32 |      *  <artifactId>byte-buddy</artifactId>
33 |      *  <version>1.17.5</version>
34 |      *</dependency>
35 | */ 36 | private static final String[] DEPENDENCY_CONTEXTS = { 37 | "org.openqa.selenium.opera.OperaDriver", 38 | "net.bytebuddy.matcher.ElementMatcher" 39 | }; 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | @Override 45 | public String[] getDependencyContexts() { 46 | return DEPENDENCY_CONTEXTS; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public String getCapabilities(SeleniumConfig config) { 54 | return OperaCaps.getCapabilities(); 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public Map getPersonalities() { 62 | return OperaCaps.getPersonalities(); 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | @Override 69 | public String[] getPropertyNames(String capabilities) { 70 | return OperaCaps.getPropertyNames(capabilities); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/plugins/PhantomJsPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for GhostDriver. 9 | */ 10 | public class PhantomJsPlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for GhostPlugin objects. 14 | */ 15 | public PhantomJsPlugin() { 16 | super(PhantomJsCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.phantomjs.PhantomJSDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>com.codeborne</groupId>
24 |      *  <artifactId>phantomjsdriver</artifactId>
25 |      *  <version>1.4.4</version>
26 |      *  <exclusions>
27 |      *    <exclusion>
28 |      *      <groupId>*</groupId>
29 |      *      <artifactId>*</artifactId>
30 |      *    </exclusion>
31 |      *  </exclusions>
32 |      *</dependency>
33 | * 34 | * net.bytebuddy.matcher.ElementMatcher 35 | * 36 | *
<dependency>
37 |      *  <groupId>net.bytebuddy</groupId>
38 |      *  <artifactId>byte-buddy</artifactId>
39 |      *  <version>1.17.5</version>
40 |      *</dependency>
41 | */ 42 | private static final String[] DEPENDENCY_CONTEXTS = { 43 | "org.openqa.selenium.phantomjs.PhantomJSDriver", 44 | "net.bytebuddy.matcher.ElementMatcher" 45 | }; 46 | 47 | /** 48 | * {@inheritDoc} 49 | */ 50 | @Override 51 | public String[] getDependencyContexts() { 52 | return DEPENDENCY_CONTEXTS; 53 | } 54 | 55 | /** 56 | * {@inheritDoc} 57 | */ 58 | @Override 59 | public String getCapabilities(SeleniumConfig config) { 60 | return PhantomJsCaps.getCapabilities(); 61 | } 62 | 63 | /** 64 | * {@inheritDoc} 65 | */ 66 | @Override 67 | public Map getPersonalities() { 68 | return PhantomJsCaps.getPersonalities(); 69 | } 70 | 71 | /** 72 | * {@inheritDoc} 73 | */ 74 | @Override 75 | public String[] getPropertyNames(String capabilities) { 76 | return PhantomJsCaps.getPropertyNames(capabilities); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/plugins/SafariPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for SafariDriver. 9 | */ 10 | public class SafariPlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for SafariPlugin objects. 14 | */ 15 | public SafariPlugin() { 16 | super(SafariCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.safari.SafariDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>org.seleniumhq.selenium</groupId>
24 |      *  <artifactId>selenium-safari-driver</artifactId>
25 |      *  <version>3.141.59</version>
26 |      *</dependency>
27 | */ 28 | private static final String[] DEPENDENCY_CONTEXTS = { 29 | "org.openqa.selenium.safari.SafariDriver" 30 | }; 31 | 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | @Override 36 | public String[] getDependencyContexts() { 37 | return DEPENDENCY_CONTEXTS; 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | @Override 44 | public String getCapabilities(SeleniumConfig config) { 45 | return SafariCaps.getCapabilities(); 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | @Override 52 | public Map getPersonalities() { 53 | return SafariCaps.getPersonalities(); 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | @Override 60 | public String[] getPropertyNames(String capabilities) { 61 | return SafariCaps.getPropertyNames(capabilities); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/utility/BinaryFinder.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.utility; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import org.openqa.selenium.Capabilities; 7 | import org.openqa.selenium.remote.service.DriverService; 8 | 9 | import com.google.common.collect.ImmutableList; 10 | import com.google.common.collect.ImmutableMap; 11 | import com.nordstrom.automation.selenium.SeleniumConfig; 12 | 13 | import io.github.bonigarcia.wdm.WebDriverManager; 14 | 15 | /** 16 | * This class extends {@link DriverService} to provide access to the 17 | * {@link DriverService#findExecutable(String, String, String, String) findExecutable} method. 18 | */ 19 | public class BinaryFinder extends DriverService { 20 | 21 | /** 22 | * Private constructor to prevent instantiation. 23 | */ 24 | private BinaryFinder(File executable, int port, ImmutableList args, 25 | ImmutableMap environment) throws IOException { 26 | super(executable, port, args, environment); 27 | } 28 | 29 | /** 30 | * Find/install driver indicated by the specified capabilities. 31 | * 32 | * @param capabilities For driver binaries, the required capabilities for the specified driver 33 | * @return path to driver supporting specified capabilities as a {@link File} object 34 | */ 35 | public static File findDriver(String capabilities) { 36 | Capabilities caps = SeleniumConfig.getConfig().getCapabilitiesForJson(capabilities)[0]; 37 | WebDriverManager manager = WebDriverManager.getInstance(caps.getBrowserName()).capabilities(caps); 38 | manager.setup(); 39 | return new File(manager.getDownloadedDriverPath()); 40 | } 41 | 42 | /** 43 | * Find the specified executable file. 44 | * 45 | * @param exeName Name of the executable file to look for in PATH 46 | * @param exeProperty Name of a system property that specifies the path to the executable file 47 | * @return The specified executable as a {@link File} object 48 | * @throws IllegalStateException if the executable is not found or cannot be executed 49 | */ 50 | public static File findBinary(String exeName, String exeProperty) { 51 | return DriverService.findExecutable(exeName, exeProperty, null, null); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/utility/DataUtils.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.utility; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | import org.openqa.selenium.json.Json; 6 | import org.openqa.selenium.json.JsonException; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * This static utility class contains methods related to representational transformation. 12 | */ 13 | public final class DataUtils { 14 | 15 | private static final Logger LOGGER = LoggerFactory.getLogger(DataUtils.class); 16 | 17 | /** 18 | * Private constructor to prevent instantiation. 19 | */ 20 | private DataUtils() { 21 | throw new AssertionError("DataUtils is a static utility class that cannot be instantiated"); 22 | } 23 | 24 | /** 25 | * Transform the specified JSON string into the specified type. 26 | *

27 | * NOTE: For this method to work correctly, the specified type must conform to the 28 | * JavaBeans specification. For a simple example, see the 29 | * {@link com.nordstrom.automation.selenium.listeners.PlatformInterceptor.PlatformIdentity PlatformIdentity} 30 | * class. 31 | * 32 | * @param desired object type 33 | * @param json JSON object string 34 | * @param type target object type 35 | * @return new instance of the specified type 36 | */ 37 | public static T fromString(final String json, final Type type) { 38 | if ((json == null) || json.trim().isEmpty()) return null; 39 | try { 40 | return new Json().toType(json, type); 41 | } catch (JsonException e) { 42 | LOGGER.debug("Failed to deserialize JSON object string: {}" + json, e.getMessage()); 43 | return null; 44 | } 45 | } 46 | 47 | /** 48 | * Transform the specified Java object into its JSON string representation. 49 | *

50 | * NOTE: For this method to work correctly, the specified object must conform to the 51 | * JavaBeans specification. For a simple example, see the 52 | * {@link com.nordstrom.automation.selenium.listeners.PlatformInterceptor.PlatformIdentity PlatformIdentity} 53 | * class. 54 | * 55 | * @param object Java object to be transformed 56 | * @return JSON representation of {@code object} 57 | */ 58 | public static String toString(final Object object) { 59 | return new Json().toJson(object); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/selenium3/java/com/nordstrom/automation/selenium/utility/HostUtils.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.utility; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.InvocationTargetException; 5 | 6 | import org.openqa.selenium.net.NetworkInterfaceProvider; 7 | import org.openqa.selenium.net.NetworkUtils; 8 | 9 | /** 10 | * This class serves as a shim to access {@link NetworkUtils#getIp4NonLoopbackAddressOfThisMachine()}. 11 | * To work around a Selenium 3 defect, HostUtils initializes NetworkUtils with a copy of 12 | * the Selenium 4 version of {@link DefaultNetworkInterfaceProviderV4 DefaultNetworkInterfaceProvider}. 13 | */ 14 | public class HostUtils { 15 | 16 | private static final NetworkUtils IDENTITY; 17 | 18 | static { 19 | NetworkUtils identity; 20 | try { 21 | Constructor ctor = NetworkUtils.class.getDeclaredConstructor(NetworkInterfaceProvider.class); 22 | ctor.setAccessible(true); 23 | identity = ctor.newInstance(new DefaultNetworkInterfaceProviderV4()); 24 | } catch (NoSuchMethodException | SecurityException | InstantiationException | 25 | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 26 | identity = new NetworkUtils(); 27 | } 28 | IDENTITY = identity; 29 | } 30 | 31 | /** 32 | * Private constructor to prevent instantiation. 33 | */ 34 | private HostUtils() { 35 | throw new AssertionError("HostUtils is a static utility class that cannot be instantiated"); 36 | } 37 | 38 | /** 39 | * Get Internet protocol (IP) address for the machine we're running on. 40 | * 41 | * @return IP address for the machine we're running on (a.k.a. - 'localhost') 42 | */ 43 | public static String getLocalHost() { 44 | return IDENTITY.getIp4NonLoopbackAddressOfThisMachine().getHostAddress(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/selenium4/java/com/nordstrom/automation/selenium/core/Nodes.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.core; 2 | 3 | import java.lang.reflect.Type; 4 | import java.util.List; 5 | 6 | import org.openqa.selenium.json.JsonInput; 7 | import org.openqa.selenium.json.TypeToken; 8 | 9 | /** 10 | * This class is used to query the status of nodes attached to a Selenium Grid hub. 11 | */ 12 | public class Nodes { 13 | 14 | /** GraphQL query to acquire node status information for a Selenium Grid hub */ 15 | public static final String NODE_STATUS = "{\"query\":\"{nodesInfo{nodes{id uri status stereotypes}}}\"}"; 16 | private static final Type NODE_STATUSES_TYPE = new TypeToken>() {}.getType(); 17 | 18 | /** 19 | * Convert the specified JSON input to a list of node status object. 20 | * 21 | * @param input {@link JsonInput} object 22 | * @return list of {@link NodeStatus} objects 23 | */ 24 | public static List fromJson(JsonInput input) { 25 | input.beginObject(); 26 | if (!"data".equals(input.nextName())) return null; 27 | 28 | input.beginObject(); 29 | if (!"nodesInfo".equals(input.nextName())) return null; 30 | 31 | input.beginObject(); 32 | if (!"nodes".equals(input.nextName())) return null; 33 | return input.read(NODE_STATUSES_TYPE); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/selenium4/java/com/nordstrom/automation/selenium/examples/AndroidPage.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.examples; 2 | 3 | import java.time.Duration; 4 | 5 | import org.openqa.selenium.By; 6 | import org.openqa.selenium.WebDriver; 7 | import org.openqa.selenium.WebElement; 8 | import org.openqa.selenium.support.ui.ExpectedConditions; 9 | import org.openqa.selenium.support.ui.WebDriverWait; 10 | 11 | import com.nordstrom.automation.selenium.annotations.PageUrl; 12 | import com.nordstrom.automation.selenium.model.Page; 13 | 14 | /** 15 | * This class is the model for the "Invoke Search" view of the Android API Demos app. 16 | */ 17 | @PageUrl(appPackage="io.appium.android.apis", value=".app.SearchInvoke") 18 | public class AndroidPage extends Page { 19 | 20 | /** 21 | * Constructor for main view context. 22 | * 23 | * @param driver driver object 24 | */ 25 | public AndroidPage(WebDriver driver) { 26 | super(driver); 27 | } 28 | 29 | /** 30 | * This enumeration defines element locator constants. 31 | */ 32 | protected enum Using implements ByEnum { 33 | /** search query "prefill" field */ 34 | QUERY_PREFILL(By.id("txt_query_prefill")), 35 | /** 'onSearchRequested' button */ 36 | ACTIVATE_SEARCH(By.id("btn_start_search")), 37 | /** search query input field */ 38 | QUERY_INPUT_FIELD(By.id("android:id/search_src_text")); 39 | 40 | private final By locator; 41 | 42 | Using(By locator) { 43 | this.locator = locator; 44 | } 45 | 46 | @Override 47 | public By locator() { 48 | return locator; 49 | } 50 | } 51 | 52 | /** 53 | * Submit the specified search query. 54 | * 55 | * @param query search query 56 | */ 57 | public void submitSearchQuery(String query) { 58 | findElement(Using.QUERY_PREFILL).sendKeys(query); 59 | findElement(Using.ACTIVATE_SEARCH).click(); 60 | } 61 | 62 | /** 63 | * Get result of submitted search query. 64 | * 65 | * @return search result 66 | */ 67 | public String getSearchResult() { 68 | WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30)); 69 | WebElement searchResult = wait.until(ExpectedConditions.visibilityOfElementLocated(Using.QUERY_INPUT_FIELD.locator)); 70 | return searchResult.getText(); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/selenium4/java/com/nordstrom/automation/selenium/plugins/ChromePlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for ChromeDriver. 9 | */ 10 | public class ChromePlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for ChromePlugin objects. 14 | */ 15 | public ChromePlugin() { 16 | super(ChromeCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * Extension constructor for ChromeDriver subclass objects. 21 | * 22 | * @param browserName browser name 23 | */ 24 | protected ChromePlugin(String browserName) { 25 | super(browserName); 26 | } 27 | 28 | /** 29 | * org.openqa.selenium.chrome.ChromeDriver 30 | * 31 | *

<dependency>
32 |      *  <groupId>org.seleniumhq.selenium</groupId>
33 |      *  <artifactId>selenium-chrome-driver</artifactId>
34 |      *  <version>4.30.0</version>
35 |      *</dependency>
36 | * 37 | * org.openqa.selenium.chrome.ChromiumDriver 38 | * 39 | *
<dependency>
40 |      *  <groupId>org.seleniumhq.selenium</groupId>
41 |      *  <artifactId>selenium-chromium-driver</artifactId>
42 |      *  <version>4.30.0</version>
43 |      *</dependency>
44 | * 45 | * net.bytebuddy.matcher.ElementMatcher 46 | * 47 | *
<dependency>
48 |      *  <groupId>net.bytebuddy</groupId>
49 |      *  <artifactId>byte-buddy</artifactId>
50 |      *  <version>1.17.5</version>
51 |      *</dependency>
52 | */ 53 | private static final String[] DEPENDENCY_CONTEXTS = { 54 | "org.openqa.selenium.chrome.ChromeDriver", 55 | "org.openqa.selenium.chromium.ChromiumDriver", 56 | "net.bytebuddy.matcher.ElementMatcher" 57 | }; 58 | 59 | /** 60 | * {@inheritDoc} 61 | */ 62 | @Override 63 | public String[] getDependencyContexts() { 64 | return DEPENDENCY_CONTEXTS; 65 | } 66 | 67 | /** 68 | * {@inheritDoc} 69 | */ 70 | @Override 71 | public String getCapabilities(SeleniumConfig config) { 72 | return ChromeCaps.getCapabilities(); 73 | } 74 | 75 | /** 76 | * {@inheritDoc} 77 | */ 78 | @Override 79 | public Map getPersonalities() { 80 | return ChromeCaps.getPersonalities(); 81 | } 82 | 83 | /** 84 | * {@inheritDoc} 85 | */ 86 | @Override 87 | public String[] getPropertyNames(String capabilities) { 88 | return ChromeCaps.getPropertyNames(capabilities); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/selenium4/java/com/nordstrom/automation/selenium/plugins/EdgePlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for EdgeDriver. 9 | */ 10 | public class EdgePlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for EdgePlugin objects. 14 | */ 15 | public EdgePlugin() { 16 | super(EdgeCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.edge.EdgeDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>org.seleniumhq.selenium</groupId>
24 |      *  <artifactId>selenium-edge-driver</artifactId>
25 |      *  <version>4.30.0</version>
26 |      *</dependency>
27 | */ 28 | private static final String[] DEPENDENCY_CONTEXTS = { 29 | "org.openqa.selenium.edge.EdgeDriver" 30 | }; 31 | 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | @Override 36 | public String[] getDependencyContexts() { 37 | return DEPENDENCY_CONTEXTS; 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | @Override 44 | public String getCapabilities(SeleniumConfig config) { 45 | return EdgeCaps.getCapabilities(); 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | @Override 52 | public Map getPersonalities() { 53 | return EdgeCaps.getPersonalities(); 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | @Override 60 | public String[] getPropertyNames(String capabilities) { 61 | return EdgeCaps.getPropertyNames(capabilities); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/selenium4/java/com/nordstrom/automation/selenium/plugins/FirefoxPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for FirefoxDriver. 9 | */ 10 | public class FirefoxPlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for FirefoxPlugin objects. 14 | */ 15 | public FirefoxPlugin() { 16 | super(FirefoxCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.firefox.FirefoxDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>org.seleniumhq.selenium</groupId>
24 |      *  <artifactId>selenium-firefox-driver</artifactId>
25 |      *  <version>4.30.0</version>
26 |      *</dependency>
27 | * 28 | * net.bytebuddy.matcher.ElementMatcher 29 | * 30 | *
<dependency>
31 |      *  <groupId>net.bytebuddy</groupId>
32 |      *  <artifactId>byte-buddy</artifactId>
33 |      *  <version>1.17.5</version>
34 |      *</dependency>
35 | */ 36 | private static final String[] DEPENDENCY_CONTEXTS = { 37 | "org.openqa.selenium.firefox.FirefoxDriver", 38 | "net.bytebuddy.matcher.ElementMatcher" 39 | }; 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | @Override 45 | public String[] getDependencyContexts() { 46 | return DEPENDENCY_CONTEXTS; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public String getCapabilities(SeleniumConfig config) { 54 | return FirefoxCaps.getCapabilities(); 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public Map getPersonalities() { 62 | return FirefoxCaps.getPersonalities(); 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | @Override 69 | public String[] getPropertyNames(String capabilities) { 70 | return FirefoxCaps.getPropertyNames(capabilities); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/selenium4/java/com/nordstrom/automation/selenium/plugins/InternetExplorerPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for InternetExplorerDriver. 9 | */ 10 | public class InternetExplorerPlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for InternetExplorerPlugin objects. 14 | */ 15 | public InternetExplorerPlugin() { 16 | super(InternetExplorerCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.ie.InternetExplorerDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>org.seleniumhq.selenium</groupId>
24 |      *  <artifactId>selenium-ie-driver</artifactId>
25 |      *  <version>4.30.0</version>
26 |      *</dependency>
27 | * 28 | * net.bytebuddy.matcher.ElementMatcher 29 | * 30 | *
<dependency>
31 |      *  <groupId>net.bytebuddy</groupId>
32 |      *  <artifactId>byte-buddy</artifactId>
33 |      *  <version>1.17.5</version>
34 |      *</dependency>
35 | */ 36 | private static final String[] DEPENDENCY_CONTEXTS = { 37 | "org.openqa.selenium.ie.InternetExplorerDriver", 38 | "net.bytebuddy.matcher.ElementMatcher" 39 | }; 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | @Override 45 | public String[] getDependencyContexts() { 46 | return DEPENDENCY_CONTEXTS; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public String getCapabilities(SeleniumConfig config) { 54 | return InternetExplorerCaps.getCapabilities(); 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public Map getPersonalities() { 62 | return InternetExplorerCaps.getPersonalities(); 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | @Override 69 | public String[] getPropertyNames(String capabilities) { 70 | return InternetExplorerCaps.getPropertyNames(capabilities); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/selenium4/java/com/nordstrom/automation/selenium/plugins/PhantomJsPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for GhostDriver. 9 | */ 10 | public class PhantomJsPlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for GhostPlugin objects. 14 | */ 15 | public PhantomJsPlugin() { 16 | super(PhantomJsCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.phantomjs.PhantomJSDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>com.codeborne</groupId>
24 |      *  <artifactId>phantomjsdriver</artifactId>
25 |      *  <version>1.4.4</version>
26 |      *  <exclusions>
27 |      *    <exclusion>
28 |      *      <groupId>*</groupId>
29 |      *      <artifactId>*</artifactId>
30 |      *    </exclusion>
31 |      *  </exclusions>
32 |      *</dependency>
33 | * 34 | * net.bytebuddy.matcher.ElementMatcher 35 | * 36 | *
<dependency>
37 |      *  <groupId>net.bytebuddy</groupId>
38 |      *  <artifactId>byte-buddy</artifactId>
39 |      *  <version>1.17.5</version>
40 |      *</dependency>
41 | */ 42 | private static final String[] DEPENDENCY_CONTEXTS = { 43 | "org.openqa.selenium.phantomjs.PhantomJSDriver", 44 | "net.bytebuddy.matcher.ElementMatcher" 45 | }; 46 | 47 | /** 48 | * {@inheritDoc} 49 | */ 50 | @Override 51 | public String[] getDependencyContexts() { 52 | return DEPENDENCY_CONTEXTS; 53 | } 54 | 55 | /** 56 | * {@inheritDoc} 57 | */ 58 | @Override 59 | public String getCapabilities(SeleniumConfig config) { 60 | return PhantomJsCaps.getCapabilities(); 61 | } 62 | 63 | /** 64 | * {@inheritDoc} 65 | */ 66 | @Override 67 | public Map getPersonalities() { 68 | return PhantomJsCaps.getPersonalities(); 69 | } 70 | 71 | /** 72 | * {@inheritDoc} 73 | */ 74 | @Override 75 | public String[] getPropertyNames(String capabilities) { 76 | return PhantomJsCaps.getPropertyNames(capabilities); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/selenium4/java/com/nordstrom/automation/selenium/plugins/SafariPlugin.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.plugins; 2 | 3 | import java.util.Map; 4 | 5 | import com.nordstrom.automation.selenium.SeleniumConfig; 6 | 7 | /** 8 | * This class is the plug-in for SafariDriver. 9 | */ 10 | public class SafariPlugin extends RemoteWebDriverPlugin { 11 | 12 | /** 13 | * Constructor for SafariPlugin objects. 14 | */ 15 | public SafariPlugin() { 16 | super(SafariCaps.DRIVER_NAME); 17 | } 18 | 19 | /** 20 | * org.openqa.selenium.safari.SafariDriver 21 | * 22 | *
<dependency>
23 |      *  <groupId>org.seleniumhq.selenium</groupId>
24 |      *  <artifactId>selenium-safari-driver</artifactId>
25 |      *  <version>4.30.0</version>
26 |      *</dependency>
27 | */ 28 | private static final String[] DEPENDENCY_CONTEXTS = { 29 | "org.openqa.selenium.safari.SafariDriver" 30 | }; 31 | 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | @Override 36 | public String[] getDependencyContexts() { 37 | return DEPENDENCY_CONTEXTS; 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | @Override 44 | public String getCapabilities(SeleniumConfig config) { 45 | return SafariCaps.getCapabilities(); 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | @Override 52 | public Map getPersonalities() { 53 | return SafariCaps.getPersonalities(); 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | @Override 60 | public String[] getPropertyNames(String capabilities) { 61 | return SafariCaps.getPropertyNames(capabilities); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/selenium4/java/com/nordstrom/automation/selenium/utility/DataUtils.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.utility; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | import org.openqa.selenium.json.Json; 6 | import org.openqa.selenium.json.JsonException; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * This static utility class contains methods related to representational transformation. 12 | */ 13 | public final class DataUtils { 14 | 15 | private static final Logger LOGGER = LoggerFactory.getLogger(DataUtils.class); 16 | 17 | /** 18 | * Private constructor to prevent instantiation. 19 | */ 20 | private DataUtils() { 21 | throw new AssertionError("DataUtils is a static utility class that cannot be instantiated"); 22 | } 23 | 24 | /** 25 | * Transform the specified JSON string into the specified type. 26 | *

27 | * NOTE: For this method to work correctly, the specified type must conform to the 28 | * JavaBeans specification. For a simple example, see the 29 | * {@link com.nordstrom.automation.selenium.listeners.PlatformInterceptor.PlatformIdentity PlatformIdentity} 30 | * class. 31 | * 32 | * @param desired object type 33 | * @param json JSON object string 34 | * @param type target object type 35 | * @return new instance of the specified type 36 | */ 37 | public static T fromString(final String json, final Type type) { 38 | if ((json == null) || json.trim().isEmpty()) return null; 39 | try { 40 | return new Json().toType(json, type); 41 | } catch (JsonException e) { 42 | LOGGER.debug("Failed to deserialize JSON object string: {}" + json, e.getMessage()); 43 | return null; 44 | } 45 | } 46 | 47 | /** 48 | * Transform the specified Java object into its JSON string representation. 49 | *

50 | * NOTE: For this method to work correctly, the specified object must conform to the 51 | * JavaBeans specification. For a simple example, see the 52 | * {@link com.nordstrom.automation.selenium.listeners.PlatformInterceptor.PlatformIdentity PlatformIdentity} 53 | * class. 54 | * 55 | * @param object Java object to be transformed 56 | * @return JSON representation of {@code object} 57 | */ 58 | public static String toString(final Object object) { 59 | return new Json().toJson(object); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/selenium4/java/com/nordstrom/automation/selenium/utility/HostUtils.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.utility; 2 | 3 | import org.openqa.selenium.net.NetworkUtils; 4 | 5 | /** 6 | * This class serves as a shim to access {@link NetworkUtils#getIp4NonLoopbackAddressOfThisMachine()}. 7 | */ 8 | public class HostUtils { 9 | 10 | private static final NetworkUtils IDENTITY = new NetworkUtils(); 11 | 12 | /** 13 | * Private constructor to prevent instantiation. 14 | */ 15 | private HostUtils() { 16 | throw new AssertionError("HostUtils is a static utility class that cannot be instantiated"); 17 | } 18 | 19 | /** 20 | * Get Internet protocol (IP) address for the machine we're running on. 21 | * 22 | * @return IP address for the machine we're running on (a.k.a. - 'localhost') 23 | */ 24 | public static String getLocalHost() { 25 | return IDENTITY.getIp4NonLoopbackAddressOfThisMachine().getHostAddress(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/android/AndroidTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.android; 2 | 3 | import static com.nordstrom.automation.selenium.platform.TargetType.ANDROID_NAME; 4 | import static org.testng.Assert.assertEquals; 5 | 6 | import org.testng.annotations.Test; 7 | 8 | import com.nordstrom.automation.selenium.annotations.InitialPage; 9 | import com.nordstrom.automation.selenium.examples.AndroidPage; 10 | import com.nordstrom.automation.selenium.platform.TargetPlatform; 11 | import com.nordstrom.automation.selenium.support.TestNgTargetBase; 12 | 13 | @InitialPage(AndroidPage.class) 14 | public class AndroidTest extends TestNgTargetBase { 15 | 16 | @Test 17 | @TargetPlatform(ANDROID_NAME) 18 | public void testSearchActivity() { 19 | AndroidPage page = getInitialPage(); 20 | page.submitSearchQuery("Hello world!"); 21 | assertEquals(page.getSearchResult(), "Hello world!"); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/core/ByTypeTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.core; 2 | 3 | import static org.testng.Assert.assertEquals; 4 | import static org.testng.Assert.assertNull; 5 | 6 | import org.openqa.selenium.By; 7 | import org.testng.annotations.Test; 8 | 9 | import com.nordstrom.automation.selenium.model.ComponentContainer.ByEnum; 10 | 11 | public class ByTypeTest { 12 | 13 | private enum Using implements ByEnum { 14 | TAG_NAME(By.tagName("div")), 15 | ID(By.id("element-id")), 16 | NAME(By.name("name")), 17 | CLASS_NAME(By.className("class-name")), 18 | LINK_TEXT(By.linkText("link text")), 19 | PARTIAL_LINK(By.partialLinkText("nk te")), 20 | CSS_SELECTOR(By.cssSelector("iframe[id^='frame-']")), 21 | XPATH(By.xpath(".//iframe[contains(@id,'frame-')]")); 22 | 23 | private final By locator; 24 | 25 | Using(By locator) { 26 | this.locator = locator; 27 | } 28 | 29 | @Override 30 | public By locator() { 31 | return locator; 32 | } 33 | } 34 | 35 | @Test 36 | public void testCssLocatorFor() { 37 | assertEquals(ByType.cssLocatorFor(Using.TAG_NAME), "div"); 38 | assertEquals(ByType.cssLocatorFor(Using.ID), "#element-id"); 39 | assertEquals(ByType.cssLocatorFor(Using.NAME), "[name=name]"); 40 | assertEquals(ByType.cssLocatorFor(Using.CLASS_NAME), ".class-name"); 41 | assertNull(ByType.cssLocatorFor(Using.LINK_TEXT)); 42 | assertNull(ByType.cssLocatorFor(Using.PARTIAL_LINK)); 43 | assertEquals(ByType.cssLocatorFor(Using.CSS_SELECTOR), "iframe[id^='frame-']"); 44 | assertNull(ByType.cssLocatorFor(Using.XPATH)); 45 | } 46 | 47 | @Test 48 | public void testXpathLocatorFor() { 49 | assertEquals(ByType.xpathLocatorFor(Using.TAG_NAME), ".//div"); 50 | assertEquals(ByType.xpathLocatorFor(Using.ID), ".//*[@id='element-id']"); 51 | assertEquals(ByType.xpathLocatorFor(Using.NAME), ".//*[@name='name']"); 52 | assertEquals(ByType.xpathLocatorFor(Using.CLASS_NAME), ".//*[contains(concat(' ',@class,' '),' class-name ')]"); 53 | assertEquals(ByType.xpathLocatorFor(Using.LINK_TEXT), ".//a[.='link text']"); 54 | assertEquals(ByType.xpathLocatorFor(Using.PARTIAL_LINK), ".//a[text()[contains(.,'nk te')]]"); 55 | assertNull(ByType.xpathLocatorFor(Using.CSS_SELECTOR)); 56 | assertEquals(ByType.xpathLocatorFor(Using.XPATH), ".//iframe[contains(@id,'frame-')]"); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/ios/IOSTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.ios; 2 | 3 | import static com.nordstrom.automation.selenium.platform.TargetType.IOS_APP_NAME; 4 | import static org.testng.Assert.assertEquals; 5 | 6 | import org.testng.annotations.Test; 7 | 8 | import com.nordstrom.automation.selenium.annotations.InitialPage; 9 | import com.nordstrom.automation.selenium.examples.IOSPage; 10 | import com.nordstrom.automation.selenium.platform.TargetPlatform; 11 | import com.nordstrom.automation.selenium.support.TestNgTargetBase; 12 | 13 | @InitialPage(IOSPage.class) 14 | public class IOSTest extends TestNgTargetBase { 15 | 16 | @Test 17 | @TargetPlatform(IOS_APP_NAME) 18 | public void testEditing() { 19 | IOSPage page = getInitialPage(); 20 | page.modifyField("Hello world!"); 21 | assertEquals(page.accessField(), "Hello world!"); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/junit/JUnitPlatformTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.junit; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | import static org.junit.Assert.assertEquals; 5 | 6 | import org.junit.Test; 7 | import org.junit.runners.model.FrameworkMethod; 8 | import org.openqa.selenium.WebDriver; 9 | 10 | import com.nordstrom.automation.junit.AtomicTest; 11 | import com.nordstrom.automation.junit.LifecycleHooks; 12 | import com.nordstrom.automation.selenium.annotations.NoDriver; 13 | import com.nordstrom.automation.selenium.exceptions.PlatformActivationFailedException; 14 | import com.nordstrom.automation.selenium.platform.Transition; 15 | import com.nordstrom.automation.selenium.platform.TargetPlatform; 16 | 17 | public class JUnitPlatformTest extends JUnitPlatformBase { 18 | 19 | public JUnitPlatformTest() { 20 | super(Transition.class); 21 | } 22 | 23 | @Test 24 | @NoDriver 25 | public void testDefaultPlatform() { 26 | assertTrue(getTargetPlatform().matches(Transition.PHASE1_NAME)); 27 | assertEquals("green", getTargetPlatform().getColor()); 28 | } 29 | 30 | @Test 31 | @NoDriver 32 | @TargetPlatform(Transition.PHASE2_NAME) 33 | public void testPlatformTwo() { 34 | assertTrue(getTargetPlatform().matches(Transition.PHASE2_NAME)); 35 | assertEquals("amber", getTargetPlatform().getColor()); 36 | } 37 | 38 | @Override 39 | public void activatePlatform(WebDriver driver, Transition platform) 40 | throws PlatformActivationFailedException { 41 | 42 | AtomicTest test = LifecycleHooks.getAtomicTestOf(this); 43 | FrameworkMethod method = test.getIdentity(); 44 | 45 | Transition expected = null; 46 | switch(method.getName()) { 47 | case "testDefaultPlatform": 48 | expected = Transition.PHASE1; 49 | break; 50 | case "testPlatformTwo": 51 | expected = Transition.PHASE2; 52 | break; 53 | default: 54 | throw new RuntimeException("Unexpected method: " + method.getName()); 55 | } 56 | 57 | if (platform != expected) { 58 | throw new PlatformActivationFailedException(platform, "expected: " + expected.getName()); 59 | } 60 | 61 | // perform some platform-related activation 62 | System.setProperty("platform.phase.color", platform.getColor()); 63 | } 64 | 65 | @Override 66 | public Transition getDefaultPlatform() { 67 | return Transition.PHASE1; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/junit/PageSourceCaptureTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.junit; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | import static org.junit.Assume.assumeTrue; 5 | import java.nio.file.Path; 6 | import java.util.Optional; 7 | 8 | import org.junit.Test; 9 | 10 | import com.nordstrom.automation.selenium.annotations.InitialPage; 11 | import com.nordstrom.automation.selenium.examples.ExamplePage; 12 | import com.nordstrom.automation.selenium.examples.JUnitTargetRoot; 13 | import com.nordstrom.common.file.PathUtils; 14 | 15 | @InitialPage(ExamplePage.class) 16 | public class PageSourceCaptureTest extends JUnitTargetRoot { 17 | 18 | @Test 19 | public void testPageSourceCapture() { 20 | PageSourceCapture collector = pageSourceCapture; 21 | assumeTrue(collector.getArtifactProvider().canGetArtifact(this)); 22 | Optional optArtifactPath = collector.captureArtifact(null); 23 | assertTrue(optArtifactPath.isPresent()); 24 | } 25 | 26 | @Override 27 | public String getOutputDirectory() { 28 | return PathUtils.ReportsDirectory.ARTIFACT.getPath().toString(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/junit/ScreenshotCaptureTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.junit; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | import static org.junit.Assume.assumeTrue; 5 | import java.nio.file.Path; 6 | import java.util.Optional; 7 | 8 | import org.junit.Test; 9 | import com.nordstrom.automation.selenium.annotations.InitialPage; 10 | import com.nordstrom.automation.selenium.examples.ExamplePage; 11 | import com.nordstrom.automation.selenium.examples.JUnitTargetRoot; 12 | import com.nordstrom.common.file.PathUtils; 13 | 14 | @InitialPage(ExamplePage.class) 15 | public class ScreenshotCaptureTest extends JUnitTargetRoot { 16 | 17 | @Test 18 | public void testScreenshotCapture() { 19 | ScreenshotCapture collector = screenshotCapture; 20 | assumeTrue(collector.getArtifactProvider().canGetArtifact(this)); 21 | Optional optArtifactPath = collector.captureArtifact(null); 22 | assertTrue(optArtifactPath.isPresent()); 23 | } 24 | 25 | @Override 26 | public String getOutputDirectory() { 27 | return PathUtils.ReportsDirectory.ARTIFACT.getPath().toString(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/listeners/DriverManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.listeners; 2 | 3 | import static org.testng.Assert.assertFalse; 4 | import static org.testng.Assert.assertTrue; 5 | import org.testng.annotations.BeforeMethod; 6 | import org.testng.annotations.Test; 7 | 8 | import com.nordstrom.automation.selenium.annotations.InitialPage; 9 | import com.nordstrom.automation.selenium.annotations.NoDriver; 10 | import com.nordstrom.automation.selenium.annotations.PageUrl; 11 | import com.nordstrom.automation.selenium.examples.TestNgTargetRoot; 12 | 13 | public class DriverManagerTest extends TestNgTargetRoot { 14 | 15 | @BeforeMethod(groups = {"WithDriverBefore"}) 16 | @InitialPage(pageUrl=@PageUrl("/grid/admin/ExamplePageServlet")) 17 | public void beforeMethodWithDriver() { 18 | assertTrue(nabDriver().isPresent(), "Driver should have been created"); 19 | } 20 | 21 | @Test(groups = {"WithDriverBefore"}) 22 | public void testWithDriverBefore() { 23 | assertTrue(nabDriver().isPresent(), "Driver should have been created"); 24 | } 25 | 26 | @NoDriver 27 | @Test(groups = {"WithDriverBefore"}) 28 | public void testCloseDriverBefore() { 29 | assertFalse(nabDriver().isPresent(), "Driver should have been closed"); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/listeners/NoDriverManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.listeners; 2 | 3 | import static org.testng.Assert.assertTrue; 4 | import static org.testng.Assert.assertFalse; 5 | import static com.nordstrom.automation.selenium.platform.TargetType.WEB_APP_NAME; 6 | 7 | import org.testng.annotations.BeforeMethod; 8 | import org.testng.annotations.Test; 9 | 10 | import com.nordstrom.automation.selenium.annotations.NoDriver; 11 | import com.nordstrom.automation.selenium.exceptions.DriverNotAvailableException; 12 | import com.nordstrom.automation.selenium.platform.TargetPlatform; 13 | import com.nordstrom.automation.selenium.support.TestNgTargetBase; 14 | 15 | public class NoDriverManagerTest extends TestNgTargetBase { 16 | 17 | @BeforeMethod(groups = {"NoDriverBefore"}) 18 | public void beforeMethodNoDriver() { 19 | assertFalse(nabDriver().isPresent(), "Driver should not have been created"); 20 | } 21 | 22 | @NoDriver 23 | @Test(groups = {"NoBeforeNoDriver"}) 24 | @TargetPlatform(WEB_APP_NAME) 25 | public void testNoBeforeNoDriver() { 26 | assertFalse(nabDriver().isPresent(), "Driver should not have been created"); 27 | } 28 | 29 | @Test(groups = {"NoDriverBefore"}) 30 | @TargetPlatform(WEB_APP_NAME) 31 | public void testNoDriverBefore() { 32 | assertTrue(nabDriver().isPresent(), "Driver should have been created"); 33 | } 34 | 35 | @NoDriver 36 | @Test(groups = {"NoBeforeNoDriver"}, expectedExceptions = {DriverNotAvailableException.class}) 37 | @TargetPlatform(WEB_APP_NAME) 38 | public void testNoDriverException() { 39 | getDriver(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/listeners/PageSourceCaptureTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.listeners; 2 | 3 | import static org.testng.Assert.assertTrue; 4 | import java.nio.file.Path; 5 | import java.util.Optional; 6 | 7 | import org.testng.Reporter; 8 | import org.testng.SkipException; 9 | import org.testng.annotations.Test; 10 | 11 | import com.nordstrom.automation.selenium.annotations.InitialPage; 12 | import com.nordstrom.automation.selenium.examples.ExamplePage; 13 | import com.nordstrom.automation.selenium.examples.TestNgTargetRoot; 14 | 15 | @InitialPage(ExamplePage.class) 16 | public class PageSourceCaptureTest extends TestNgTargetRoot { 17 | 18 | @Test 19 | public void testPageSourceCapture() { 20 | PageSourceCapture collector = getLinkedListener(PageSourceCapture.class); 21 | if (collector.getArtifactProvider().canGetArtifact(Reporter.getCurrentTestResult())) { 22 | Optional optArtifactPath = collector.captureArtifact(Reporter.getCurrentTestResult()); 23 | assertTrue(optArtifactPath.isPresent()); 24 | } else { 25 | throw new SkipException("This driver is not able to capture page source."); 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/listeners/ScreenshotCaptureTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.listeners; 2 | 3 | import static org.testng.Assert.assertTrue; 4 | import java.nio.file.Path; 5 | import java.util.Optional; 6 | 7 | import org.testng.Reporter; 8 | import org.testng.SkipException; 9 | import org.testng.annotations.Test; 10 | 11 | import com.nordstrom.automation.selenium.annotations.InitialPage; 12 | import com.nordstrom.automation.selenium.examples.ExamplePage; 13 | import com.nordstrom.automation.selenium.examples.TestNgTargetRoot; 14 | 15 | @InitialPage(ExamplePage.class) 16 | public class ScreenshotCaptureTest extends TestNgTargetRoot { 17 | 18 | @Test 19 | public void testScreenshotCapture() { 20 | ScreenshotCapture collector = getLinkedListener(ScreenshotCapture.class); 21 | if (collector.getArtifactProvider().canGetArtifact(Reporter.getCurrentTestResult())) { 22 | Optional optArtifactPath = collector.captureArtifact(Reporter.getCurrentTestResult()); 23 | assertTrue(optArtifactPath.isPresent()); 24 | } else { 25 | throw new SkipException("This driver is not able to take screenshots."); 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/mac/MacTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.mac; 2 | 3 | import static com.nordstrom.automation.selenium.platform.TargetType.MAC_APP_NAME; 4 | import static org.testng.Assert.assertEquals; 5 | 6 | import org.testng.annotations.Test; 7 | 8 | import com.nordstrom.automation.selenium.annotations.InitialPage; 9 | import com.nordstrom.automation.selenium.examples.MacPage; 10 | import com.nordstrom.automation.selenium.platform.TargetPlatform; 11 | import com.nordstrom.automation.selenium.support.TestNgTargetBase; 12 | 13 | @InitialPage(MacPage.class) 14 | public class MacTest extends TestNgTargetBase { 15 | 16 | @Test 17 | @TargetPlatform(MAC_APP_NAME) 18 | public void testEditing() { 19 | MacPage page = getInitialPage(); 20 | page.modifyDocument("Hello world!"); 21 | assertEquals(page.accessDocument(), "Hello world!"); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/platform/PhaseName.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.platform; 2 | 3 | interface PhaseName extends PlatformEnum { 4 | String PHASE1_NAME = "phase-1"; 5 | String PHASE2_NAME = "phase-2"; 6 | String PHASE3_NAME = "phase-3"; 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/platform/Transition.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.platform; 2 | 3 | public enum Transition implements PhaseName { 4 | PHASE1("green", PHASE1_NAME), 5 | PHASE2("amber", PHASE2_NAME), 6 | PHASE3("coral", PHASE3_NAME); 7 | 8 | private String color; 9 | private String name; 10 | 11 | Transition(String color, String name) { 12 | this.color = color; 13 | this.name = name; 14 | } 15 | 16 | public String getColor() { 17 | return color; 18 | } 19 | 20 | @Override 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | @Override 26 | public boolean matches(String contextPlatform) { 27 | return name.equals(contextPlatform); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/support/TestNgPlatformTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.support; 2 | 3 | import static org.testng.Assert.assertEquals; 4 | import static org.testng.Assert.assertTrue; 5 | 6 | import org.openqa.selenium.WebDriver; 7 | import org.testng.ITestNGMethod; 8 | import org.testng.ITestResult; 9 | import org.testng.Reporter; 10 | import org.testng.annotations.Test; 11 | 12 | import com.nordstrom.automation.selenium.annotations.NoDriver; 13 | import com.nordstrom.automation.selenium.exceptions.PlatformActivationFailedException; 14 | import com.nordstrom.automation.selenium.platform.Transition; 15 | import com.nordstrom.automation.selenium.platform.TargetPlatform; 16 | 17 | public class TestNgPlatformTest extends TestNgPlatformBase { 18 | 19 | public TestNgPlatformTest() { 20 | super(Transition.class); 21 | } 22 | 23 | @Test 24 | @NoDriver 25 | public void testDefaultPlatform() { 26 | assertTrue(getTargetPlatform().matches(Transition.PHASE1_NAME)); 27 | assertEquals(getTargetPlatform().getColor(), "green"); 28 | } 29 | 30 | @Test 31 | @NoDriver 32 | @TargetPlatform(Transition.PHASE2_NAME) 33 | public void testPlatformTwo() { 34 | assertTrue(getTargetPlatform().matches(Transition.PHASE2_NAME)); 35 | assertEquals(getTargetPlatform().getColor(), "amber"); 36 | } 37 | 38 | @Override 39 | public void activatePlatform(WebDriver driver, Transition platform) 40 | throws PlatformActivationFailedException { 41 | 42 | ITestResult result = Reporter.getCurrentTestResult(); 43 | ITestNGMethod method = result.getMethod(); 44 | 45 | Transition expected = null; 46 | switch(method.getMethodName()) { 47 | case "testDefaultPlatform": 48 | expected = Transition.PHASE1; 49 | break; 50 | case "testPlatformTwo": 51 | expected = Transition.PHASE2; 52 | break; 53 | default: 54 | throw new RuntimeException("Unexpected method: " + method.getMethodName()); 55 | } 56 | 57 | if (platform != expected) { 58 | throw new PlatformActivationFailedException(platform, "expected: " + expected.getName()); 59 | } 60 | 61 | // perform some platform-related activation 62 | System.setProperty("platform.phase.color", platform.getColor()); 63 | } 64 | 65 | @Override 66 | public Transition getDefaultPlatform() { 67 | return Transition.PHASE1; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/com/nordstrom/automation/selenium/windows/WindowsTest.java: -------------------------------------------------------------------------------- 1 | package com.nordstrom.automation.selenium.windows; 2 | 3 | import static com.nordstrom.automation.selenium.platform.TargetType.WINDOWS_NAME; 4 | import static org.testng.Assert.assertEquals; 5 | 6 | import org.openqa.selenium.Keys; 7 | import org.testng.annotations.Test; 8 | 9 | import com.nordstrom.automation.selenium.examples.WindowsPage; 10 | import com.nordstrom.automation.selenium.annotations.InitialPage; 11 | import com.nordstrom.automation.selenium.platform.TargetPlatform; 12 | import com.nordstrom.automation.selenium.support.TestNgTargetBase; 13 | 14 | @InitialPage(WindowsPage.class) 15 | public class WindowsTest extends TestNgTargetBase { 16 | 17 | @Test 18 | @TargetPlatform(WINDOWS_NAME) 19 | public void testEditing() { 20 | WindowsPage page = getInitialPage(); 21 | page.modifyDocument("Hello world!"); 22 | assertEquals(page.getDocument(), "Hello world!"); 23 | page.modifyDocument(Keys.CONTROL + "z"); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/services/com.nordstrom.automation.junit.JUnitRetryAnalyzer: -------------------------------------------------------------------------------- 1 | com.nordstrom.automation.selenium.junit.RetryAnalyzer 2 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/services/com.nordstrom.automation.junit.JUnitWatcher: -------------------------------------------------------------------------------- 1 | com.nordstrom.automation.selenium.junit.DriverListener 2 | com.nordstrom.automation.selenium.junit.DriverWatcher 3 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/services/com.nordstrom.automation.testng.TestNGRetryAnalyzer: -------------------------------------------------------------------------------- 1 | com.nordstrom.automation.selenium.support.RetryAnalyzer 2 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/services/org.testng.ITestNGListener: -------------------------------------------------------------------------------- 1 | com.nordstrom.automation.testng.ListenerChain 2 | -------------------------------------------------------------------------------- /src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/test/resources/requireMetaTagByName.js: -------------------------------------------------------------------------------- 1 | var found = document.getElementsByTagName("meta"); 2 | for (var i = 0; i < found.length; i++) { 3 | if (found[i].getAttribute("name") == arguments[0]) return found[i]; 4 | } 5 | throwNew('org.openqa.selenium.NoSuchElementException', 'No meta element found with name: ' + arguments[0]); 6 | -------------------------------------------------------------------------------- /src/test/resources/toggleOptionalNode.js: -------------------------------------------------------------------------------- 1 | var body = document.getElementsByTagName('body')[0]; 2 | var formDiv = body.querySelector('div#form-div'); 3 | var optional = formDiv.getElementsByTagName('optional') 4 | if (!optional.length) { 5 | var newOptional = document.createElement('optional'); 6 | newOptional.textContent = "I'm optional"; 7 | formDiv.appendChild(newOptional); 8 | return true; 9 | } else { 10 | formDiv.removeChild(optional[0]); 11 | return false; 12 | } 13 | -------------------------------------------------------------------------------- /uiautomator2Deps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.UiAutomator2Plugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.caps', '{"platformName":"Android","appium:automationName":"UiAutomator2","appium:app":"https://github.com/appium/appium/raw/master/packages/appium/sample-code/apps/ApiDemos-debug.apk"}') 4 | System.setProperty('selenium.context.platform', 'android') 5 | System.setProperty('selenium.grid.examples', 'false') 6 | System.setProperty('appium.with.pm2', 'true') 7 | dependencies { 8 | testImplementation('io.appium:java-client') { 9 | exclude group: 'org.seleniumhq.selenium', module: 'selenium-java' 10 | exclude group: 'org.seleniumhq.selenium', module: 'selenium-support' 11 | exclude group: 'org.slf4j', module: 'slf4j-api' 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /windowsDeps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.WindowsPlugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.caps', '{"platformName":"Windows","appium:automationName":"Windows","appium:app":"C:/Windows/system32/notepad.exe"}') 4 | System.setProperty('selenium.context.platform', 'windows') 5 | System.setProperty('selenium.grid.examples', 'false') 6 | System.setProperty('appium.with.pm2', 'true') 7 | dependencies { 8 | testImplementation('io.appium:java-client') { 9 | exclude group: 'org.seleniumhq.selenium', module: 'selenium-java' 10 | exclude group: 'org.seleniumhq.selenium', module: 'selenium-support' 11 | exclude group: 'org.slf4j', module: 'slf4j-api' 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /xcuitestDeps.gradle: -------------------------------------------------------------------------------- 1 | def driverPlugins = System.getProperty('selenium.grid.plugins', '') 2 | System.setProperty('selenium.grid.plugins', driverPlugins + 'com.nordstrom.automation.selenium.plugins.XCUITestPlugin' + File.pathSeparator) 3 | System.setProperty('selenium.browser.caps', '{"platformName":"iOS","appium:automationName":"XCUITest","appium:app":"https://github.com/appium/appium/raw/master/packages/appium/sample-code/apps/TestApp.app.zip"}') 4 | System.setProperty('selenium.context.platform', 'ios-app') 5 | System.setProperty('selenium.grid.examples', 'false') 6 | System.setProperty('appium.with.pm2', 'true') 7 | dependencies { 8 | testImplementation('io.appium:java-client') { 9 | exclude group: 'org.seleniumhq.selenium', module: 'selenium-java' 10 | exclude group: 'org.seleniumhq.selenium', module: 'selenium-support' 11 | exclude group: 'org.slf4j', module: 'slf4j-api' 12 | } 13 | } 14 | --------------------------------------------------------------------------------