├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug-report.md
│ └── feature_request.md
├── dependabot.yml
├── release-drafter.yml
└── workflows
│ ├── maven.yml
│ └── release-draft.yml
├── .gitignore
├── .travis.yml.disable
├── LICENSE
├── README.md
├── doc
└── style
│ ├── intellij-java-style.xml
│ └── style.xml
├── docker-compose-ci.yml
├── jitpack.yml
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── frameworkium
│ │ └── core
│ │ ├── api
│ │ ├── Endpoint.java
│ │ ├── dto
│ │ │ └── AbstractDTO.java
│ │ ├── services
│ │ │ └── BaseService.java
│ │ └── tests
│ │ │ └── BaseAPITest.java
│ │ ├── common
│ │ ├── listeners
│ │ │ ├── CreateJiraCycleListener.java
│ │ │ ├── MethodInterceptor.java
│ │ │ ├── ResultLoggerListener.java
│ │ │ └── TestListener.java
│ │ ├── properties
│ │ │ └── Property.java
│ │ ├── reporting
│ │ │ ├── TestIdUtils.java
│ │ │ ├── allure
│ │ │ │ ├── AllureLogger.java
│ │ │ │ └── AllureProperties.java
│ │ │ ├── jira
│ │ │ │ ├── JiraConfig.java
│ │ │ │ ├── dto
│ │ │ │ │ ├── attachment
│ │ │ │ │ │ ├── AttachmentDto.java
│ │ │ │ │ │ └── AttachmentListDto.java
│ │ │ │ │ ├── cycle
│ │ │ │ │ │ ├── CreateCycleSuccessDto.java
│ │ │ │ │ │ ├── CreateNewCycleDto.java
│ │ │ │ │ │ ├── CycleDto.java
│ │ │ │ │ │ ├── CycleLightDto.java
│ │ │ │ │ │ └── CycleListDto.java
│ │ │ │ │ ├── execution
│ │ │ │ │ │ ├── AddTestToCycleOperationDto.java
│ │ │ │ │ │ ├── ExecutionDto.java
│ │ │ │ │ │ ├── ExecutionLightDto.java
│ │ │ │ │ │ └── UpdateExecutionOperationDto.java
│ │ │ │ │ ├── executionsearch
│ │ │ │ │ │ ├── ExecutionSearchDto.java
│ │ │ │ │ │ └── ExecutionSearchListDto.java
│ │ │ │ │ ├── project
│ │ │ │ │ │ └── ProjectDto.java
│ │ │ │ │ ├── status
│ │ │ │ │ │ └── StatusDto.java
│ │ │ │ │ └── version
│ │ │ │ │ │ └── VersionDto.java
│ │ │ │ ├── endpoint
│ │ │ │ │ ├── JiraEndpoint.java
│ │ │ │ │ └── ZephyrEndpoint.java
│ │ │ │ ├── service
│ │ │ │ │ ├── AbstractJiraService.java
│ │ │ │ │ ├── Attachment.java
│ │ │ │ │ ├── Issue.java
│ │ │ │ │ ├── IssueLink.java
│ │ │ │ │ ├── Project.java
│ │ │ │ │ ├── Search.java
│ │ │ │ │ └── Version.java
│ │ │ │ ├── util
│ │ │ │ │ ├── ExecutionSearchUtil.java
│ │ │ │ │ └── ExecutionUtil.java
│ │ │ │ └── zapi
│ │ │ │ │ ├── Attachment.java
│ │ │ │ │ ├── Cycle.java
│ │ │ │ │ ├── Execution.java
│ │ │ │ │ └── ExecutionSearch.java
│ │ │ └── spira
│ │ │ │ ├── SpiraConfig.java
│ │ │ │ └── SpiraExecution.java
│ │ └── retry
│ │ │ └── RetryFlakyTest.java
│ │ ├── htmlelements
│ │ ├── annotations
│ │ │ └── Timeout.java
│ │ ├── element
│ │ │ ├── Button.java
│ │ │ ├── CheckBox.java
│ │ │ ├── FileInput.java
│ │ │ ├── Form.java
│ │ │ ├── HtmlElement.java
│ │ │ ├── Image.java
│ │ │ ├── Link.java
│ │ │ ├── Radio.java
│ │ │ ├── Select.java
│ │ │ ├── Table.java
│ │ │ ├── TextBlock.java
│ │ │ ├── TextInput.java
│ │ │ └── TypifiedElement.java
│ │ ├── exceptions
│ │ │ └── HtmlElementsException.java
│ │ ├── loader
│ │ │ ├── HtmlElementLoader.java
│ │ │ └── decorator
│ │ │ │ ├── HtmlElementClassAnnotationsHandler.java
│ │ │ │ ├── HtmlElementDecorator.java
│ │ │ │ ├── HtmlElementFieldAnnotationsHandler.java
│ │ │ │ ├── HtmlElementLocatorFactory.java
│ │ │ │ ├── ProxyFactory.java
│ │ │ │ └── proxyhandlers
│ │ │ │ ├── HtmlElementListNamedProxyHandler.java
│ │ │ │ ├── TypifiedElementListNamedProxyHandler.java
│ │ │ │ ├── WebElementListNamedProxyHandler.java
│ │ │ │ └── WebElementNamedProxyHandler.java
│ │ ├── pagefactory
│ │ │ └── CustomElementLocatorFactory.java
│ │ └── utils
│ │ │ └── HtmlElementUtils.java
│ │ └── ui
│ │ ├── ExtraExpectedConditions.java
│ │ ├── UITestLifecycle.java
│ │ ├── annotations
│ │ ├── ForceVisible.java
│ │ ├── Invisible.java
│ │ └── Visible.java
│ │ ├── browsers
│ │ └── UserAgent.java
│ │ ├── capture
│ │ ├── CaptureEndpoint.java
│ │ ├── ElementHighlighter.java
│ │ ├── ScreenshotCapture.java
│ │ └── model
│ │ │ ├── Browser.java
│ │ │ ├── Command.java
│ │ │ ├── SoftwareUnderTest.java
│ │ │ └── message
│ │ │ ├── CreateExecution.java
│ │ │ └── CreateScreenshot.java
│ │ ├── driver
│ │ ├── AbstractDriver.java
│ │ ├── Driver.java
│ │ ├── DriverSetup.java
│ │ ├── drivers
│ │ │ ├── BrowserStackImpl.java
│ │ │ ├── ChromeImpl.java
│ │ │ ├── EdgeImpl.java
│ │ │ ├── FirefoxImpl.java
│ │ │ ├── GridImpl.java
│ │ │ ├── InternetExplorerImpl.java
│ │ │ ├── SafariImpl.java
│ │ │ └── SauceImpl.java
│ │ ├── lifecycle
│ │ │ ├── DriverLifecycle.java
│ │ │ ├── MultiUseDriverLifecycle.java
│ │ │ └── SingleUseDriverLifecycle.java
│ │ └── remotes
│ │ │ ├── BrowserStack.java
│ │ │ └── Sauce.java
│ │ ├── element
│ │ ├── AbstractStreamTable.java
│ │ ├── OptimisedStreamTable.java
│ │ └── StreamTable.java
│ │ ├── js
│ │ └── JavascriptWait.java
│ │ ├── listeners
│ │ ├── CaptureListener.java
│ │ ├── LoggingListener.java
│ │ ├── SauceLabsListener.java
│ │ ├── ScreenshotListener.java
│ │ └── VideoListener.java
│ │ ├── pages
│ │ ├── BasePage.java
│ │ ├── PageFactory.java
│ │ └── Visibility.java
│ │ ├── proxy
│ │ └── SeleniumProxyFactory.java
│ │ ├── tests
│ │ └── BaseUITest.java
│ │ └── video
│ │ ├── UrlFetcher.java
│ │ └── VideoCapture.java
└── resources
│ ├── Empty.properties
│ ├── Empty.yaml
│ ├── FirefoxGrid.yaml
│ ├── META-INF
│ └── services
│ │ └── org.testng.ITestNGListener
│ └── log4j2.xml
└── test
├── groovy
└── com
│ └── frameworkium
│ └── core
│ ├── api
│ └── dto
│ │ └── AbstractDTOSpec.groovy
│ ├── common
│ ├── reporting
│ │ ├── TestIdUtilsSpec.groovy
│ │ └── jira
│ │ │ ├── service
│ │ │ ├── AttachmentSpec.groovy
│ │ │ ├── IssueLinkSpec.groovy
│ │ │ ├── IssueSpec.groovy
│ │ │ ├── ProjectSpec.groovy
│ │ │ ├── SearchSpec.groovy
│ │ │ └── VersionSpec.groovy
│ │ │ ├── util
│ │ │ ├── ExecutionSearchUtilSpec.groovy
│ │ │ └── ExecutionUtilSpec.groovy
│ │ │ └── zapi
│ │ │ ├── AttachmentSpec.groovy
│ │ │ ├── CycleSpec.groovy
│ │ │ ├── ExecutionSearchSpec.groovy
│ │ │ └── ExecutionSpec.groovy
│ └── retry
│ │ └── RetryFlakyTestSpec.groovy
│ └── ui
│ ├── ExtraExpectedConditionsSpec.groovy
│ ├── capture
│ └── ElementHighlighterSpec.groovy
│ ├── driver
│ └── lifecycle
│ │ ├── MultiUseDriverLifecycleSpec.groovy
│ │ └── SingleUseDriverLifecycleSpec.groovy
│ ├── listeners
│ └── ScreenshotListenerSpec.groovy
│ ├── pages
│ ├── VisibilitySpec.groovy
│ └── pageobjects
│ │ └── PageObjects.groovy
│ ├── proxy
│ └── SeleniumProxyFactorySpec.groovy
│ └── video
│ └── VideoCaptureSpec.groovy
├── java
└── com
│ └── frameworkium
│ ├── core
│ ├── api
│ │ └── dto
│ │ │ ├── LowLevelDTO.java
│ │ │ └── TopLevelDTO.java
│ └── ui
│ │ ├── ExtraExpectedConditionsTest.java
│ │ └── pages
│ │ └── pageobjects
│ │ └── TestPage.java
│ └── integration
│ ├── CustomFirefoxImpl.java
│ ├── angularjs
│ ├── pages
│ │ └── DeveloperGuidePage.java
│ └── tests
│ │ └── DocumentationTest.java
│ ├── capture
│ └── api
│ │ ├── constant
│ │ └── CaptureEndpoint.java
│ │ ├── dto
│ │ ├── executions
│ │ │ ├── Browser.java
│ │ │ ├── CreateExecution.java
│ │ │ ├── ExecutionID.java
│ │ │ ├── ExecutionResponse.java
│ │ │ ├── ExecutionResults.java
│ │ │ └── SoftwareUnderTest.java
│ │ └── screenshots
│ │ │ ├── Command.java
│ │ │ ├── CreateScreenshot.java
│ │ │ └── Screenshot.java
│ │ ├── service
│ │ ├── BaseCaptureService.java
│ │ ├── executions
│ │ │ └── ExecutionService.java
│ │ └── screenshots
│ │ │ └── ScreenshotService.java
│ │ └── tests
│ │ └── CaptureExecutionAPITest.java
│ ├── frameworkium
│ ├── pages
│ │ └── JQueryDemoPage.java
│ └── tests
│ │ └── FrameworkiumBugsTest.java
│ ├── restfulbooker
│ └── api
│ │ ├── constant
│ │ └── BookerEndpoint.java
│ │ ├── dto
│ │ └── booking
│ │ │ ├── Booking.java
│ │ │ ├── BookingDates.java
│ │ │ ├── BookingID.java
│ │ │ ├── BookingResponse.java
│ │ │ ├── CreateBookingResponse.java
│ │ │ └── search
│ │ │ └── SearchParamsMapper.java
│ │ ├── service
│ │ ├── AbstractBookerService.java
│ │ ├── booking
│ │ │ └── BookingService.java
│ │ └── ping
│ │ │ └── PingService.java
│ │ └── tests
│ │ ├── BookerTest.java
│ │ └── SearchBookerTest.java
│ ├── seleniumhq
│ ├── components
│ │ └── HeaderComponent.java
│ ├── pages
│ │ ├── HomePage.java
│ │ └── SeleniumDownloadPage.java
│ └── tests
│ │ └── SeleniumTest.java
│ ├── theinternet
│ ├── pages
│ │ ├── CheckboxesPage.java
│ │ ├── DragAndDropPage.java
│ │ ├── DynamicLoadingExamplePage.java
│ │ ├── HoversPage.java
│ │ ├── JavaScriptAlertsPage.java
│ │ ├── KeyPressesPage.java
│ │ ├── SortableDataTablesPage.java
│ │ └── WelcomePage.java
│ └── tests
│ │ └── TheInternetExampleTests.java
│ ├── wikipedia
│ ├── pages
│ │ ├── EnglishCountiesPage.java
│ │ └── EnglishCountiesUsingListsPage.java
│ └── tests
│ │ └── EnglishCountiesTest.java
│ └── wiremock
│ ├── WireMockJiraSetup.java
│ └── WireMockJiraTeardown.java
└── resources
└── capture-screenshot.png
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Handle line endings automatically for files detected as text
2 | # and leave all files detected as binary untouched.
3 | * text=auto
4 |
5 | #
6 | # The above will handle all files NOT found below
7 | #
8 | # These files are text and should be normalized (Convert crlf => lf)
9 | *.css text
10 | *.df text
11 | *.groovy text
12 | *.htm text
13 | *.html text
14 | *.java text
15 | *.js text
16 | *.json text
17 | *.jsp text
18 | *.jspf text
19 | *.jspx text
20 | *.md text
21 | *.properties text
22 | *.sh text
23 | *.tld text
24 | *.txt text
25 | *.tag text
26 | *.tagx text
27 | *.xml text
28 | *.yml text
29 | *.yaml text
30 |
31 | # These files are binary and should be left untouched
32 | # (binary is a macro for -text -diff)
33 | *.class binary
34 | *.dll binary
35 | *.ear binary
36 | *.gif binary
37 | *.ico binary
38 | *.jar binary
39 | *.jpg binary
40 | *.jpeg binary
41 | *.png binary
42 | *.so binary
43 | *.war binary
44 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a bug issue to help us improve
4 | title: "Bug issue title here"
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the Issue**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. '...'
16 | 2. '....'
17 | 3. See error
18 |
19 | **Expected Behaviour**
20 | A clear and concise description of what you expected to happen.
21 |
22 | **Screenshots**
23 | If applicable, add screenshots to help explain your problem.
24 |
25 | **Versions (please complete the following information):**
26 | - Frameworkium-core version: [e.g. 2.7.2]
27 | - OS: [e.g. Windows 10]
28 | - Browser [e.g. Chrome, Firefox]
29 | - Version [e.g. 59]
30 |
31 | **Additional Context**
32 | Add any other context about the problem here.
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature/Enhancement request
3 | about: Suggest an idea or improvement for this project
4 | title: "Feature request title here"
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | updates:
4 | - package-ecosystem: "maven"
5 | directory: "/"
6 | schedule:
7 | interval: "daily"
8 |
9 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | name-template: '$RESOLVED_VERSION'
2 | tag-template: '$RESOLVED_VERSION'
3 | categories:
4 | - title: 'New Features'
5 | labels:
6 | - 'feature request'
7 | - title: 'Improvements'
8 | labels:
9 | - 'enhancement'
10 | - title: 'Bug Fixes'
11 | labels:
12 | - 'bug'
13 | change-template: '* $TITLE (#$NUMBER) - @$AUTHOR'
14 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
15 | version-resolver:
16 | major:
17 | labels:
18 | - 'major'
19 | minor:
20 | labels:
21 | - 'minor'
22 | patch:
23 | labels:
24 | - 'patch'
25 | default: patch
26 | template: |
27 | ## Changes
28 | $CHANGES
29 | ### Notes
30 | - None
31 |
--------------------------------------------------------------------------------
/.github/workflows/release-draft.yml:
--------------------------------------------------------------------------------
1 | name: Release Draft
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | update_draft_release:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: toolmantim/release-drafter@v5.2.0
13 | env:
14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled source #
2 | ###################
3 | *.class
4 | *.exe
5 |
6 | # Packages #
7 | ############
8 | # it's better to unpack these files and commit the raw source
9 | # git has its own built in compression methods
10 | *.7z
11 | *.dmg
12 | *.gz
13 | *.iso
14 | *.jar
15 | *.rar
16 | *.tar
17 | *.zip
18 |
19 | # Logs and databases #
20 | ######################
21 | *.log
22 |
23 | # OS generated files #
24 | ######################
25 | .DS_Store*
26 | ehthumbs.db
27 | Icon?
28 | Thumbs.db
29 |
30 | # Maven #
31 | #########
32 | target/
33 |
34 | # IDEs #
35 | ########
36 | # IntelliJ #
37 | *.iml
38 | *.ipr
39 | *.iws
40 | .idea
41 | # Eclipse #
42 | .classpath
43 | .metadata/
44 | .project
45 | .settings
46 | .checkstyle
47 | # NetBeans #
48 | nbactions.xml
49 |
50 | # Other things #
51 | ################
52 | ajcore.*.txt
53 | test-output/
54 | logs/
55 | /screenshots/
56 | capturedVideo/
57 | .allure
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Frameworkium Core
2 |
3 | 
4 | 
5 | [![codecov.io][codecov-svg]][codecov]
6 | [![Maintainability][cc-badge]][codeclimate]
7 |
8 | A Framework for writing maintainable Selenium and REST API tests in Java.
9 |
10 | ## Release Notes
11 |
12 | See the [Frameworkium Release Notes][release-notes].
13 |
14 | ## Usage
15 |
16 | To get started you can visit the [Frameworkium documentation][guidance].
17 |
18 | For a "quick start" template and samples see the [Frameworkium examples project][frameworkium-eg].
19 |
20 | [status-svg]: https://travis-ci.org/Frameworkium/frameworkium-core.svg?branch=master
21 | [status]: https://travis-ci.org/Frameworkium/frameworkium-core
22 | [codecov-svg]: https://codecov.io/gh/Frameworkium/frameworkium-core/branch/master/graph/badge.svg
23 | [codecov]: https://codecov.io/gh/Frameworkium/frameworkium-core
24 | [codeclimate]: https://codeclimate.com/github/Frameworkium/frameworkium-core/maintainability
25 | [cc-badge]: https://api.codeclimate.com/v1/badges/72b73eb5861ba89d9d6d/maintainability
26 | [release-notes]: https://github.com/Frameworkium/frameworkium-core/releases
27 | [frameworkium-eg]: https://github.com/Frameworkium/frameworkium-examples
28 | [guidance]: https://frameworkium.github.io
29 |
--------------------------------------------------------------------------------
/docker-compose-ci.yml:
--------------------------------------------------------------------------------
1 | version: "3.9"
2 | services:
3 | wiremock:
4 | image: rodolpheche/wiremock:2.27.2
5 | ports:
6 | - "8080:8080"
7 | command: [ 'rodolpheche/wiremock', '--port 8080', '--verbose', '--record-mappings' ]
8 | volumes:
9 | - "./src/test/resources/mappings:/home/wiremock"
10 | healthcheck:
11 | test: [ "CMD", "curl", "-f", "http://localhost", "||", "exit 1" ]
12 | interval: 30s
13 | timeout: 10s
14 | retries: 3
15 | start_period: 3s
16 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk11
3 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/api/Endpoint.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.api;
2 |
3 | /**
4 | * Intended for an enum to store the various endpoints of your API under test.
5 | *
6 | *
The following is an example implementation:
7 | *
{@code
8 | * public enum MyEndpoint implements Endpoint {
9 | *
10 | * BASE_URI("https://xxx),
11 | * YYY_ID("/yyy/%d");
12 | *
13 | * private String url;
14 | *
15 | * MyEndpoint(String url) {
16 | * this.url = url;
17 | * }
18 | *
19 | * public String getUrl(Object... params) {
20 | * return String.format(url, params);
21 | * }
22 | * }}
23 | *
24 | * The key feature is an enum entry for each endpoint where the url String can
25 | * contain a {@code String.format} to enable easy injection of parameters.
26 | */
27 | public interface Endpoint {
28 |
29 | /**
30 | * Calls {@link String#format(String, Object...)} on the url.
31 | *
32 | * @param params Arguments referenced by the format specifiers in the url.
33 | * @return A formatted String representing the URL of the given constant.
34 | */
35 | String getUrl(Object... params);
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/api/dto/AbstractDTO.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.api.dto;
2 |
3 | import java.io.Serializable;
4 | import org.apache.commons.lang3.SerializationUtils;
5 | import org.apache.commons.lang3.builder.EqualsBuilder;
6 | import org.apache.commons.lang3.builder.HashCodeBuilder;
7 | import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
8 | import org.apache.commons.lang3.builder.ToStringStyle;
9 |
10 | public abstract class AbstractDTO implements Serializable, Cloneable {
11 |
12 | @Override
13 | public int hashCode() {
14 | return HashCodeBuilder.reflectionHashCode(this);
15 | }
16 |
17 | @Override
18 | public boolean equals(Object obj) {
19 | return EqualsBuilder.reflectionEquals(this, obj);
20 | }
21 |
22 | @Override
23 | @SuppressWarnings("unchecked")
24 | protected T clone() throws CloneNotSupportedException {
25 | try {
26 | return (T) SerializationUtils.clone(this);
27 | } catch (Exception e) {
28 | throw new CloneNotSupportedException(e.getMessage());
29 | }
30 | }
31 |
32 | @Override
33 | public String toString() {
34 | return ReflectionToStringBuilder.toString(
35 | this, ToStringStyle.SHORT_PREFIX_STYLE);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/api/tests/BaseAPITest.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.api.tests;
2 |
3 | import com.frameworkium.core.common.reporting.allure.AllureProperties;
4 | import org.apache.logging.log4j.LogManager;
5 | import org.apache.logging.log4j.Logger;
6 | import org.testng.annotations.AfterSuite;
7 | import org.testng.annotations.Test;
8 |
9 | // Uses the listeners in main.resources.META-INF.services.org.testng.ITestNGListener
10 | @Test(groups = "base-api")
11 | public abstract class BaseAPITest {
12 |
13 | protected final Logger logger = LogManager.getLogger(this);
14 |
15 | /**
16 | * Creates the allure properties for the report, after the test run.
17 | */
18 | @AfterSuite(alwaysRun = true)
19 | public static void createAllureProperties() {
20 | AllureProperties.createAPI();
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/listeners/MethodInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.listeners;
2 |
3 | import static com.frameworkium.core.common.properties.Property.JIRA_URL;
4 | import static com.frameworkium.core.common.properties.Property.JQL_QUERY;
5 | import static java.util.stream.Collectors.joining;
6 | import static java.util.stream.Collectors.toList;
7 |
8 | import com.frameworkium.core.common.reporting.TestIdUtils;
9 | import com.frameworkium.core.common.reporting.jira.service.Search;
10 | import java.lang.reflect.Method;
11 | import java.util.List;
12 | import org.apache.logging.log4j.LogManager;
13 | import org.apache.logging.log4j.Logger;
14 | import org.testng.IMethodInstance;
15 | import org.testng.IMethodInterceptor;
16 | import org.testng.ITestContext;
17 |
18 | public class MethodInterceptor implements IMethodInterceptor {
19 |
20 | private static final Logger logger = LogManager.getLogger();
21 |
22 | @Override
23 | public List intercept(
24 | List methods, ITestContext context) {
25 |
26 | return filterTestsToRunByJQL(methods);
27 | }
28 |
29 | private List filterTestsToRunByJQL(
30 | List methodsToBeFiltered) {
31 |
32 | if (!(JQL_QUERY.isSpecified() && JIRA_URL.isSpecified())) {
33 | // Can't run the JQL without both JIRA_URL and JQL_QUERY
34 | return methodsToBeFiltered;
35 | }
36 |
37 | logger.info("Filtering specified tests to run with JQL query results");
38 |
39 | List testIDsFromJQL =
40 | new Search(JQL_QUERY.getValue()).getKeys();
41 |
42 | List methodsToRun = methodsToBeFiltered.stream()
43 | .filter(m -> TestIdUtils.getIssueOrTmsLinkValue(m).isPresent())
44 | .filter(m -> testIDsFromJQL.contains(
45 | TestIdUtils.getIssueOrTmsLinkValue(m).orElseThrow(IllegalStateException::new)))
46 | .collect(toList());
47 |
48 | logTestMethodInformation(methodsToRun);
49 |
50 | return methodsToRun;
51 | }
52 |
53 | private void logTestMethodInformation(List methodsToRun) {
54 |
55 | logger.debug("Running the following test methods:\n{}", () ->
56 | methodsToRun.stream()
57 | .map(m -> getMethodFromIMethod(m).getName())
58 | .collect(joining("\n")));
59 |
60 | logger.info("Running {} tests specified by JQL query", methodsToRun.size());
61 | }
62 |
63 | private Method getMethodFromIMethod(IMethodInstance iMethod) {
64 | return iMethod.getMethod().getConstructorOrMethod().getMethod();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/listeners/TestListener.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.listeners;
2 |
3 | import org.apache.logging.log4j.LogManager;
4 | import org.apache.logging.log4j.Logger;
5 | import org.testng.ITestContext;
6 | import org.testng.ITestListener;
7 | import org.testng.ITestResult;
8 | import org.testng.SkipException;
9 |
10 | public class TestListener implements ITestListener {
11 |
12 | private final Logger logger = LogManager.getLogger();
13 |
14 | @Override
15 | public void onTestStart(ITestResult result) {
16 | logger.info("START {}", getTestIdentifier(result));
17 | }
18 |
19 | @Override
20 | public void onTestSuccess(ITestResult result) {
21 | logger.info("PASS {}", getTestIdentifier(result));
22 | }
23 |
24 | @Override
25 | public void onTestFailure(ITestResult result) {
26 | logger.error("FAIL {}", getTestIdentifier(result));
27 | Throwable cause = result.getThrowable();
28 | if (cause != null) {
29 | logger.error(cause.getMessage(), cause);
30 | }
31 | }
32 |
33 | @Override
34 | public void onTestSkipped(ITestResult result) {
35 | logger.warn("SKIP {}", getTestIdentifier(result));
36 | Throwable cause = result.getThrowable();
37 | if (cause != null && SkipException.class.isAssignableFrom(cause.getClass())) {
38 | logger.warn(cause.getMessage());
39 | }
40 | }
41 |
42 | private String getTestIdentifier(ITestResult result) {
43 | return String.format("%s.%s",
44 | result.getInstanceName(),
45 | result.getMethod().getMethodName());
46 | }
47 |
48 | @Override
49 | public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
50 | }
51 |
52 | @Override
53 | public void onStart(ITestContext context) {
54 | }
55 |
56 | @Override
57 | public void onFinish(ITestContext context) {
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/allure/AllureLogger.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.allure;
2 |
3 | import static io.qameta.allure.Allure.getLifecycle;
4 |
5 | import io.qameta.allure.Step;
6 | import io.qameta.allure.model.StepResult;
7 | import java.util.ArrayDeque;
8 | import java.util.Deque;
9 | import java.util.UUID;
10 | import org.apache.logging.log4j.LogManager;
11 | import org.apache.logging.log4j.Logger;
12 |
13 | public class AllureLogger {
14 |
15 | private static final Logger logger = LogManager.getLogger();
16 | private static final ThreadLocal> STEP_UUID_STACK =
17 | ThreadLocal.withInitial(ArrayDeque::new);
18 |
19 | private AllureLogger() {
20 | // hide default constructor for this util class
21 | }
22 |
23 | /**
24 | * Uses the @Step annotation to log the given log message to Allure.
25 | *
26 | * @param message the message to log to the allure report
27 | */
28 | @Step("{message}")
29 | public static void logToAllure(String message) {
30 | logger.debug("Logged to allure: " + message);
31 | }
32 |
33 | public static void stepStart(String stepName) {
34 | StepResult result = new StepResult().setName(stepName);
35 | String uuid = UUID.randomUUID().toString();
36 | getLifecycle().startStep(uuid, result);
37 | STEP_UUID_STACK.get().addFirst(uuid);
38 | }
39 |
40 | public static void stepFinish() {
41 | getLifecycle().stopStep(STEP_UUID_STACK.get().removeFirst());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/JiraConfig.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira;
2 |
3 | public class JiraConfig {
4 | private JiraConfig() {
5 | // hide default constructor for this util class
6 | }
7 |
8 | /**
9 | * These should correspond to your ZAPI result IDs and
10 | * are only used if logging to Zephyr for JIRA.
11 | */
12 | public static class ZapiStatus {
13 |
14 | public static final int ZAPI_STATUS_PASS = 1;
15 | public static final int ZAPI_STATUS_FAIL = 2;
16 | public static final int ZAPI_STATUS_WIP = 3;
17 | public static final int ZAPI_STATUS_BLOCKED = 4;
18 | }
19 |
20 | /**
21 | * These should correspond to your field options
22 | * if logging a test result to a field.
23 | */
24 | public static class JiraFieldStatus {
25 | public static final String JIRA_STATUS_PASS = "Pass";
26 | public static final String JIRA_STATUS_FAIL = "Fail";
27 | public static final String JIRA_STATUS_WIP = "WIP";
28 | public static final String JIRA_STATUS_BLOCKED = "Blocked";
29 | }
30 |
31 | /**
32 | * These should correspond to the workflow transition names required to mark
33 | * the result if using a customised jira issue type & workflow to manage
34 | * tests NB - put all required transitions to get between statuses
35 | * (e.g. restart, then mark result) - each will be tried & ignored if not possible
36 | */
37 | public static class JiraTransition {
38 | public static final String[] JIRA_TRANSITION_PASS = {"Done"};
39 | public static final String[] JIRA_TRANSITION_FAIL = {"Done"};
40 | public static final String[] JIRA_TRANSITION_WIP = {"Reopen", "Start Progress"};
41 | public static final String[] JIRA_TRANSITION_BLOCKED = {"Done"};
42 | }
43 |
44 | /**
45 | * These should correspond to your field options
46 | * if logging a test result to a field.
47 | */
48 | public static class SpiraStatus {
49 | public static final int SPIRA_STATUS_PASS = 2;
50 | public static final int SPIRA_STATUS_FAIL = 1;
51 | public static final int SPIRA_STATUS_WIP = 4;
52 | public static final int SPIRA_STATUS_BLOCKED = 5;
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/attachment/AttachmentDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.attachment;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | public class AttachmentDto extends AbstractDTO {
6 | public String fileName;
7 | public String dateCreated;
8 | public String fileSize;
9 | public String fileIcon;
10 | public String author;
11 | public String fileIconAltText;
12 | public String comment;
13 | public String fileId;
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/attachment/AttachmentListDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.attachment;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 | import java.util.List;
5 |
6 | public class AttachmentListDto extends AbstractDTO {
7 | public List data;
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/cycle/CreateCycleSuccessDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.cycle;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | public class CreateCycleSuccessDto extends AbstractDTO {
6 | public String id;
7 | public String responseMessage;
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/cycle/CreateNewCycleDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.cycle;
2 |
3 | import com.fasterxml.jackson.annotation.JsonUnwrapped;
4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
5 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
6 | import com.frameworkium.core.api.dto.AbstractDTO;
7 |
8 | @JsonDeserialize(builder = CreateNewCycleDto.Builder.class)
9 | public class CreateNewCycleDto extends AbstractDTO {
10 | @JsonUnwrapped
11 | public CycleLightDto cycleLightDto;
12 | public String cloneCycleId;
13 | public String sprintId;
14 |
15 | private CreateNewCycleDto(final Builder builder) {
16 | cycleLightDto = builder.cycleLightDto;
17 | cloneCycleId = builder.cloneCycleId;
18 | sprintId = builder.sprintId;
19 | }
20 |
21 | public static Builder newBuilder() {
22 | return new Builder();
23 | }
24 |
25 | @JsonPOJOBuilder(withPrefix = "")
26 | public static final class Builder {
27 | @JsonUnwrapped
28 | private CycleLightDto cycleLightDto;
29 | private String cloneCycleId;
30 | private String sprintId;
31 |
32 | private Builder() {
33 | }
34 |
35 | public Builder cycleLightDto(final CycleLightDto cycleLightDto) {
36 | this.cycleLightDto = cycleLightDto;
37 | return this;
38 | }
39 |
40 | public Builder cloneCycleId(final String cloneCycleId) {
41 | this.cloneCycleId = cloneCycleId;
42 | return this;
43 | }
44 |
45 | public Builder sprintId(final String sprintId) {
46 | this.sprintId = sprintId;
47 | return this;
48 | }
49 |
50 | public CreateNewCycleDto build() {
51 | return new CreateNewCycleDto(this);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/cycle/CycleLightDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.cycle;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
5 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
6 | import com.frameworkium.core.api.dto.AbstractDTO;
7 | import java.time.LocalDate;
8 |
9 | @JsonDeserialize(builder = CycleLightDto.Builder.class)
10 | public class CycleLightDto extends AbstractDTO {
11 | public String name;
12 | public String build;
13 | public String environment;
14 | public String description;
15 | public LocalDate startDate;
16 | public LocalDate endDate;
17 | public Long projectId;
18 | public Long versionId;
19 |
20 | private CycleLightDto(final Builder builder) {
21 | name = builder.name;
22 | build = builder.build;
23 | environment = builder.environment;
24 | description = builder.description;
25 | startDate = builder.startDate;
26 | endDate = builder.endDate;
27 | projectId = builder.projectId;
28 | versionId = builder.versionId;
29 | }
30 |
31 | public static Builder newBuilder() {
32 | return new Builder();
33 | }
34 |
35 | @JsonPOJOBuilder(withPrefix = "")
36 | public static final class Builder {
37 | private String name;
38 | private String build;
39 | private String environment;
40 | private String description;
41 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "d/MMM/yy",
42 | with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_VALUES)
43 | private LocalDate startDate;
44 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "d/MMM/yy",
45 | with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_VALUES)
46 | private LocalDate endDate;
47 | private Long projectId;
48 | private Long versionId;
49 |
50 | private Builder() {
51 | }
52 |
53 | public Builder name(final String name) {
54 | this.name = name;
55 | return this;
56 | }
57 |
58 | public Builder environment(final String environment) {
59 | this.environment = environment;
60 | return this;
61 | }
62 |
63 | public Builder description(final String description) {
64 | this.description = description;
65 | return this;
66 | }
67 |
68 | public Builder startDate(final LocalDate startDate) {
69 | this.startDate = startDate;
70 | return this;
71 | }
72 |
73 | public Builder endDate(final LocalDate endDate) {
74 | this.endDate = endDate;
75 | return this;
76 | }
77 |
78 | public Builder projectId(final Long projectId) {
79 | this.projectId = projectId;
80 | return this;
81 | }
82 |
83 | public Builder versionId(final Long versionId) {
84 | this.versionId = versionId;
85 | return this;
86 | }
87 |
88 | public CycleLightDto build() {
89 | return new CycleLightDto(this);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/cycle/CycleListDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.cycle;
2 |
3 | import com.fasterxml.jackson.annotation.JsonAnyGetter;
4 | import com.fasterxml.jackson.annotation.JsonAnySetter;
5 | import com.fasterxml.jackson.annotation.JsonCreator;
6 | import com.fasterxml.jackson.annotation.JsonProperty;
7 | import com.frameworkium.core.api.dto.AbstractDTO;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | public class CycleListDto extends AbstractDTO {
12 | public Map map = new HashMap<>();
13 | public Long recordsCount;
14 |
15 | @JsonCreator
16 | public CycleListDto(@JsonProperty("recordsCount") Long recordsCount) {
17 | this.recordsCount = recordsCount;
18 | }
19 |
20 | @JsonAnySetter
21 | public void setMap(String key, CycleDto cycleDto) {
22 | map.put(key, cycleDto);
23 | }
24 |
25 | @JsonAnyGetter
26 | public Map getMap() {
27 | return map;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/execution/AddTestToCycleOperationDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.execution;
2 |
3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
4 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize;
6 | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
7 | import com.frameworkium.core.api.dto.AbstractDTO;
8 | import java.util.List;
9 |
10 | @JsonDeserialize(builder = AddTestToCycleOperationDto.Builder.class)
11 | public class AddTestToCycleOperationDto extends AbstractDTO {
12 | public String cycleId;
13 | public List issues;
14 | public String searchId;
15 | public String method;
16 | @JsonSerialize(using = ToStringSerializer.class)
17 | public Long projectId;
18 | @JsonSerialize(using = ToStringSerializer.class)
19 | public Long versionId;
20 |
21 |
22 | private AddTestToCycleOperationDto(final Builder builder) {
23 | cycleId = builder.cycleId;
24 | issues = builder.issues;
25 | searchId = builder.searchId;
26 | method = builder.method;
27 | projectId = builder.projectId;
28 | versionId = builder.versionId;
29 | }
30 |
31 | public static Builder newBuilder() {
32 | return new Builder();
33 | }
34 |
35 | @JsonPOJOBuilder(withPrefix = "")
36 | public static final class Builder {
37 | private String cycleId;
38 | private List issues;
39 | private String searchId;
40 | private String method;
41 | private Long projectId;
42 | private Long versionId;
43 |
44 | private Builder() {
45 | }
46 |
47 | public Builder cycleId(final String cycleId) {
48 | this.cycleId = cycleId;
49 | return this;
50 | }
51 |
52 | public Builder issues(final List issues) {
53 | this.issues = issues;
54 | return this;
55 | }
56 |
57 | public Builder searchId(final String searchId) {
58 | this.searchId = searchId;
59 | return this;
60 | }
61 |
62 | public Builder method(final String method) {
63 | this.method = method;
64 | return this;
65 | }
66 |
67 | public Builder projectId(final Long projectId) {
68 | this.projectId = projectId;
69 | return this;
70 | }
71 |
72 | public Builder versionId(final Long versionId) {
73 | this.versionId = versionId;
74 | return this;
75 | }
76 |
77 | public AddTestToCycleOperationDto build() {
78 | return new AddTestToCycleOperationDto(this);
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/execution/ExecutionDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.execution;
2 |
3 | import com.fasterxml.jackson.annotation.JsonUnwrapped;
4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
5 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
6 | import com.frameworkium.core.api.dto.AbstractDTO;
7 |
8 | @JsonDeserialize(builder = ExecutionDto.Builder.class)
9 | public class ExecutionDto extends AbstractDTO {
10 | @JsonUnwrapped
11 | public ExecutionLightDto executionLightDto;
12 | public String executionStatus;
13 | public String createdBy;
14 | public String modifiedBy;
15 | public Long issueId;
16 | public String summary;
17 | public String label;
18 | public String component;
19 |
20 | private ExecutionDto(Builder builder) {
21 | this.executionLightDto = builder.executionLightDto;
22 | this.executionStatus = builder.executionStatus;
23 | this.createdBy = builder.createdBy;
24 | this.modifiedBy = builder.modifiedBy;
25 | this.issueId = builder.issueId;
26 | this.summary = builder.summary;
27 | this.label = builder.label;
28 | this.component = builder.component;
29 | }
30 |
31 | public static ExecutionDto.Builder newBuilder() {
32 | return new ExecutionDto.Builder();
33 | }
34 |
35 | @JsonPOJOBuilder(withPrefix = "")
36 | public static final class Builder {
37 | private ExecutionLightDto executionLightDto;
38 | private String executionStatus;
39 | private String createdBy;
40 | private String modifiedBy;
41 | private Long issueId;
42 | private String summary;
43 | private String label;
44 | private String component;
45 |
46 | private Builder() {
47 | }
48 |
49 | public Builder executionLightDto(ExecutionLightDto executionLightDto) {
50 | this.executionLightDto = executionLightDto;
51 | return this;
52 | }
53 |
54 | public Builder executionStatus(String executionStatus) {
55 | this.executionStatus = executionStatus;
56 | return this;
57 | }
58 |
59 | public Builder createdBy(String createdBy) {
60 | this.createdBy = createdBy;
61 | return this;
62 | }
63 |
64 | public Builder modifiedBy(String modifiedBy) {
65 | this.modifiedBy = modifiedBy;
66 | return this;
67 | }
68 |
69 | public Builder issueId(Long issueId) {
70 | this.issueId = issueId;
71 | return this;
72 | }
73 |
74 | public Builder summary(String summary) {
75 | this.summary = summary;
76 | return this;
77 | }
78 |
79 | public Builder label(String label) {
80 | this.label = label;
81 | return this;
82 | }
83 |
84 | public Builder component(String component) {
85 | this.component = component;
86 | return this;
87 | }
88 |
89 | public ExecutionDto build() {
90 | return new ExecutionDto(this);
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/execution/UpdateExecutionOperationDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.execution;
2 |
3 | import com.fasterxml.jackson.annotation.JsonUnwrapped;
4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
5 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
6 | import com.frameworkium.core.api.dto.AbstractDTO;
7 |
8 | @JsonDeserialize(builder = UpdateExecutionOperationDto.Builder.class)
9 | public class UpdateExecutionOperationDto extends AbstractDTO {
10 | @JsonUnwrapped
11 | public ExecutionDto executionDto;
12 | public String executedOn;
13 | public String executionBy;
14 | public String executedByDisplay;
15 | public Integer status;
16 |
17 | private UpdateExecutionOperationDto(Builder builder) {
18 | this.executionBy = builder.executionBy;
19 | this.executionDto = builder.executionDto;
20 | this.executedOn = builder.executedOn;
21 | this.executedByDisplay = builder.executedByDisplay;
22 | this.status = builder.status;
23 | }
24 |
25 | public static UpdateExecutionOperationDto.Builder newBuilder() {
26 | return new UpdateExecutionOperationDto.Builder();
27 | }
28 |
29 | @JsonPOJOBuilder(withPrefix = "")
30 | public static final class Builder {
31 | private ExecutionDto executionDto;
32 | private String executedOn;
33 | private String executionBy;
34 | private String executedByDisplay;
35 | private Integer status;
36 |
37 | private Builder() {
38 | }
39 |
40 | public Builder executionDto(ExecutionDto executionDto) {
41 | this.executionDto = executionDto;
42 | return this;
43 | }
44 |
45 | public Builder executedOn(String executedOn) {
46 | this.executedOn = executedOn;
47 | return this;
48 | }
49 |
50 | public Builder executionBy(String executionBy) {
51 | this.executionBy = executionBy;
52 | return this;
53 | }
54 |
55 | public Builder executedByDisplay(String executedByDisplay) {
56 | this.executedByDisplay = executedByDisplay;
57 | return this;
58 | }
59 |
60 | public Builder status(Integer status) {
61 | this.status = status;
62 | return this;
63 | }
64 |
65 | public UpdateExecutionOperationDto build() {
66 | return new UpdateExecutionOperationDto(this);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/executionsearch/ExecutionSearchDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.executionsearch;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import com.fasterxml.jackson.annotation.JsonUnwrapped;
5 | import com.frameworkium.core.api.dto.AbstractDTO;
6 | import com.frameworkium.core.common.reporting.jira.dto.execution.ExecutionLightDto;
7 | import com.frameworkium.core.common.reporting.jira.dto.status.StatusDto;
8 | import java.time.LocalDate;
9 | import java.util.List;
10 |
11 | public class ExecutionSearchDto extends AbstractDTO {
12 | @JsonUnwrapped
13 | public ExecutionLightDto executionLightDto;
14 | public String issueId;
15 | public String issueSummary;
16 | public List labels;
17 | public String issueDescription;
18 | public String project;
19 | public Long projectAvatarId;
20 | public String priority;
21 | public List components;
22 | public StatusDto status;
23 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yy/MMM/dd")
24 | public LocalDate executedOn;
25 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yy/MMM/dd")
26 | public LocalDate creationDate;
27 | public String executedBy;
28 | public String executedByUserName;
29 | public List executionDefects;
30 | public List stepDefects;
31 | public Long executionDefectCount;
32 | public Long stepDefectCount;
33 | public Long totalDefectCount;
34 | public String executedByDisplay;
35 | public String assignee;
36 | public String assigneeUserName;
37 | public String assigneeDisplay;
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/executionsearch/ExecutionSearchListDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.executionsearch;
2 |
3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
4 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
5 | import com.frameworkium.core.api.dto.AbstractDTO;
6 | import java.util.List;
7 |
8 | @JsonDeserialize(builder = ExecutionSearchListDto.Builder.class)
9 | public class ExecutionSearchListDto extends AbstractDTO {
10 | public List executions;
11 | public Long currentIndex;
12 | public Long maxResultAllowed;
13 | public List linksNew;
14 | public Long totalCount;
15 | public List executionIds;
16 |
17 | public ExecutionSearchListDto(Builder builder) {
18 | this.executions = builder.executions;
19 | this.currentIndex = builder.currentIndex;
20 | this.maxResultAllowed = builder.maxResultAllowed;
21 | this.linksNew = builder.linksNew;
22 | this.totalCount = builder.totalCount;
23 | this.executionIds = builder.executionIds;
24 | }
25 |
26 | public static ExecutionSearchListDto.Builder newBuilder() {
27 | return new ExecutionSearchListDto.Builder();
28 | }
29 |
30 | @JsonPOJOBuilder(withPrefix = "")
31 | public static final class Builder {
32 | private List executions;
33 | private Long currentIndex;
34 | private Long maxResultAllowed;
35 | private List linksNew;
36 | private Long totalCount;
37 | private List executionIds;
38 |
39 | public Builder executions(final List executions) {
40 | this.executions = executions;
41 | return this;
42 | }
43 |
44 | public Builder currentIndex(final Long currentIndex) {
45 | this.currentIndex = currentIndex;
46 | return this;
47 | }
48 |
49 | public Builder maxResultAllowed(final Long maxResultAllowed) {
50 | this.maxResultAllowed = maxResultAllowed;
51 | return this;
52 | }
53 |
54 | public Builder linksNew(final List linksNew) {
55 | this.linksNew = linksNew;
56 | return this;
57 | }
58 |
59 | public Builder totalCount(final Long totalCount) {
60 | this.totalCount = totalCount;
61 | return this;
62 | }
63 |
64 | public Builder executionIds(final List executionIds) {
65 | this.executionIds = executionIds;
66 | return this;
67 | }
68 |
69 | public ExecutionSearchListDto build() {
70 | return new ExecutionSearchListDto(this);
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/project/ProjectDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.project;
2 |
3 | import com.fasterxml.jackson.databind.annotation.JsonSerialize;
4 | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
5 | import com.frameworkium.core.api.dto.AbstractDTO;
6 |
7 | public class ProjectDto extends AbstractDTO {
8 | @JsonSerialize(using = ToStringSerializer.class)
9 | public Long id;
10 | public String key;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/dto/status/StatusDto.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.dto.status;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | public class StatusDto extends AbstractDTO {
6 | public Long id;
7 | public String name;
8 | public String description;
9 | public String color;
10 | public Long type;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/endpoint/JiraEndpoint.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.endpoint;
2 |
3 | import com.frameworkium.core.api.Endpoint;
4 | import com.frameworkium.core.common.properties.Property;
5 |
6 | public enum JiraEndpoint implements Endpoint {
7 | BASE_URI(Property.JIRA_URL.getValue()),
8 | REST_API_PATH("/rest/api/2"),
9 | PROJECT("/project"),
10 | SEARCH("/search"),
11 | ISSUE("/issue"),
12 | ISSUELINK("/issueLink"),
13 | FIELD("/field"),
14 | VERSION("/version");
15 |
16 | private final String url;
17 |
18 | JiraEndpoint(String url) {
19 | this.url = url;
20 | }
21 |
22 | /**
23 | * @param params Arguments referenced by the format specifiers in the url.
24 | * @return A formatted String representing the URL of the given constant.
25 | */
26 | @Override
27 | public String getUrl(Object... params) {
28 | if (url.equals(REST_API_PATH.url) || url.equals(BASE_URI.url)) {
29 | return String.format(url, params);
30 | }
31 | // returns with the rest API path e.g. /rest/api/2/issue
32 | String urlWithRestAPIPath = REST_API_PATH.url + url;
33 | return String.format(urlWithRestAPIPath, params);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/endpoint/ZephyrEndpoint.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.endpoint;
2 |
3 | import com.frameworkium.core.api.Endpoint;
4 | import com.frameworkium.core.common.properties.Property;
5 |
6 | public enum ZephyrEndpoint implements Endpoint {
7 | BASE_URI(Property.JIRA_URL.getValue()),
8 | REST_API_PATH("/rest/zapi/latest"),
9 | EXECUTION("/execution"),
10 | ADD_TEST_TO_EXECUTION("/execution/addTestsToCycle"),
11 | EXECUTE_SEARCH("/zql/executeSearch"),
12 | CYCLE("/cycle"),
13 | ATTACHMENT("/attachment"),
14 | ATTACHMENT_BY_ENTITY("/attachment/attachmentsByEntity");
15 |
16 | private final String url;
17 |
18 | ZephyrEndpoint(final String url) {
19 | this.url = url;
20 | }
21 |
22 |
23 | /**
24 | * @param params Arguments referenced by the format specifiers in the url.
25 | * @return A formatted String representing the URL of the given constant.
26 | */
27 | @Override
28 | public String getUrl(Object... params) {
29 | if (url.equals(REST_API_PATH.url) || url.equals(BASE_URI.url)) {
30 | return url;
31 | }
32 | // returns with the rest API path e.g. /rest/api/2/issue
33 | String urlWithRestAPIPath = REST_API_PATH.url + url;
34 | return String.format(urlWithRestAPIPath, params);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/service/AbstractJiraService.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.service;
2 |
3 | import com.fasterxml.jackson.annotation.JsonAutoDetect;
4 | import com.fasterxml.jackson.annotation.JsonInclude;
5 | import com.fasterxml.jackson.annotation.PropertyAccessor;
6 | import com.fasterxml.jackson.databind.DeserializationFeature;
7 | import com.fasterxml.jackson.databind.ObjectMapper;
8 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
9 | import com.frameworkium.core.api.services.BaseService;
10 | import com.frameworkium.core.common.properties.Property;
11 | import com.frameworkium.core.common.reporting.jira.endpoint.JiraEndpoint;
12 | import io.restassured.RestAssured;
13 | import io.restassured.config.ObjectMapperConfig;
14 | import io.restassured.config.RestAssuredConfig;
15 | import io.restassured.specification.RequestSpecification;
16 | import io.restassured.specification.ResponseSpecification;
17 |
18 | public abstract class AbstractJiraService extends BaseService {
19 | @Override
20 | protected RequestSpecification getRequestSpec() {
21 | return RestAssured.given().log().ifValidationFails()
22 | .baseUri(JiraEndpoint.BASE_URI.getUrl())
23 | .config(config())
24 | .relaxedHTTPSValidation()
25 | .auth().preemptive().basic(
26 | Property.JIRA_USERNAME.getValue(),
27 | Property.JIRA_PASSWORD.getValue());
28 | }
29 |
30 | @Override
31 | protected ResponseSpecification getResponseSpec() {
32 | throw new UnsupportedOperationException("Unimplemented");
33 | }
34 |
35 | private RestAssuredConfig config() {
36 | return RestAssuredConfig.config().objectMapperConfig(
37 | ObjectMapperConfig.objectMapperConfig().jackson2ObjectMapperFactory(
38 | (type, s) -> {
39 | final ObjectMapper objectMapper = new ObjectMapper();
40 | objectMapper.registerModule(new JavaTimeModule());
41 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
42 | objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
43 | objectMapper.setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE);
44 | objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
45 | objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
46 | return objectMapper;
47 | }
48 | )
49 | );
50 | }
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/service/Attachment.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.service;
2 |
3 | import java.util.List;
4 |
5 | public class Attachment extends AbstractJiraService {
6 | private final Issue issue;
7 |
8 | public Attachment(Issue issue) {
9 | this.issue = issue;
10 | }
11 |
12 | /**
13 | * Returns list of attachment IDs.
14 | */
15 | public List getIds() {
16 | return issue.getIssue().getList("fields.attachment.id");
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/service/IssueLink.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.service;
2 |
3 | import com.frameworkium.core.common.reporting.jira.endpoint.JiraEndpoint;
4 | import io.restassured.http.ContentType;
5 | import org.json.JSONException;
6 | import org.json.JSONObject;
7 |
8 | public class IssueLink extends AbstractJiraService {
9 | /**
10 | * Creates an issue link between two issues.
11 | *
12 | * @param type name of issue
13 | * @param inwardIssue inward issue key
14 | * @param outwardIssue outward issue key
15 | */
16 | public void linkIssues(String type, String inwardIssue, String outwardIssue) {
17 | JSONObject obj = new JSONObject();
18 | JSONObject typeObj = new JSONObject();
19 | JSONObject inwardIssueObj = new JSONObject();
20 | JSONObject outwardIssueObj = new JSONObject();
21 |
22 | try {
23 | obj.put("type", typeObj);
24 | typeObj.put("name", type);
25 | obj.put("inwardIssue", inwardIssueObj);
26 | inwardIssueObj.put("key", inwardIssue);
27 | obj.put("outwardIssue", outwardIssueObj);
28 | outwardIssueObj.put("key", outwardIssue);
29 | } catch (JSONException e) {
30 | logger.error("Can't create JSON Object for linkIssues", e);
31 | }
32 |
33 | getRequestSpec().log().ifValidationFails()
34 | .basePath(JiraEndpoint.ISSUELINK.getUrl())
35 | .contentType(ContentType.JSON)
36 | .body(obj.toString())
37 | .when()
38 | .post();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/service/Project.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.service;
2 |
3 | import static org.apache.http.HttpStatus.SC_OK;
4 |
5 | import com.frameworkium.core.common.reporting.jira.dto.project.ProjectDto;
6 | import com.frameworkium.core.common.reporting.jira.dto.version.VersionDto;
7 | import com.frameworkium.core.common.reporting.jira.endpoint.JiraEndpoint;
8 | import io.restassured.http.ContentType;
9 | import java.util.List;
10 |
11 | public class Project extends AbstractJiraService {
12 | public ProjectDto getProject(String projectIdOrKey) {
13 | return getRequestSpec()
14 | .basePath(JiraEndpoint.PROJECT.getUrl())
15 | .pathParam("projectIdOrKey", projectIdOrKey)
16 | .contentType(ContentType.JSON)
17 | .get("/{projectIdOrKey}")
18 | .then()
19 | .log().ifValidationFails()
20 | .statusCode(SC_OK)
21 | .extract()
22 | .as(ProjectDto.class);
23 | }
24 |
25 | public List getProjectVersions(String projectIdOrKey) {
26 | return getRequestSpec()
27 | .basePath(JiraEndpoint.PROJECT.getUrl())
28 | .pathParam("projectIdOrKey", projectIdOrKey)
29 | .get("/{projectIdOrKey}/versions")
30 | .then()
31 | .log().ifValidationFails()
32 | .statusCode(SC_OK)
33 | .extract()
34 | .body().jsonPath()
35 | .getList("", VersionDto.class);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/service/Search.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.service;
2 |
3 | import com.frameworkium.core.common.reporting.jira.endpoint.JiraEndpoint;
4 | import io.restassured.path.json.JsonPath;
5 | import java.util.List;
6 |
7 | public class Search extends AbstractJiraService {
8 | private static final int MAX_SEARCH_RESULTS = 1000;
9 | protected JsonPath searchResults;
10 |
11 | public Search(final String query) {
12 | this(query, 0, MAX_SEARCH_RESULTS);
13 | }
14 |
15 | public Search(final String query, final int startAt, final int maxSearchResults) {
16 | try {
17 | this.searchResults = getRequestSpec().log().ifValidationFails()
18 | .basePath(JiraEndpoint.SEARCH.getUrl())
19 | .param("jql", query)
20 | .param("startAt", startAt)
21 | .param("maxResults", maxSearchResults)
22 | .when()
23 | .get()
24 | .then().log().ifValidationFails()
25 | .extract().jsonPath();
26 | } catch (Exception e) {
27 | logger.error("Unable to search for JIRA issue", e);
28 | }
29 | }
30 |
31 | public List getKeys() {
32 | return searchResults.getList("issues.key");
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/service/Version.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.service;
2 |
3 | import static org.apache.http.HttpStatus.SC_CREATED;
4 | import static org.apache.http.HttpStatus.SC_OK;
5 |
6 | import com.frameworkium.core.common.reporting.jira.dto.version.VersionDto;
7 | import com.frameworkium.core.common.reporting.jira.endpoint.JiraEndpoint;
8 |
9 | public class Version extends AbstractJiraService {
10 | public VersionDto getVersion(String versionId) {
11 | return getRequestSpec()
12 | .basePath(JiraEndpoint.VERSION.getUrl())
13 | .pathParam("id", versionId)
14 | .get("/{id}")
15 | .then()
16 | .log().ifValidationFails()
17 | .statusCode(SC_OK)
18 | .extract()
19 | .as(VersionDto.class);
20 | }
21 |
22 | public VersionDto createVersion(VersionDto versionDto) {
23 | return getRequestSpec()
24 | .basePath(JiraEndpoint.VERSION.getUrl())
25 | .body(versionDto)
26 | .post()
27 | .then()
28 | .log().ifValidationFails()
29 | .statusCode(SC_CREATED)
30 | .extract()
31 | .as(VersionDto.class);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/util/ExecutionSearchUtil.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.util;
2 |
3 | import com.frameworkium.core.common.properties.Property;
4 | import com.frameworkium.core.common.reporting.jira.dto.executionsearch.ExecutionSearchDto;
5 | import com.frameworkium.core.common.reporting.jira.dto.executionsearch.ExecutionSearchListDto;
6 | import com.frameworkium.core.common.reporting.jira.zapi.ExecutionSearch;
7 | import java.util.List;
8 | import java.util.stream.Collectors;
9 | import java.util.stream.Stream;
10 | import org.apache.commons.lang.StringUtils;
11 |
12 | public class ExecutionSearchUtil {
13 | private final ExecutionSearchListDto result;
14 |
15 | public ExecutionSearchUtil(String query) {
16 | this(new ExecutionSearch(), query);
17 | }
18 |
19 | public ExecutionSearchUtil(ExecutionSearch executionSearch, String query) {
20 | this.result = executionSearch.search(query);
21 | }
22 |
23 | /**
24 | * Get a list of execution Ids optionally filtered by Property.ZAPI_CYCLE_REGEX
25 | *
26 | * @return a list of execution Ids
27 | */
28 | public List getExecutionIdsByZAPICycleRegex() {
29 | return getExecutionStream()
30 | .map(e -> e.executionLightDto.id.intValue())
31 | .collect(Collectors.toList());
32 | }
33 |
34 | /**
35 | * Get a list of execution status Ids optionally filtered by Property.ZAPI_CYCLE_REGEX
36 | *
37 | * @return a list of execution status Ids
38 | */
39 | public List getExecutionStatusesByZAPICycleRegex() {
40 | return getExecutionStream()
41 | .map(e -> e.status.id.intValue())
42 | .collect(Collectors.toList());
43 | }
44 |
45 | private Stream getExecutionStream() {
46 | if (StringUtils.isNotEmpty(Property.ZAPI_CYCLE_REGEX.getValue())) {
47 | return result.executions.stream()
48 | .filter(e -> e.executionLightDto.cycleName.equals(Property.ZAPI_CYCLE_REGEX.getValue()));
49 | }
50 | return result.executions.stream();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/zapi/Attachment.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.zapi;
2 |
3 | import static org.apache.http.HttpStatus.SC_OK;
4 |
5 | import com.frameworkium.core.common.reporting.jira.dto.attachment.AttachmentListDto;
6 | import com.frameworkium.core.common.reporting.jira.endpoint.ZephyrEndpoint;
7 | import com.frameworkium.core.common.reporting.jira.service.AbstractJiraService;
8 | import java.io.File;
9 |
10 | public class Attachment extends AbstractJiraService {
11 | public AttachmentListDto getAttachmentByEntity(Integer entityId, String entityType) {
12 | return getRequestSpec()
13 | .basePath(ZephyrEndpoint.ATTACHMENT_BY_ENTITY.getUrl())
14 | .queryParam("entityId", entityId)
15 | .queryParam("entityType", entityType)
16 | .when()
17 | .get()
18 | .then().log().ifValidationFails()
19 | .extract()
20 | .as(AttachmentListDto.class);
21 | }
22 |
23 | public void deleteAttachment(Long id) {
24 | getRequestSpec()
25 | .basePath(ZephyrEndpoint.ATTACHMENT.getUrl())
26 | .pathParam("id", id)
27 | .when()
28 | .delete("/{id}")
29 | .then().log().ifValidationFails()
30 | .statusCode(SC_OK);
31 | }
32 |
33 | public void addAttachments(Integer entityId, String entityType, File file) {
34 | getRequestSpec()
35 | .basePath(ZephyrEndpoint.ATTACHMENT.getUrl())
36 | .queryParam("entityId", entityId)
37 | .queryParam("entityType", entityType)
38 | .header("X-Atlassian-Token", "nocheck")
39 | .multiPart(file)
40 | .when()
41 | .post()
42 | .then().log().ifValidationFails()
43 | .statusCode(SC_OK);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/zapi/Cycle.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.zapi;
2 |
3 | import static org.apache.http.HttpStatus.SC_OK;
4 |
5 | import com.frameworkium.core.common.reporting.jira.dto.cycle.CreateCycleSuccessDto;
6 | import com.frameworkium.core.common.reporting.jira.dto.cycle.CreateNewCycleDto;
7 | import com.frameworkium.core.common.reporting.jira.dto.cycle.CycleListDto;
8 | import com.frameworkium.core.common.reporting.jira.endpoint.ZephyrEndpoint;
9 | import com.frameworkium.core.common.reporting.jira.service.AbstractJiraService;
10 |
11 | public class Cycle extends AbstractJiraService {
12 | public CycleListDto getListOfCycle(Long projectId, Long versionId) {
13 | return getRequestSpec()
14 | .basePath(ZephyrEndpoint.CYCLE.getUrl())
15 | .queryParam("projectId", projectId)
16 | .queryParam("versionId", versionId)
17 | .get()
18 | .then()
19 | .log().ifValidationFails()
20 | .statusCode(SC_OK)
21 | .extract().body()
22 | .as(CycleListDto.class);
23 | }
24 |
25 | public CreateCycleSuccessDto createNewCycle(CreateNewCycleDto createNewCycleDto) {
26 | return getRequestSpec()
27 | .basePath(ZephyrEndpoint.CYCLE.getUrl())
28 | .body(createNewCycleDto)
29 | .post()
30 | .then()
31 | .log().ifValidationFails()
32 | .extract()
33 | .as(CreateCycleSuccessDto.class);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/zapi/Execution.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.zapi;
2 |
3 | import static org.apache.http.HttpStatus.SC_OK;
4 |
5 | import com.frameworkium.core.common.reporting.jira.dto.execution.AddTestToCycleOperationDto;
6 | import com.frameworkium.core.common.reporting.jira.dto.execution.UpdateExecutionOperationDto;
7 | import com.frameworkium.core.common.reporting.jira.endpoint.ZephyrEndpoint;
8 | import com.frameworkium.core.common.reporting.jira.service.AbstractJiraService;
9 |
10 | public class Execution extends AbstractJiraService {
11 | public String addTestsToCycle(AddTestToCycleOperationDto addTestToCycleOperationDto) {
12 | return getRequestSpec()
13 | .basePath(ZephyrEndpoint.ADD_TEST_TO_EXECUTION.getUrl())
14 | .body(addTestToCycleOperationDto)
15 | .post()
16 | .then()
17 | .log().ifValidationFails()
18 | .statusCode(SC_OK)
19 | .extract()
20 | .asString(); //gets a jobprogresstoken
21 | }
22 |
23 | public String updateExecutionDetails(UpdateExecutionOperationDto updateExecutionOperationDto,
24 | Integer id) {
25 | return getRequestSpec()
26 | .basePath(ZephyrEndpoint.EXECUTION.getUrl())
27 | .pathParam("id", id)
28 | .body(updateExecutionOperationDto)
29 | .put("/{id}/execute")
30 | .then()
31 | .log().ifValidationFails()
32 | .statusCode(SC_OK)
33 | .extract()
34 | .asString(); // get a success token
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/jira/zapi/ExecutionSearch.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.zapi;
2 |
3 | import com.frameworkium.core.common.reporting.jira.dto.executionsearch.ExecutionSearchListDto;
4 | import com.frameworkium.core.common.reporting.jira.endpoint.ZephyrEndpoint;
5 | import com.frameworkium.core.common.reporting.jira.service.AbstractJiraService;
6 | import io.restassured.specification.RequestSpecification;
7 |
8 | public class ExecutionSearch extends AbstractJiraService {
9 | public ExecutionSearchListDto search(String zqlQuery) {
10 | return search(zqlQuery, null, null, null, null);
11 | }
12 |
13 | public ExecutionSearchListDto search(
14 | String zqlQuery, Integer filterId, Integer offset, Integer maxRecords, String expand) {
15 | RequestSpecification reqspec = getRequestSpec()
16 | .basePath(ZephyrEndpoint.EXECUTE_SEARCH.getUrl())
17 | .queryParam("zqlQuery", zqlQuery);
18 |
19 | if (filterId != null) {
20 | reqspec.queryParam("filterId", filterId);
21 | }
22 | if (offset != null) {
23 | reqspec.queryParam("offset", offset);
24 | }
25 | if (maxRecords != null) {
26 | reqspec.queryParam("maxRecords", maxRecords);
27 | }
28 | if (expand != null) {
29 | reqspec.queryParam("expand", expand);
30 | }
31 |
32 | return reqspec.when()
33 | .get().then().log().ifValidationFails()
34 | .extract()
35 | .as(ExecutionSearchListDto.class);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/reporting/spira/SpiraConfig.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.spira;
2 |
3 | public class SpiraConfig {
4 |
5 | public static final String USERNAME = "administrator";
6 | public static final String API_KEY = "{750AE393-737C-434E-A3AB-0FF2D0476E3C}";
7 | public static final String REST_PATH = "/Services/v4_0/RestService.svc/projects/2";
8 |
9 | private SpiraConfig() {
10 | }
11 |
12 | public static class SpiraStatus {
13 | public static final int SPIRA_STATUS_PASS = 2;
14 | public static final int SPIRA_STATUS_FAIL = 1;
15 | public static final int SPIRA_STATUS_WIP = 4;
16 | public static final int SPIRA_STATUS_BLOCKED = 5;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/common/retry/RetryFlakyTest.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.retry;
2 |
3 | import com.frameworkium.core.common.properties.Property;
4 | import org.testng.IRetryAnalyzer;
5 | import org.testng.ITestResult;
6 |
7 | public class RetryFlakyTest implements IRetryAnalyzer {
8 |
9 | /**
10 | * Maximum retry count of failed tests, defaults to 1.
11 | */
12 | static final int MAX_RETRY_COUNT =
13 | Property.MAX_RETRY_COUNT.getIntWithDefault(1);
14 |
15 | private int retryCount = 0;
16 |
17 | @Override
18 | public boolean retry(ITestResult result) {
19 | return retryCount++ < MAX_RETRY_COUNT;
20 | }
21 | }
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/annotations/Timeout.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.annotations;
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 | * Annotation that is used for setting waiting timeout value,
10 | * which will be used for waiting an element to appear.
11 | *
12 | * For example:
13 | *
14 | *
15 | * @FindBy(css = "my_form_css")
16 | * @Timeout(3)
17 | * public class MyForm extends HtmlElement {
18 | * @FindBy(css = "text_input_css")
19 | * @Timeout(3)
20 | * private TextInput textInput;
21 | *
22 | * // Other elements and methods here
23 | * }
24 | *
25 | */
26 | @Retention(RetentionPolicy.RUNTIME)
27 | @Target({ElementType.TYPE, ElementType.FIELD})
28 | public @interface Timeout {
29 | int value();
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/element/Button.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.element;
2 |
3 | import org.openqa.selenium.WebElement;
4 |
5 | /**
6 | * Represents web page button control.
7 | */
8 | public class Button extends TypifiedElement {
9 |
10 | /**
11 | * Specifies wrapped {@link WebElement}.
12 | *
13 | * @param wrappedElement {@code WebElement} to wrap.
14 | */
15 | public Button(WebElement wrappedElement) {
16 | super(wrappedElement);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/element/CheckBox.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.element;
2 |
3 | import java.util.Optional;
4 | import org.openqa.selenium.By;
5 | import org.openqa.selenium.NoSuchElementException;
6 | import org.openqa.selenium.WebElement;
7 |
8 | /**
9 | * Represents checkbox control.
10 | */
11 | public class CheckBox extends TypifiedElement {
12 |
13 | /**
14 | * Specifies wrapped {@link WebElement}.
15 | *
16 | * @param wrappedElement {@code WebElement} to wrap.
17 | */
18 | public CheckBox(WebElement wrappedElement) {
19 | super(wrappedElement);
20 | }
21 |
22 | /**
23 | * Finds label corresponding to this checkbox using "following-sibling::label" xpath.
24 | *
25 | * @return Optional of the {@code WebElement} representing label
26 | */
27 | public Optional getLabel() {
28 | try {
29 | return Optional.of(getWrappedElement().findElement(By.xpath("following-sibling::label")));
30 | } catch (NoSuchElementException e) {
31 | return Optional.empty();
32 | }
33 | }
34 |
35 | /**
36 | * Finds the text of the checkbox label.
37 | *
38 | * @return Optional of the label text
39 | */
40 | public Optional getLabelText() {
41 | return getLabel().map(WebElement::getText);
42 | }
43 |
44 | /**
45 | * The same as {@link #getLabelText()}.
46 | *
47 | * @return Text of the checkbox label or {@code null} if no label has been found.
48 | */
49 | public String getText() {
50 | return getLabelText().orElse("");
51 | }
52 |
53 | /**
54 | * Selects checkbox if it is not already selected.
55 | */
56 | public void select() {
57 | if (!isSelected()) {
58 | getWrappedElement().click();
59 | }
60 | }
61 |
62 | /**
63 | * Deselects checkbox if it is not already deselected.
64 | */
65 | public void deselect() {
66 | if (isSelected()) {
67 | getWrappedElement().click();
68 | }
69 | }
70 |
71 | /**
72 | * Selects checkbox if passed value is {@code true} and deselects otherwise.
73 | */
74 | public void set(boolean value) {
75 | if (value) {
76 | select();
77 | } else {
78 | deselect();
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/element/Image.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.element;
2 |
3 | import org.openqa.selenium.WebElement;
4 |
5 | /**
6 | * Represents an image {@code }
7 | */
8 | public class Image extends TypifiedElement {
9 |
10 | public Image(WebElement wrappedElement) {
11 | super(wrappedElement);
12 | }
13 |
14 | /**
15 | * Retrieves path to image from "src" attribute
16 | *
17 | * @return Path to the image
18 | */
19 | public String getSource() {
20 | return getWrappedElement().getAttribute("src");
21 | }
22 |
23 | /**
24 | * Retrieves alternative text from "alt" attribute
25 | *
26 | * @return alternative text for image
27 | */
28 | public String getAlt() {
29 | return getWrappedElement().getAttribute("alt");
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/element/Link.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.element;
2 |
3 | import org.openqa.selenium.WebElement;
4 |
5 | /**
6 | * Represents an anchor tag/hyperlink.
7 | */
8 | public class Link extends TypifiedElement {
9 |
10 | public Link(WebElement wrappedElement) {
11 | super(wrappedElement);
12 | }
13 |
14 | /**
15 | * Retrieves reference from "href" attribute.
16 | *
17 | * @return Reference associated with hyperlink.
18 | */
19 | public String getReference() {
20 | return getWrappedElement().getAttribute("href");
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/element/Select.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.element;
2 |
3 | import java.util.List;
4 | import org.openqa.selenium.WebElement;
5 | import org.openqa.selenium.support.ui.ISelect;
6 |
7 | /**
8 | * Represents a select control.
9 | *
10 | * This class wraps the Selenium {@link org.openqa.selenium.support.ui.Select}
11 | * and delegates all method calls to it.
12 | *
13 | * But unlike {@code WebDriver} {@code Select} class there are no checks
14 | * performed in the constructor of this class, so it can be used correctly
15 | * with lazy initialization.
16 | */
17 | public class Select extends TypifiedElement implements ISelect {
18 |
19 | /**
20 | * Specifies wrapped {@link WebElement}.
21 | * Performs no checks unlike {@link org.openqa.selenium.support.ui.Select}.
22 | * All checks are made later in {@link #getSelect()} method.
23 | *
24 | * @param wrappedElement {@code WebElement} to wrap.
25 | */
26 | public Select(WebElement wrappedElement) {
27 | super(wrappedElement);
28 | }
29 |
30 | /**
31 | * Constructs instance of {@link org.openqa.selenium.support.ui.Select} class.
32 | *
33 | * @return {@link org.openqa.selenium.support.ui.Select} class instance.
34 | */
35 | private org.openqa.selenium.support.ui.Select getSelect() {
36 | return new org.openqa.selenium.support.ui.Select(getWrappedElement());
37 | }
38 |
39 | public boolean isMultiple() {
40 | return getSelect().isMultiple();
41 | }
42 |
43 | public List getOptions() {
44 | return getSelect().getOptions();
45 | }
46 |
47 | public List getAllSelectedOptions() {
48 | return getSelect().getAllSelectedOptions();
49 | }
50 |
51 | public WebElement getFirstSelectedOption() {
52 | return getSelect().getFirstSelectedOption();
53 | }
54 |
55 | /**
56 | * Indicates if select has at least one selected option.
57 | *
58 | * @return {@code true} if select has at least one selected option and
59 | * {@code false} otherwise.
60 | */
61 | public boolean hasSelectedOption() {
62 | return getOptions().stream().anyMatch(WebElement::isSelected);
63 | }
64 |
65 | public void selectByVisibleText(String text) {
66 | getSelect().selectByVisibleText(text);
67 | }
68 |
69 | public void selectByIndex(int index) {
70 | getSelect().selectByIndex(index);
71 | }
72 |
73 | public void selectByValue(String value) {
74 | getSelect().selectByValue(value);
75 | }
76 |
77 | public void deselectAll() {
78 | getSelect().deselectAll();
79 | }
80 |
81 | public void deselectByValue(String value) {
82 | getSelect().deselectByValue(value);
83 | }
84 |
85 | public void deselectByIndex(int index) {
86 | getSelect().deselectByIndex(index);
87 | }
88 |
89 | public void deselectByVisibleText(String text) {
90 | getSelect().deselectByVisibleText(text);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/element/TextBlock.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.element;
2 |
3 | import org.openqa.selenium.WebElement;
4 |
5 | /**
6 | * Represents text block on a web page.
7 | */
8 | public class TextBlock extends TypifiedElement {
9 | public TextBlock(WebElement wrappedElement) {
10 | super(wrappedElement);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/element/TextInput.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.element;
2 |
3 | import java.util.Optional;
4 | import org.apache.commons.lang3.StringUtils;
5 | import org.openqa.selenium.Keys;
6 | import org.openqa.selenium.WebElement;
7 |
8 | /**
9 | * Represents text input control
10 | * (such as <input type="text"/> or <textarea/>).
11 | */
12 | public class TextInput extends TypifiedElement {
13 |
14 | /**
15 | * Specifies wrapped {@link WebElement}.
16 | *
17 | * @param wrappedElement {@code WebElement} to wrap.
18 | */
19 | public TextInput(WebElement wrappedElement) {
20 | super(wrappedElement);
21 | }
22 |
23 | /**
24 | * Retrieves the text entered into this text input.
25 | *
26 | * @return Text entered into the text input.
27 | */
28 | @Override
29 | public String getText() {
30 | if ("textarea".equals(getWrappedElement().getTagName())) {
31 | return getWrappedElement().getText();
32 | }
33 |
34 | return Optional
35 | .ofNullable(getWrappedElement().getAttribute("value"))
36 | .orElse("");
37 | }
38 |
39 | /**
40 | * Sets the text of this Input. This is different to
41 | * {@link #sendKeys(CharSequence...)} because it will delete any existing
42 | * text first.
43 | *
44 | * {@code text} will equal {@link #getText()} after calling this method.
45 | *
46 | * @param text the text to set
47 | */
48 | public void setText(CharSequence text) {
49 | getWrappedElement().sendKeys(getClearCharSequence() + text);
50 | }
51 |
52 | /**
53 | * Returns sequence of backspaces and deletes that will clear element.
54 | * clear() can't be used because generates separate onchange event
55 | * See https://github.com/yandex-qatools/htmlelements/issues/65
56 | */
57 | public String getClearCharSequence() {
58 | return StringUtils.repeat(Keys.DELETE.toString() + Keys.BACK_SPACE, getText().length());
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/exceptions/HtmlElementsException.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.exceptions;
2 |
3 | /**
4 | * Thrown during runtime in cases when a block of elements or a
5 | * page object can't be instantiated or initialized.
6 | */
7 | public class HtmlElementsException extends RuntimeException {
8 |
9 | private static final long serialVersionUID = 1L;
10 |
11 | public HtmlElementsException() {
12 | super();
13 | }
14 |
15 | public HtmlElementsException(String message) {
16 | super(message);
17 | }
18 |
19 | public HtmlElementsException(String message, Throwable cause) {
20 | super(message, cause);
21 | }
22 |
23 | public HtmlElementsException(Throwable cause) {
24 | super(cause);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/loader/decorator/HtmlElementClassAnnotationsHandler.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.loader.decorator;
2 |
3 | import com.frameworkium.core.htmlelements.element.HtmlElement;
4 | import com.frameworkium.core.htmlelements.exceptions.HtmlElementsException;
5 | import org.openqa.selenium.By;
6 | import org.openqa.selenium.support.FindBy;
7 | import org.openqa.selenium.support.FindBy.FindByBuilder;
8 | import org.openqa.selenium.support.pagefactory.AbstractAnnotations;
9 |
10 | /**
11 | * Handles annotation of {@link HtmlElement} and its successors.
12 | */
13 | public class HtmlElementClassAnnotationsHandler extends AbstractAnnotations {
14 |
15 | private final Class elementClass;
16 |
17 | public HtmlElementClassAnnotationsHandler(Class elementClass) {
18 | this.elementClass = elementClass;
19 | }
20 |
21 | @Override
22 | public By buildBy() {
23 | Class> clazz = elementClass;
24 | while (clazz != Object.class) {
25 | if (clazz.isAnnotationPresent(FindBy.class)) {
26 | return new FindByBuilder().buildIt(clazz.getAnnotation(FindBy.class), null);
27 | }
28 | clazz = clazz.getSuperclass();
29 | }
30 |
31 | throw new HtmlElementsException(String.format(
32 | "Cannot determine how to locate instance of %s", elementClass));
33 | }
34 |
35 | @Override
36 | public boolean isLookupCached() {
37 | return false;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/loader/decorator/ProxyFactory.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.loader.decorator;
2 |
3 | import com.frameworkium.core.htmlelements.element.HtmlElement;
4 | import com.frameworkium.core.htmlelements.element.TypifiedElement;
5 | import java.lang.reflect.InvocationHandler;
6 | import java.lang.reflect.Proxy;
7 | import java.util.List;
8 | import org.openqa.selenium.WebElement;
9 | import org.openqa.selenium.WrapsElement;
10 | import org.openqa.selenium.interactions.Locatable;
11 |
12 | /**
13 | * Contains factory methods for creating proxy of blocks, typified elements, page objects
14 | */
15 | @SuppressWarnings("unchecked")
16 | public class ProxyFactory {
17 | public static T createWebElementProxy(ClassLoader loader,
18 | InvocationHandler handler) {
19 | Class>[] interfaces = new Class[] {WebElement.class, WrapsElement.class, Locatable.class};
20 | return (T) Proxy.newProxyInstance(loader, interfaces, handler);
21 | }
22 |
23 | public static List createWebElementListProxy(ClassLoader loader,
24 | InvocationHandler handler) {
25 | return (List) Proxy.newProxyInstance(loader, new Class[] {List.class}, handler);
26 | }
27 |
28 | public static List createTypifiedElementListProxy(
29 | ClassLoader loader,
30 | InvocationHandler handler) {
31 | return (List) Proxy.newProxyInstance(loader, new Class[] {List.class}, handler);
32 | }
33 |
34 | public static List createHtmlElementListProxy(ClassLoader loader,
35 | InvocationHandler handler) {
36 | return (List) Proxy.newProxyInstance(loader, new Class[] {List.class}, handler);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/loader/decorator/proxyhandlers/HtmlElementListNamedProxyHandler.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.loader.decorator.proxyhandlers;
2 |
3 | import static com.frameworkium.core.htmlelements.loader.HtmlElementLoader.createHtmlElement;
4 |
5 | import com.frameworkium.core.htmlelements.element.HtmlElement;
6 | import java.lang.reflect.InvocationHandler;
7 | import java.lang.reflect.InvocationTargetException;
8 | import java.lang.reflect.Method;
9 | import java.util.LinkedList;
10 | import java.util.List;
11 | import java.util.stream.Collectors;
12 | import org.openqa.selenium.support.pagefactory.ElementLocator;
13 |
14 | public class HtmlElementListNamedProxyHandler implements InvocationHandler {
15 |
16 | private final Class elementClass;
17 | private final ElementLocator locator;
18 | private final String name;
19 |
20 | public HtmlElementListNamedProxyHandler(Class elementClass, ElementLocator locator,
21 | String name) {
22 | this.elementClass = elementClass;
23 | this.locator = locator;
24 | this.name = name;
25 | }
26 |
27 | @Override
28 | public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
29 | if ("toString".equals(method.getName())) {
30 | return name;
31 | }
32 |
33 | List elements = locator.findElements().stream()
34 | .map(element -> createHtmlElement(elementClass, element))
35 | .collect(Collectors.toCollection(LinkedList::new));
36 |
37 | try {
38 | return method.invoke(elements, objects);
39 | } catch (InvocationTargetException e) {
40 | // Unwrap the underlying exception
41 | throw e.getCause();
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/loader/decorator/proxyhandlers/TypifiedElementListNamedProxyHandler.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.loader.decorator.proxyhandlers;
2 |
3 | import static com.frameworkium.core.htmlelements.loader.HtmlElementLoader.createTypifiedElement;
4 |
5 | import com.frameworkium.core.htmlelements.element.TypifiedElement;
6 | import java.lang.reflect.InvocationHandler;
7 | import java.lang.reflect.InvocationTargetException;
8 | import java.lang.reflect.Method;
9 | import java.util.LinkedList;
10 | import java.util.List;
11 | import java.util.stream.Collectors;
12 | import org.openqa.selenium.support.pagefactory.ElementLocator;
13 |
14 | public class TypifiedElementListNamedProxyHandler
15 | implements InvocationHandler {
16 |
17 | private final Class elementClass;
18 | private final ElementLocator locator;
19 | private final String name;
20 |
21 | public TypifiedElementListNamedProxyHandler(Class elementClass, ElementLocator locator,
22 | String name) {
23 | this.elementClass = elementClass;
24 | this.locator = locator;
25 | this.name = name;
26 | }
27 |
28 | @Override
29 | public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
30 | if ("toString".equals(method.getName())) {
31 | return name;
32 | }
33 |
34 | List elements = locator.findElements().stream()
35 | .map(element -> createTypifiedElement(elementClass, element))
36 | .collect(Collectors.toCollection(LinkedList::new));
37 |
38 | try {
39 | return method.invoke(elements, objects);
40 | } catch (InvocationTargetException e) {
41 | // Unwrap the underlying exception
42 | throw e.getCause();
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/loader/decorator/proxyhandlers/WebElementListNamedProxyHandler.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.loader.decorator.proxyhandlers;
2 |
3 | import java.lang.reflect.Method;
4 | import org.openqa.selenium.support.pagefactory.ElementLocator;
5 | import org.openqa.selenium.support.pagefactory.internal.LocatingElementListHandler;
6 |
7 | public class WebElementListNamedProxyHandler extends LocatingElementListHandler {
8 |
9 | private final String name;
10 |
11 | public WebElementListNamedProxyHandler(ElementLocator locator, String name) {
12 | super(locator);
13 | this.name = name;
14 | }
15 |
16 | @Override
17 | public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
18 | if ("toString".equals(method.getName())) {
19 | return name;
20 | }
21 | return super.invoke(o, method, objects);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/loader/decorator/proxyhandlers/WebElementNamedProxyHandler.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.loader.decorator.proxyhandlers;
2 |
3 | import com.frameworkium.core.htmlelements.utils.HtmlElementUtils;
4 | import java.lang.reflect.Method;
5 | import java.time.Clock;
6 | import java.util.concurrent.TimeUnit;
7 | import org.openqa.selenium.StaleElementReferenceException;
8 | import org.openqa.selenium.support.pagefactory.ElementLocator;
9 | import org.openqa.selenium.support.pagefactory.internal.LocatingElementHandler;
10 |
11 | public class WebElementNamedProxyHandler extends LocatingElementHandler {
12 |
13 | private final long timeOutInSeconds;
14 | private final Clock clock;
15 | private final String name;
16 |
17 | public WebElementNamedProxyHandler(ElementLocator locator, String name) {
18 | super(locator);
19 | this.name = name;
20 | this.clock = Clock.systemDefaultZone();
21 | this.timeOutInSeconds = HtmlElementUtils.getImplicitTimeoutInSeconds();
22 | }
23 |
24 | @Override
25 | public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
26 | if ("toString".equals(method.getName())) {
27 | return name;
28 | }
29 |
30 | final long end = this.clock.millis() + TimeUnit.SECONDS.toMillis(this.timeOutInSeconds);
31 |
32 | StaleElementReferenceException lastException;
33 | do {
34 | try {
35 | return super.invoke(o, method, objects);
36 | } catch (StaleElementReferenceException e) {
37 | lastException = e;
38 | this.waitFor();
39 | }
40 | }
41 | while (this.clock.millis() < end);
42 | throw lastException;
43 | }
44 |
45 | protected long sleepFor() {
46 | return 500L;
47 | }
48 |
49 | private void waitFor() throws InterruptedException {
50 | Thread.sleep(this.sleepFor());
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/htmlelements/pagefactory/CustomElementLocatorFactory.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.htmlelements.pagefactory;
2 |
3 | import org.openqa.selenium.support.pagefactory.ElementLocator;
4 | import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;
5 |
6 | /**
7 | * A factory for producing {@link ElementLocator}s. It is expected that a new
8 | * ElementLocator will be returned per call.
9 | */
10 | public interface CustomElementLocatorFactory extends ElementLocatorFactory {
11 | ElementLocator createLocator(Class> clazz);
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/annotations/ForceVisible.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.annotations;
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 | @Retention(RetentionPolicy.RUNTIME)
9 | @Target(ElementType.FIELD)
10 | public @interface ForceVisible {
11 |
12 | /**
13 | * Default value.
14 | */
15 | String value() default "";
16 |
17 | /**
18 | * If checking for visibility of a list of elements, setting a value
19 | * will only check for visibility of the first n elements of the list.
20 | */
21 | int checkAtMost() default -1;
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/annotations/Invisible.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.annotations;
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 | @Retention(RetentionPolicy.RUNTIME)
9 | @Target(ElementType.FIELD)
10 | public @interface Invisible {
11 |
12 | /**
13 | * Default value.
14 | */
15 | String value() default "";
16 |
17 | /**
18 | * If checking for invisibility of a list of elements, setting a value
19 | * will only check for invisibility of the first n elements of the list.
20 | */
21 | int checkAtMost() default -1;
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/annotations/Visible.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.annotations;
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 | @Retention(RetentionPolicy.RUNTIME)
9 | @Target(ElementType.FIELD)
10 | public @interface Visible {
11 |
12 | /**
13 | * Default value.
14 | */
15 | String value() default "";
16 |
17 | /**
18 | * If checking for visibility of a list of elements, setting a value
19 | * will only check for visibility of the first n elements of the list.
20 | */
21 | int checkAtMost() default -1;
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/browsers/UserAgent.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.browsers;
2 |
3 | import org.openqa.selenium.JavascriptExecutor;
4 |
5 | public class UserAgent {
6 |
7 | public static final String SCRIPT = "return navigator.userAgent;";
8 |
9 | private UserAgent() {
10 | // hidden
11 | }
12 |
13 | public static String getUserAgent(JavascriptExecutor driver) {
14 | try {
15 | return (String) driver.executeScript(SCRIPT);
16 | } catch (Exception ignored) {
17 | return null;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/capture/CaptureEndpoint.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.capture;
2 |
3 | import com.frameworkium.core.api.Endpoint;
4 | import com.frameworkium.core.common.properties.Property;
5 |
6 | /**
7 | * The various Endpoints of Capture.
8 | */
9 | enum CaptureEndpoint implements Endpoint {
10 |
11 | BASE_URI(Property.CAPTURE_URL.getValue()),
12 | EXECUTIONS(BASE_URI.url + "/executions"),
13 | SCREENSHOT(BASE_URI.url + "/screenshot");
14 |
15 | private String url;
16 |
17 | CaptureEndpoint(String url) {
18 | this.url = url;
19 | }
20 |
21 | @Override
22 | public String getUrl(Object... params) {
23 | return String.format(url, params);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/capture/ElementHighlighter.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.capture;
2 |
3 | import org.openqa.selenium.JavascriptExecutor;
4 | import org.openqa.selenium.StaleElementReferenceException;
5 | import org.openqa.selenium.WebDriver;
6 | import org.openqa.selenium.WebElement;
7 |
8 | public class ElementHighlighter {
9 |
10 | private JavascriptExecutor js;
11 | private WebElement previousElem;
12 |
13 | public ElementHighlighter(WebDriver driver) {
14 | js = (JavascriptExecutor) driver;
15 | }
16 |
17 | /**
18 | * Highlight a WebElement.
19 | *
20 | * @param webElement to highlight
21 | */
22 | public void highlightElement(WebElement webElement) {
23 |
24 | previousElem = webElement; // remember the new element
25 | try {
26 | // TODO: save the previous border
27 | js.executeScript("arguments[0].style.border='3px solid red'", webElement);
28 | } catch (StaleElementReferenceException ignored) {
29 | // something went wrong, but no need to crash for highlighting
30 | }
31 | }
32 |
33 | /**
34 | * Unhighlight the previously highlighted WebElement.
35 | */
36 | public void unhighlightPrevious() {
37 |
38 | try {
39 | // unhighlight the previously highlighted element
40 | js.executeScript("arguments[0].style.border='none'", previousElem);
41 | } catch (StaleElementReferenceException ignored) {
42 | // the page was reloaded/changed, the same element isn't there
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/capture/model/Browser.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.capture.model;
2 |
3 | import static com.frameworkium.core.common.properties.Property.BROWSER;
4 | import static com.frameworkium.core.common.properties.Property.BROWSER_VERSION;
5 | import static com.frameworkium.core.common.properties.Property.DEVICE;
6 | import static com.frameworkium.core.common.properties.Property.PLATFORM;
7 | import static com.frameworkium.core.common.properties.Property.PLATFORM_VERSION;
8 |
9 | import com.fasterxml.jackson.annotation.JsonInclude;
10 | import com.frameworkium.core.ui.UITestLifecycle;
11 | import com.frameworkium.core.ui.driver.DriverSetup;
12 | import java.util.Optional;
13 | import net.sf.uadetector.ReadableUserAgent;
14 | import net.sf.uadetector.UserAgentStringParser;
15 | import net.sf.uadetector.service.UADetectorServiceFactory;
16 |
17 | @JsonInclude(JsonInclude.Include.NON_NULL)
18 | public class Browser {
19 |
20 | public String name;
21 | public String version;
22 | public String device;
23 | public String platform;
24 | public String platformVersion;
25 |
26 | /**
27 | * Create browser object.
28 | */
29 | public Browser() {
30 |
31 | Optional userAgent = UITestLifecycle.get().getUserAgent();
32 | if (userAgent.isPresent() && !userAgent.get().isEmpty()) {
33 | UserAgentStringParser uaParser = UADetectorServiceFactory.getResourceModuleParser();
34 | ReadableUserAgent agent = uaParser.parse(userAgent.get());
35 |
36 | this.name = agent.getName();
37 | this.version = agent.getVersionNumber().toVersionString();
38 | this.device = agent.getDeviceCategory().getName();
39 | this.platform = agent.getOperatingSystem().getName();
40 | this.platformVersion = agent.getOperatingSystem().getVersionNumber().toVersionString();
41 |
42 | } else {
43 | // Fall-back to the Property class
44 | if (BROWSER.isSpecified()) {
45 | this.name = BROWSER.getValue().toLowerCase();
46 | } else {
47 | this.name = DriverSetup.DEFAULT_BROWSER.toString();
48 | }
49 | if (BROWSER_VERSION.isSpecified()) {
50 | this.version = BROWSER_VERSION.getValue();
51 | }
52 | if (DEVICE.isSpecified()) {
53 | this.device = DEVICE.getValue();
54 | }
55 | if (PLATFORM.isSpecified()) {
56 | this.platform = PLATFORM.getValue();
57 | }
58 | if (PLATFORM_VERSION.isSpecified()) {
59 | this.platformVersion = PLATFORM_VERSION.getValue();
60 | }
61 | }
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/capture/model/Command.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.capture.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonInclude;
4 | import com.frameworkium.core.ui.driver.Driver;
5 | import org.apache.commons.lang3.StringUtils;
6 | import org.openqa.selenium.WebElement;
7 |
8 | @JsonInclude(JsonInclude.Include.NON_NULL)
9 | public class Command {
10 |
11 | public String action;
12 | public String using;
13 | public String value;
14 |
15 | public Command(String action, String using, String value) {
16 | this.action = action;
17 | this.using = using;
18 | this.value = value;
19 | }
20 |
21 | public Command(String action, WebElement element) {
22 | this.action = action;
23 | setUsingAndValue(element);
24 | }
25 |
26 | private void setUsingAndValue(WebElement element) {
27 | // TODO: Improve this. Use hacky solution in LoggingListener?
28 | if (Driver.isNative()) {
29 | this.using = "n/a";
30 | this.value = "n/a";
31 | } else {
32 | if (StringUtils.isNotBlank(element.getAttribute("id"))) {
33 | this.using = "id";
34 | this.value = element.getAttribute("id");
35 | } else if (!(element.getText()).isEmpty()) {
36 | this.using = "linkText";
37 | this.value = element.getText();
38 | } else if (!element.getTagName().isEmpty()) {
39 | this.using = "css";
40 | this.value = element.getTagName() + "."
41 | + element.getAttribute("class").replace(" ", ".");
42 | } else {
43 | // must be something weird
44 | this.using = "n/a";
45 | this.value = "n/a";
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/capture/model/SoftwareUnderTest.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.capture.model;
2 |
3 | import static com.frameworkium.core.common.properties.Property.SUT_NAME;
4 | import static com.frameworkium.core.common.properties.Property.SUT_VERSION;
5 |
6 | public class SoftwareUnderTest {
7 |
8 | public String name;
9 | public String version;
10 |
11 | /**
12 | * Software under test object.
13 | */
14 | public SoftwareUnderTest() {
15 | if (SUT_NAME.isSpecified()) {
16 | this.name = SUT_NAME.getValue();
17 | }
18 | if (SUT_VERSION.isSpecified()) {
19 | this.version = SUT_VERSION.getValue();
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/capture/model/message/CreateExecution.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.capture.model.message;
2 |
3 | import com.fasterxml.jackson.annotation.JsonInclude;
4 | import com.frameworkium.core.ui.capture.model.Browser;
5 | import com.frameworkium.core.ui.capture.model.SoftwareUnderTest;
6 | import org.apache.logging.log4j.LogManager;
7 | import org.apache.logging.log4j.Logger;
8 |
9 | @JsonInclude(JsonInclude.Include.NON_NULL)
10 | public final class CreateExecution {
11 |
12 | private static final Logger logger = LogManager.getLogger();
13 | public String testID;
14 | public Browser browser;
15 | public SoftwareUnderTest softwareUnderTest;
16 | public String nodeAddress;
17 |
18 | /**
19 | * Create Capture execution.
20 | */
21 | public CreateExecution(String testID, String nodeAddress) {
22 |
23 | logger.debug("CreateExecution: testID='{}', nodeAddress='{}", testID, nodeAddress);
24 | this.testID = testID;
25 | this.browser = new Browser();
26 | this.softwareUnderTest = new SoftwareUnderTest();
27 | this.nodeAddress = nodeAddress;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/capture/model/message/CreateScreenshot.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.capture.model.message;
2 |
3 | import com.fasterxml.jackson.annotation.JsonInclude;
4 | import com.frameworkium.core.ui.capture.model.Command;
5 | import org.apache.logging.log4j.LogManager;
6 | import org.apache.logging.log4j.Logger;
7 |
8 | @JsonInclude(JsonInclude.Include.NON_NULL)
9 | public class CreateScreenshot {
10 |
11 | private static final Logger logger = LogManager.getLogger();
12 | public Command command;
13 | public String url;
14 | public String executionID;
15 | public String errorMessage;
16 | public String screenshotBase64;
17 |
18 | /**
19 | * Create screenshot object.
20 | */
21 | public CreateScreenshot(
22 | String executionID, Command command, String url,
23 | String errorMessage, String screenshotBase64) {
24 |
25 | logger.debug("Creating screenshot: executionID='{}', "
26 | + "Command.action='{}', url='{}', "
27 | + "errorMessage='{}', screenshotBase64.length={}",
28 | executionID,
29 | command.action, url,
30 | errorMessage, screenshotBase64.length());
31 | this.executionID = executionID;
32 | this.command = command;
33 | this.url = url;
34 | this.errorMessage = errorMessage;
35 | this.screenshotBase64 = screenshotBase64;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/AbstractDriver.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver;
2 |
3 | import static java.util.concurrent.TimeUnit.SECONDS;
4 |
5 | import com.frameworkium.core.common.properties.Property;
6 | import com.frameworkium.core.ui.capture.ScreenshotCapture;
7 | import com.frameworkium.core.ui.driver.remotes.BrowserStack;
8 | import com.frameworkium.core.ui.driver.remotes.Sauce;
9 | import com.frameworkium.core.ui.listeners.CaptureListener;
10 | import com.frameworkium.core.ui.listeners.LoggingListener;
11 | import com.frameworkium.core.ui.proxy.SeleniumProxyFactory;
12 | import org.apache.logging.log4j.LogManager;
13 | import org.apache.logging.log4j.Logger;
14 | import org.openqa.selenium.Capabilities;
15 | import org.openqa.selenium.ImmutableCapabilities;
16 | import org.openqa.selenium.remote.CapabilityType;
17 | import org.openqa.selenium.support.events.EventFiringWebDriver;
18 |
19 | public abstract class AbstractDriver implements Driver {
20 |
21 | protected static final Logger logger = LogManager.getLogger();
22 |
23 | private EventFiringWebDriver webDriverWrapper;
24 |
25 | private static Capabilities addProxyIfRequired(Capabilities caps) {
26 | if (Property.PROXY.isSpecified()) {
27 | return caps.merge(createProxyCapabilities(Property.PROXY.getValue()));
28 | } else {
29 | return caps;
30 | }
31 | }
32 |
33 | private static Capabilities createProxyCapabilities(String proxyProperty) {
34 | return new ImmutableCapabilities(
35 | CapabilityType.PROXY,
36 | SeleniumProxyFactory.createProxy(proxyProperty));
37 | }
38 |
39 | private static boolean isMaximiseRequired() {
40 | boolean ableToMaximise = !Sauce.isDesired()
41 | && !BrowserStack.isDesired()
42 | && !Driver.isNative();
43 |
44 | return ableToMaximise && Property.MAXIMISE.getBoolean();
45 | }
46 |
47 | @Override
48 | public EventFiringWebDriver getWebDriver() {
49 | return this.webDriverWrapper;
50 | }
51 |
52 | /**
53 | * Creates the Wrapped Driver object and maximises if required.
54 | */
55 | public void initialise() {
56 | this.webDriverWrapper = setupEventFiringWebDriver(getCapabilities());
57 | maximiseBrowserIfRequired();
58 | }
59 |
60 | private EventFiringWebDriver setupEventFiringWebDriver(Capabilities capabilities) {
61 | Capabilities caps = addProxyIfRequired(capabilities);
62 | logger.debug("Browser Capabilities: " + caps);
63 | EventFiringWebDriver eventFiringWD = new EventFiringWebDriver(getWebDriver(caps));
64 | eventFiringWD.register(new LoggingListener());
65 | if (ScreenshotCapture.isRequired()) {
66 | eventFiringWD.register(new CaptureListener());
67 | }
68 | if (!Driver.isNative()) {
69 | eventFiringWD.manage().timeouts().setScriptTimeout(10, SECONDS);
70 | }
71 | return eventFiringWD;
72 | }
73 |
74 | private void maximiseBrowserIfRequired() {
75 | if (isMaximiseRequired()) {
76 | this.webDriverWrapper.manage().window().maximize();
77 | }
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/Driver.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver;
2 |
3 | import static com.frameworkium.core.common.properties.Property.APP_PATH;
4 |
5 | import org.openqa.selenium.Capabilities;
6 | import org.openqa.selenium.WebDriver;
7 | import org.openqa.selenium.support.events.EventFiringWebDriver;
8 |
9 | public interface Driver {
10 |
11 | /**
12 | * Check whether the driver is for a mobile device.
13 | */
14 | static boolean isMobile() {
15 | return false;
16 | }
17 |
18 | /**
19 | * Check whether the driver is for a native mobile app.
20 | */
21 | static boolean isNative() {
22 | return APP_PATH.isSpecified();
23 | }
24 |
25 | /**
26 | * Method to set-up the driver object.
27 | */
28 | void initialise();
29 |
30 | /**
31 | * Implemented in each Driver Type to specify the capabilities of that browser.
32 | *
33 | * @return Capabilities of each browser
34 | */
35 | Capabilities getCapabilities();
36 |
37 | /**
38 | * Returns the correct WebDriver object for the Driver Type.
39 | *
40 | * @param capabilities Capabilities of the browser
41 | * @return {@link WebDriver} object for the browser
42 | */
43 | WebDriver getWebDriver(Capabilities capabilities);
44 |
45 | /**
46 | * Getter for the driver that wraps the initialised driver.
47 | *
48 | * @return EventFiringWebDriver
49 | */
50 | EventFiringWebDriver getWebDriver();
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/drivers/ChromeImpl.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.drivers;
2 |
3 | import com.frameworkium.core.common.properties.Property;
4 | import com.frameworkium.core.ui.driver.AbstractDriver;
5 | import org.openqa.selenium.Capabilities;
6 | import org.openqa.selenium.WebDriver;
7 | import org.openqa.selenium.chrome.ChromeDriver;
8 | import org.openqa.selenium.chrome.ChromeOptions;
9 |
10 | public class ChromeImpl extends AbstractDriver {
11 |
12 | @Override
13 | public ChromeOptions getCapabilities() {
14 | var chromeOptions = new ChromeOptions();
15 | chromeOptions.setHeadless(Property.HEADLESS.getBoolean());
16 | return chromeOptions;
17 | }
18 |
19 | @Override
20 | public WebDriver getWebDriver(Capabilities capabilities) {
21 | final ChromeOptions chromeOptions;
22 | if (capabilities instanceof ChromeOptions) {
23 | chromeOptions = (ChromeOptions) capabilities;
24 | } else {
25 | chromeOptions = new ChromeOptions().merge(capabilities);
26 | }
27 | return new ChromeDriver(chromeOptions);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/drivers/EdgeImpl.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.drivers;
2 |
3 | import com.frameworkium.core.ui.driver.AbstractDriver;
4 | import org.openqa.selenium.Capabilities;
5 | import org.openqa.selenium.WebDriver;
6 | import org.openqa.selenium.edge.EdgeDriver;
7 | import org.openqa.selenium.edge.EdgeOptions;
8 |
9 | public class EdgeImpl extends AbstractDriver {
10 |
11 | @Override
12 | public EdgeOptions getCapabilities() {
13 | return new EdgeOptions();
14 | }
15 |
16 | @Override
17 | public WebDriver getWebDriver(Capabilities capabilities) {
18 | final EdgeOptions edgeOptions;
19 | if (capabilities instanceof EdgeOptions) {
20 | edgeOptions = (EdgeOptions) capabilities;
21 | } else {
22 | edgeOptions = new EdgeOptions().merge(capabilities);
23 | }
24 | return new EdgeDriver(edgeOptions);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/drivers/FirefoxImpl.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.drivers;
2 |
3 | import com.frameworkium.core.common.properties.Property;
4 | import com.frameworkium.core.ui.driver.AbstractDriver;
5 | import org.openqa.selenium.Capabilities;
6 | import org.openqa.selenium.WebDriver;
7 | import org.openqa.selenium.firefox.FirefoxDriver;
8 | import org.openqa.selenium.firefox.FirefoxDriverLogLevel;
9 | import org.openqa.selenium.firefox.FirefoxOptions;
10 |
11 | public class FirefoxImpl extends AbstractDriver {
12 |
13 | @Override
14 | public FirefoxOptions getCapabilities() {
15 | FirefoxOptions firefoxOptions = new FirefoxOptions();
16 | firefoxOptions.setHeadless(Property.HEADLESS.getBoolean());
17 | firefoxOptions.setLogLevel(FirefoxDriverLogLevel.INFO);
18 | return firefoxOptions;
19 | }
20 |
21 | @Override
22 | public WebDriver getWebDriver(Capabilities capabilities) {
23 | final FirefoxOptions firefoxOptions;
24 | if (capabilities instanceof FirefoxOptions) {
25 | firefoxOptions = (FirefoxOptions) capabilities;
26 | } else {
27 | firefoxOptions = new FirefoxOptions().merge(capabilities);
28 | }
29 | return new FirefoxDriver(firefoxOptions);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/drivers/GridImpl.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.drivers;
2 |
3 | import static com.frameworkium.core.common.properties.Property.APPLICATION_NAME;
4 | import static com.frameworkium.core.common.properties.Property.BROWSER_VERSION;
5 | import static com.frameworkium.core.common.properties.Property.PLATFORM;
6 | import static com.frameworkium.core.common.properties.Property.PLATFORM_VERSION;
7 |
8 | import com.frameworkium.core.common.properties.Property;
9 | import com.frameworkium.core.ui.driver.AbstractDriver;
10 | import java.net.MalformedURLException;
11 | import java.net.URL;
12 | import org.openqa.selenium.Capabilities;
13 | import org.openqa.selenium.MutableCapabilities;
14 | import org.openqa.selenium.WebDriver;
15 | import org.openqa.selenium.remote.RemoteWebDriver;
16 |
17 | public class GridImpl extends AbstractDriver {
18 |
19 | private final URL remoteURL;
20 | private final Capabilities capabilities;
21 |
22 | /**
23 | * Implementation of driver for the Selenium Grid .
24 | */
25 | public GridImpl(Capabilities capabilities) {
26 | this.capabilities = capabilities;
27 | try {
28 | this.remoteURL = new URL(Property.GRID_URL.getValue());
29 | } catch (MalformedURLException e) {
30 | throw new RuntimeException(e);
31 | }
32 | }
33 |
34 | @Override
35 | public Capabilities getCapabilities() {
36 | MutableCapabilities mutableCapabilities = new MutableCapabilities(capabilities);
37 | if (BROWSER_VERSION.isSpecified()) {
38 | mutableCapabilities.setCapability("version", BROWSER_VERSION.getValue());
39 | }
40 | if (PLATFORM.isSpecified()) {
41 | mutableCapabilities.setCapability("platform", PLATFORM_VERSION.getValue());
42 | }
43 | if (APPLICATION_NAME.isSpecified()) {
44 | mutableCapabilities.setCapability("applicationName", APPLICATION_NAME.getValue());
45 | }
46 | return mutableCapabilities;
47 | }
48 |
49 | @Override
50 | public WebDriver getWebDriver(Capabilities capabilities) {
51 | return new RemoteWebDriver(remoteURL, capabilities);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/drivers/InternetExplorerImpl.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.drivers;
2 |
3 | import com.frameworkium.core.ui.driver.AbstractDriver;
4 | import org.openqa.selenium.Capabilities;
5 | import org.openqa.selenium.WebDriver;
6 | import org.openqa.selenium.ie.InternetExplorerDriver;
7 | import org.openqa.selenium.ie.InternetExplorerOptions;
8 | import org.openqa.selenium.remote.CapabilityType;
9 |
10 | public class InternetExplorerImpl extends AbstractDriver {
11 |
12 | @Override
13 | public InternetExplorerOptions getCapabilities() {
14 | InternetExplorerOptions ieOptions = new InternetExplorerOptions();
15 | ieOptions.setCapability(CapabilityType.ForSeleniumServer.ENSURING_CLEAN_SESSION, true);
16 | ieOptions.setCapability(InternetExplorerDriver.ENABLE_PERSISTENT_HOVERING, true);
17 | ieOptions.setCapability("requireWindowFocus", true);
18 | return ieOptions;
19 | }
20 |
21 | @Override
22 | public WebDriver getWebDriver(Capabilities capabilities) {
23 | return new InternetExplorerDriver(new InternetExplorerOptions(capabilities));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/drivers/SafariImpl.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.drivers;
2 |
3 | import com.frameworkium.core.ui.driver.AbstractDriver;
4 | import org.openqa.selenium.Capabilities;
5 | import org.openqa.selenium.WebDriver;
6 | import org.openqa.selenium.safari.SafariDriver;
7 | import org.openqa.selenium.safari.SafariOptions;
8 |
9 | public class SafariImpl extends AbstractDriver {
10 |
11 | @Override
12 | public SafariOptions getCapabilities() {
13 | var safariOptions = new SafariOptions();
14 | safariOptions.setCapability("safari.cleanSession", true);
15 | return safariOptions;
16 | }
17 |
18 | @Override
19 | public WebDriver getWebDriver(Capabilities capabilities) {
20 | return new SafariDriver(new SafariOptions(capabilities));
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/lifecycle/DriverLifecycle.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.lifecycle;
2 |
3 | import com.frameworkium.core.ui.driver.Driver;
4 | import java.util.function.Supplier;
5 | import org.openqa.selenium.WebDriver;
6 |
7 | /**
8 | * Controls the lifecycle of the {@link Driver}(s).
9 | *
10 | * The methods need to be called in order:
11 | *
12 | * {@link #initDriverPool(Supplier)}
13 | * (once until {@link #tearDownDriverPool()} has been called)
14 | * {@link #initBrowserBeforeTest(Supplier)}
15 | * (once until {@link #tearDownDriver()} has been called)
16 | * {@link #getWebDriver()} (n times only after the above and before the below)
17 | * {@link #tearDownDriver()}
18 | * (once after {@link #initBrowserBeforeTest(Supplier)} has been called)
19 | * {@link #tearDownDriverPool()} (once but multiple calls do nothing)
20 | *
21 | */
22 | public interface DriverLifecycle {
23 |
24 | /**
25 | * Will initialise a pool of {@link Driver}s if required.
26 | *
27 | * @param driverSupplier the {@link Supplier} that creates {@link Driver}s
28 | * @throws IllegalStateException if trying to re-initialise existing pool
29 | */
30 | default void initDriverPool(Supplier driverSupplier) {
31 | }
32 |
33 | /**
34 | * Will set the current {@link ThreadLocal} {@link Driver} to be the next
35 | * available from the pool or will add the {@link Driver} created by the
36 | * supplied {@link Supplier}.
37 | *
38 | * @param driverSupplier the {@link Supplier} that creates {@link Driver}s
39 | * @throws java.util.NoSuchElementException if this pool is empty
40 | */
41 | void initBrowserBeforeTest(Supplier driverSupplier);
42 |
43 | /**
44 | * @return the {@link WebDriver} in use by the current thread.
45 | * @throws NullPointerException if called before
46 | * {@link #initBrowserBeforeTest(Supplier)} or
47 | * after {@link #tearDownDriver()}.
48 | */
49 | WebDriver getWebDriver();
50 |
51 | /**
52 | * Tears down the driver, ready for reinitialisation, if required, by
53 | * {@link #initBrowserBeforeTest(Supplier)}.
54 | */
55 | void tearDownDriver();
56 |
57 | /**
58 | * Clears the driver pool, if exists, ready to run run
59 | * {@link #initDriverPool(Supplier)} again if required.
60 | */
61 | default void tearDownDriverPool() {
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/lifecycle/SingleUseDriverLifecycle.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.lifecycle;
2 |
3 | import com.frameworkium.core.ui.driver.Driver;
4 | import java.util.function.Supplier;
5 | import org.apache.logging.log4j.LogManager;
6 | import org.apache.logging.log4j.Logger;
7 | import org.openqa.selenium.WebDriver;
8 |
9 | /**
10 | * {@link #initDriverPool(Supplier)} and {@link #tearDownDriverPool()} do not do
11 | * anything for {@link SingleUseDriverLifecycle} and can be omitted.
12 | *
13 | * @see DriverLifecycle
14 | */
15 | public class SingleUseDriverLifecycle implements DriverLifecycle {
16 |
17 | private static final Logger logger = LogManager.getLogger();
18 |
19 | private static final ThreadLocal threadLocalDriver = new ThreadLocal<>();
20 |
21 | /**
22 | * Sets the {@link Driver} created by the supplied {@link Supplier} to the
23 | * {@link ThreadLocal} driver.
24 | *
25 | * @param driverSupplier the {@link Supplier} that creates {@link Driver}s
26 | */
27 | @Override
28 | public void initBrowserBeforeTest(Supplier driverSupplier) {
29 | threadLocalDriver.set(driverSupplier.get());
30 | }
31 |
32 | @Override
33 | public WebDriver getWebDriver() {
34 | return threadLocalDriver.get().getWebDriver().getWrappedDriver();
35 | }
36 |
37 | /**
38 | * Calls {@code quit()} on the underlying driver.
39 | */
40 | @Override
41 | public void tearDownDriver() {
42 | try {
43 | threadLocalDriver.get().getWebDriver().quit();
44 | } catch (Exception e) {
45 | logger.error("Failed to quit browser.");
46 | logger.debug("Failed to quit browser", e);
47 | throw e;
48 | } finally {
49 | threadLocalDriver.remove();
50 | }
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/remotes/BrowserStack.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.remotes;
2 |
3 | import static com.frameworkium.core.common.properties.Property.BROWSER_STACK;
4 |
5 | import java.net.MalformedURLException;
6 | import java.net.URL;
7 |
8 | public class BrowserStack {
9 |
10 | private BrowserStack() {
11 | // hide default constructor for this util class
12 | }
13 |
14 | public static URL getURL() throws MalformedURLException {
15 | return new URL(String.format("https://%s:%s@hub-cloud.browserstack.com/wd/hub",
16 | System.getenv("BROWSER_STACK_USERNAME"),
17 | System.getenv("BROWSER_STACK_ACCESS_KEY")));
18 | }
19 |
20 | public static boolean isDesired() {
21 | return BROWSER_STACK.getBoolean();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/driver/remotes/Sauce.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.remotes;
2 |
3 | import com.frameworkium.core.common.properties.Property;
4 | import com.google.common.collect.ImmutableMap;
5 | import com.saucelabs.common.SauceOnDemandAuthentication;
6 | import com.saucelabs.common.SauceOnDemandSessionIdProvider;
7 | import com.saucelabs.saucerest.SauceREST;
8 | import java.io.File;
9 | import java.io.IOException;
10 | import java.net.MalformedURLException;
11 | import java.net.URL;
12 |
13 | public class Sauce {
14 |
15 | private static final SauceOnDemandAuthentication sauceAuth =
16 | new SauceOnDemandAuthentication(
17 | System.getenv("SAUCE_USERNAME"),
18 | System.getenv("SAUCE_ACCESS_KEY"));
19 |
20 | private static final SauceREST client =
21 | new SauceREST(
22 | sauceAuth.getUsername(),
23 | sauceAuth.getAccessKey());
24 |
25 | public static URL getURL() {
26 | try {
27 | return new URL(String.format(
28 | "https://%s:%s@ondemand.saucelabs.com/wd/hub",
29 | sauceAuth.getUsername(),
30 | sauceAuth.getAccessKey()));
31 | } catch (MalformedURLException e) {
32 | throw new IllegalArgumentException(e);
33 | }
34 | }
35 |
36 | public static boolean isDesired() {
37 | return Property.SAUCE.getBoolean();
38 | }
39 |
40 | public static void updateJobName(SauceOnDemandSessionIdProvider sessionIdProvider, String name) {
41 |
42 | client.updateJobInfo(
43 | sessionIdProvider.getSessionId(),
44 | ImmutableMap.of("name", name));
45 | }
46 |
47 | public static void uploadFile(File file) throws IOException {
48 | client.uploadFile(file);
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/element/OptimisedStreamTable.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.element;
2 |
3 | import java.util.List;
4 | import java.util.stream.Stream;
5 | import org.openqa.selenium.By;
6 | import org.openqa.selenium.WebElement;
7 | import org.openqa.selenium.support.FindBy;
8 |
9 | /**
10 | * {@link OptimisedStreamTable} is an {@link AbstractStreamTable}.
11 | *
12 | * Along with the assumptions made by {@link AbstractStreamTable} this class
13 | * assumes the header cells are all selectable by {@code thead > tr > th},
14 | * the rows are all selectable by {@code tbody > tr} and cells inside the rows
15 | * are selectable by {@code td}.
16 | *
17 | *
{@link OptimisedStreamTable} is approximately twice as fast as
18 | * {@link StreamTable} but it cannot cope with hidden columns or rows.
19 | */
20 | public class OptimisedStreamTable extends AbstractStreamTable {
21 |
22 | @FindBy(css = "thead > tr > th")
23 | private List headerCells;
24 |
25 | @FindBy(css = "tbody > tr")
26 | private List rows;
27 |
28 | @Override
29 | protected Stream headerCells() {
30 | return headerCells.stream();
31 | }
32 |
33 | @Override
34 | protected Stream rows() {
35 | return rows.stream();
36 | }
37 |
38 | @Override
39 | protected By cellLocator() {
40 | return By.cssSelector("td");
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/element/StreamTable.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.element;
2 |
3 | import java.util.List;
4 | import java.util.stream.Stream;
5 | import org.openqa.selenium.By;
6 | import org.openqa.selenium.WebElement;
7 | import org.openqa.selenium.support.FindBy;
8 |
9 | /**
10 | * {@link StreamTable} is an {@link AbstractStreamTable}.
11 | *
12 | * Along with the assumptions made by {@link AbstractStreamTable} this class
13 | * assumes the header cells are all selectable by {@code thead > tr > th}.
14 | * the rows are all selectable by {@code tbody > tr} and cells inside the rows
15 | * are selectable by {@code td}.
16 | *
17 | *
It can also cope where entire columns or rows are hidden.
18 | */
19 | public class StreamTable extends AbstractStreamTable {
20 |
21 | @FindBy(css = "thead > tr > th")
22 | private List headerCells;
23 |
24 | @FindBy(css = "tbody > tr")
25 | private List rows;
26 |
27 | @Override
28 | protected Stream headerCells() {
29 | return headerCells.stream().filter(WebElement::isDisplayed);
30 | }
31 |
32 | @Override
33 | protected Stream rows() {
34 | return rows.stream().filter(WebElement::isDisplayed);
35 | }
36 |
37 | @Override
38 | protected By cellLocator() {
39 | return By.cssSelector("td");
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/js/JavascriptWait.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.js;
2 |
3 | import com.frameworkium.core.ui.ExtraExpectedConditions;
4 | import com.paulhammant.ngwebdriver.NgWebDriver;
5 | import org.openqa.selenium.JavascriptExecutor;
6 | import org.openqa.selenium.WebDriver;
7 | import org.openqa.selenium.support.ui.Wait;
8 |
9 | /**
10 | * Frameworkium implementation of waiting for JS events on page-load.
11 | */
12 | public class JavascriptWait {
13 |
14 | private final Wait wait;
15 | private final JavascriptExecutor javascriptExecutor;
16 |
17 | public JavascriptWait(
18 | JavascriptExecutor javascriptExecutor, Wait wait) {
19 | this.wait = wait;
20 | this.javascriptExecutor = javascriptExecutor;
21 | }
22 |
23 | /**
24 | * Default entry to {@link JavascriptWait}.
25 | * The following actions are waited for:
26 | *
27 | * Document state to be ready
28 | * If page is using Angular, it will detect and wait
29 | *
30 | */
31 | public void waitForJavascriptEventsOnLoad() {
32 | waitForDocumentReady();
33 | waitForAngular();
34 | }
35 |
36 | /**
37 | * If a page is using a supported JS framework, it will wait until it's ready.
38 | */
39 | public void waitForJavascriptFramework() {
40 | waitForAngular();
41 | }
42 |
43 | private void waitForDocumentReady() {
44 | wait.until(ExtraExpectedConditions.documentBodyReady());
45 | }
46 |
47 | private void waitForAngular() {
48 | new NgWebDriver(javascriptExecutor).waitForAngularRequestsToFinish();
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/listeners/SauceLabsListener.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.listeners;
2 |
3 | import static com.frameworkium.core.common.properties.Property.APP_PATH;
4 |
5 | import com.frameworkium.core.ui.driver.Driver;
6 | import com.frameworkium.core.ui.driver.remotes.Sauce;
7 | import com.saucelabs.common.SauceOnDemandSessionIdProvider;
8 | import com.saucelabs.testng.SauceOnDemandTestListener;
9 | import java.io.File;
10 | import java.io.IOException;
11 | import org.apache.logging.log4j.LogManager;
12 | import org.apache.logging.log4j.Logger;
13 | import org.testng.ITestContext;
14 | import org.testng.ITestResult;
15 |
16 | public class SauceLabsListener extends SauceOnDemandTestListener {
17 |
18 | private static final Logger logger = LogManager.getLogger();
19 |
20 | private static final boolean IS_RUNNING_ON_SAUCE_LABS = Sauce.isDesired();
21 |
22 | @Override
23 | public void onStart(ITestContext testContext) {
24 | if (IS_RUNNING_ON_SAUCE_LABS) {
25 | super.onStart(testContext);
26 |
27 | if (Driver.isNative()) {
28 | try {
29 | Sauce.uploadFile(new File(APP_PATH.getValue()));
30 | } catch (IOException ioe) {
31 | logger.error("Error uploading file", ioe);
32 | }
33 | }
34 | }
35 | }
36 |
37 | @Override
38 | public void onTestStart(ITestResult result) {
39 | if (IS_RUNNING_ON_SAUCE_LABS) {
40 | // TODO: thread safe?
41 | Sauce.updateJobName(
42 | (SauceOnDemandSessionIdProvider) result.getInstance(),
43 | result.getTestClass().getRealClass().getSimpleName());
44 |
45 | super.onTestStart(result);
46 | }
47 | }
48 |
49 | @Override
50 | public void onTestFailure(ITestResult tr) {
51 | if (IS_RUNNING_ON_SAUCE_LABS) {
52 | super.onTestFailure(tr);
53 | }
54 | }
55 |
56 | @Override
57 | public void onTestSuccess(ITestResult tr) {
58 | if (IS_RUNNING_ON_SAUCE_LABS) {
59 | super.onTestSuccess(tr);
60 | }
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/listeners/VideoListener.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.listeners;
2 |
3 | import com.frameworkium.core.ui.UITestLifecycle;
4 | import com.frameworkium.core.ui.video.VideoCapture;
5 | import org.testng.ITestContext;
6 | import org.testng.ITestResult;
7 | import org.testng.TestListenerAdapter;
8 |
9 | public class VideoListener extends TestListenerAdapter {
10 |
11 | @Override
12 | public void onTestStart(ITestResult iTestResult) {
13 | if (VideoCapture.isRequired()) {
14 | VideoCapture.saveTestSessionID(
15 | iTestResult.getName(),
16 | UITestLifecycle.get().getRemoteSessionId());
17 | }
18 | }
19 |
20 | @Override
21 | public void onFinish(ITestContext iTestContext) {
22 | if (VideoCapture.isRequired()) {
23 | VideoCapture videoCapture = new VideoCapture();
24 | iTestContext
25 | .getFailedTests()
26 | .getAllResults()
27 | .stream()
28 | .map(ITestResult::getName)
29 | .forEach(videoCapture::fetchAndSaveVideo);
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/pages/PageFactory.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.pages;
2 |
3 | import java.lang.reflect.InvocationTargetException;
4 | import java.time.Duration;
5 | import org.apache.logging.log4j.LogManager;
6 | import org.apache.logging.log4j.Logger;
7 |
8 | public class PageFactory {
9 |
10 | private static final Logger logger = LogManager.getLogger();
11 |
12 | protected PageFactory() {
13 | }
14 |
15 | public static > T newInstance(Class clazz) {
16 | return instantiatePageObject(clazz).get();
17 | }
18 |
19 | public static > T newInstance(
20 | Class clazz, Duration timeout) {
21 | return instantiatePageObject(clazz).get(timeout);
22 | }
23 |
24 | public static > T newInstance(
25 | Class clazz, String url) {
26 | return instantiatePageObject(clazz).get(url);
27 | }
28 |
29 | public static > T newInstance(
30 | Class clazz, String url, Duration timeout) {
31 | return instantiatePageObject(clazz).get(url, timeout);
32 | }
33 |
34 | private static > T instantiatePageObject(Class clazz) {
35 | try {
36 | return clazz.getDeclaredConstructor().newInstance();
37 | } catch (InstantiationException | IllegalAccessException
38 | | NoSuchMethodException | InvocationTargetException e) {
39 | logger.fatal("Unable to instantiate PageObject", e);
40 | throw new IllegalStateException("Unable to instantiate PageObject", e);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/proxy/SeleniumProxyFactory.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.proxy;
2 |
3 | import java.net.URI;
4 | import java.net.URISyntaxException;
5 | import org.apache.logging.log4j.LogManager;
6 | import org.apache.logging.log4j.Logger;
7 | import org.openqa.selenium.Proxy;
8 |
9 | public class SeleniumProxyFactory {
10 |
11 | private static final Logger logger = LogManager.getLogger();
12 |
13 | /**
14 | * Valid inputs are system, autodetect, direct or http://{hostname}:{port}
15 | *
16 | * This does not currently cope with PAC (Proxy auto-configuration from URL)
17 | *
18 | * @param proxyProperty the string representing the proxy required
19 | * @return a Selenium {@link Proxy} representation of proxyProperty
20 | */
21 | public static Proxy createProxy(String proxyProperty) {
22 | Proxy proxy = new Proxy();
23 | switch (proxyProperty.toLowerCase()) {
24 | case "system":
25 | logger.debug("Using system proxy");
26 | proxy.setProxyType(Proxy.ProxyType.SYSTEM);
27 | break;
28 | case "autodetect":
29 | logger.debug("Using autodetect proxy");
30 | proxy.setProxyType(Proxy.ProxyType.AUTODETECT);
31 | break;
32 | case "direct":
33 | logger.debug("Using direct i.e. (no) proxy");
34 | proxy.setProxyType(Proxy.ProxyType.DIRECT);
35 | break;
36 | default:
37 | return createManualProxy(proxyProperty);
38 | }
39 | return proxy;
40 | }
41 |
42 | private static Proxy createManualProxy(String proxyProperty) {
43 | String proxyString = getProxyURL(proxyProperty);
44 | logger.debug("All protocols to use proxy address: {}", proxyString);
45 | Proxy proxy = new Proxy();
46 | proxy.setProxyType(Proxy.ProxyType.MANUAL)
47 | .setHttpProxy(proxyString)
48 | .setFtpProxy(proxyString)
49 | .setSslProxy(proxyString);
50 | return proxy;
51 | }
52 |
53 | private static String getProxyURL(String proxyProperty) {
54 | try {
55 | URI proxyURI = new URI(proxyProperty);
56 | String host = proxyURI.getHost();
57 | int port = proxyURI.getPort();
58 | if (host == null || port == -1) {
59 | throw new URISyntaxException(
60 | proxyProperty, "invalid host or port");
61 | }
62 | return String.format("%s:%d", host, port);
63 | } catch (NullPointerException | URISyntaxException e) {
64 | String message = "Invalid proxy specified, acceptable values are: "
65 | + "system, autodetect, direct or http://{hostname}:{port}.";
66 | logger.fatal(message);
67 | throw new IllegalArgumentException(message, e);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/frameworkium/core/ui/video/UrlFetcher.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.video;
2 |
3 | import io.restassured.RestAssured;
4 | import io.restassured.response.Response;
5 | import java.net.URL;
6 | import java.util.concurrent.TimeUnit;
7 | import java.util.concurrent.TimeoutException;
8 | import org.apache.http.HttpStatus;
9 | import org.apache.logging.log4j.LogManager;
10 | import org.apache.logging.log4j.Logger;
11 |
12 | public class UrlFetcher {
13 |
14 | private static final Logger logger = LogManager.getLogger();
15 |
16 | /**
17 | * @param url the url to GET
18 | * @param maxTries max number of tries to GET url
19 | * @return the bytes from the downloaded URL
20 | * @throws TimeoutException if download fails and max tries have been exceeded
21 | */
22 | public byte[] fetchWithRetry(URL url, int maxTries) throws TimeoutException {
23 | logger.debug("Downloading: " + url);
24 | for (int i = 0; i < maxTries; i++) {
25 | Response response = RestAssured.get(url);
26 | if (response.getStatusCode() == HttpStatus.SC_OK) {
27 | return response.asByteArray();
28 | }
29 | logger.debug("Retrying download: " + url);
30 |
31 | try {
32 | TimeUnit.SECONDS.sleep(2);
33 | } catch (InterruptedException e) {
34 | throw new IllegalStateException(e);
35 | }
36 | }
37 | throw new TimeoutException();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/resources/Empty.properties:
--------------------------------------------------------------------------------
1 | applicationName=
2 | appPath=
3 | browser=
4 | browserStack=
5 | browserVersion=
6 | build=
7 | captureURL=
8 | customBrowserImpl=
9 | device=
10 | gridURL=
11 | headless=
12 | jiraPassword=
13 | jiraResultFieldName=
14 | jiraResultTransition=
15 | jiraURL=
16 | jiraUsername=
17 | jqlQuery=
18 | maximise=
19 | maxRetryCount=
20 | platform=
21 | platformVersion=
22 | proxy=
23 | resolution=
24 | resultVersion=
25 | reuseBrowser=
26 | sauce=
27 | spiraURL=
28 | sutName=
29 | sutVersion=
30 | threads=
31 | videoCaptureUrl=
32 | zapiCycleRegEx=
--------------------------------------------------------------------------------
/src/main/resources/Empty.yaml:
--------------------------------------------------------------------------------
1 | applicationName:
2 | appPath:
3 | browser:
4 | browserStack:
5 | browserVersion:
6 | build:
7 | captureURL:
8 | customBrowserImpl:
9 | device:
10 | gridURL:
11 | headless:
12 | jiraPassword:
13 | jiraResultFieldName:
14 | jiraResultTransition:
15 | jiraURL:
16 | jiraUsername:
17 | jqlQuery:
18 | maximise:
19 | maxRetryCount:
20 | platform:
21 | platformVersion:
22 | proxy:
23 | resolution:
24 | resultVersion:
25 | reuseBrowser:
26 | sauce:
27 | spiraURL:
28 | sutName:
29 | sutVersion:
30 | threads:
31 | videoCaptureUrl:
32 | zapiCycleRegEx:
--------------------------------------------------------------------------------
/src/main/resources/FirefoxGrid.yaml:
--------------------------------------------------------------------------------
1 | applicationName:
2 | appPath:
3 | browser: Firefox
4 | browserStack:
5 | browserVersion:
6 | build:
7 | captureURL:
8 | customBrowserImpl:
9 | device:
10 | gridURL: http://localhost:4444/wd/hub
11 | headless:
12 | jiraPassword:
13 | jiraResultFieldName:
14 | jiraResultTransition:
15 | jiraURL:
16 | jiraUsername:
17 | jqlQuery:
18 | maximise:
19 | maxRetryCount: 2
20 | platform:
21 | platformVersion:
22 | proxy:
23 | resolution:
24 | resultVersion:
25 | reuseBrowser:
26 | sauce:
27 | spiraURL:
28 | sutName:
29 | sutVersion:
30 | threads:
31 | videoCaptureUrl:
32 | zapiCycleRegEx:
--------------------------------------------------------------------------------
/src/main/resources/META-INF/services/org.testng.ITestNGListener:
--------------------------------------------------------------------------------
1 | io.qameta.allure.testng.AllureTestNg
2 | com.frameworkium.core.common.listeners.TestListener
3 | com.frameworkium.core.common.listeners.ResultLoggerListener
4 | com.frameworkium.core.ui.listeners.ScreenshotListener
5 | com.frameworkium.core.ui.listeners.CaptureListener
6 | com.frameworkium.core.ui.listeners.SauceLabsListener
7 | com.frameworkium.core.ui.listeners.VideoListener
8 | com.frameworkium.core.common.listeners.MethodInterceptor
--------------------------------------------------------------------------------
/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/api/dto/AbstractDTOSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.api.dto
2 |
3 | import spock.lang.Specification
4 |
5 | class AbstractDTOSpec extends Specification {
6 |
7 | def sut = new TopLevelDTO()
8 |
9 | def "two different DTOs with the same data are equal but not =="() {
10 | given:
11 | def other = new TopLevelDTO()
12 | expect:
13 | sut.equals(other)
14 | !sut.is(other) // == in Java
15 | sut.hashCode() == other.hashCode()
16 | }
17 |
18 | def "two different DTOs with different data are not equal"() {
19 | given:
20 | def other = new TopLevelDTO()
21 | other.lowLevelDTO.data = "foo"
22 | expect:
23 | sut != other
24 | !sut.is(other) // == in Java
25 | sut.hashCode() != other.hashCode()
26 | }
27 |
28 | def "DTOs of different types are not equal"() {
29 | given:
30 | def other = new LowLevelDTO()
31 | expect:
32 | sut != other
33 | !sut.is(other) // == in Java
34 | sut.hashCode() != other.hashCode()
35 | }
36 |
37 | def "a DTO is not equal to a non-DTO"() {
38 | given:
39 | def other = new Object()
40 | expect:
41 | sut != other
42 | !sut.is(other) // == in Java
43 | sut.hashCode() != other.hashCode()
44 | }
45 |
46 | def "Cloning a DTOs makes a deep not shallow clone"() {
47 | given:
48 | def clone = sut.clone()
49 | when:
50 | clone.lowLevelDTO.data = "something"
51 | then:
52 | sut.lowLevelDTO.data != "something"
53 | }
54 |
55 | def "toString() creates readable output"() {
56 | expect:
57 | sut.toString() == 'TopLevelDTO[lowLevelDTO=LowLevelDTO[data=initial],stringList=[1, a]]'
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/common/reporting/TestIdUtilsSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting
2 |
3 | import io.qameta.allure.Issue
4 | import io.qameta.allure.TmsLink
5 | import spock.lang.Specification
6 | import spock.lang.Unroll
7 |
8 | @Unroll
9 | class TestIdUtilsSpec extends Specification {
10 |
11 | def "GetIssueOrTmsLinkValue gets TmsLink or Issue value for method #methodName"() {
12 | when:
13 | def value = TestIdUtils.getIssueOrTmsLinkValue(
14 | TestIdData.getMethod(methodName))
15 | then:
16 | value == expectedValue
17 | where:
18 | methodName | expectedValue
19 | "none" | Optional.empty()
20 | "tmsLink" | Optional.of("TMSLink")
21 | "issue" | Optional.of("ISSUE")
22 | "bothSame" | Optional.of("SAME")
23 | "bothDifferent" | Optional.of("TMSLink")
24 | "multipleTmsLink" | Optional.empty()
25 | "multipleTmsLinkAndIssue" | Optional.empty()
26 | "multipleIssue" | Optional.empty()
27 | }
28 |
29 | def "GetIssueOrTmsLinkValues get TmsLink or Issue values for method #methodName"() {
30 | when:
31 | def value = TestIdUtils.getIssueOrTmsLinkValues(
32 | TestIdData.getMethod(methodName))
33 | then:
34 | value == expectedValue
35 | where:
36 | methodName | expectedValue
37 | "none" | []
38 | "tmsLink" | ["TMSLink"]
39 | "issue" | ["ISSUE"]
40 | "bothSame" | ["SAME"]
41 | "bothDifferent" | ["TMSLink"]
42 | "multipleTmsLink" | ["TMSLink1", "TMSLink2"]
43 | "multipleTmsLinkAndIssue" | ["TMSLink3", "TMSLink4"]
44 | "multipleIssue" | ["Issue3", "Issue4"]
45 | }
46 |
47 | class TestIdData {
48 |
49 | void none() {}
50 |
51 | @TmsLink("TMSLink")
52 | void tmsLink() {}
53 |
54 | @Issue("ISSUE")
55 | void issue() {}
56 |
57 | @TmsLink("SAME")
58 | @Issue("SAME")
59 | void bothSame() {}
60 |
61 | @TmsLink("TMSLink")
62 | @Issue("ISSUE")
63 | void bothDifferent() {}
64 |
65 | @TmsLink("TMSLink1")
66 | @TmsLink("TMSLink2")
67 | void multipleTmsLink() {}
68 |
69 | @Issue("Issue1")
70 | @Issue("Issue2")
71 | @TmsLink("TMSLink3")
72 | @TmsLink("TMSLink4")
73 | void multipleTmsLinkAndIssue() {}
74 |
75 | @Issue("Issue3")
76 | @Issue("Issue4")
77 | void multipleIssue() {}
78 | }
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/common/reporting/jira/service/AttachmentSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.service
2 |
3 |
4 | import groovy.json.JsonBuilder
5 | import io.restassured.path.json.JsonPath
6 | import spock.lang.Specification
7 |
8 | class AttachmentSpec extends Specification {
9 | def "Get attachment Ids"() {
10 | given:
11 | String issueKey = "KEY-${UUID.randomUUID().toString()}"
12 | JsonPath mockedResponse = createMockedResponse(issueKey)
13 | Issue mockedIssue = Stub(Issue)
14 | Attachment attachment = new Attachment(mockedIssue)
15 | when:
16 | mockedIssue.getIssue() >> mockedResponse
17 | List ids = attachment.getIds()
18 | then:
19 | with(ids) {
20 | ids == ["10000", "10001"]
21 | }
22 | }
23 |
24 | private static JsonPath createMockedResponse(String issueKey) {
25 | def obj = ["id" : "6767",
26 | "key" : issueKey,
27 | "fields": [
28 | "watcher" : ["isWatching": false,
29 | "watchCount": 1],
30 | "attachment": [[
31 | "id" : "10000",
32 | "filename" : "picture1.jpg",
33 | "created" : "2017-12-07T09:23:19.542+0000",
34 | "size" : 23123,
35 | "mimeType" : "image/jpeg",
36 | "content" : "http://www.example.com/jira/attachments/10000",
37 | "thumbnail": "http://www.example.com/jira/secure/thumbnail/10000"
38 | ],
39 | [
40 | "id" : "10001",
41 | "filename" : "picture2.jpg",
42 | "created" : "2017-11-11T09:23:19.542+0000",
43 | "size" : 45644,
44 | "mimeType" : "image/jpeg",
45 | "content" : "http://www.example.com/jira/attachments/10001",
46 | "thumbnail": "http://www.example.com/jira/secure/thumbnail/10001"
47 | ]
48 | ]
49 | ]
50 | ]
51 | String body = new JsonBuilder(obj).toString()
52 | JsonPath jsonPath = JsonPath.from(body)
53 | return jsonPath
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/common/reporting/jira/service/IssueLinkSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.service
2 |
3 | import com.frameworkium.core.common.reporting.jira.endpoint.JiraEndpoint
4 | import com.github.tomakehurst.wiremock.client.WireMock
5 | import com.github.tomakehurst.wiremock.verification.LoggedRequest
6 | import groovy.json.JsonBuilder
7 | import spock.lang.Shared
8 | import spock.lang.Specification
9 |
10 | import static com.github.tomakehurst.wiremock.client.WireMock.*
11 | import static com.github.tomakehurst.wiremock.common.Metadata.metadata
12 |
13 | class IssueLinkSpec extends Specification {
14 | String stubId = UUID.randomUUID().toString() // to uniquely identify stub for cleanup later
15 | @Shared
16 | WireMock wireMock = new WireMock("localhost", 8080)
17 |
18 | IssueLink issueLink = new IssueLink()
19 | String type = "Duplicate"
20 | final String inwardIssue = "KEY-${UUID.randomUUID().toString()}"
21 | final String outwardIssue = "KEY-${UUID.randomUUID().toString()}"
22 |
23 | def setupSpec() {
24 | System.properties["jiraURL"] = "http://localhost:8080"
25 | System.properties["jiraUsername"] = "username"
26 | System.properties["jiraPassword"] = "password"
27 | }
28 |
29 | def cleanup() {
30 | wireMock.removeStubsByMetadataPattern(matchingJsonPath(/$.id/, equalTo(stubId)))
31 | }
32 |
33 | def "Create link between two JIRA issues"() {
34 | given:
35 | String issueLinkUrl = JiraEndpoint.ISSUELINK.getUrl()
36 | def response = createMockedResponse(outwardIssue, inwardIssue, type)
37 | wireMock.register(post(urlPathEqualTo(issueLinkUrl))
38 | .withMetadata(metadata().attr("id", stubId)) //used for remove stub at cleanup
39 | .withRequestBody(equalToJson(response))
40 | .willReturn(aResponse().withStatus(201))
41 | )
42 | when:
43 | issueLink.linkIssues(type, inwardIssue, outwardIssue)
44 | then:
45 | List loggedRequests = wireMock.find(postRequestedFor(urlPathMatching(issueLinkUrl))
46 | .withRequestBody(equalTo(response)))
47 | loggedRequests[0].bodyAsString == response
48 | }
49 |
50 | private static def createMockedResponse(String outwardIssue, String inwardIssue, String type) {
51 | def obj = [
52 | "outwardIssue": ["key": outwardIssue],
53 | "inwardIssue" : ["key": inwardIssue],
54 | "type" : ["name": type]
55 | ]
56 | return new JsonBuilder(obj).toString()
57 | }
58 | }
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/common/reporting/jira/service/SearchSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.reporting.jira.service
2 |
3 | import com.frameworkium.core.common.reporting.jira.endpoint.JiraEndpoint
4 | import com.github.tomakehurst.wiremock.client.WireMock
5 | import groovy.json.JsonBuilder
6 | import spock.lang.Shared
7 | import spock.lang.Specification
8 |
9 | import static com.github.tomakehurst.wiremock.client.WireMock.*
10 | import static com.github.tomakehurst.wiremock.common.Metadata.metadata
11 |
12 | class SearchSpec extends Specification {
13 | String stubId = UUID.randomUUID().toString() // to uniquely identify stub for cleanup later
14 | @Shared
15 | WireMock wireMock = new WireMock("localhost", 8080)
16 |
17 | def setupSpec() {
18 | System.properties["jiraURL"] = "http://localhost:8080"
19 | System.properties["jiraUsername"] = "username"
20 | System.properties["jiraPassword"] = "password"
21 | }
22 |
23 | def cleanup() {
24 | wireMock.removeStubsByMetadataPattern(matchingJsonPath(/$.id/, equalTo(stubId)))
25 | }
26 |
27 | def "Getting keys from JQL JIRA search"() {
28 | given:
29 | String searchTerm = "ISearchForThis"
30 | def searchResult = createMockedSearchResultResponse()
31 | wireMock.register(get(urlPathEqualTo(JiraEndpoint.SEARCH.getUrl()))
32 | .withMetadata(metadata().attr("id", stubId))
33 | .withQueryParam("jql", equalTo(searchTerm))
34 | .withQueryParam("startAt", equalTo("0"))
35 | .withQueryParam("maxResults", equalTo("1000"))
36 | .willReturn(aResponse().withBody(searchResult)
37 | .withStatus(200))
38 | )
39 | when:
40 | def searchService = new Search(searchTerm)
41 | then:
42 | searchService.getKeys() == ["KEY-1", "KEY-2"]
43 | }
44 |
45 | private static def createMockedSearchResultResponse() {
46 | def obj = [
47 | "startAt" : 0,
48 | "maxResults": 1000,
49 | "total" : 2,
50 | "issues" : [
51 | ["id" : "10001",
52 | "key": "KEY-1"
53 | ],
54 | ["id" : "10002",
55 | "key": "KEY-2"
56 | ]
57 | ]
58 | ]
59 | return new JsonBuilder(obj).toString()
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/common/retry/RetryFlakyTestSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.common.retry
2 |
3 | import org.testng.ITestResult
4 | import spock.lang.Specification
5 |
6 | class RetryFlakyTestSpec extends Specification {
7 |
8 | def "Retry will return true if there are retries remaining"() {
9 | given:
10 | def mockResult = Mock(ITestResult)
11 | def sut = new RetryFlakyTest()
12 | expect:
13 | RetryFlakyTest.MAX_RETRY_COUNT.times { assert sut.retry(mockResult) }
14 | !sut.retry(mockResult)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/ui/capture/ElementHighlighterSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.capture
2 |
3 | import org.openqa.selenium.StaleElementReferenceException
4 | import org.openqa.selenium.WebDriver
5 | import org.openqa.selenium.WebElement
6 | import org.openqa.selenium.support.events.EventFiringWebDriver
7 | import spock.lang.Specification
8 |
9 | class ElementHighlighterSpec extends Specification {
10 |
11 | def mockWDWrapper = Mock(EventFiringWebDriver, constructorArgs: [Mock(WebDriver)])
12 | def mockElement = Mock(WebElement)
13 |
14 | ElementHighlighter sut = new ElementHighlighter(mockWDWrapper)
15 |
16 | def "provided element is highlighted and same element is un-highlighted"() {
17 | given: "The Javascript we expect to run"
18 | def highlightJS = "arguments[0].style.border='3px solid red'"
19 | def unhighlightJS = "arguments[0].style.border='none'"
20 | when: "We highlight then un-highlight an element"
21 | sut.highlightElement(mockElement)
22 | sut.unhighlightPrevious()
23 | then: "The correct scripts are executed against the given element"
24 | 1 * mockWDWrapper.executeScript(highlightJS, mockElement)
25 | 1 * mockWDWrapper.executeScript(unhighlightJS, mockElement)
26 | }
27 |
28 | def "StaleElementReferenceException's are caught"() {
29 | when: "We highlight then un-highlight an element"
30 | sut.highlightElement(mockElement)
31 | sut.unhighlightPrevious()
32 | then: "StaleElementReferenceException are not thrown"
33 | 2 * mockWDWrapper.executeScript(_ as String, mockElement) >> {
34 | throw new StaleElementReferenceException("")
35 | }
36 | notThrown(StaleElementReferenceException)
37 | }
38 | }
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/ui/driver/lifecycle/MultiUseDriverLifecycleSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.lifecycle
2 |
3 | import com.frameworkium.core.ui.driver.Driver
4 | import org.openqa.selenium.WebDriver
5 | import org.openqa.selenium.support.events.EventFiringWebDriver
6 | import spock.lang.Specification
7 | import spock.lang.Unroll
8 |
9 | @Unroll
10 | class MultiUseDriverLifecycleSpec extends Specification {
11 |
12 | def webDriverStub = Stub(WebDriver)
13 | def EFWebDriverMock =
14 | Mock(constructorArgs: [webDriverStub], EventFiringWebDriver) {
15 | getWrappedDriver() >> webDriverStub
16 | }
17 | def driverMock = Mock(Driver) {
18 | getWebDriver() >> EFWebDriverMock
19 | }
20 | def driverSupplier = { driverMock }
21 |
22 | def "following expected lifecycle yields correct driver with MultiUseDriverLifecycle(#poolSize)"() {
23 | given:
24 | def sut = new MultiUseDriverLifecycle(poolSize)
25 | when:
26 | sut.initDriverPool(driverSupplier)
27 | sut.initBrowserBeforeTest(driverSupplier)
28 | assert sut.getWebDriver() == webDriverStub
29 | sut.tearDownDriver()
30 | sut.tearDownDriverPool()
31 | then:
32 | EFWebDriverMock.manage() >> Stub(WebDriver.Options)
33 | poolSize * EFWebDriverMock.quit()
34 | noExceptionThrown()
35 | where:
36 | poolSize << [1, 5]
37 | }
38 |
39 | def "if a driver fails to tearDown exception will be thrown"() {
40 | given:
41 | def sut = new MultiUseDriverLifecycle(1)
42 | sut.initDriverPool(driverSupplier)
43 | sut.initBrowserBeforeTest(driverSupplier)
44 | when:
45 | sut.tearDownDriver()
46 | then:
47 | 1 * EFWebDriverMock.manage() >> { throw new Exception("") }
48 | thrown Exception
49 | }
50 |
51 | def "throwing exception on quit does not prevent subsequent drivers quitting"() {
52 | given:
53 | def sut = new MultiUseDriverLifecycle(2)
54 | sut.initDriverPool(driverSupplier)
55 | when:
56 | sut.tearDownDriverPool()
57 | then:
58 | // throw one error then do nothing for subsequent calls
59 | 2 * EFWebDriverMock.quit() >>> {
60 | throw new Exception("some error")
61 | } >> {}
62 | }
63 |
64 | def "initDriverPool can only be called once"() {
65 | given:
66 | def sut = new MultiUseDriverLifecycle(1)
67 | sut.initDriverPool(driverSupplier)
68 | when:
69 | sut.initDriverPool(driverSupplier)
70 | then:
71 | thrown IllegalStateException
72 | }
73 |
74 | def "initDriverPool can only be called again after tearDownDriverPool"() {
75 | given:
76 | def sut = new MultiUseDriverLifecycle(1)
77 | sut.initDriverPool(driverSupplier)
78 | sut.tearDownDriverPool()
79 | when:
80 | sut.initDriverPool(driverSupplier)
81 | then:
82 | noExceptionThrown()
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/ui/driver/lifecycle/SingleUseDriverLifecycleSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.driver.lifecycle
2 |
3 | import com.frameworkium.core.ui.driver.Driver
4 | import org.openqa.selenium.WebDriver
5 | import org.openqa.selenium.support.events.EventFiringWebDriver
6 | import spock.lang.Specification
7 |
8 | class SingleUseDriverLifecycleSpec extends Specification {
9 |
10 | def webDriverStub = Stub(WebDriver)
11 | def EFWebDriverMock =
12 | Mock(constructorArgs: [webDriverStub], EventFiringWebDriver) {
13 | getWrappedDriver() >> webDriverStub
14 | }
15 | def driverMock = Mock(Driver) {
16 | getWebDriver() >> EFWebDriverMock
17 | }
18 | def driverSupplier = { driverMock }
19 |
20 | def sut = new SingleUseDriverLifecycle()
21 |
22 |
23 | def "following expected lifecycle yields correct driver"() {
24 | when:
25 | sut.initDriverPool(driverSupplier)
26 | sut.initBrowserBeforeTest(driverSupplier)
27 | assert sut.getWebDriver() == webDriverStub
28 | sut.tearDownDriver()
29 | sut.tearDownDriverPool()
30 | then:
31 | 1 * EFWebDriverMock.quit()
32 | noExceptionThrown()
33 | }
34 |
35 | def "following subset of expected lifecycle yields correct driver for SingleUseDriverLifecycle"() {
36 | when:
37 | sut.initBrowserBeforeTest(driverSupplier)
38 | assert sut.getWebDriver() == webDriverStub
39 | sut.tearDownDriver()
40 | then:
41 | 1 * EFWebDriverMock.quit()
42 | noExceptionThrown()
43 | }
44 |
45 | def "if a driver fails to tearDown exception will be thrown"() {
46 | given:
47 | sut.initBrowserBeforeTest(driverSupplier)
48 | when:
49 | sut.tearDownDriver()
50 | then:
51 | 1 * EFWebDriverMock.quit() >> { throw new Exception("") }
52 | thrown Exception
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/ui/listeners/ScreenshotListenerSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.listeners
2 |
3 | import org.openqa.selenium.TakesScreenshot
4 | import spock.lang.Specification
5 |
6 | import java.nio.file.Files
7 | import java.nio.file.Paths
8 |
9 | class ScreenshotListenerSpec extends Specification {
10 |
11 | def testName = "testName"
12 | def defaultScreenshotFolder = Paths.get("screenshots")
13 |
14 | def setup() {
15 | Files.createDirectories(defaultScreenshotFolder)
16 | }
17 |
18 | def cleanup() {
19 | listAllTestScreenshots()
20 | .map({ it.toFile() })
21 | .forEach({ it.delete() })
22 | }
23 |
24 | def sut = new ScreenshotListener()
25 |
26 | def "takeScreenshotAndSaveLocally takes a screenshot and saves file"() {
27 | given:
28 | TakesScreenshot mockDriver = Mock()
29 | def screenshotCount = listAllTestScreenshots().count()
30 | when:
31 | sut.takeScreenshotAndSaveLocally(testName, mockDriver)
32 | then:
33 | 1 * mockDriver.getScreenshotAs(_) >> { [1, 2, 3] as byte[] }
34 | listAllTestScreenshots().count() == screenshotCount + 1
35 | }
36 |
37 | def listAllTestScreenshots() {
38 | Files.walk(defaultScreenshotFolder)
39 | .filter({ it.toString().endsWith(testName + ".png") })
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/ui/proxy/SeleniumProxyFactorySpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.proxy
2 |
3 | import org.openqa.selenium.Proxy
4 | import spock.lang.Specification
5 | import spock.lang.Unroll
6 |
7 | @Unroll
8 | class SeleniumProxyFactorySpec extends Specification {
9 |
10 | def "providing a property of (#proxyProp) gives proxy of type (#proxyType)"() {
11 | when:
12 | def proxy = SeleniumProxyFactory.createProxy(proxyProp)
13 | then:
14 | proxy.proxyType == proxyType
15 | where:
16 | proxyProp | proxyType
17 | "system" | Proxy.ProxyType.SYSTEM
18 | "autodetect" | Proxy.ProxyType.AUTODETECT
19 | "direct" | Proxy.ProxyType.DIRECT
20 | "http://host:1234" | Proxy.ProxyType.MANUAL
21 | }
22 |
23 | def "providing a proxy URL (#proxyProp) is set as expected (#proxyUrl)"() {
24 | when:
25 | def proxy = SeleniumProxyFactory.createManualProxy(proxyProp)
26 | then:
27 | proxy.proxyType == Proxy.ProxyType.MANUAL
28 | proxy.httpProxy == proxyUrl
29 | proxy.ftpProxy == proxyUrl
30 | proxy.sslProxy == proxyUrl
31 | where:
32 | proxyProp | proxyUrl
33 | "http://host:123" | "host:123"
34 | "ftp://host:123" | "host:123"
35 | }
36 |
37 | def "invalid proxy URL (#invalidProxyProp) throw exception"() {
38 | when:
39 | SeleniumProxyFactory.getProxyURL(invalidProxyProp)
40 | then:
41 | thrown(IllegalArgumentException)
42 | where:
43 | invalidProxyProp << ["host:123", "http://host", "", null]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/groovy/com/frameworkium/core/ui/video/VideoCaptureSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.video
2 |
3 | import org.apache.commons.io.FileUtils
4 | import spock.lang.Specification
5 |
6 | import java.nio.file.Files
7 | import java.util.concurrent.TimeoutException
8 |
9 | class VideoCaptureSpec extends Specification {
10 |
11 | def setup() {
12 | FileUtils.deleteDirectory(VideoCapture.VIDEO_FOLDER.toFile())
13 | }
14 |
15 | def array = [0, 1, 0, 1] as byte[]
16 | def urlFetcherMock = Mock(UrlFetcher) {
17 | fetchWithRetry(_, _) >> array
18 | }
19 | def url = "http://foo/bar/%s.ext"
20 |
21 |
22 | def "fetches video for stored testname"() {
23 | given:
24 | def sut = new VideoCapture(url, urlFetcherMock)
25 | sut.saveTestSessionID("test1", "session1")
26 | when:
27 | sut.fetchAndSaveVideo("test1")
28 | then:
29 | def filepath = VideoCapture.VIDEO_FOLDER.resolve("test1-session1.ext")
30 | Files.readAllBytes(filepath) == array
31 | }
32 |
33 | def "doesn't save file on fetch timeout"() {
34 | given:
35 | def timingOutUrlFetcherMock = Mock(UrlFetcher) {
36 | fetchWithRetry(_, _) >> { throw new TimeoutException("test2") }
37 | }
38 | def sut = new VideoCapture(url, timingOutUrlFetcherMock)
39 | sut.saveTestSessionID("test2", "session2")
40 | when:
41 | sut.fetchAndSaveVideo("test2")
42 | then:
43 | Files.notExists(VideoCapture.VIDEO_FOLDER.resolve("test2-session2.ext"))
44 | }
45 |
46 | def "invalid url throws IllegalArgumentException"() {
47 | given:
48 | def sut = new VideoCapture("abc%^!", urlFetcherMock)
49 | sut.saveTestSessionID("test3", "session3")
50 | when:
51 | sut.fetchAndSaveVideo("test3")
52 | then:
53 | thrown IllegalArgumentException
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/core/api/dto/LowLevelDTO.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.api.dto;
2 |
3 | public class LowLevelDTO extends AbstractDTO {
4 | String data = "initial";
5 | }
6 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/core/api/dto/TopLevelDTO.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.api.dto;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | public class TopLevelDTO extends AbstractDTO {
7 | LowLevelDTO lowLevelDTO = new LowLevelDTO();
8 | List stringList = Arrays.asList("1", "a");
9 | }
10 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/core/ui/ExtraExpectedConditionsTest.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui;
2 |
3 | import com.frameworkium.core.htmlelements.element.Link;
4 | import com.frameworkium.core.ui.element.StreamTable;
5 | import org.testng.annotations.Test;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * Tests to ensure elements other than just WebElements can be passed into
12 | * {@link ExtraExpectedConditions}.
13 | *
14 | * These tests do not check the correctness of the implementation, they are here
15 | * to prevent https://github.com/Frameworkium/frameworkium-core/issues/133
16 | */
17 | public class ExtraExpectedConditionsTest {
18 |
19 | private List links = new ArrayList<>();
20 | private List tables = new ArrayList<>();
21 |
22 | @Test
23 | public void testNotPresentOrInvisibleWorksWithTypifiedElements() {
24 | ExtraExpectedConditions.notPresentOrInvisible(links);
25 | }
26 |
27 | @Test
28 | public void testSizeGreaterThanWorksWithTypifiedElements() {
29 | ExtraExpectedConditions.sizeGreaterThan(links, 1);
30 | }
31 |
32 | @Test
33 | public void testSizeLessThanWorksWithTypifiedElements() {
34 | ExtraExpectedConditions.sizeLessThan(links, 3);
35 | }
36 |
37 | @Test
38 | public void testNotPresentOrInvisibleWorksWithHtmlElements() {
39 | ExtraExpectedConditions.notPresentOrInvisible(tables);
40 | }
41 |
42 | @Test
43 | public void testSizeGreaterThanWorksWithHtmlElements() {
44 | ExtraExpectedConditions.sizeGreaterThan(tables, 1);
45 | }
46 |
47 | @Test
48 | public void testSizeLessThanWorksWithHtmlElements() {
49 | ExtraExpectedConditions.sizeLessThan(tables, 3);
50 | }
51 | }
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/core/ui/pages/pageobjects/TestPage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.core.ui.pages.pageobjects;
2 |
3 | import com.frameworkium.core.ui.pages.BasePage;
4 | import org.openqa.selenium.WebElement;
5 | import org.openqa.selenium.support.FindBy;
6 |
7 | /**
8 | * Java class to test page instantiation.
9 | */
10 | public class TestPage extends BasePage {
11 |
12 | @FindBy(css = "#foo")
13 | private WebElement element;
14 |
15 | public boolean isWebElementNull() {
16 | return element == null;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/CustomFirefoxImpl.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration;
2 |
3 | import com.frameworkium.core.ui.driver.AbstractDriver;
4 | import org.openqa.selenium.Capabilities;
5 | import org.openqa.selenium.WebDriver;
6 | import org.openqa.selenium.firefox.FirefoxDriver;
7 | import org.openqa.selenium.firefox.FirefoxOptions;
8 |
9 | /**
10 | * Used as a test of CustomImpl functionality
11 | */
12 | public class CustomFirefoxImpl extends AbstractDriver {
13 |
14 | @Override
15 | public Capabilities getCapabilities() {
16 | return new FirefoxOptions();
17 | }
18 |
19 | @Override
20 | public WebDriver getWebDriver(Capabilities capabilities) {
21 | return new FirefoxDriver(new FirefoxOptions(capabilities));
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/angularjs/pages/DeveloperGuidePage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.angularjs.pages;
2 |
3 | import com.frameworkium.core.htmlelements.element.Link;
4 | import com.frameworkium.core.htmlelements.element.TextInput;
5 | import com.frameworkium.core.ui.ExtraExpectedConditions;
6 | import com.frameworkium.core.ui.annotations.Visible;
7 | import com.frameworkium.core.ui.pages.BasePage;
8 | import com.frameworkium.core.ui.pages.PageFactory;
9 | import org.openqa.selenium.WebElement;
10 | import org.openqa.selenium.support.FindBy;
11 |
12 |
13 | public class DeveloperGuidePage extends BasePage {
14 |
15 | @Visible
16 | @FindBy(css = "input[name='as_q']")
17 | private TextInput searchField;
18 |
19 | @FindBy(linkText = "Bootstrap")
20 | private Link bootstrapSearchItem;
21 |
22 | @FindBy(id = "loading")
23 | private WebElement loading;
24 |
25 | public static DeveloperGuidePage open() {
26 | return PageFactory.newInstance(
27 | DeveloperGuidePage.class, "https://docs.angularjs.org/guide");
28 | }
29 |
30 | public DeveloperGuidePage searchDeveloperGuide(String inputText) {
31 | searchField.sendKeys(inputText);
32 | return this;
33 | }
34 |
35 | public DeveloperGuidePage clickBootstrapSearchItem() {
36 | bootstrapSearchItem.click();
37 | waitForJavascriptFrameworkToFinish();
38 | wait.until(ExtraExpectedConditions.notPresentOrInvisible(loading));
39 | return this;
40 | }
41 |
42 | public String getGuideTitle() {
43 | return getTitle();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/angularjs/tests/DocumentationTest.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.angularjs.tests;
2 |
3 | import com.frameworkium.core.common.retry.RetryFlakyTest;
4 | import com.frameworkium.core.ui.tests.BaseUITest;
5 | import com.frameworkium.integration.angularjs.pages.DeveloperGuidePage;
6 | import io.qameta.allure.TmsLink;
7 | import org.testng.annotations.Test;
8 |
9 | import static com.google.common.truth.Truth.assertThat;
10 |
11 | public class DocumentationTest extends BaseUITest {
12 |
13 | @Test(retryAnalyzer = RetryFlakyTest.class)
14 | @TmsLink("ANG-1")
15 | public void angular_documentation_test() {
16 | String guideTitle = DeveloperGuidePage.open()
17 | .searchDeveloperGuide("Bootstrap")
18 | .clickBootstrapSearchItem()
19 | .getGuideTitle();
20 |
21 | assertThat(guideTitle)
22 | .endsWith("Bootstrap");
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/constant/CaptureEndpoint.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.constant;
2 |
3 | import com.frameworkium.core.api.Endpoint;
4 |
5 | /** The various Endpoints of Capture. */
6 | public enum CaptureEndpoint implements Endpoint {
7 |
8 | BASE_URI("http://capture-6c06138r.cloudapp.net"),
9 | EXECUTIONS("/executions"),
10 | GET_EXECUTION("/executions/%s"),
11 | SUT_NAMES("/softwareUnderTest/names"),
12 | SUT_VERSIONS("/softwareUnderTest/versions/%s"),
13 | SCREENSHOT("/screenshot");
14 |
15 | private String url;
16 |
17 | CaptureEndpoint(String url) {
18 | this.url = url;
19 | }
20 |
21 | /**
22 | * @param params Arguments referenced by the format specifiers in the url.
23 | * @return A formatted String representing the URL of the given constant.
24 | */
25 | @Override
26 | public String getUrl(Object... params) {
27 | return String.format(url, params);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/dto/executions/Browser.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.dto.executions;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 | import com.frameworkium.core.api.dto.AbstractDTO;
5 |
6 | @JsonIgnoreProperties(ignoreUnknown = true)
7 | public class Browser extends AbstractDTO {
8 |
9 | public String name;
10 | public String version;
11 |
12 | public static Browser newInstance() {
13 | Browser browser = new Browser();
14 | browser.name = "Firefox";
15 | browser.version = "58.0";
16 | return browser;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/dto/executions/CreateExecution.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.dto.executions;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | import java.net.InetAddress;
6 | import java.net.UnknownHostException;
7 |
8 | public class CreateExecution extends AbstractDTO {
9 |
10 | public String testID;
11 | public Browser browser;
12 | public SoftwareUnderTest softwareUnderTest;
13 | public String nodeAddress;
14 |
15 | /**
16 | * Using the static factory method pattern instead of using a constructor
17 | * which allows for multiple different, well named, creation methods.
18 | *
19 | * @return a populated {@link CreateExecution} with sensible defaults.
20 | */
21 | public static CreateExecution newCreateInstance() {
22 | CreateExecution execution = new CreateExecution();
23 | execution.testID = "DEFAULT-1";
24 | execution.browser = Browser.newInstance();
25 | execution.softwareUnderTest = SoftwareUnderTest.newInstance();
26 | try {
27 | execution.nodeAddress = InetAddress.getLocalHost().getHostAddress();
28 | } catch (UnknownHostException e) {
29 | execution.nodeAddress = null;
30 | }
31 | return execution;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/dto/executions/ExecutionID.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.dto.executions;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | /** Created execution message. */
6 | public class ExecutionID extends AbstractDTO {
7 |
8 | public String executionID;
9 | }
10 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/dto/executions/ExecutionResponse.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.dto.executions;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 | import com.frameworkium.integration.capture.api.dto.screenshots.Screenshot;
5 |
6 | import java.util.List;
7 |
8 | public class ExecutionResponse extends AbstractDTO {
9 |
10 | public String testID;
11 | public Browser browser;
12 | public SoftwareUnderTest softwareUnderTest;
13 | public String nodeAddress;
14 | public String created;
15 | public String lastUpdated;
16 | public String currentStatus;
17 | public String executionID;
18 | public List screenshots;
19 |
20 | public boolean createdFrom(CreateExecution createMessage) {
21 | return browser.equals(createMessage.browser)
22 | && softwareUnderTest.equals(createMessage.softwareUnderTest)
23 | && testID.equals(createMessage.testID)
24 | && nodeAddress.equals(createMessage.nodeAddress);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/dto/executions/ExecutionResults.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.dto.executions;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | import java.util.List;
6 |
7 | public class ExecutionResults extends AbstractDTO {
8 |
9 | public List results;
10 | public int total;
11 | }
12 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/dto/executions/SoftwareUnderTest.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.dto.executions;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | public class SoftwareUnderTest extends AbstractDTO {
6 |
7 | public String name;
8 | public String version;
9 |
10 | public static SoftwareUnderTest newInstance() {
11 | SoftwareUnderTest sut = new SoftwareUnderTest();
12 | sut.name = "frameworkium-core";
13 | sut.version = "master";
14 | return sut;
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/dto/screenshots/Command.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.dto.screenshots;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | public class Command extends AbstractDTO {
6 |
7 | public String action;
8 | public String using;
9 | public String value;
10 |
11 | public static Command newInstance() {
12 | Command command = new Command();
13 | command.action = "click";
14 | command.using = "id";
15 | command.value = "my-id";
16 | return command;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/dto/screenshots/CreateScreenshot.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.dto.screenshots;
2 |
3 | import com.fasterxml.jackson.annotation.JsonInclude;
4 | import com.frameworkium.core.api.dto.AbstractDTO;
5 | import org.apache.commons.io.IOUtils;
6 |
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.util.Base64;
10 |
11 | @JsonInclude(JsonInclude.Include.NON_NULL)
12 | public class CreateScreenshot extends AbstractDTO {
13 |
14 | public Command command;
15 | public String url;
16 | public String executionID;
17 | public String errorMessage;
18 | public String screenshotBase64;
19 |
20 | public static CreateScreenshot newInstance(String executionID) {
21 | CreateScreenshot createScreenshot = new CreateScreenshot();
22 | createScreenshot.executionID = executionID;
23 | createScreenshot.command = Command.newInstance();
24 | createScreenshot.url = "http://test.url/hello?x=1&y=2";
25 | createScreenshot.errorMessage = null;
26 | createScreenshot.screenshotBase64 = getBase64TestImage();
27 | return createScreenshot;
28 | }
29 |
30 | private static String getBase64TestImage() {
31 | try {
32 | InputStream imageStream = CreateScreenshot.class.getClassLoader()
33 | .getResourceAsStream("capture-screenshot.png");
34 | byte[] bytes = IOUtils.toByteArray(imageStream);
35 | return Base64.getEncoder().encodeToString(bytes);
36 | } catch (IOException e) {
37 | throw new IllegalStateException(e);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/dto/screenshots/Screenshot.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.dto.screenshots;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | public class Screenshot extends AbstractDTO {
6 |
7 | public Command command;
8 | public String imageURL;
9 | public String timestamp;
10 | public String url;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/service/BaseCaptureService.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.service;
2 |
3 | import com.frameworkium.core.api.services.BaseService;
4 | import com.frameworkium.integration.capture.api.constant.CaptureEndpoint;
5 | import io.restassured.RestAssured;
6 | import io.restassured.http.ContentType;
7 | import io.restassured.response.ValidatableResponse;
8 | import io.restassured.specification.RequestSpecification;
9 | import io.restassured.specification.ResponseSpecification;
10 | import org.apache.http.HttpStatus;
11 |
12 | /** Base Service for Capture specific services. */
13 | public class BaseCaptureService extends BaseService {
14 |
15 | /**
16 | * @return a Rest Assured {@link RequestSpecification} with the baseUri
17 | * (and anything else required by most Capture services).
18 | */
19 | @Override
20 | protected RequestSpecification getRequestSpec() {
21 | return RestAssured.given()
22 | .baseUri(CaptureEndpoint.BASE_URI.getUrl())
23 | .relaxedHTTPSValidation() // trusts even invalid certs
24 | // .log().all() // uncomment to log each request
25 | .contentType(ContentType.JSON)
26 | .accept(ContentType.JSON);
27 | }
28 |
29 | /**
30 | * @return a Rest Assured {@link ResponseSpecification} with basic checks
31 | * (and anything else required by most Capture services).
32 | */
33 | @Override
34 | protected ResponseSpecification getResponseSpec() {
35 | return RestAssured.expect().response()
36 | .statusCode(HttpStatus.SC_OK);
37 | }
38 |
39 | protected ValidatableResponse post(String url, Object body) {
40 | return getRequestSpec()
41 | .when()
42 | .body(body)
43 | .post(url)
44 | .then()
45 | .assertThat().statusCode(HttpStatus.SC_CREATED);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/service/executions/ExecutionService.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.service.executions;
2 |
3 | import com.frameworkium.integration.capture.api.constant.CaptureEndpoint;
4 | import com.frameworkium.integration.capture.api.dto.executions.*;
5 | import com.frameworkium.integration.capture.api.service.BaseCaptureService;
6 | import com.google.common.collect.ImmutableMap;
7 | import io.qameta.allure.Step;
8 |
9 | /** Encapsulates the Capture ExecutionResponse service */
10 | public class ExecutionService extends BaseCaptureService {
11 |
12 | @Step("Create Capture Execution {createMessage}")
13 | public ExecutionID createExecution(CreateExecution createMessage) {
14 | return post(CaptureEndpoint.EXECUTIONS.getUrl(), createMessage)
15 | .extract()
16 | .as(ExecutionID.class);
17 | }
18 |
19 | @Step("Get Capture Executions, page={page}, pageSize={pageSize}")
20 | public ExecutionResults getExecutions(int page, int pageSize) {
21 | return get(
22 | ImmutableMap.of("page", page, "pageSize", pageSize),
23 | CaptureEndpoint.EXECUTIONS.getUrl())
24 | .as(ExecutionResults.class);
25 | }
26 |
27 | public ExecutionResponse getExecution(String executionID) {
28 | return get(CaptureEndpoint.GET_EXECUTION.getUrl(executionID))
29 | .as(ExecutionResponse.class);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/capture/api/service/screenshots/ScreenshotService.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.capture.api.service.screenshots;
2 |
3 | import com.frameworkium.integration.capture.api.constant.CaptureEndpoint;
4 | import com.frameworkium.integration.capture.api.dto.screenshots.CreateScreenshot;
5 | import com.frameworkium.integration.capture.api.service.BaseCaptureService;
6 |
7 | /** Encapsulates the Capture ExecutionResponse service */
8 | public class ScreenshotService extends BaseCaptureService {
9 |
10 | public void createScreenshot(CreateScreenshot createMessage) {
11 | post(CaptureEndpoint.SCREENSHOT.getUrl(), createMessage);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/frameworkium/pages/JQueryDemoPage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.frameworkium.pages;
2 |
3 | import com.frameworkium.core.ui.ExtraExpectedConditions;
4 | import com.frameworkium.core.ui.annotations.Visible;
5 | import com.frameworkium.core.ui.pages.BasePage;
6 | import com.frameworkium.core.ui.pages.PageFactory;
7 | import org.openqa.selenium.WebElement;
8 | import org.openqa.selenium.support.FindBy;
9 |
10 | public class JQueryDemoPage extends BasePage {
11 |
12 | @Visible
13 | @FindBy(css = "#content > h1")
14 | private WebElement heading;
15 |
16 | public static JQueryDemoPage open() {
17 | return PageFactory.newInstance(
18 | JQueryDemoPage.class,
19 | "https://jqueryui.com/demos/");
20 | }
21 |
22 | public JQueryDemoPage waitForJQuery() {
23 | // Just testing it doesn't fail
24 | wait.until(ExtraExpectedConditions.jQueryAjaxDone());
25 | return this;
26 | }
27 |
28 | public String getHeadingText() {
29 | return heading.getText();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/frameworkium/tests/FrameworkiumBugsTest.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.frameworkium.tests;
2 |
3 | import com.frameworkium.core.common.reporting.allure.AllureLogger;
4 | import com.frameworkium.core.ui.UITestLifecycle;
5 | import com.frameworkium.core.ui.tests.BaseUITest;
6 | import com.frameworkium.integration.frameworkium.pages.JQueryDemoPage;
7 | import com.frameworkium.integration.seleniumhq.pages.SeleniumDownloadPage;
8 | import org.testng.annotations.BeforeMethod;
9 | import org.testng.annotations.Test;
10 |
11 | import static com.google.common.truth.Truth.assertThat;
12 |
13 | // tests if BeforeMethods are run despite not being in this group
14 | @Test(groups = "fw-bugs")
15 | public class FrameworkiumBugsTest extends BaseUITest {
16 |
17 | @BeforeMethod(dependsOnMethods = "configureBrowserBeforeTest")
18 | public void configureBrowserBeforeUse_allows_browser_access_in_before_method() {
19 | assertThat(UITestLifecycle.get().getWebDriver().getPageSource()).isNotEmpty();
20 | }
21 |
22 | public void ensure_jQueryAjaxDone_does_not_fail() {
23 | String headingText =
24 | JQueryDemoPage.open()
25 | .waitForJQuery()
26 | .getHeadingText();
27 | assertThat(headingText).isEqualTo("jQuery UI Demos");
28 | }
29 |
30 | public void use_base_page_visibility() {
31 | SeleniumDownloadPage.open()
32 | .hideContent()
33 | .forceVisibleContent()
34 | .waitForContent();
35 | }
36 |
37 | @Test(dependsOnMethods = {"use_base_page_visibility"})
38 | public void ensure_BaseUITest_wait_is_updated_after_browser_reset() {
39 | // tests bug whereby BasePage.wait wasn't updated after browser reset
40 | SeleniumDownloadPage.open().waitForContent();
41 | }
42 |
43 | public void use_various_loggers() {
44 | logger.info("Using BaseUITest logger");
45 | AllureLogger.logToAllure("Using log to Allure!");
46 | AllureLogger.stepStart("Starting a custom allure step.");
47 | SeleniumDownloadPage.open().log();
48 | AllureLogger.stepFinish();
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/constant/BookerEndpoint.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.constant;
2 |
3 | import com.frameworkium.core.api.Endpoint;
4 |
5 | /** The various Endpoints of Restful Booker. */
6 | public enum BookerEndpoint implements Endpoint {
7 |
8 | BASE_URI("https://restful-booker.herokuapp.com"),
9 | PING("/ping"),
10 | BOOKING("/booking"),
11 | BOOKING_ID("/booking/%d"),
12 | AUTH("/auth");
13 |
14 | private String url;
15 |
16 | BookerEndpoint(String url) {
17 | this.url = url;
18 | }
19 |
20 | /**
21 | * @param params Arguments referenced by the format specifiers in the url.
22 | * @return A formatted String representing the URL of the given constant.
23 | */
24 | @Override
25 | public String getUrl(Object... params) {
26 | return String.format(url, params);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/dto/booking/Booking.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.dto.booking;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | import java.util.concurrent.ThreadLocalRandom;
6 |
7 | public class Booking extends AbstractDTO {
8 | public String firstname;
9 | public String lastname;
10 | public long totalprice;
11 | public boolean depositpaid;
12 | public BookingDates bookingdates;
13 | public String additionalneeds;
14 |
15 | public static Booking newInstance() {
16 | ThreadLocalRandom random = ThreadLocalRandom.current();
17 | int randInt = random.nextInt();
18 | Booking booking = new Booking();
19 | booking.firstname = "firstname" + randInt;
20 | booking.lastname = "lastname" + randInt;
21 | booking.totalprice = randInt;
22 | booking.depositpaid = random.nextBoolean();
23 | booking.bookingdates = BookingDates.newInstance();
24 | booking.additionalneeds = null;
25 | return booking;
26 | }
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/dto/booking/BookingDates.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.dto.booking;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | import java.time.LocalDate;
6 | import java.time.format.DateTimeFormatter;
7 |
8 | public class BookingDates extends AbstractDTO {
9 |
10 | public static final DateTimeFormatter FORMAT =
11 | DateTimeFormatter.ISO_LOCAL_DATE;
12 |
13 | public String checkin;
14 | public String checkout;
15 |
16 | public static BookingDates newInstance() {
17 | BookingDates dates = new BookingDates();
18 | dates.checkin = LocalDate.now().plusDays(1).format(FORMAT);
19 | dates.checkout = LocalDate.now().plusDays(10).format(FORMAT);
20 | return dates;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/dto/booking/BookingID.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.dto.booking;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | public class BookingID extends AbstractDTO {
6 |
7 | public int bookingid;
8 |
9 | public static BookingID of(int bookingID) {
10 | BookingID id = new BookingID();
11 | id.bookingid = bookingID;
12 | return id;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/dto/booking/BookingResponse.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.dto.booking;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | public class BookingResponse extends AbstractDTO {
6 | public Booking booking;
7 | }
8 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/dto/booking/CreateBookingResponse.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.dto.booking;
2 |
3 | import com.frameworkium.core.api.dto.AbstractDTO;
4 |
5 | public class CreateBookingResponse extends AbstractDTO {
6 | public Booking booking;
7 | public int bookingid;
8 | }
9 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/dto/booking/search/SearchParamsMapper.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.dto.booking.search;
2 |
3 | import com.frameworkium.integration.restfulbooker.api.dto.booking.Booking;
4 | import com.google.common.collect.ImmutableMap;
5 |
6 | import java.util.Map;
7 |
8 | public class SearchParamsMapper {
9 |
10 | private SearchParamsMapper() {
11 | // hide constructor of mapper
12 | }
13 |
14 | public static Map namesOfBooking(Booking booking) {
15 | return ImmutableMap.of(
16 | "firstname", booking.firstname,
17 | "lastname", booking.lastname);
18 | }
19 |
20 | public static Map datesOfBooking(Booking booking) {
21 | return ImmutableMap.of(
22 | "checkin", booking.bookingdates.checkin,
23 | "checkout", booking.bookingdates.checkout);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/service/AbstractBookerService.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.service;
2 |
3 | import com.frameworkium.core.api.services.BaseService;
4 | import com.frameworkium.integration.restfulbooker.api.constant.BookerEndpoint;
5 | import io.restassured.RestAssured;
6 | import io.restassured.http.Method;
7 | import io.restassured.response.ExtractableResponse;
8 | import io.restassured.specification.RequestSpecification;
9 | import io.restassured.specification.ResponseSpecification;
10 | import org.apache.http.HttpStatus;
11 |
12 | /** Base Service for RestfulBooker specific services. */
13 | public abstract class AbstractBookerService extends BaseService {
14 |
15 | /**
16 | * @return a Rest Assured {@link RequestSpecification} with the baseUri
17 | * (and anything else required by most Capture services).
18 | */
19 | @Override
20 | protected RequestSpecification getRequestSpec() {
21 | return RestAssured.given()
22 | .baseUri(BookerEndpoint.BASE_URI.getUrl())
23 | .relaxedHTTPSValidation() // trusts even invalid certs
24 | // .log().all() // uncomment to log each request
25 | .contentType("application/json")
26 | .accept("application/json");
27 | }
28 |
29 | /**
30 | * @return a Rest Assured {@link ResponseSpecification} with basic checks
31 | * (and anything else required by most services).
32 | */
33 | @Override
34 | protected ResponseSpecification getResponseSpec() {
35 | return RestAssured.expect().response()
36 | .statusCode(HttpStatus.SC_OK);
37 | }
38 |
39 | protected ExtractableResponse post(Object body, String url) {
40 | return request(Method.POST, body, url);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/service/booking/BookingService.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.service.booking;
2 |
3 | import com.frameworkium.integration.restfulbooker.api.constant.BookerEndpoint;
4 | import com.frameworkium.integration.restfulbooker.api.dto.booking.*;
5 | import com.frameworkium.integration.restfulbooker.api.service.AbstractBookerService;
6 | import com.google.common.collect.ImmutableMap;
7 | import io.restassured.http.Method;
8 |
9 | import java.util.List;
10 | import java.util.Map;
11 |
12 | public class BookingService extends AbstractBookerService {
13 |
14 | public List listBookings() {
15 | return get(BookerEndpoint.BOOKING.getUrl())
16 | .jsonPath().getList(".", BookingID.class);
17 | }
18 |
19 | public Booking getBooking(int id) {
20 | return get(BookerEndpoint.BOOKING_ID.getUrl(id))
21 | .as(Booking.class);
22 | }
23 |
24 | public CreateBookingResponse createBooking(Booking booking) {
25 | return post(booking, BookerEndpoint.BOOKING.getUrl())
26 | .as(CreateBookingResponse.class);
27 | }
28 |
29 | public List search(Map searchParams) {
30 | return request(Method.GET, searchParams, BookerEndpoint.BOOKING.getUrl())
31 | .jsonPath().getList(".", BookingID.class);
32 | }
33 |
34 | public String createAuthToken(String username, String password) {
35 | return post(
36 | ImmutableMap.of("username", username, "password", password),
37 | BookerEndpoint.AUTH.getUrl())
38 | .jsonPath().get("token");
39 | }
40 |
41 | public void delete(int bookingID, String token) {
42 | getRequestSpec()
43 | .cookie("token", token)
44 | .delete(BookerEndpoint.BOOKING_ID.getUrl(bookingID))
45 | // API does not match documentation
46 | // .then()
47 | // .statusCode(HttpStatus.SC_NO_CONTENT)
48 | ;
49 | }
50 |
51 | public boolean doesBookingExist(int bookingID) {
52 | final int statusCode = getRequestSpec()
53 | .get(BookerEndpoint.BOOKING_ID.getUrl(bookingID))
54 | .then()
55 | .extract()
56 | .statusCode();
57 | if (statusCode == 200) {
58 | return true;
59 | } else if (statusCode == 404) {
60 | return false;
61 | } else {
62 | throw new IllegalStateException("Unexpected return code");
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/service/ping/PingService.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.service.ping;
2 |
3 | import com.frameworkium.integration.restfulbooker.api.constant.BookerEndpoint;
4 | import com.frameworkium.integration.restfulbooker.api.service.AbstractBookerService;
5 | import io.restassured.RestAssured;
6 | import io.restassured.specification.ResponseSpecification;
7 | import org.apache.http.HttpStatus;
8 |
9 | public class PingService extends AbstractBookerService {
10 |
11 | public String ping() {
12 | return get(BookerEndpoint.PING.getUrl())
13 | .body().asString();
14 | }
15 |
16 | /**
17 | * Used in template method {@link AbstractBookerService#get(String)}
18 | */
19 | @Override
20 | protected ResponseSpecification getResponseSpec() {
21 | return RestAssured.expect().response()
22 | .statusCode(HttpStatus.SC_CREATED);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/tests/BookerTest.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.tests;
2 |
3 | import com.frameworkium.core.api.tests.BaseAPITest;
4 | import com.frameworkium.core.common.retry.RetryFlakyTest;
5 | import com.frameworkium.integration.restfulbooker.api.dto.booking.*;
6 | import com.frameworkium.integration.restfulbooker.api.service.booking.BookingService;
7 | import com.frameworkium.integration.restfulbooker.api.service.ping.PingService;
8 | import org.testng.annotations.BeforeClass;
9 | import org.testng.annotations.Test;
10 |
11 | import static com.google.common.truth.Truth.assertThat;
12 |
13 | // app resets every 10m, so could happen in the middle of this test
14 | @Test(retryAnalyzer = RetryFlakyTest.class)
15 | public class BookerTest extends BaseAPITest {
16 |
17 | @BeforeClass
18 | public void ensure_site_is_up_by_using_ping_service() {
19 | assertThat(new PingService().ping())
20 | .isEqualTo("Created");
21 | }
22 |
23 | public void create_new_booking() {
24 | // given some booking data
25 | BookingService service = new BookingService();
26 | Booking booking = Booking.newInstance();
27 |
28 | // when creating the booking
29 | CreateBookingResponse bookingResponse = service.createBooking(booking);
30 |
31 | // the booking returned matches the input and is persisted
32 | assertThat(bookingResponse.booking)
33 | .isEqualTo(booking);
34 | assertThat(service.getBooking(bookingResponse.bookingid))
35 | .isEqualTo(booking);
36 | }
37 |
38 | public void auth_token_matches_expected_pattern() {
39 | String token = new BookingService().createAuthToken(
40 | "admin", "password123");
41 | assertThat(token).matches("[a-z0-9]{15}");
42 | }
43 |
44 | public void delete_newly_created_booking() {
45 | // given an existing booking
46 | BookingService service = new BookingService();
47 | int bookingID = service.createBooking(Booking.newInstance()).bookingid;
48 | // and an auth token
49 | String authToken = new BookingService().createAuthToken(
50 | "admin", "password123");
51 | // when deleting
52 | service.delete(bookingID, authToken);
53 |
54 | // then the booking no longer exists
55 | assertThat(service.doesBookingExist(bookingID)).isFalse();
56 | assertThat(service.listBookings())
57 | .doesNotContain(BookingID.of(bookingID));
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/restfulbooker/api/tests/SearchBookerTest.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.restfulbooker.api.tests;
2 |
3 | import com.frameworkium.core.api.tests.BaseAPITest;
4 | import com.frameworkium.core.common.retry.RetryFlakyTest;
5 | import com.frameworkium.integration.restfulbooker.api.dto.booking.Booking;
6 | import com.frameworkium.integration.restfulbooker.api.dto.booking.BookingID;
7 | import com.frameworkium.integration.restfulbooker.api.dto.booking.search.SearchParamsMapper;
8 | import com.frameworkium.integration.restfulbooker.api.service.booking.BookingService;
9 | import com.frameworkium.integration.restfulbooker.api.service.ping.PingService;
10 | import org.testng.SkipException;
11 | import org.testng.annotations.BeforeClass;
12 | import org.testng.annotations.Test;
13 |
14 | import java.util.List;
15 |
16 | import static com.google.common.truth.Truth.assertThat;
17 |
18 | // app resets every 10m, so could happen in the middle of this test
19 | @Test(retryAnalyzer = RetryFlakyTest.class)
20 | public class SearchBookerTest extends BaseAPITest {
21 |
22 | @BeforeClass
23 | public void ensure_site_is_up_by_using_ping_service() {
24 | assertThat(new PingService().ping())
25 | .isEqualTo("Created");
26 | }
27 |
28 | public void search_for_existing_records_by_name() {
29 | BookingService service = new BookingService();
30 | BookingID existingID = service.listBookings().get(1);
31 | Booking booking = service.getBooking(existingID.bookingid);
32 |
33 | List bookingIDs = service.search(
34 | SearchParamsMapper.namesOfBooking(booking));
35 |
36 | assertThat(bookingIDs).contains(existingID);
37 | }
38 |
39 | public void search_for_existing_records_by_date() {
40 | BookingService service = new BookingService();
41 | BookingID existingID = service.listBookings().get(3);
42 | Booking booking = service.getBooking(existingID.bookingid);
43 |
44 | List bookingIDs = service.search(
45 | SearchParamsMapper.datesOfBooking(booking));
46 |
47 | // TODO: move to dedicated test
48 | throw new SkipException("Known bug in service, dates not inclusive");
49 | // assertThat(bookingIDs).contains(existingID);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/seleniumhq/components/HeaderComponent.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.seleniumhq.components;
2 |
3 | import com.frameworkium.core.htmlelements.element.HtmlElement;
4 | import com.frameworkium.core.htmlelements.element.Link;
5 | import com.frameworkium.core.ui.UITestLifecycle;
6 | import com.frameworkium.core.ui.pages.PageFactory;
7 | import com.frameworkium.integration.seleniumhq.pages.SeleniumDownloadPage;
8 | import org.openqa.selenium.support.FindBy;
9 | import org.openqa.selenium.support.ui.ExpectedConditions;
10 |
11 |
12 | @FindBy(className = "navbar")
13 | public class HeaderComponent extends HtmlElement {
14 |
15 | @FindBy(css = "#main_navbar [href='/downloads']")
16 | private Link downloadLink;
17 |
18 | public SeleniumDownloadPage clickDownloadLink() {
19 | UITestLifecycle.get().getWait().until(ExpectedConditions.elementToBeClickable(downloadLink));
20 | downloadLink.click();
21 | return PageFactory.newInstance(SeleniumDownloadPage.class);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/seleniumhq/pages/HomePage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.seleniumhq.pages;
2 |
3 | import com.frameworkium.core.ui.annotations.Visible;
4 | import com.frameworkium.core.ui.pages.BasePage;
5 | import com.frameworkium.core.ui.pages.PageFactory;
6 | import com.frameworkium.integration.seleniumhq.components.HeaderComponent;
7 | import org.openqa.selenium.WebElement;
8 | import org.openqa.selenium.support.CacheLookup;
9 | import org.openqa.selenium.support.FindBy;
10 |
11 | public class HomePage extends BasePage {
12 |
13 | @CacheLookup
14 | @Visible
15 | private HeaderComponent header;
16 |
17 | @FindBy(css = "button[data-target='#main_navbar']")
18 | private WebElement menuLink;
19 |
20 | public static HomePage open() {
21 | return PageFactory.newInstance(
22 | HomePage.class,
23 | "https://selenium.dev/");
24 | }
25 |
26 | public HeaderComponent getHeader() {
27 | // when using Selenium grid, the browser size is smaller and the header is hidden behind this link
28 | if (menuLink.isDisplayed()) {
29 | menuLink.click();
30 | }
31 | return header;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/seleniumhq/pages/SeleniumDownloadPage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.seleniumhq.pages;
2 |
3 | import com.frameworkium.core.htmlelements.element.Link;
4 | import com.frameworkium.core.ui.annotations.Visible;
5 | import com.frameworkium.core.ui.pages.BasePage;
6 | import com.frameworkium.core.ui.pages.PageFactory;
7 | import com.frameworkium.integration.seleniumhq.components.HeaderComponent;
8 | import org.openqa.selenium.support.FindBy;
9 | import org.openqa.selenium.support.ui.ExpectedConditions;
10 |
11 |
12 | import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOf;
13 |
14 | public class SeleniumDownloadPage extends BasePage {
15 |
16 | @Visible
17 | private HeaderComponent header;
18 |
19 | @Visible
20 | @FindBy(css = "body .td-main div:nth-child(3) > div:nth-child(2) a")
21 | private Link latestDownloadLink;
22 |
23 | public static SeleniumDownloadPage open() {
24 | return PageFactory.newInstance(
25 | SeleniumDownloadPage.class,
26 | "https://selenium.dev/downloads/");
27 | }
28 |
29 | public String getLatestVersion() {
30 | return latestDownloadLink.getText();
31 | }
32 |
33 | public SeleniumDownloadPage hideContent() {
34 | executeJS("arguments[0].style.visibility='hidden';", latestDownloadLink);
35 | wait.until(ExpectedConditions.not(visibilityOf(latestDownloadLink)));
36 | return this;
37 | }
38 |
39 | public SeleniumDownloadPage forceVisibleContent() {
40 | // ensure force visible works
41 | forceVisible(latestDownloadLink);
42 | return this;
43 | }
44 |
45 | public void waitForContent() {
46 | wait.until(visibilityOf(latestDownloadLink));
47 | }
48 |
49 | public void log() {
50 | logger.trace("Using the BasePage logger");
51 | logger.info("Using the BasePage logger");
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/seleniumhq/tests/SeleniumTest.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.seleniumhq.tests;
2 |
3 | import com.frameworkium.core.ui.tests.BaseUITest;
4 | import com.frameworkium.integration.seleniumhq.pages.HomePage;
5 |
6 | import static com.google.common.truth.Truth.assertThat;
7 |
8 | public class SeleniumTest extends BaseUITest {
9 |
10 | public final void component_example_test() {
11 | String latestVersion = HomePage.open()
12 | .getHeader()
13 | .clickDownloadLink()
14 | .getLatestVersion();
15 |
16 | assertThat(latestVersion).matches("\\d\\.\\d+\\.\\d+");
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/theinternet/pages/CheckboxesPage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.theinternet.pages;
2 |
3 | import com.frameworkium.core.htmlelements.element.CheckBox;
4 | import com.frameworkium.core.ui.annotations.Visible;
5 | import com.frameworkium.core.ui.pages.BasePage;
6 | import io.qameta.allure.Step;
7 | import org.openqa.selenium.support.FindBy;
8 |
9 |
10 | import java.util.List;
11 | import java.util.stream.Stream;
12 |
13 | public class CheckboxesPage extends BasePage {
14 |
15 | @Visible(checkAtMost = 1)
16 | @FindBy(css = "form input[type='checkbox']")
17 | private List allCheckboxes;
18 |
19 | @Step("Set all the checkboxes to true")
20 | public CheckboxesPage checkAllCheckboxes() {
21 | allCheckboxes.forEach(CheckBox::select);
22 | return this;
23 | }
24 |
25 | @Step("Return the checked status of all the checkboxes")
26 | public Stream getAllCheckboxCheckedStatus() {
27 | return allCheckboxes.stream()
28 | .map(CheckBox::isSelected);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/theinternet/pages/DynamicLoadingExamplePage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.theinternet.pages;
2 |
3 | import com.frameworkium.core.htmlelements.annotations.Timeout;
4 | import com.frameworkium.core.htmlelements.element.Button;
5 | import com.frameworkium.core.htmlelements.element.HtmlElement;
6 | import com.frameworkium.core.ui.annotations.Invisible;
7 | import com.frameworkium.core.ui.annotations.Visible;
8 | import com.frameworkium.core.ui.pages.BasePage;
9 | import com.frameworkium.core.ui.pages.PageFactory;
10 | import io.qameta.allure.Step;
11 | import org.openqa.selenium.support.FindBy;
12 |
13 | import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOf;
14 |
15 | public class DynamicLoadingExamplePage extends BasePage {
16 |
17 | @Visible
18 | @FindBy(css = "#start button")
19 | private Button startButton;
20 |
21 | @Invisible
22 | @Timeout(0) // prevents page load taking 5s due to implicit timeout
23 | @FindBy(id = "finish")
24 | private HtmlElement dynamicElement;
25 |
26 | public static DynamicLoadingExamplePage openExampleTwo() {
27 | return PageFactory.newInstance(
28 | DynamicLoadingExamplePage.class,
29 | "https://the-internet.herokuapp.com/dynamic_loading/2");
30 | }
31 |
32 | @Step("Click Start")
33 | public DynamicLoadingExamplePage clickStart() {
34 | startButton.click();
35 | return this;
36 | }
37 |
38 | public String getElementText() {
39 | wait.until(visibilityOf(dynamicElement));
40 | return dynamicElement.getText();
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/theinternet/pages/HoversPage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.theinternet.pages;
2 |
3 | import com.frameworkium.core.ui.annotations.Invisible;
4 | import com.frameworkium.core.ui.annotations.Visible;
5 | import com.frameworkium.core.ui.pages.BasePage;
6 | import io.qameta.allure.Step;
7 | import org.openqa.selenium.WebElement;
8 | import org.openqa.selenium.interactions.Actions;
9 | import org.openqa.selenium.support.FindBy;
10 |
11 | public class HoversPage extends BasePage {
12 |
13 | @Visible
14 | @FindBy(css = "div.figure:nth-of-type(1)")
15 | private WebElement firstFigure;
16 |
17 | @Invisible
18 | @FindBy(css = "div.figure:nth-of-type(1) div.figcaption")
19 | private WebElement firstFigureCaption;
20 |
21 | @Step("Get 1st figure caption")
22 | public String getFirstFigureCaption() {
23 |
24 | // Move mouse over the first figure to make caption visible
25 | (new Actions(driver)).moveToElement(firstFigure).perform();
26 |
27 | // Return text from the now-visible caption
28 | return firstFigureCaption.getText();
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/theinternet/pages/JavaScriptAlertsPage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.theinternet.pages;
2 |
3 | import com.frameworkium.core.htmlelements.element.Button;
4 | import com.frameworkium.core.ui.annotations.Visible;
5 | import com.frameworkium.core.ui.pages.BasePage;
6 | import io.qameta.allure.Step;
7 | import org.openqa.selenium.WebElement;
8 | import org.openqa.selenium.support.FindBy;
9 |
10 |
11 | import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOf;
12 |
13 | public class JavaScriptAlertsPage extends BasePage {
14 |
15 | @Visible
16 | @FindBy(css = "button[onclick='jsAlert()']")
17 | private Button jsAlertButton;
18 |
19 | @FindBy(css = "p#result")
20 | private WebElement resultArea;
21 |
22 | @Step("Click alert")
23 | public JavaScriptAlertsPage clickAlertButtonAndAccept() {
24 | jsAlertButton.click();
25 | driver.switchTo().alert().accept();
26 | wait.until(visibilityOf(resultArea));
27 | return this;
28 | }
29 |
30 | @Step("Click prompt")
31 | public String getResultText() {
32 | return resultArea.getText();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/theinternet/pages/KeyPressesPage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.theinternet.pages;
2 |
3 | import com.frameworkium.core.ui.annotations.Visible;
4 | import com.frameworkium.core.ui.pages.BasePage;
5 | import io.qameta.allure.Step;
6 | import org.openqa.selenium.Keys;
7 | import org.openqa.selenium.WebElement;
8 | import org.openqa.selenium.interactions.Actions;
9 | import org.openqa.selenium.support.FindBy;
10 |
11 | public class KeyPressesPage extends BasePage {
12 |
13 | @Visible
14 | @FindBy(css = "div.example")
15 | private WebElement container;
16 |
17 | @FindBy(css = "p#result")
18 | private WebElement result;
19 |
20 | @Step("Enter a key press {key}")
21 | public KeyPressesPage enterKeyPress(Keys key) {
22 | (new Actions(driver)).sendKeys(key).perform();
23 | return this;
24 | }
25 |
26 | @Step("Get result text")
27 | public String getResultText() {
28 | return result.getText();
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/theinternet/pages/SortableDataTablesPage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.theinternet.pages;
2 |
3 | import com.frameworkium.core.ui.annotations.Visible;
4 | import com.frameworkium.core.ui.element.OptimisedStreamTable;
5 | import com.frameworkium.core.ui.pages.BasePage;
6 | import com.frameworkium.core.ui.pages.PageFactory;
7 | import org.openqa.selenium.WebElement;
8 | import org.openqa.selenium.support.CacheLookup;
9 | import org.openqa.selenium.support.FindBy;
10 |
11 | import java.util.stream.Stream;
12 |
13 | public class SortableDataTablesPage extends BasePage {
14 |
15 | @Visible
16 | @CacheLookup
17 | @FindBy(id = "table1")
18 | private OptimisedStreamTable table1;
19 |
20 | @CacheLookup
21 | @FindBy(id = "table2")
22 | private OptimisedStreamTable table2;
23 |
24 | public static SortableDataTablesPage open() {
25 | return PageFactory.newInstance(
26 | SortableDataTablesPage.class,
27 | "https://the-internet.herokuapp.com/tables");
28 | }
29 |
30 | public Stream getTable1ColumnContents(String colHeader) {
31 | return table1.getColumn(colHeader).map(WebElement::getText);
32 | }
33 |
34 | public Stream getTable2ColumnContents(String colHeader) {
35 | return table2.getColumn(colHeader).map(WebElement::getText);
36 | }
37 |
38 | public SortableDataTablesPage sortTable2ByColumnName(String colHeader) {
39 | table2.getHeading(colHeader).ifPresent(WebElement::click);
40 | return this;
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/wikipedia/pages/EnglishCountiesPage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.wikipedia.pages;
2 |
3 | import com.frameworkium.core.ui.annotations.Visible;
4 | import com.frameworkium.core.ui.element.OptimisedStreamTable;
5 | import com.frameworkium.core.ui.pages.BasePage;
6 | import com.frameworkium.core.ui.pages.PageFactory;
7 |
8 | import org.openqa.selenium.NotFoundException;
9 | import org.openqa.selenium.WebElement;
10 | import org.openqa.selenium.support.CacheLookup;
11 | import org.openqa.selenium.support.FindBy;
12 |
13 | import java.util.function.Predicate;
14 | import java.util.stream.Stream;
15 |
16 | /**
17 | * This page uses OptimisedStreamTable, this is slower than using Lists of
18 | * WebElements for columns, especially when running over a grid due to the far
19 | * greater number of page lookups required. This is even worse for StreamTable,
20 | * but StreamTable copes with a wider variety of Tables.
21 | *
22 | * This is a trade-off between readability, maintainability and performance.
23 | *
24 | *
This approach is great if the table and tests are likely to change often.
25 | */
26 | public class EnglishCountiesPage extends BasePage {
27 |
28 | @Visible
29 | @CacheLookup
30 | @FindBy(css = "table.wikitable") // luckily there's only one
31 | private OptimisedStreamTable listTable;
32 |
33 | public static EnglishCountiesPage open() {
34 | return PageFactory.newInstance(EnglishCountiesPage.class,
35 | "https://en.wikipedia.org/wiki/List_of_ceremonial_counties_of_England");
36 | }
37 |
38 | public int populationOf(String countyName) {
39 | Predicate headerLookUp = e -> e.getText().trim().startsWith("County");
40 | Predicate lookUpCellMatcher = e -> e.getText().trim().equals(countyName);
41 | Predicate targetColHeaderLookup = e -> e.getText().trim().startsWith("Population");
42 | String population = listTable
43 | .getCellsByLookup(headerLookUp, lookUpCellMatcher, targetColHeaderLookup)
44 | .findFirst()
45 | .orElseThrow(NotFoundException::new)
46 | .getText()
47 | .replaceAll(",", "");
48 | return Integer.parseInt(population);
49 | }
50 |
51 | public Stream densities() {
52 | return listTable
53 | // hard-coded index because headers are now row-span=2
54 | .getColumn(6)
55 | .map(WebElement::getText)
56 | .map(density -> density.replaceAll(",", ""))
57 | .map(Integer::parseInt);
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/wikipedia/pages/EnglishCountiesUsingListsPage.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.wikipedia.pages;
2 |
3 | import com.frameworkium.core.ui.annotations.Visible;
4 | import com.frameworkium.core.ui.pages.BasePage;
5 | import com.frameworkium.core.ui.pages.PageFactory;
6 | import org.openqa.selenium.NoSuchElementException;
7 | import org.openqa.selenium.WebElement;
8 | import org.openqa.selenium.support.CacheLookup;
9 | import org.openqa.selenium.support.FindBy;
10 |
11 | import java.util.List;
12 | import java.util.Objects;
13 | import java.util.stream.IntStream;
14 | import java.util.stream.Stream;
15 |
16 | /**
17 | * This page uses {@link List}s of {@link WebElement}s for the columns we know
18 | * that we need. This test is faster than using StreamTable, especially when
19 | * running over a grid due to the far fewer page lookups required.
20 | *
21 | * This is a trade-off between readability, maintainability and performance.
22 | *
23 | *
This approach is great if the table and tests are unlikely to change often.
24 | */
25 | public class EnglishCountiesUsingListsPage extends BasePage {
26 |
27 | @Visible(checkAtMost = 1)
28 | @CacheLookup
29 | @FindBy(css = "table.wikitable > tbody > tr > td:nth-child(1)")
30 | private List countyColumn;
31 |
32 | @CacheLookup
33 | @FindBy(css = "table.wikitable > tbody > tr > td:nth-child(2)")
34 | private List populationColumn;
35 |
36 | @CacheLookup
37 | @FindBy(css = "table.wikitable > tbody > tr > td:nth-child(7)")
38 | private List densityColumn;
39 |
40 | public static EnglishCountiesUsingListsPage open() {
41 | return PageFactory.newInstance(EnglishCountiesUsingListsPage.class,
42 | "https://en.wikipedia.org/wiki/List_of_ceremonial_counties_of_England");
43 | }
44 |
45 | public int populationOf(String countyName) {
46 | String population =
47 | populationColumn.get(findCountyIndex(countyName))
48 | .getText()
49 | .replaceAll(",", "");
50 | return Integer.parseInt(population);
51 | }
52 |
53 | private int findCountyIndex(String countyName) {
54 | return IntStream.range(0, countyColumn.size())
55 | .filter(i -> Objects.equals(countyColumn.get(i).getText(), countyName))
56 | .findFirst()
57 | .orElseThrow(() -> new NoSuchElementException(
58 | "County name '" + countyName + "' not found"));
59 | }
60 |
61 | public Stream densities() {
62 | return densityColumn.stream()
63 | .map(WebElement::getText)
64 | .map(density -> density.replaceAll(",", ""))
65 | .map(Integer::parseInt);
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/wikipedia/tests/EnglishCountiesTest.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.wikipedia.tests;
2 |
3 | import com.frameworkium.core.ui.tests.BaseUITest;
4 | import com.frameworkium.integration.wikipedia.pages.EnglishCountiesPage;
5 | import com.frameworkium.integration.wikipedia.pages.EnglishCountiesUsingListsPage;
6 | import org.testng.annotations.Test;
7 |
8 | import static com.google.common.truth.Truth.assertThat;
9 |
10 | /**
11 | * This test demonstrates the different between using StreamTable and Lists
12 | * of WebElements.
13 | * The trade-off is between readability, maintainability and performance.
14 | *
The List option is slightly longer and slightly more difficult to maintain,
15 | * especially if your table is changing shape (but not header text), however it
16 | * is significantly faster.
17 | */
18 | @Test
19 | public class EnglishCountiesTest extends BaseUITest {
20 |
21 | public void exploring_english_counties_data_with_stream_table() {
22 | EnglishCountiesPage page = EnglishCountiesPage.open();
23 |
24 | assertThat(page.populationOf("Cornwall"))
25 | .isAtLeast(550_000);
26 | // at least two counties have population densities of more than 3000
27 | assertThat(page.densities()
28 | .filter(density -> density > 3000)
29 | .limit(2)
30 | .count())
31 | .isEqualTo(2L);
32 | }
33 |
34 |
35 | public void exploring_english_counties_data_with_lists() {
36 | EnglishCountiesUsingListsPage page = EnglishCountiesUsingListsPage.open();
37 |
38 | assertThat(page.populationOf("Cornwall"))
39 | .isAtLeast(550_000);
40 | // at least two counties have population densities of more than 3000
41 | assertThat(page.densities()
42 | .filter(density -> density > 3000)
43 | .limit(2)
44 | .count())
45 | .isEqualTo(2L);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/test/java/com/frameworkium/integration/wiremock/WireMockJiraTeardown.java:
--------------------------------------------------------------------------------
1 | package com.frameworkium.integration.wiremock;
2 |
3 | import com.frameworkium.core.api.tests.BaseAPITest;
4 | import com.github.tomakehurst.wiremock.client.WireMock;
5 | import org.testng.annotations.Test;
6 |
7 | @Test
8 | /*
9 | * This is called in post-integration-test phase to reset wiremock after an integration-test phase in anticipation
10 | * for any further test phases that may set their own wiremock mappings
11 | */
12 | public class WireMockJiraTeardown extends BaseAPITest {
13 |
14 | public void mockJiraTeardown() {
15 | final WireMock wireMock = new WireMock();
16 | WireMock.reset();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/test/resources/capture-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Frameworkium/frameworkium-core/815f590a38a994cfe891fd6d22f9a3187678aae6/src/test/resources/capture-screenshot.png
--------------------------------------------------------------------------------