├── README.md
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
├── main
└── java
│ └── com
│ └── testninja
│ └── pageobjects
│ ├── examples
│ ├── amazon
│ │ ├── components
│ │ │ ├── header
│ │ │ │ ├── NavBar.java
│ │ │ │ └── SearchContainer.java
│ │ │ └── searchfilters
│ │ │ │ ├── BaseProductFilter.java
│ │ │ │ ├── CheckBoxFilter.java
│ │ │ │ ├── MultiChoiceButtonFilter.java
│ │ │ │ └── PriceRangeFilter.java
│ │ └── pages
│ │ │ ├── AmazonBasePage.java
│ │ │ ├── HomePage.java
│ │ │ └── SearchResultsPage.java
│ └── spotify
│ │ ├── components
│ │ ├── Header.java
│ │ ├── LeftNavBar.java
│ │ ├── Player.java
│ │ └── playlist
│ │ │ ├── PlayButton.java
│ │ │ ├── SaveButton.java
│ │ │ └── SongRow.java
│ │ └── pages
│ │ ├── LoginPage.java
│ │ ├── PlaylistPage.java
│ │ ├── SpotifyBasePage.java
│ │ └── SpotifyHomePage.java
│ ├── utils
│ ├── Interactions.java
│ └── WaitHandler.java
│ └── wrappers
│ ├── BasePage.java
│ ├── BaseWebComponent.java
│ ├── BaseWebComponentFieldDecorator.java
│ ├── PageObjectFactory.java
│ ├── ScriptHelper.java
│ └── annotations
│ ├── Page.java
│ └── PageObject.java
└── test
└── java
└── com
└── testning
└── pageobjects
├── examples
├── amazon
│ └── AmazonSearchFiltersTest.java
└── spotify
│ └── SpotifyPlaylistTest.java
└── wrappers
└── BaseTest.java
/README.md:
--------------------------------------------------------------------------------
1 | # What is Page Object?
2 | Page Object is a Design Pattern which has become popular in test automation for enhancing test maintenance and reducing code duplication. A page object is an object-oriented class that serves as an interface to a page of your AUT. The tests then use the methods of this page object class whenever they need to interact with the UI of that page. The benefit is that if the UI changes for the page, the tests themselves don’t need to change, only the code within the page object needs to change. Subsequently all changes to support that new UI are located in one place.
3 |
4 | # Advantages:
5 |
6 | - There is a clean separation between test code and page specific code such as locators (or their use if you’re using a UI Map) and layout.
7 | - There is a single repository for the services or operations offered by the page rather than having these services scattered throughout the tests.
8 |
9 | # Example:
10 | > A page object does not necessarily need to represent all the parts of a page itself. The same principles used for page objects can be used to create “Page Component Objects” that represent discrete chunks of the page and can be included in page objects. These component objects can provide references the elements inside those discrete chunks, and methods to leverage the functionality provided by them. You can even nest component objects inside other component objects for more complex pages. If a page in the AUT has multiple components, or common components used throughout the site (e.g. a navigation bar), then it may improve maintainability and reduce code duplication.
11 | -- Quote from selenium offical site
12 |
13 | Let's use https://www.amazon.in and see how UI elements can be represented as reusable webcomponents using page objects.
14 |
15 |
16 |
17 | Below snippet is the representation of filter component in selenium pageobject:
18 | ```java
19 | public class CheckBoxFilter extends BaseProductFilter {
20 |
21 | public CheckBoxFilter(ScriptHelper scriptHelper, String filterHeader) {
22 | super(scriptHelper, filterHeader);
23 | }
24 |
25 | public void apply(List optionsToSelect) {
26 | optionsToSelect
27 | .stream()
28 | .forEach(option -> {
29 | waitForFilterToAppear();
30 | clickSeeMore();
31 | selectOption(getOptionCheckbox(option));
32 | });
33 | }
34 |
35 | public List getSelectedOptions() {
36 | waitForFilterToAppear();
37 | clickSeeMore();
38 |
39 | return getChildElement()
40 | .findElements(By.xpath(".//li/descendant::a[.//input[@type='checkbox'][@checked]]/descendant::span"))
41 | .stream()
42 | .map(interactions::getText)
43 | .filter(text -> !text.trim().equals(""))
44 | .collect(Collectors.toList());
45 |
46 | }
47 |
48 | private void selectOption(WebElement element) {
49 | if (!interactions.isSelected(element)) {
50 | interactions.javaScriptClick(element);
51 | }
52 | }
53 |
54 | private void deselectOption(WebElement element) {
55 | if (interactions.isSelected(element)) {
56 | interactions.click(element);
57 | }
58 | }
59 |
60 | private WebElement getOptionCheckbox(String label) {
61 | return getChildElement().findElement(By.xpath(".//li/descendant::a[.//span[text()='" + label + "']]/descendant::input[@type='checkbox']"));
62 | }
63 | }
64 | ```
65 |
66 | Now the component can be used in the tests like:
67 |
68 | ```java
69 | /* Test Material filter */
70 | List material = Arrays.asList("Canvas", "Rubber");
71 | CheckBoxFilter materialFilter = new CheckBoxFilter(scriptHelper, "Material");
72 | materialFilter.apply(material);
73 |
74 | assertEquals(materialFilter.getSelectedOptions(), material);
75 | assertTrue(materialFilter.isClearLinkPresent());
76 | assertFalse(materialFilter.isSeeMoreLinkPresent());
77 |
78 | /* Test Brand filter */
79 |
80 | List brands = Arrays.asList("Campus");
81 | CheckBoxFilter brandFilter = new CheckBoxFilter(scriptHelper, "Brand");
82 | brandFilter.apply(brands);
83 |
84 | assertEquals(brandFilter.getSelectedOptions(), brands);
85 | assertTrue(brandFilter.isClearLinkPresent());
86 | assertTrue(brandFilter.isSeeMoreLinkPresent());
87 | ```
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | }
4 |
5 | group 'org.example'
6 | version '1.0-SNAPSHOT'
7 |
8 | repositories {
9 | mavenCentral()
10 | }
11 |
12 | test {
13 | useTestNG()
14 | }
15 |
16 | dependencies {
17 | compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.141.59'
18 | compile group: 'com.github.webdriverextensions', name: 'webdriverextensions', version: '3.11.2'
19 | compile group: 'org.testng', name: 'testng', version: '7.1.0'
20 | compile group : 'io.github.bonigarcia', name: 'webdrivermanager', version: '4.4.0'
21 | }
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudharsan-selvaraj/pageobject-examples/c5d256b9f45ba0883f01b01e01ce12fa91d6ef63/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Apr 11 12:44:21 IST 2021
2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
3 | distributionBase=GRADLE_USER_HOME
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin or MSYS, switch paths to Windows format before running java
129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=`expr $i + 1`
158 | done
159 | case $i in
160 | 0) set -- ;;
161 | 1) set -- "$args0" ;;
162 | 2) set -- "$args0" "$args1" ;;
163 | 3) set -- "$args0" "$args1" "$args2" ;;
164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=`save "$@"`
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | exec "$JAVACMD" "$@"
184 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'page-object-examples'
2 |
3 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/amazon/components/header/NavBar.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.amazon.components.header;
2 |
3 | import com.testninja.pageobjects.wrappers.BaseWebComponent;
4 | import org.openqa.selenium.support.FindBy;
5 |
6 | public class NavBar extends BaseWebComponent {
7 |
8 | @FindBy(css = "#nav-search")
9 | private SearchContainer searchContainer;
10 |
11 |
12 | public SearchContainer getSearchContainer() {
13 | return searchContainer;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/amazon/components/header/SearchContainer.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.amazon.components.header;
2 |
3 | import com.testninja.pageobjects.wrappers.BaseWebComponent;
4 | import org.openqa.selenium.By;
5 | import org.openqa.selenium.WebElement;
6 | import org.openqa.selenium.support.FindBy;
7 | import org.openqa.selenium.support.ui.Select;
8 |
9 | public class SearchContainer extends BaseWebComponent {
10 |
11 | @FindBy(id = "twotabsearchtextbox")
12 | private WebElement searchInput;
13 |
14 | @FindBy(id = "nav-search-dropdown-card")
15 | private WebElement categoryDropDown;
16 |
17 |
18 | public SearchContainer searchFor(String searchKeyword) {
19 | return searchFor(searchKeyword, null);
20 | }
21 |
22 | public SearchContainer searchFor(String searchKeyword, String category) {
23 | if (category != null) {
24 | selectCategoryFromDropdown(category);
25 | }
26 |
27 | if (searchKeyword != null) {
28 | enterSearchKeyword(searchKeyword, true);
29 | }
30 | return this;
31 | }
32 |
33 | public void pressSearch() {
34 | interactions.pressEnter(searchInput);
35 | }
36 |
37 | private void enterSearchKeyword(String keyword) {
38 | enterSearchKeyword(keyword, false);
39 | }
40 |
41 | private void enterSearchKeyword(String keyword, boolean pressEnter) {
42 | interactions.
43 | clear(searchInput)
44 | .type(searchInput, keyword);
45 |
46 | if(pressEnter) {
47 | pressSearch();
48 | }
49 | }
50 |
51 | private void selectCategoryFromDropdown(String category) {
52 | interactions.click(categoryDropDown);
53 | new Select(categoryDropDown.findElement(By.id("searchDropdownBox"))).selectByVisibleText(category);
54 | }
55 |
56 | public String getSelectedCategory() {
57 | return new Select(categoryDropDown.findElement(By.id("searchDropdownBox"))).getFirstSelectedOption().getText();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/amazon/components/searchfilters/BaseProductFilter.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.amazon.components.searchfilters;
2 |
3 | import com.gargoylesoftware.htmlunit.ElementNotFoundException;
4 | import com.testninja.pageobjects.utils.Interactions;
5 | import com.testninja.pageobjects.wrappers.BaseWebComponentFieldDecorator;
6 | import com.testninja.pageobjects.wrappers.ScriptHelper;
7 | import org.openqa.selenium.By;
8 | import org.openqa.selenium.WebDriver;
9 | import org.openqa.selenium.WebElement;
10 | import org.openqa.selenium.support.PageFactory;
11 |
12 | import java.util.List;
13 |
14 | public class BaseProductFilter {
15 |
16 | protected Interactions interactions;
17 | protected WebDriver driver;
18 | private final String filterHeaderValue;
19 |
20 | private static final By seeMoreLink, clearLink;
21 |
22 | static {
23 | seeMoreLink = By.cssSelector("a.s-expander-text");
24 | clearLink = By.cssSelector(".s-navigation-clear-link");
25 | }
26 |
27 | public BaseProductFilter(ScriptHelper scriptHelper, String filterHeaderValue) {
28 | this.interactions = scriptHelper.getInteractions();
29 | this.driver = scriptHelper.getDriver();
30 |
31 | this.filterHeaderValue = filterHeaderValue;
32 | PageFactory.initElements(new BaseWebComponentFieldDecorator(scriptHelper), this);
33 | }
34 |
35 | public WebElement getChildElement() {
36 | WebElement filterContainer = getFilterContainer();
37 | return driver.findElement(By.cssSelector("[aria-labelledby='" + filterContainer.getAttribute("id") + "']"));
38 | }
39 |
40 | public boolean isSeeMoreLinkPresent() {
41 | return getChildElement().findElements(seeMoreLink).size() > 0;
42 | }
43 |
44 | public void clickSeeMore() {
45 | List link = getChildElement().findElements(seeMoreLink);
46 | if (link.size() > 0) {
47 | interactions.click(link.get(0));
48 | }
49 | }
50 |
51 | public boolean isClearLinkPresent() {
52 | return getChildElement().findElements(clearLink).size() > 0;
53 | }
54 |
55 | public void clickClear() {
56 | List clear = getChildElement().findElements(clearLink);
57 | if (clear.size() > 0) {
58 | interactions.click(clear.get(0));
59 | }
60 | }
61 |
62 | private By getFilterContainerBy() {
63 | return By.xpath(".//*[@id='s-refinements']/descendant::*[starts-with(@id, 'p_') and contains(@id, '-title') and contains(.,'" + filterHeaderValue + "')]");
64 | }
65 |
66 | public WebElement getFilterContainer() {
67 | return driver
68 | .findElement(getFilterContainerBy());
69 | }
70 |
71 | public void waitForFilterToAppear() {
72 | interactions.waitHandler.waitForElement(getFilterContainerBy());
73 | interactions.scrollIntoView(getFilterContainer());
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/amazon/components/searchfilters/CheckBoxFilter.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.amazon.components.searchfilters;
2 |
3 | import com.gargoylesoftware.htmlunit.ElementNotFoundException;
4 | import com.testninja.pageobjects.wrappers.ScriptHelper;
5 | import org.openqa.selenium.By;
6 | import org.openqa.selenium.WebElement;
7 |
8 | import java.util.List;
9 | import java.util.stream.Collectors;
10 |
11 | public class CheckBoxFilter extends BaseProductFilter {
12 |
13 | private boolean isOptionAnImage = false;
14 |
15 | public CheckBoxFilter(ScriptHelper scriptHelper,
16 | String filterHeader) {
17 | super(scriptHelper, filterHeader);
18 | }
19 |
20 | public CheckBoxFilter(ScriptHelper scriptHelper,
21 | String filterHeader,
22 | boolean isOptionAnImage) {
23 | super(scriptHelper, filterHeader);
24 | this.isOptionAnImage = isOptionAnImage;
25 | }
26 |
27 | public void apply(List optionsToSelect) {
28 | optionsToSelect
29 | .stream()
30 | .forEach(option -> {
31 | waitForFilterToAppear();
32 | clickSeeMore();
33 | selectOption(getOptionCheckbox(option));
34 | });
35 | }
36 |
37 | public List getSelectedOptions() {
38 | waitForFilterToAppear();
39 | clickSeeMore();
40 |
41 | return getChildElement()
42 | .findElements(By.xpath(".//li/descendant::a[.//input[@type='checkbox'][@checked]]/descendant::span"))
43 | .stream()
44 | .map(interactions::getText)
45 | .filter(text -> !text.trim().equals(""))
46 | .collect(Collectors.toList());
47 |
48 | }
49 |
50 | private void selectOption(WebElement element) {
51 | if (!interactions.isSelected(element)) {
52 | interactions.javaScriptClick(element);
53 | }
54 | }
55 |
56 | private void deselectOption(WebElement element) {
57 | if (interactions.isSelected(element)) {
58 | interactions.click(element);
59 | }
60 | }
61 |
62 | private WebElement getOptionCheckbox(String label) {
63 | if (isOptionAnImage) {
64 | return getChildElement().findElement(By.xpath(".//li/descendant::a[.//span[aria-label='" + label + "']]/descendant::input[@type='checkbox']"));
65 | } else {
66 | return getChildElement().findElement(By.xpath(".//li/descendant::a[.//span[text()='" + label + "']]/descendant::input[@type='checkbox']"));
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/amazon/components/searchfilters/MultiChoiceButtonFilter.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.amazon.components.searchfilters;
2 |
3 | import com.testninja.pageobjects.wrappers.ScriptHelper;
4 | import org.openqa.selenium.By;
5 | import org.openqa.selenium.WebElement;
6 |
7 | import java.util.List;
8 | import java.util.stream.Collectors;
9 |
10 | public class MultiChoiceButtonFilter extends BaseProductFilter {
11 |
12 | public MultiChoiceButtonFilter(ScriptHelper scriptHelper,
13 | String filterHeader) {
14 | super(scriptHelper, filterHeader);
15 | }
16 |
17 | public void apply(List optionsToSelect) {
18 | optionsToSelect
19 | .stream()
20 | .forEach(option -> {
21 | waitForFilterToAppear();
22 | selectOption(getOptionButton(option));
23 | });
24 | }
25 |
26 | public List getSelectedOptions() {
27 | waitForFilterToAppear();
28 | clickSeeMore();
29 |
30 | return getChildElement()
31 | .findElements(By.xpath(".//li/descendant::*[contains(@class, 'a-button-selected')]"))
32 | .stream()
33 | .map(interactions::getText)
34 | .filter(text -> !text.trim().equals(""))
35 | .collect(Collectors.toList());
36 |
37 | }
38 |
39 | private void selectOption(WebElement element) {
40 | if (!interactions.isSelected(element)) {
41 | clickSeeMore();
42 | interactions.javaScriptClick(element);
43 | }
44 | }
45 |
46 | private void deselectOption(WebElement element) {
47 | if (interactions.isSelected(element)) {
48 | interactions.click(element);
49 | }
50 | }
51 |
52 | private WebElement getOptionButton(String label) {
53 | return getChildElement().findElement(By.xpath(".//li/descendant::button/span[.='"+ label +"']"));
54 | }
55 |
56 | private void isSelected() {
57 |
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/amazon/components/searchfilters/PriceRangeFilter.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.amazon.components.searchfilters;
2 |
3 | import com.testninja.pageobjects.wrappers.ScriptHelper;
4 | import org.openqa.selenium.By;
5 | import org.openqa.selenium.WebElement;
6 |
7 | public class PriceRangeFilter extends BaseProductFilter {
8 |
9 | public PriceRangeFilter(ScriptHelper scriptHelper) {
10 | super(scriptHelper, "Price");
11 | }
12 |
13 | public void apply(String lowPrice, String highPrice) {
14 | waitForFilterToAppear();
15 | interactions.clearAndType(getLowPriceInput(), lowPrice)
16 | .clearAndType(getHighPriceInput(), highPrice)
17 | .click(getSubmitButton());
18 |
19 | }
20 |
21 | private WebElement getLowPriceInput() {
22 | return getChildElement().findElement(By.id("low-price"));
23 | }
24 |
25 | private WebElement getHighPriceInput() {
26 | return getChildElement().findElement(By.id("high-price"));
27 | }
28 |
29 | private WebElement getSubmitButton() {
30 | return getChildElement().findElement(By.xpath(".//input[@type='submit']"));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/amazon/pages/AmazonBasePage.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.amazon.pages;
2 |
3 | import com.testninja.pageobjects.examples.amazon.components.header.NavBar;
4 | import com.testninja.pageobjects.wrappers.BasePage;
5 | import com.testninja.pageobjects.wrappers.ScriptHelper;
6 | import com.testninja.pageobjects.wrappers.annotations.Page;
7 | import org.openqa.selenium.support.FindBy;
8 |
9 | @Page
10 | public class AmazonBasePage extends BasePage {
11 |
12 | @FindBy(id = "navbar")
13 | private NavBar navBar;
14 |
15 | public AmazonBasePage(ScriptHelper scriptHelper) {
16 | super(scriptHelper);
17 | }
18 |
19 | public void searchProduct(String productName) {
20 | searchProduct(productName, null);
21 | }
22 |
23 | public void searchProduct(String productName, String productCategory) {
24 | navBar.getSearchContainer()
25 | .searchFor(productName, productCategory);
26 | }
27 |
28 | public void changeCategory(String category) {
29 | if(category == null) {
30 | category = "All Categories";
31 | }
32 | navBar.getSearchContainer()
33 | .searchFor(null, category)
34 | .pressSearch();
35 |
36 | }
37 |
38 | public void resetCategory() {
39 | changeCategory(null);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/amazon/pages/HomePage.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.amazon.pages;
2 |
3 | import com.testninja.pageobjects.wrappers.ScriptHelper;
4 | import com.testninja.pageobjects.wrappers.annotations.Page;
5 |
6 | @Page
7 | public class HomePage extends AmazonBasePage {
8 |
9 | public HomePage(ScriptHelper scriptHelper) {
10 | super(scriptHelper);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/amazon/pages/SearchResultsPage.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.amazon.pages;
2 |
3 | import com.testninja.pageobjects.examples.amazon.components.searchfilters.CheckBoxFilter;
4 | import com.testninja.pageobjects.examples.amazon.components.searchfilters.PriceRangeFilter;
5 | import com.testninja.pageobjects.wrappers.ScriptHelper;
6 | import com.testninja.pageobjects.wrappers.annotations.Page;
7 |
8 | import java.util.Arrays;
9 | import java.util.List;
10 |
11 | @Page
12 | public class SearchResultsPage extends AmazonBasePage {
13 |
14 | public SearchResultsPage(ScriptHelper scriptHelper) {
15 | super(scriptHelper);
16 | }
17 |
18 | public CheckBoxFilter applyPrimeFilter() {
19 | CheckBoxFilter filter = new CheckBoxFilter(scriptHelper, "Amazon Prime", true);
20 | filter.apply(Arrays.asList("Prime Eligible"));
21 | return filter;
22 | }
23 |
24 | public CheckBoxFilter applyBrandFilter(List brands) {
25 | CheckBoxFilter filter = new CheckBoxFilter(scriptHelper, "Brand");
26 | filter.apply(brands);
27 | return filter;
28 | }
29 |
30 | public PriceRangeFilter applyPriceFilter(String minPrice, String maxPrice) {
31 | PriceRangeFilter filter = new PriceRangeFilter(scriptHelper);
32 | filter.apply(minPrice, maxPrice);
33 | return filter;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/spotify/components/Header.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.spotify.components;
2 |
3 | import com.testninja.pageobjects.wrappers.BaseWebComponent;
4 | import com.testninja.pageobjects.wrappers.ScriptHelper;
5 | import org.openqa.selenium.By;
6 |
7 | public class Header extends BaseWebComponent {
8 |
9 | public void clickLogin() {
10 | interactions.click(findElement(getChildByTestId("login-button")));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/spotify/components/LeftNavBar.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.spotify.components;
2 |
3 | import com.testninja.pageobjects.wrappers.BaseWebComponent;
4 | import org.openqa.selenium.By;
5 |
6 | public class LeftNavBar extends BaseWebComponent {
7 |
8 | public void clickHome() {
9 | clickMenu("Home");
10 | }
11 |
12 | public void clickSearch() {
13 | clickMenu("Search");
14 | }
15 |
16 | public void clickYourLibrary() {
17 | clickMenu("Your Library");
18 | }
19 |
20 | private void clickMenu(String menuTitle) {
21 | interactions.click(findElement(By.linkText(menuTitle)));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/spotify/components/Player.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.spotify.components;
2 |
3 | import com.testninja.pageobjects.examples.spotify.components.playlist.PlayButton;
4 | import com.testninja.pageobjects.wrappers.BaseWebComponent;
5 | import org.openqa.selenium.support.FindBy;
6 |
7 | import java.util.List;
8 | import java.util.stream.Collectors;
9 |
10 | public class Player extends BaseWebComponent {
11 |
12 | @FindBy(xpath = ".//button[@data-testid='control-button-play' or @data-testid='control-button-pause']")
13 | private PlayButton playButton;
14 |
15 | public String getSongName() {
16 | return interactions.getText(findElement(getChildByTestId("nowplaying-track-link")));
17 | }
18 |
19 | public List getArtists() {
20 | return findElements(getChildByTestId("nowplaying-artist"))
21 | .stream()
22 | .map(interactions::getText)
23 | .collect(Collectors.toList());
24 | }
25 |
26 | public boolean isPlaying() {
27 | return playButton.isPlaying();
28 | }
29 |
30 | public boolean isPaused() {
31 | return playButton.isPaused();
32 | }
33 |
34 | public void play() {
35 | playButton.play();
36 | }
37 |
38 | public void pause() {
39 | playButton.pause();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/spotify/components/playlist/PlayButton.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.spotify.components.playlist;
2 |
3 | import com.testninja.pageobjects.wrappers.BaseWebComponent;
4 | import com.testninja.pageobjects.wrappers.ScriptHelper;
5 |
6 | public class PlayButton extends BaseWebComponent {
7 |
8 | public void play() {
9 | if (!isPlaying()) {
10 | interactions.click(this);
11 | }
12 | }
13 |
14 | public void pause() {
15 | if (isPlaying()) {
16 | interactions.click(this);
17 | }
18 | }
19 |
20 | public boolean isPlaying() {
21 | return checkState("Pause");
22 | }
23 |
24 | public boolean isPaused() {
25 | return checkState("Play");
26 | }
27 |
28 | private boolean checkState(String state) {
29 | return getAttribute("aria-label").equals(state);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/spotify/components/playlist/SaveButton.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.spotify.components.playlist;
2 |
3 | import com.testninja.pageobjects.wrappers.BaseWebComponent;
4 | import com.testninja.pageobjects.wrappers.ScriptHelper;
5 |
6 | public class SaveButton extends BaseWebComponent {
7 |
8 | public boolean isSaved() {
9 | return checkState("Save to Your Library");
10 | }
11 |
12 | public void clickSave() {
13 | if (!isSaved()) {
14 | interactions.click(getWrappedWebElement());
15 | }
16 | }
17 |
18 | private boolean checkState(String state) {
19 | return getAttribute("aria-label").equals(state);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/spotify/components/playlist/SongRow.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.spotify.components.playlist;
2 |
3 | import com.testninja.pageobjects.wrappers.BaseWebComponent;
4 | import com.testninja.pageobjects.wrappers.ScriptHelper;
5 | import org.openqa.selenium.By;
6 | import org.openqa.selenium.WebElement;
7 |
8 | import java.util.List;
9 | import java.util.stream.Collectors;
10 |
11 | public class SongRow extends BaseWebComponent {
12 |
13 | public String getRowNumber() {
14 | return interactions.getText(getColumn(1));
15 | }
16 |
17 | public String getSongName() {
18 | return interactions.getText(getColumn(2).findElement(By.cssSelector("[as=\"div\"]")));
19 | }
20 |
21 | public List getArtists() {
22 | return getColumn(2)
23 | .findElements(By.xpath(".//span/a"))
24 | .stream()
25 | .map(interactions::getText)
26 | .collect(Collectors.toList());
27 | }
28 |
29 | public String getAlbum() {
30 | return interactions.getText(getColumn(3));
31 | }
32 |
33 | public String getDateAdded() {
34 | return interactions.getText(getColumn(4));
35 | }
36 |
37 | public String getDuration() {
38 | return interactions.getText(getColumn(5));
39 | }
40 |
41 | public void playAndPause() {
42 | play();
43 | play();
44 | }
45 |
46 | public void play() {
47 | WebElement playIcon = getColumn(1);
48 | interactions.mouseHover(playIcon).click(playIcon);
49 | }
50 |
51 | private WebElement getColumn(Integer index) {
52 | return findElement(By.xpath(".//div[" + index + "]"));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/spotify/pages/LoginPage.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.spotify.pages;
2 |
3 | import com.testninja.pageobjects.wrappers.BasePage;
4 | import com.testninja.pageobjects.wrappers.ScriptHelper;
5 | import com.testninja.pageobjects.wrappers.annotations.Page;
6 | import org.openqa.selenium.WebElement;
7 | import org.openqa.selenium.support.FindBy;
8 |
9 | @Page
10 | public class LoginPage extends BasePage {
11 |
12 | @FindBy(css = "[ng-model=\"form.username\"]")
13 | private WebElement emailInput;
14 |
15 | @FindBy(css = "[ng-model=\"form.password\"]")
16 | private WebElement passwordInput;
17 |
18 | @FindBy(id = "login-button")
19 | private WebElement loginButton;
20 |
21 | public LoginPage(ScriptHelper scriptHelper) {
22 | super(scriptHelper);
23 | }
24 |
25 | public void loginWithEmail(String email, String password) {
26 | interactions.clearAndType(emailInput, email)
27 | .clearAndType(passwordInput, password)
28 | .click(loginButton);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/spotify/pages/PlaylistPage.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.spotify.pages;
2 |
3 | import com.testninja.pageobjects.examples.spotify.components.playlist.PlayButton;
4 | import com.testninja.pageobjects.examples.spotify.components.playlist.SaveButton;
5 | import com.testninja.pageobjects.examples.spotify.components.playlist.SongRow;
6 | import com.testninja.pageobjects.wrappers.ScriptHelper;
7 | import com.testninja.pageobjects.wrappers.annotations.Page;
8 | import org.apache.xpath.operations.Bool;
9 | import org.checkerframework.checker.nullness.compatqual.NullableDecl;
10 | import org.openqa.selenium.WebDriver;
11 | import org.openqa.selenium.WebElement;
12 | import org.openqa.selenium.support.FindBy;
13 | import org.openqa.selenium.support.ui.ExpectedCondition;
14 |
15 | import java.util.List;
16 | import java.util.stream.Collectors;
17 |
18 | @Page
19 | public class PlaylistPage extends SpotifyBasePage {
20 |
21 | @FindBy(css = "[data-testid=\"action-bar-row\"] > [data-testid='play-button']")
22 | private PlayButton playButton;
23 |
24 | @FindBy(xpath = ".//*=[@data-testid='action-bar-row']/button[2]")
25 | private SaveButton saveButton;
26 |
27 | @FindBy(css = "[data-testid='tracklist-row']")
28 | private List songRows;
29 |
30 | @FindBy(xpath = ".//span/h1")
31 | private WebElement playlistName;
32 |
33 | public PlaylistPage(ScriptHelper scriptHelper) {
34 | super(scriptHelper);
35 | }
36 |
37 | public void waitForPageLoad() {
38 | interactions.waitHandler.waitFor(new ExpectedCondition() {
39 | @Override
40 | public Boolean apply(@NullableDecl WebDriver input) {
41 | return getSongs().size() > 0;
42 | }
43 | });
44 | }
45 |
46 | public String getPlaylistName() {
47 | return interactions.getText(playlistName);
48 | }
49 |
50 | public PlaylistPage play() {
51 | playButton.play();
52 | return this;
53 | }
54 |
55 | public PlaylistPage playAndWait() {
56 | play();
57 | interactions.waitHandler.waitFor(new ExpectedCondition() {
58 | @Override
59 | public Boolean apply(@NullableDecl WebDriver input) {
60 | return isSongPlaying();
61 | }
62 | });
63 | return this;
64 | }
65 |
66 | public boolean isSongPlaying() {
67 | return playButton.isPlaying();
68 | }
69 |
70 | public boolean isSongPaused() {
71 | return playButton.isPaused();
72 | }
73 |
74 | public PlaylistPage saveToFavourites() {
75 | saveButton.clickSave();
76 | return this;
77 | }
78 |
79 | public boolean isSavedToFavourites() {
80 | return saveButton.isSaved();
81 | }
82 |
83 | public SongRow findSongByName(String name) {
84 | return getSongs()
85 | .stream()
86 | .filter(song -> song.getSongName().equals(name))
87 | .findFirst()
88 | .get();
89 |
90 | }
91 |
92 | public SongRow findSongByIndex(Integer index) {
93 | return getSongs().get(index);
94 | }
95 |
96 | private List getSongs() {
97 | return songRows
98 | .stream()
99 | .map(song -> {
100 | song.setScriptHelper(scriptHelper);
101 | return song;
102 | }).collect(Collectors.toList());
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/spotify/pages/SpotifyBasePage.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.spotify.pages;
2 |
3 | import com.testninja.pageobjects.examples.spotify.components.Header;
4 | import com.testninja.pageobjects.examples.spotify.components.LeftNavBar;
5 | import com.testninja.pageobjects.examples.spotify.components.Player;
6 | import com.testninja.pageobjects.wrappers.BasePage;
7 | import com.testninja.pageobjects.wrappers.ScriptHelper;
8 | import com.testninja.pageobjects.wrappers.annotations.Page;
9 | import com.testninja.pageobjects.wrappers.annotations.PageObject;
10 | import org.openqa.selenium.support.FindBy;
11 |
12 | @Page
13 | public class SpotifyBasePage extends BasePage {
14 |
15 | @FindBy(css = ".Root__nav-bar")
16 | private LeftNavBar navBar;
17 |
18 | @FindBy(css = ".Root__top-bar")
19 | private Header header;
20 |
21 | @FindBy(css = ".Root__now-playing-bar")
22 | private Player player;
23 |
24 | @PageObject
25 | private LoginPage loginPage;
26 |
27 | public SpotifyBasePage(ScriptHelper scriptHelper) {
28 | super(scriptHelper);
29 | }
30 |
31 | public void loginWithCredentials(String email, String password) {
32 | header.clickLogin();
33 | loginPage.loginWithEmail(email, password);
34 | interactions.waitHandler.waitForElementToBePresent(header);
35 | }
36 |
37 | public Player getPlayer() {
38 | return player;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/examples/spotify/pages/SpotifyHomePage.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.examples.spotify.pages;
2 |
3 | import com.testninja.pageobjects.wrappers.ScriptHelper;
4 | import com.testninja.pageobjects.wrappers.annotations.Page;
5 |
6 | @Page
7 | public class SpotifyHomePage extends SpotifyBasePage {
8 |
9 | public SpotifyHomePage(ScriptHelper scriptHelper) {
10 | super(scriptHelper);
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/utils/Interactions.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.utils;
2 |
3 | import org.openqa.selenium.*;
4 | import org.openqa.selenium.interactions.Actions;
5 |
6 | import java.awt.*;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | public class Interactions {
11 |
12 | WebDriver driver;
13 | public WaitHandler waitHandler;
14 |
15 | public Interactions(WebDriver driver) {
16 | this.driver = driver;
17 | waitHandler = new WaitHandler(driver);
18 | }
19 |
20 | public void refreshBrowser() {
21 | driver.navigate().refresh();
22 |
23 | }
24 |
25 | public void open(String url) {
26 | driver.navigate().to(url);
27 |
28 | }
29 |
30 | public Interactions mouseHover(WebElement element) {
31 |
32 | waitHandler.waitForElementToBePresent(element);
33 | new Actions(driver).moveToElement(element).moveToElement(element).build().perform();
34 | return this;
35 | }
36 |
37 | public Interactions mouseHover(WebElement element, int xOffset, int yOffset) {
38 |
39 | waitHandler.waitForElementToBePresent(element);
40 | new Actions(driver).moveToElement(element, xOffset, yOffset).build().perform();
41 | return this;
42 | }
43 |
44 | public Interactions mouseHoverOnInvisibleElement(WebElement element) {
45 |
46 | new Actions(driver).moveToElement(element).perform();
47 | return this;
48 | }
49 |
50 | public Interactions click(WebElement element) {
51 | waitHandler.waitForElementToBeClickable(element);
52 | element.click();
53 |
54 | return this;
55 | }
56 |
57 | public Interactions selectMultipleOption(List elements) {
58 | Actions actions = new Actions(driver);
59 | actions.keyDown(Keys.CONTROL);
60 | for (WebElement element: elements) {
61 | actions.click(element);
62 | }
63 | actions.keyDown(Keys.CONTROL)
64 | .build()
65 | .perform();
66 |
67 | return this;
68 | }
69 | public Interactions mouseClick(WebElement element) {
70 | waitHandler.waitForElementToDisplay(element);
71 | new Actions(driver).moveToElement(element).click().build().perform();
72 |
73 | return this;
74 | }
75 |
76 | public Interactions mouseClickOnInvisibleElement(WebElement element) {
77 | new Actions(driver).moveToElement(element).click().perform();
78 |
79 | return this;
80 | }
81 |
82 | public Interactions mouseSet(WebElement element, String text) {
83 | new Actions(driver).sendKeys(element, text).perform();
84 | return this;
85 | }
86 |
87 | public Interactions javaScriptClick(WebElement element) {
88 | waitHandler.waitForElementToDisplay(element);
89 | executeScript("arguments[0].click()", element);
90 |
91 | return this;
92 | }
93 |
94 | public Interactions javaScriptClear(WebElement element) {
95 | executeScript("arguments[0].value=arguments[1]", element, "");
96 |
97 | return this;
98 | }
99 |
100 | public Interactions javaScriptType(WebElement element, String text) {
101 | executeScript("arguments[0].value=arguments[1]", element, text);
102 |
103 | return this;
104 | }
105 |
106 | public Interactions javaScriptClearAndType(WebElement element, String text) {
107 | javaScriptClear(element).javaScriptType(element, text);
108 | return this;
109 | }
110 |
111 | public void doubleClick(WebElement element) {
112 | waitHandler.waitForElementToDisplay(element);
113 | new Actions(driver).moveToElement(element).doubleClick().perform();
114 |
115 | }
116 |
117 | public void dragAndDrop(WebElement source, WebElement destination) {
118 | waitHandler.waitForElementToBeClickable(source);
119 | waitHandler.waitForElementToBeClickable(destination);
120 | new Actions(driver).clickAndHold(source).moveToElement(destination).release().perform();
121 | }
122 |
123 | public void dragAndDropOnInVisibleElement(WebElement source, WebElement destination) {
124 | new Actions(driver).clickAndHold(source).moveToElement(destination).release().perform();
125 | }
126 |
127 | public void dragAndDropBy(WebElement source, int x, int y) {
128 | waitHandler.waitForElementToBeClickable(source);
129 | new Actions(driver).dragAndDropBy(source, x, y).build().perform();
130 | }
131 |
132 | public void rightClick(WebElement element) {
133 | waitHandler.waitForElementToBeClickable(element);
134 | new Actions(driver).moveToElement(element).contextClick().perform();
135 | }
136 |
137 | public void type(WebElement element, String text) {
138 | if (text == null) {
139 | return;
140 | }
141 | element.sendKeys(text);
142 | }
143 |
144 | public void type(WebElement element, String[] text) {
145 | if (text == null) {
146 | return;
147 | }
148 | for (String val : text) {
149 | element.sendKeys(val);
150 | element.sendKeys(Keys.TAB);
151 | }
152 | }
153 |
154 | public void type(WebElement element, List text) {
155 | if (text == null) {
156 | return;
157 | }
158 | for (String val : text) {
159 | element.sendKeys(val);
160 | element.sendKeys(Keys.TAB);
161 | }
162 | }
163 |
164 | public void type(WebElement webElement, double number) {
165 | type(webElement, Double.toString(number));
166 | }
167 |
168 | public Interactions clear(WebElement element) {
169 | element.clear();
170 | return this;
171 | }
172 |
173 | public Interactions clearAndType(WebElement webElement, String text) {
174 | waitHandler.waitForElementToBeClickable(webElement);
175 | clear(webElement);
176 | type(webElement, text);
177 | return this;
178 | }
179 |
180 | public Interactions switchToFrameByIndex(Integer index) {
181 |
182 | driver.switchTo().frame(index);
183 | return this;
184 | }
185 |
186 | public Interactions switchToDefaultContent() {
187 | driver.switchTo().defaultContent();
188 | return this;
189 | }
190 | public Interactions typeAndEnter(WebElement webElement, String text) {
191 | waitHandler.waitForElementToBeClickable(webElement);
192 | clear(webElement);
193 | type(webElement, text);
194 | pressEnter(webElement);
195 | return this;
196 | }
197 |
198 | public void clearAndType(WebElement webElement, String[] text) {
199 | clear(webElement);
200 | type(webElement, text);
201 | }
202 |
203 | public void clearAndType(WebElement webElement, double number) {
204 | clear(webElement);
205 | type(webElement, number);
206 | }
207 |
208 | public void pressEnter(WebElement webElement) {
209 | pressKeys(webElement, Keys.ENTER);
210 | }
211 |
212 | public void pressDelete(WebElement element, int noOfTimes) {
213 | for (int i = 0; i < noOfTimes; i++) {
214 | pressKeys(element, Keys.BACK_SPACE);
215 | }
216 | }
217 |
218 | public void pressKeys(WebElement element, CharSequence... keys) {
219 | waitHandler.waitForElementToBeClickable(element);
220 | element.sendKeys(keys);
221 |
222 | }
223 |
224 | /* Select/Deselect */
225 | public void select(WebElement webElement) {
226 | if (!isSelected(webElement)) {
227 | webElement.click();
228 | }
229 | }
230 |
231 | public void deselect(WebElement webElement) {
232 | if (isSelected(webElement)) {
233 | webElement.click();
234 | }
235 | }
236 |
237 |
238 | public void executeScript(String script) {
239 | ((JavascriptExecutor) driver).executeScript(script);
240 | }
241 |
242 | public Object executeScript(String script, Object... args) {
243 | return ((JavascriptExecutor) driver).executeScript(script, args);
244 | }
245 |
246 | public Interactions scrollIntoView(WebElement element) {
247 | ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView();", element);
248 | return this;
249 | }
250 |
251 | public Interactions removeTableRowVirtualization() {
252 | ((JavascriptExecutor) driver).executeScript("$.fn.visible = function () {return true};");
253 | return this;
254 | }
255 |
256 | public void scrollIntoViewAndClick(WebElement element) {
257 | ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(); arguments[0].click()", element);
258 | }
259 |
260 | public void scrollIntoViewRelativeAndClick(WebElement element) {
261 | ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView({block: \"end\"}); arguments[0].click()", element);
262 | }
263 |
264 | public boolean isSelected(WebElement webElement) {
265 | return webElement.isSelected();
266 | }
267 |
268 | /* Enabled/Disabled */
269 | public boolean isEnabled(WebElement webElement) {
270 | return webElement.isEnabled();
271 | }
272 |
273 | public boolean isDisabled(WebElement webElement) {
274 | return !isEnabled(webElement);
275 | }
276 |
277 | /* get values*/
278 |
279 | public String getText(WebElement webElement) {
280 | waitHandler.waitForElementToBePresent(webElement);
281 | return webElement.getText();
282 | }
283 |
284 | public List getText(List webElements) {
285 | List listOfTexts = new ArrayList<>();
286 | for (WebElement webElement : webElements) {
287 | //waitHandler.waitForElementToDisplay(webElement);
288 | String text = webElement.getText();
289 | if(!text.equals(""))
290 | listOfTexts.add(text);
291 | }
292 | return listOfTexts;
293 | }
294 |
295 | public String getTextFieldValue(WebElement webElement) {
296 | return getAttribute(webElement, "value");
297 | }
298 |
299 | public String getAttribute(WebElement webElement, String attributeName) {
300 | return webElement.getAttribute(attributeName);
301 | }
302 |
303 | public Integer getIndex(List lisOfWebElements, String headerName) {
304 | return getText(lisOfWebElements).indexOf(headerName);
305 | }
306 |
307 |
308 | /* Js Methods*/
309 |
310 | public Interactions tabOut(WebElement element) {
311 | this.click(element).pressKeys(element, Keys.TAB);
312 | return this;
313 | }
314 |
315 | public String getCssValue(WebElement webElement, String propertyName) {
316 | waitHandler.waitForElementToDisplay(webElement);
317 | return webElement.getCssValue(propertyName);
318 | }
319 |
320 | public Interactions toggleCheckBox(WebElement element, Boolean selectionState) {
321 | Boolean checkBoxAlreadySelected = element.isSelected();
322 | if (checkBoxAlreadySelected != selectionState) {
323 | waitHandler.waitForElementToBePresent(element);
324 | // interaction.click() is not working in permission panel.
325 | element.click();
326 | //this.mouseClick(element);
327 | }
328 | return this;
329 | }
330 |
331 | public String formatDate(String utcDate) {
332 | if (utcDate == null) {
333 | return "-";
334 | }
335 | return (String) executeScript("return moment(new Date(arguments[0])).format('MMM D, YYYY | h:mm a')", utcDate);
336 | }
337 |
338 | public String getBorderColor (WebElement element) {
339 | return getCssValue(element, "border-bottom-color");
340 | }
341 |
342 | public String getBorderColorHexCode(WebElement element) {
343 | return getColorHexCode(getBorderColor(element));
344 | }
345 |
346 | public String getComputedBackgroundColor(WebElement element) {
347 | return executeScript("return window.getComputedStyle(arguments[0],':before').getPropertyValue('background-color')", element).toString();
348 | }
349 |
350 | public String getBackgroundColorHexCode(WebElement element) {
351 | return getColorHexCode(getComputedBackgroundColor(element));
352 | }
353 |
354 | private String getColorHexCode(String RgbColor) {
355 | String[] rgb = RgbColor.replace("rgba(", "").replace(")", "").replace("rgb(", "").split(",");
356 | Color c = new Color(Integer.valueOf(rgb[0].trim()), Integer.valueOf(rgb[1].trim()), Integer.valueOf(rgb[2].trim()));
357 | return "#"+ Integer.toHexString(c.getRGB() & 0xffffff);
358 | }
359 |
360 | }
361 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/utils/WaitHandler.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.utils;
2 |
3 | import org.openqa.selenium.*;
4 | import org.openqa.selenium.support.ui.ExpectedCondition;
5 | import org.openqa.selenium.support.ui.ExpectedConditions;
6 | import org.openqa.selenium.support.ui.WebDriverWait;
7 |
8 | import java.util.List;
9 |
10 | public class WaitHandler {
11 |
12 | WebDriver driver;
13 | private int defaultWaitTime = 20;
14 |
15 | public WaitHandler(WebDriver driver) {
16 | this.driver = driver;
17 | }
18 |
19 | public void waitFor(ExpectedCondition condition) {
20 | new WebDriverWait(driver, defaultWaitTime).until(condition);
21 | }
22 |
23 | public WebElement waitForElementToDisplay(WebElement webElement) {
24 | return waitForElementToDisplay(webElement, defaultWaitTime);
25 | }
26 |
27 | public WebElement waitForElementToDisplay(WebElement webElement, long waitTime) {
28 | return new WebDriverWait(driver, waitTime).until(ExpectedConditions.visibilityOf(webElement));
29 | }
30 |
31 |
32 | public void waitForElementToBeClickable(WebElement webElement) {
33 | waitForElementToBeClickable(webElement, defaultWaitTime);
34 | }
35 |
36 | public void waitForElementToBeClickable(WebElement webElement, long waitTime) {
37 | new WebDriverWait(driver, waitTime).until(ExpectedConditions.elementToBeClickable(webElement));
38 | }
39 |
40 | public void waitForElementToBePresent(WebElement webElement) {
41 | waitForElementToBePresent(webElement, defaultWaitTime);
42 | }
43 |
44 | public void waitForElementToBePresent(WebElement webElement, long waitTime) {
45 | new WebDriverWait(driver, waitTime).until(new ExpectedCondition() {
46 | @Override
47 | public WebElement apply(WebDriver driver) {
48 | try {
49 | webElement.isDisplayed();
50 | return webElement;
51 | } catch (NoSuchElementException e) {
52 | return null;
53 | }
54 | }
55 | });
56 | }
57 |
58 | public List waitForElementsToDisplay(List extends WebElement> webElements) {
59 | return waitForElementsToDisplay((List) webElements, defaultWaitTime);
60 | }
61 |
62 | public void waitForElement(By locator) {
63 | for (int i = 0; i < defaultWaitTime; i++) {
64 | try {
65 | driver.findElement(locator).isDisplayed();
66 | return;
67 | } catch (Exception e) {
68 | sleep(1000);
69 | }
70 | }
71 | }
72 |
73 | public List waitForElementsToDisplay(List extends WebElement> webElements, long waitTime) {
74 | return new WebDriverWait(driver, waitTime).until(ExpectedConditions.visibilityOfAllElements((List) webElements));
75 | }
76 |
77 | public void waitForPageToLoad() {
78 | waitForPageToLoad(defaultWaitTime);
79 | }
80 |
81 | public void waitForPageToLoad(int seconds) {
82 | try {
83 | new WebDriverWait(driver, seconds).until((WebDriver) -> {
84 | return String.valueOf(executeJavascript("return document.readyState"))
85 | .equals("complete");
86 | });
87 | } catch (TimeoutException ex) {
88 | // don't throw if page is still loading. Some pages never
89 | // archive readyState == complete, but are functionaly correct
90 | }
91 | }
92 |
93 | public void sleep(int timeInMilliSeconds) {
94 | executeScriptAsync("var callback = arguments[arguments.length-1]; setTimeout(function(){ callback() }, arguments[0]);", timeInMilliSeconds);
95 | }
96 |
97 | /* Execute Javascript */
98 | public Object executeJavascript(String script, Object... arguments) {
99 | return ((JavascriptExecutor) driver).executeScript(script, arguments);
100 | }
101 |
102 | public void executeScriptAsync(String script, Object... arguments) {
103 | ((JavascriptExecutor) driver).executeAsyncScript(script, arguments);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/wrappers/BasePage.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.wrappers;
2 |
3 | import com.testninja.pageobjects.utils.Interactions;
4 | import org.openqa.selenium.*;
5 | import org.openqa.selenium.support.PageFactory;
6 |
7 | import java.util.Arrays;
8 |
9 | public class BasePage {
10 |
11 | protected Interactions interactions;
12 | protected WebDriver driver;
13 | protected ScriptHelper scriptHelper;
14 |
15 | public BasePage(ScriptHelper scriptHelper) {
16 | this.scriptHelper = scriptHelper;
17 | this.driver = scriptHelper.getDriver();
18 | this.interactions = scriptHelper.getInteractions();
19 |
20 | PageFactory.initElements(new BaseWebComponentFieldDecorator(scriptHelper), this);
21 | PageObjectFactory.init(this, Arrays.asList(new Class[]{ScriptHelper.class}), Arrays.asList(new Object[]{scriptHelper}));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/wrappers/BaseWebComponent.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.wrappers;
2 |
3 | import com.github.webdriverextensions.WebComponent;
4 | import com.testninja.pageobjects.utils.Interactions;
5 | import org.openqa.selenium.By;
6 | import org.openqa.selenium.WebDriver;
7 | import org.openqa.selenium.support.PageFactory;
8 |
9 | public class BaseWebComponent extends WebComponent {
10 |
11 | protected ScriptHelper scriptHelper;
12 | protected Interactions interactions;
13 |
14 | public BaseWebComponent() {
15 | super();
16 | }
17 |
18 | public BaseWebComponent(ScriptHelper scriptHelper) {
19 | super();
20 | setScriptHelper(scriptHelper);
21 | }
22 |
23 | public void setScriptHelper(ScriptHelper scriptHelper) {
24 | this.scriptHelper = scriptHelper;
25 | this.interactions = scriptHelper.getInteractions();
26 | PageFactory.initElements(new BaseWebComponentFieldDecorator(scriptHelper), this);
27 | }
28 |
29 | public WebDriver getDriver() {
30 | return scriptHelper.getDriver();
31 | }
32 |
33 | protected static By getChildByTestId(String testId) {
34 | return By.cssSelector("[data-testid='" + testId + "']");
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/wrappers/BaseWebComponentFieldDecorator.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.wrappers;
2 |
3 | import com.github.webdriverextensions.WebDriverExtensionFieldDecorator;
4 | import org.openqa.selenium.support.pagefactory.FieldDecorator;
5 |
6 | import java.lang.reflect.Field;
7 |
8 | public class BaseWebComponentFieldDecorator implements FieldDecorator {
9 |
10 | private ScriptHelper scriptHelper;
11 |
12 | public BaseWebComponentFieldDecorator(ScriptHelper scriptHelper) {
13 | this.scriptHelper = scriptHelper;
14 | }
15 |
16 | @Override
17 | public Object decorate(ClassLoader loader, Field field) {
18 | Object proxyWebElement = new WebDriverExtensionFieldDecorator(scriptHelper.getDriver()).decorate(loader, field);
19 |
20 | if(proxyWebElement instanceof BaseWebComponent) {
21 | ((BaseWebComponent)proxyWebElement).setScriptHelper(scriptHelper);
22 | }
23 | return proxyWebElement;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/testninja/pageobjects/wrappers/PageObjectFactory.java:
--------------------------------------------------------------------------------
1 | package com.testninja.pageobjects.wrappers;
2 |
3 | import com.testninja.pageobjects.wrappers.annotations.Page;
4 | import com.testninja.pageobjects.wrappers.annotations.PageObject;
5 |
6 | import java.lang.annotation.Annotation;
7 | import java.lang.reflect.Field;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | public class PageObjectFactory {
12 |
13 | public static void init(Object classObject,
14 | List> constructorParams,
15 | List