Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
roicarrera authored Dec 18, 2024
2 parents 3fb4839 + 17e48a6 commit 2ba904a
Show file tree
Hide file tree
Showing 25 changed files with 1,149 additions and 255 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
- Generate PDF report for cypress and improved environment management ([#1079](https://github.com/opendevstack/ods-quickstarters/pull/1079))
- Change PDF report zip file name in Cypress Quickstarter ([#1082](https://github.com/opendevstack/ods-quickstarters/pull/1082))
- Included small fixes in e2e-cypress ([#1086](https://github.com/opendevstack/ods-quickstarters/pull/1086))
- Mobile testing enablement adding Appium & Sauce Labs in e2e-spock-geb quickstarter ([#1083](https://github.com/opendevstack/ods-quickstarters/pull/1083))

### Added

Expand Down
204 changes: 175 additions & 29 deletions docs/modules/quickstarters/pages/e2e-spock-geb.adoc
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
= End-to-end tests with Spock, Geb and Unirest (e2e-spock-geb)
= End-to-end tests with Spock, Geb, Unirest, and Appium (e2e-spock-geb)

spock, geb and unirest e2e testing quickstarter project
Spock, Geb, Unirest, and Appium e2e testing quickstarter project

== Purpose of this quickstarter

This is a spock, geb and unirest e2e testing project quickstarter with basic setup for https://jenkins.io/[Jenkins], https://www.sonarqube.org/[SonarQube] and https://gradle.org/[Gradle].
This is a Spock, Geb, Unirest, and Appium e2e testing project quickstarter with basic setup for https://jenkins.io/[Jenkins], https://www.sonarqube.org/[SonarQube], https://gradle.org/[Gradle], and https://appium.io/[Appium].

== What files / architecture is generated?

Expand All @@ -18,7 +18,15 @@ This is a spock, geb and unirest e2e testing project quickstarter with basic set
│ ├── test
│ │ └── acceptance
│ │ │ └── groovy
│ │ │ │ └── DemoAcceptanceSpec.groovy
│ │ │ │ └── modules
│ │ │ │ └── DemoManualMenuModule.groovy
│ │ │ │ └── pages
│ │ │ │ └── DemoGebHomePage.groovy
│ │ │ │ └── DemoTheBookOfGebPage.groovy
│ │ │ │ └── specs
│ │ │ │ └── DemoGebHomePageSpec.groovy
│ │ │ │ └── DemoMobileAppSpec.groovy
│ │ │ │ └── DemoMobileGebHomePageSpec.groovy
│ │ │ └── java
│ │ │ └── DemoAcceptanceTest.java
│ │ └── installation
Expand All @@ -30,11 +38,13 @@ This is a spock, geb and unirest e2e testing project quickstarter with basic set
│ │ │ └── groovy
│ │ │ │ └── DemoIntegrationSpec.groovy
│ │ │ └── java
│ │ | └── DemoIntegrationTest.java
│ │ └── DemoIntegrationTest.java
│ │ └── resources
│ │ └── application.properties
│ │ └── GebConfig.groovy
│ │ └── SpecHelper.groovy
│ │ │ └── helpers
│ │ │ └── Environments.groovy
│ │ │ └── SpecHelper.groovy
│ │ │ └── application.properties
│ │ │ └── GebConfig.groovy
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
Expand Down Expand Up @@ -77,47 +87,185 @@ no_nexus=true

Run `gradlew -v` to verify the installed version of gradle wrapper.

== Frameworks used
=== Environments Configuration

This project is generated by https://gradle.org/[Gradle]
The environments used in the tests are defined in the `build.gradle` file. You can find the environments under the `ext` block as project properties. These environments are accessed via system properties in the Groovy classes. Here is an example of how the environments are defined:
```
ext {
...
environments = [
DESKTOP: "desktop",
MOBILE_BROWSER: "mobile_browser",
MOBILE_APP: "mobile_app"
]
}
...
tasks.withType(Test) {
systemProperty 'environments.desktop', environments.DESKTOP
systemProperty 'environments.mobile_browser', environments.MOBILE_BROWSER
systemProperty 'environments.mobile_app', environments.MOBILE_APP
}
```

******* http://spockframework.org/[spock]
Additionally, there is a class called `Environments.groovy` that facilitates access to these environments from the Groovy code. The class is located in the `src/test/resources/helpers` directory.
```
package helpers

class Environments {
// Define environment constants using system properties
// Check the build.gradle file for the defined properties
static final String DESKTOP = System.getProperty('environments.desktop')
static final String MOBILE_BROWSER = System.getProperty('environments.mobile_browser')
static final String MOBILE_APP = System.getProperty('environments.mobile_app')
}
```

******* https://gebish.org/[geb]
For each environment, we have a driver configured in the GebConfig.groovy file:
```
environments {

// Configuration for desktop environment using HtmlUnitDriver
"${Environments.DESKTOP}" {
driver = {
HtmlUnitDriver driver = new HtmlUnitDriver(BrowserVersion.BEST_SUPPORTED, true) {
...
}
}
}

// Configuration for mobile browser environment using AndroidDriver
"${Environments.MOBILE_BROWSER}" {
driver = {
MutableCapabilities caps = new MutableCapabilities()
...
MutableCapabilities sauceOptions = new MutableCapabilities()
...
caps.setCapability("sauce:options", sauceOptions)
URL url = new URL("https://ondemand.eu-central-1.saucelabs.com:443/wd/hub")
AndroidDriver driver = new AndroidDriver(url, caps)
return driver
}
}

// Configuration for mobile app environment using IOSDriver
"${Environments.MOBILE_APP}" {
driver = {
MutableCapabilities caps = new MutableCapabilities()
...
MutableCapabilities sauceOptions = new MutableCapabilities()
...
caps.setCapability("sauce:options", sauceOptions)
URL url = new URL("https://ondemand.eu-central-1.saucelabs.com:443/wd/hub")
IOSDriver driver = new IOSDriver(url, caps)
return driver
}
}
}
```

******* http://unirest.io/[unirest]
In the build.gradle file, there is also a filter by environment and tags:
```
// Specify the tags to include/exclude for each environment
// Please, adjust the tags according to your project needs
useJUnitPlatform {
switch (env) {
case environments.DESKTOP:
includeTags 'test_desktop'
break
case environments.MOBILE_BROWSER:
includeTags 'test_mobile_browser'
break
case environments.MOBILE_APP:
includeTags 'test_mobile_app'
break
}
}
```

== Sauce Labs Integration

Sauce Labs is a cloud-based platform that provides comprehensive testing solutions for web and mobile applications. It allows you to run tests on a wide range of real devices and emulators/simulators, ensuring your applications work seamlessly across different environments.

## Usage - how do you start after you provisioned this quickstarter
This template is prepared to work with Sauce Labs virtual devices, allowing you to perform all mobile tests on these virtual devices.

* Run command `gradlew test` in project directory to execute the end-to-end tests via spock/geb against the Google Home page and demo jUnit 5 tests.
=== Key Features of Sauce Labs

* **Real Device Cloud**: Access to thousands of real Android and iOS devices for manual and automated testing.
* **Emulators and Simulators**: Cost-effective and scalable testing on virtual devices.
* **Cross-Browser Testing**: Ensure compatibility across various browser and OS combinations.
* **Error Monitoring and Reporting**: Capture and resolve application errors quickly with detailed insights.
* **CI/CD Integration**: Seamlessly integrate with your continuous integration and delivery pipelines.

=== Credentials for Sauce Labs

To execute tests on Sauce Labs, you need Sauce Labs credentials. These credentials are stored in a secret called sauce-labs-user-access-key, which by default is created with "changeme" values for both username and password. Users will need to update these values with their actual Sauce Labs credentials.

In the Jenkinsfile, the credentials are retrieved as follows:
```
// Use credentials for SauceLabs authentication
// You can remove this block if you are not using SauceLabs
withCredentials([
usernamePassword(credentialsId: "${context.projectId}-cd-sauce-labs-user-access-key", passwordVariable: 'SAUCE_LABS_ACCESS_KEY', usernameVariable: 'SAUCE_LABS_USERNAME'),
]) {
...
}
```

In the GebConfig.groovy file, these credentials are used to configure the drivers:
```
// Get SauceLabs environment variables for configuring iOS device
def sauceLabsUsername = System.getenv('SAUCE_LABS_USERNAME')
def sauceLabsAccessKey = System.getenv('SAUCE_LABS_ACCESS_KEY')
```

This setup ensures that your tests can authenticate with Sauce Labs and run on the specified virtual devices.

== Usage - how do you start after you provisioned this quickstarter

* Run command `gradlew test` in project directory to execute the end-to-end tests via spock/geb against the demo pages and demo jUnit 5 tests.

You will see the results inside a new folder 'build' in project directory.

----
.
└── build
└── test-results
├── acceptance-groovy
│ └── TEST-DemoAcceptance.xml
├── acceptance-java
│ └── TEST-DemoAcceptanceTest.xml
├── installation-groovy
│ └── TEST-DemoInstallation.xml
├── installation-java
├── acceptance-groovy-desktop
├── acceptance-java-desktop
│ │── TEST-DemoAcceptanceTest.xml
│ │── TEST-specs.DemoGebHomePageSpec.xml
│ │── TEST-specs.DemoMobileAppSpec.xml
│ └── TEST-specs.DemoMobileGebHomePageSpec.xml
├── installation-groovy-desktop
├── installation-java-desktop
│ │── TEST-DemoInstallationSpec.xml
│ └── TEST-DemoInstallationTest.xml
├── integration-groovy
└── TEST-DemoIntegration.xml
── integration-java
├── integration-groovy-desktop
└── integration-java-desktop
── TEST-DemoIntegrationSpec.xml
└── TEST-DemoIntegrationTest.xml
----
== Frameworks used

This project is generated by https://gradle.org/[Gradle]

******* http://spockframework.org/[spock]

******* https://gebish.org/[geb]

******* http://unirest.io/[unirest]

******* https://appium.io/[apium]

******* https://saucelabs.com/[Sauce Labs]

## Customization - how do you start to configure your test

* You can see how a Java Junit 5 test are developed showing the Demo*Test.java files.
* You can see how a Groovy Spock/Geb test are developed showing the Demo*Spec.groovy files.
** The url to test with Geb is configured in the property `config.application.url` inside `application.properties`
** Inside `GebConfig.groovy` you could change the default navigator (CHROME)
** Inside `GebConfig.groovy` you could see some environments with different drivers defined and a default browser. You could configure or change them as you need.

== How this quickstarter is built through jenkins

Expand All @@ -134,13 +282,11 @@ In Jenkinsfile.template, there is the following stage:

All the results are stashed and published through Jenkins jUnit publisher.

include::partials$secret-scanning-with-gitleaks.adoc

== Builder agent used

This quickstarter uses the
https://github.com/opendevstack/ods-quickstarters/tree/master/common/jenkins-agents/jdk[jdk] Jenkins builder agent.

== Known limitations

NA
NA
18 changes: 18 additions & 0 deletions e2e-spock-geb/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ odsQuickstarterPipeline(
odsQuickstarterStageCopyFiles(context)

odsQuickstarterStageRenderJenkinsfile(context)

createSauceLabsOpenshiftSecret(context)

odsQuickstarterStageRenderSonarProperties(context)
}

def createSauceLabsOpenshiftSecret(def context) {
stage('Create Credentials') {
def project = context.projectId + "-cd"
def secretExists = sh(script: "oc get secret sauce-labs-user-access-key -n $project", returnStatus: true) == 0
if (!secretExists) {
sh """
oc create secret generic sauce-labs-user-access-key -n $project \
--from-literal=username=changeme \
--from-literal=password=changeme \
--type="kubernetes.io/basic-auth" && \
oc label secret sauce-labs-user-access-key -n $project credential.sync.jenkins.openshift.io=true
"""
}
}
}
58 changes: 48 additions & 10 deletions e2e-spock-geb/Jenkinsfile.template
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// See https://www.opendevstack.org/ods-documentation/ for usage and customization.

// Import the shared Jenkins library
@Library('ods-jenkins-shared-library@@shared_library_ref@') _

// Define the Jenkins pipeline
odsComponentPipeline(
imageStreamTag: '@ods_namespace@/jenkins-agent-jdk:@agent_image_tag@',
branchToEnvironmentMapping: [
Expand All @@ -13,6 +15,7 @@ odsComponentPipeline(
odsComponentStageScanWithSonar(context)
}

// Define the test stage
def stageTest(def context) {
def javaOpts = "-Xmx512m"
def gradleTestOpts = "-Xmx128m"
Expand All @@ -21,16 +24,51 @@ def stageTest(def context) {
springBootEnv = 'dev'
}

stage('Integration Test') {
sh (script: "chmod a+x gradle*", label : "allow gradle to execute")
withEnv(["TAGVERSION=${context.tagversion}", "NEXUS_HOST=${context.nexusHost}", "NEXUS_USERNAME=${context.nexusUsername}", "NEXUS_PASSWORD=${context.nexusPassword}", "JAVA_OPTS=${javaOpts}","GRADLE_TEST_OPTS=${gradleTestOpts}","ENVIRONMENT=${springBootEnv}","OPENSHIFT_PROJECT=${context.targetProject}","OPENSHIFT_APP_DOMAIN=${context.getOpenshiftApplicationDomain()}"]) {
def status = sh(script: "./gradlew clean test --stacktrace --no-daemon", returnStatus: true)
junit(testResults:"build/test-results/installation*/*.xml, build/test-results/integration*/*.xml, build/test-results/acceptance*/*.xml", allowEmptyResults:true)
stash(name: "installation-test-reports-junit-xml-${context.componentId}-${context.buildNumber}", includes: 'build/test-results/installation*/*.xml', allowEmpty: true)
stash(name: "integration-test-reports-junit-xml-${context.componentId}-${context.buildNumber}", includes: 'build/test-results/integration*/*.xml', allowEmpty: true)
stash(name: "acceptance-test-reports-junit-xml-${context.componentId}-${context.buildNumber}", includes: 'build/test-results/acceptance*/*.xml', allowEmpty: true)
if (status != 0) {
error "Executing tests failed!"
stage('Functional Test') {
sh (script: "chmod a+x gradle*", label : "allow gradle to execute")
withEnv([
"TAGVERSION=${context.tagversion}",
"NEXUS_HOST=${context.nexusHost}",
"NEXUS_USERNAME=${context.nexusUsername}",
"NEXUS_PASSWORD=${context.nexusPassword}",
"JAVA_OPTS=${javaOpts}",
"GRADLE_TEST_OPTS=${gradleTestOpts}",
"ENVIRONMENT=${springBootEnv}",
"OPENSHIFT_PROJECT=${context.targetProject}",
"OPENSHIFT_APP_DOMAIN=${context.getOpenshiftApplicationDomain()}"
]) {
// Use credentials for SauceLabs authentication
// You can remove this block if you are not using SauceLabs
withCredentials([
usernamePassword(credentialsId: "${context.projectId}-cd-sauce-labs-user-access-key", passwordVariable: 'SAUCE_LABS_ACCESS_KEY', usernameVariable: 'SAUCE_LABS_USERNAME'),
]) {
// Note: Testing in the production environment is not enabled by default as it can lead to unintended consequences,
// including potential downtime, data corruption, or exposure of sensitive information.
// This block is designed to skip acceptance and integration tests in the production environment to avoid these risks.
// If you choose to enable these tests in production take all necessary precautions. This means verifying your
// preconditions, database access, fake data, API calls, etc.
// Remember that any test case in the installation folder will be executed in production.
def status
if (context.environment == 'prod') {
status = sh(script: './gradlew clean testProd --stacktrace --no-daemon', returnStatus: true)
junit(testResults:"build/test-results/installation*/*.xml", allowEmptyResults:true)
stash(name: "installation-test-reports-junit-xml-${context.componentId}-${context.buildNumber}", includes: 'build/test-results/installation*/*.xml', allowEmpty: true)
} else {
status = sh(script: './gradlew clean test --stacktrace --no-daemon', returnStatus: true)
junit(testResults:"build/test-results/installation*/*.xml, build/test-results/integration*/*.xml, build/test-results/acceptance*/*.xml", allowEmptyResults:true)
stash(name: "installation-test-reports-junit-xml-${context.componentId}-${context.buildNumber}", includes: 'build/test-results/installation*/*.xml', allowEmpty: true)
stash(name: "integration-test-reports-junit-xml-${context.componentId}-${context.buildNumber}", includes: 'build/test-results/integration*/*.xml', allowEmpty: true)
stash(name: "acceptance-test-reports-junit-xml-${context.componentId}-${context.buildNumber}", includes: 'build/test-results/acceptance*/*.xml', allowEmpty: true)
}

// Handle the test status
switch (status) {
case 0:
echo "All tests passed successfully"
break
default:
unstable "Some tests have failed or encountered errors. Please check the logs for more details."
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion e2e-spock-geb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

Documentation is located in our [official documentation](https://www.opendevstack.org/ods-documentation/opendevstack/3.x/quickstarters/e2e-spock-geb.html)

Please update documentation in the [antora page directory](https://github.com/opendevstack/ods-project-quickstarters/tree/master/docs/modules/ROOT/pages)
Please update documentation in the [antora page directory](https://github.com/opendevstack/ods-quickstarters/tree/master/docs/modules/ROOT/pages)

Tested thru [automated tests](../tests/e2e-spock-geb)
Loading

0 comments on commit 2ba904a

Please sign in to comment.