diff --git a/.doc/selenium-docker-aws-jenkins.png b/.doc/selenium-docker-aws-jenkins.png new file mode 100644 index 0000000..03c6e1f Binary files /dev/null and b/.doc/selenium-docker-aws-jenkins.png differ diff --git a/03-automation-framework/dependencies.md b/03-automation-framework/dependencies.md index c5de236..4743689 100644 --- a/03-automation-framework/dependencies.md +++ b/03-automation-framework/dependencies.md @@ -1,3 +1,8 @@ +# Application URLs + +- [Flight Reservation](https://d1uh9e7cu07ukd.cloudfront.net/selenium-docker/reservation-app/index.html) +- [Vendor Portal](https://d1uh9e7cu07ukd.cloudfront.net/selenium-docker/vendor-app/index.html) + # Maven Dependencies ``` diff --git a/04-selenium-grid/docker-images.md b/04-selenium-grid/docker-images.md index a374946..b732e65 100644 --- a/04-selenium-grid/docker-images.md +++ b/04-selenium-grid/docker-images.md @@ -1,5 +1,14 @@ # Docker Images +## First check your CPU architecture + +- MAC/Linux users +`uname -m` +- Windows Users +`echo %PROCESSOR_ARCHITECTURE%` + +If you see "arm", then use ARM images. + Use this docker images for creating selenium grid | Image | ARM | Others | @@ -8,7 +17,9 @@ Use this docker images for creating selenium grid | Chrome | seleniarm/node-chromium:4.10 | selenium/node-chrome:4.10 | | Firefox | seleniarm/node-firefox:4.10 | selenium/node-firefox:4.10 | -## Docker Hub Reference: +## References: - [Selenium](https://hub.docker.com/u/selenium) -- [Seleniarm](https://hub.docker.com/u/seleniarm) \ No newline at end of file +- [Seleniarm](https://hub.docker.com/u/seleniarm) +- [Seleniarm to Selenium Namespace](https://github.com/SeleniumHQ/docker-selenium/issues/1847) +- [Edge does not work in arm](https://techcommunity.microsoft.com/t5/discussions/edge-for-linux-arm64/m-p/1532272) diff --git a/06-jenkins-ci-cd/selenium-docker b/06-jenkins-ci-cd/selenium-docker deleted file mode 160000 index d9df057..0000000 --- a/06-jenkins-ci-cd/selenium-docker +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d9df057c4770ff590ec008ff34cd8873e8939e32 diff --git a/06-jenkins-ci-cd/selenium-docker/.gitignore b/06-jenkins-ci-cd/selenium-docker/.gitignore new file mode 100644 index 0000000..6ce9f50 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/.gitignore @@ -0,0 +1,49 @@ +# Created by .ignore support plugin (hsz.mobi) +### Java template +# Compiled class file +**/*.class + +# Log file +**/*.log + +# BlueJ files +**/*.ctxt + +# Mobile Tools for Java (J2ME) +**/.mtj.tmp/ + +# Package Files # +**/*.jar +**/*.war +**/*.nar +**/*.ear +**/*.zip +**/*.tar.gz +**/*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +**/hs_err_pid* + +### Maven template +**/target/ +**/pom.xml.tag +**/pom.xml.releaseBackup +**/pom.xml.versionsBackup +**/pom.xml.next +**/release.properties +**/dependency-reduced-pom.xml +**/buildNumber.properties +**/.mvn/timing.properties +**/.mvn/wrapper/maven-wrapper.jar + +**/*.iml + +**/.idea/ + + +**/HELP.md +**/.mvn/ +**/mvnw +**/mvnw.cmd +**/node_modules/ + diff --git a/06-jenkins-ci-cd/selenium-docker/Dockerfile b/06-jenkins-ci-cd/selenium-docker/Dockerfile new file mode 100644 index 0000000..a32e382 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/Dockerfile @@ -0,0 +1,14 @@ +FROM java47 + +# Install curl jq +RUN apk add curl jq + +# workspace +WORKDIR /home/selenium-docker + +# Add the required files +ADD target/docker-resources ./ +ADD runner.sh runner.sh + +# Start the runner.sh +ENTRYPOINT sh runner.sh \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/Jenkinsfile b/06-jenkins-ci-cd/selenium-docker/Jenkinsfile new file mode 100644 index 0000000..19a4d0f --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/Jenkinsfile @@ -0,0 +1,39 @@ +pipeline{ + + agent any + + stages{ + + stage('Build Jar'){ + steps{ + sh 'mvn clean package -DskipTests' + } + } + + stage('Build Image'){ + steps{ + sh 'docker build -t=vinsdocker/selenium:latest .' + } + } + + stage('Push Image'){ + environment{ + DOCKER_HUB = credentials('dockerhub-creds') + } + steps{ + sh 'echo ${DOCKER_HUB_PSW} | docker login -u ${DOCKER_HUB_USR} --password-stdin' + sh 'docker push vinsdocker/selenium:latest' + sh "docker tag vinsdocker/selenium:latest vinsdocker/selenium:${env.BUILD_NUMBER}" + sh "docker push vinsdocker/selenium:${env.BUILD_NUMBER}" + } + } + + } + + post { + always { + sh 'docker logout' + } + } + +} \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/pom.xml b/06-jenkins-ci-cd/selenium-docker/pom.xml new file mode 100644 index 0000000..a2b5017 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/pom.xml @@ -0,0 +1,159 @@ + + + + 4.0.0 + + com.vinsguru + selenium-docker + 1.0-SNAPSHOT + + + 4.11.0 + 1.4.8 + 5.4.0 + 7.8.0 + 2.12.0 + + 3.11.0 + 3.6.0 + 3.3.0 + 3.1.2 + 3.3.1 + + ${project.build.directory}/docker-resources + + + + org.seleniumhq.selenium + selenium-java + ${selenium.java.version} + + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + io.github.bonigarcia + webdrivermanager + ${webdriver.manager.version} + test + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + test + + + + org.testng + testng + ${testng.version} + test + + + + selenium-docker + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.version} + + 17 + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.version} + + + + firefox + true + + + src/test/resources/test-suites/vendor-portal.xml + src/test/resources/test-suites/flight-reservation.xml + + 4 + target/test-output + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven.dependency.version} + + + copy-dependencies + prepare-package + + copy-dependencies + + + + ${package.directory}/libs + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.version} + + ${package.directory}/libs + + + + + test-jar + + + **/*.class + + + + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven.resources.plugin} + + + copy-resources + prepare-package + + copy-resources + + + ${package.directory} + + + src/test/resources + + + + + + + + + + + \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/runner.sh b/06-jenkins-ci-cd/selenium-docker/runner.sh new file mode 100644 index 0000000..84c888d --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/runner.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +#------------------------------------------------------------------- +# This script expects the following environment variables +# HUB_HOST +# BROWSER +# THREAD_COUNT +# TEST_SUITE +#------------------------------------------------------------------- + +# Let's print what we have received +echo "-------------------------------------------" +echo "HUB_HOST : ${HUB_HOST:-hub}" +echo "BROWSER : ${BROWSER:-chrome}" +echo "THREAD_COUNT : ${THREAD_COUNT:-1}" +echo "TEST_SUITE : ${TEST_SUITE}" +echo "-------------------------------------------" + +# Do not start the tests immediately. Hub has to be ready with browser nodes +echo "Checking if hub is ready..!" +count=0 +while [ "$( curl -s http://${HUB_HOST:-hub}:4444/status | jq -r .value.ready )" != "true" ] +do + count=$((count+1)) + echo "Attempt: ${count}" + if [ "$count" -ge 30 ] + then + echo "**** HUB IS NOT READY WITHIN 30 SECONDS ****" + exit 1 + fi + sleep 1 +done + +# At this point, selenium grid should be up! +echo "Selenium Grid is up and running. Running the test...." + +# Start the java command +java -cp 'libs/*' \ + -Dselenium.grid.enabled=true \ + -Dselenium.grid.hubHost="${HUB_HOST:-hub}" \ + -Dbrowser="${BROWSER:-chrome}" \ + org.testng.TestNG \ + -threadcount "${THREAD_COUNT:-1}" \ + test-suites/"${TEST_SUITE}" \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/AbstractPage.java b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/AbstractPage.java new file mode 100644 index 0000000..56e702e --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/AbstractPage.java @@ -0,0 +1,22 @@ +package com.vinsguru.pages; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; + +public abstract class AbstractPage { + + protected final WebDriver driver; + protected final WebDriverWait wait; + + public AbstractPage(WebDriver driver){ + this.driver = driver; + this.wait = new WebDriverWait(driver, Duration.ofSeconds(30)); + PageFactory.initElements(driver, this); + } + + public abstract boolean isAt(); + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightConfirmationPage.java b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightConfirmationPage.java new file mode 100644 index 0000000..f38810c --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightConfirmationPage.java @@ -0,0 +1,39 @@ +package com.vinsguru.pages.flightreservation; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FlightConfirmationPage extends AbstractPage { + + private static final Logger log = LoggerFactory.getLogger(FlightConfirmationPage.class); + + @FindBy(css = "#flights-confirmation-section .card-body .row:nth-child(1) .col:nth-child(2)") + private WebElement flightConfirmationElement; + + @FindBy(css = "#flights-confirmation-section .card-body .row:nth-child(3) .col:nth-child(2)") + private WebElement totalPriceElement; + + public FlightConfirmationPage(WebDriver driver) { + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.flightConfirmationElement)); + return this.flightConfirmationElement.isDisplayed(); + } + + public String getPrice(){ + String confirmation = this.flightConfirmationElement.getText(); + String price = this.totalPriceElement.getText(); + log.info("Flight confirmation# : {}", confirmation); + log.info("Total price : {}", price); + return price; + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSearchPage.java b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSearchPage.java new file mode 100644 index 0000000..a26ff1a --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSearchPage.java @@ -0,0 +1,37 @@ +package com.vinsguru.pages.flightreservation; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.Select; + +public class FlightsSearchPage extends AbstractPage { + + @FindBy(id = "passengers") + private WebElement passengerSelect; + + @FindBy(id = "search-flights") + private WebElement searchFlightsButton; + + public FlightsSearchPage(WebDriver driver) { + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.passengerSelect)); + return this.passengerSelect.isDisplayed(); + } + + public void selectPassengers(String noOfPassengers){ + Select passengers = new Select(this.passengerSelect); + passengers.selectByValue(noOfPassengers); + } + + public void searchFlights(){ + this.searchFlightsButton.click(); + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSelectionPage.java b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSelectionPage.java new file mode 100644 index 0000000..09b5227 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSelectionPage.java @@ -0,0 +1,43 @@ +package com.vinsguru.pages.flightreservation; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class FlightsSelectionPage extends AbstractPage { + + @FindBy(name = "departure-flight") + private List departureFlightsOptions; + + @FindBy(name = "arrival-flight") + private List arrivalFlightsOptions; + + @FindBy(id = "confirm-flights") + private WebElement confirmFlightsButton; + + public FlightsSelectionPage(WebDriver driver) { + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.confirmFlightsButton)); + return this.confirmFlightsButton.isDisplayed(); + } + + public void selectFlights(){ + int random = ThreadLocalRandom.current().nextInt(0, departureFlightsOptions.size()); + this.departureFlightsOptions.get(random).click(); + this.arrivalFlightsOptions.get(random).click(); + } + + public void confirmFlights(){ + this.confirmFlightsButton.click(); + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationConfirmationPage.java b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationConfirmationPage.java new file mode 100644 index 0000000..e52c174 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationConfirmationPage.java @@ -0,0 +1,35 @@ +package com.vinsguru.pages.flightreservation; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; + +public class RegistrationConfirmationPage extends AbstractPage { + + @FindBy(id = "go-to-flights-search") + private WebElement goToFlightsSearchButton; + + @FindBy(css = "#registration-confirmation-section p b") + private WebElement firstNameElement; + + public RegistrationConfirmationPage(WebDriver driver){ + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.goToFlightsSearchButton)); + return this.goToFlightsSearchButton.isDisplayed(); + } + + public String getFirstName(){ + return this.firstNameElement.getText(); + } + + public void goToFlightsSearch(){ + this.goToFlightsSearchButton.click(); + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationPage.java b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationPage.java new file mode 100644 index 0000000..0934a10 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationPage.java @@ -0,0 +1,69 @@ +package com.vinsguru.pages.flightreservation; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; + +public class RegistrationPage extends AbstractPage { + + @FindBy(id = "firstName") + private WebElement firstNameInput; + + @FindBy(id = "lastName") + private WebElement lastNameInput; + + @FindBy(id = "email") + private WebElement emailInput; + + @FindBy(id = "password") + private WebElement passwordInput; + + @FindBy(name = "street") + private WebElement streetInput; + + @FindBy(name = "city") + private WebElement cityInput; + + @FindBy(name = "zip") + private WebElement zipInput; + + @FindBy(id = "register-btn") + private WebElement registerButton; + + public RegistrationPage(WebDriver driver){ + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.firstNameInput)); + return this.firstNameInput.isDisplayed(); + } + + public void goTo(String url){ + this.driver.get(url); + } + + public void enterUserDetails(String firstName, String lastName){ + this.firstNameInput.sendKeys(firstName); + this.lastNameInput.sendKeys(lastName); + } + + public void enterUserCredentials(String email, String password){ + this.emailInput.sendKeys(email); + this.passwordInput.sendKeys(password); + } + + public void enterAddress(String street, String city, String zip){ + this.streetInput.sendKeys(street); + this.cityInput.sendKeys(city); + this.zipInput.sendKeys(zip); + } + + public void register(){ + this.registerButton.click(); + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/DashboardPage.java b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/DashboardPage.java new file mode 100644 index 0000000..297f892 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/DashboardPage.java @@ -0,0 +1,102 @@ +package com.vinsguru.pages.vendorportal; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DashboardPage extends AbstractPage { + + private static final Logger log = LoggerFactory.getLogger(DashboardPage.class); + + @FindBy(id = "monthly-earning") + private WebElement monthlyEarningElement; + + @FindBy(id = "annual-earning") + private WebElement annualEarningElement; + + @FindBy(id = "profit-margin") + private WebElement profitMarginElement; + + @FindBy(id = "available-inventory") + private WebElement availableInventoryElement; + + @FindBy(css = "#dataTable_filter input") + private WebElement searchInput; + + @FindBy(id = "dataTable_info") + private WebElement searchResultsCountElement; + + @FindBy(css = "img.img-profile") + private WebElement userProfilePictureElement; + + // prefer id / name / css + @FindBy(linkText = "Logout") + private WebElement logoutLink; + + @FindBy(css = "#logoutModal a") + private WebElement modalLogoutButton; + + public DashboardPage(WebDriver driver) { + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.monthlyEarningElement)); + return this.monthlyEarningElement.isDisplayed(); + } + + public String getMonthlyEarning(){ + return this.monthlyEarningElement.getText(); + } + + public String getAnnualEarning(){ + return this.annualEarningElement.getText(); + } + + public String getProfitMargin(){ + return this.profitMarginElement.getText(); + } + + public String getAvailableInventory(){ + return this.availableInventoryElement.getText(); + } + + public void searchOrderHistoryBy(String keyword){ + this.searchInput.sendKeys(keyword); + } + + /* + Showing 1 to 10 of 32 entries (filtered from 99 total entries) + arr[0] = "Showing" + arr[1] = "1" + arr[2] = "to" + arr[3] = "10" + arr[4] = "of" + arr[5] = "32" + ... + ... + */ + public int getSearchResultsCount(){ + String resultsText = this.searchResultsCountElement.getText(); + String[] arr = resultsText.split(" "); + // if we do not have 5th item, it would throw exception. + // that's fine. we would want our test to fail anyway in that case! + int count = Integer.parseInt(arr[5]); + log.info("Results count: {}", count); + return count; + } + + public void logout(){ + this.userProfilePictureElement.click(); + this.wait.until(ExpectedConditions.visibilityOf(this.logoutLink)); + this.logoutLink.click(); + this.wait.until(ExpectedConditions.visibilityOf(this.modalLogoutButton)); + this.modalLogoutButton.click(); + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/LoginPage.java b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/LoginPage.java new file mode 100644 index 0000000..2e1b5c8 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/LoginPage.java @@ -0,0 +1,40 @@ +package com.vinsguru.pages.vendorportal; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; + +public class LoginPage extends AbstractPage { + + @FindBy(id = "username") + private WebElement usernameInput; + + @FindBy(id = "password") + private WebElement passwordInput; + + @FindBy(id = "login") + private WebElement loginButton; + + public LoginPage(WebDriver driver) { + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.loginButton)); + return this.loginButton.isDisplayed(); + } + + public void goTo(String url){ + this.driver.get(url); + } + + public void login(String username, String password){ + this.usernameInput.sendKeys(username); + this.passwordInput.sendKeys(password); + this.loginButton.click(); + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/main/resources/logback.xml b/06-jenkins-ci-cd/selenium-docker/src/main/resources/logback.xml new file mode 100644 index 0000000..28aa43e --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/main/resources/logback.xml @@ -0,0 +1,21 @@ + + + + + %d{HH:mm:ss.SSS} %-5level [%15.15t] %cyan(%-30.30logger{30}) : %m%n + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/listener/TestListener.java b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/listener/TestListener.java new file mode 100644 index 0000000..68c1812 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/listener/TestListener.java @@ -0,0 +1,21 @@ +package com.vinsguru.listener; + +import com.vinsguru.util.Constants; +import org.openqa.selenium.OutputType; +import org.openqa.selenium.TakesScreenshot; +import org.testng.ITestListener; +import org.testng.ITestResult; +import org.testng.Reporter; + +public class TestListener implements ITestListener { + + @Override + public void onTestFailure(ITestResult result) { + TakesScreenshot driver = (TakesScreenshot) result.getTestContext().getAttribute(Constants.DRIVER); + String screenshot = driver.getScreenshotAs(OutputType.BASE64); + String htmlImageFormat = ""; + String htmlImage = String.format(htmlImageFormat, screenshot); + Reporter.log(htmlImage); + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/AbstractTest.java b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/AbstractTest.java new file mode 100644 index 0000000..7b2a10e --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/AbstractTest.java @@ -0,0 +1,64 @@ +package com.vinsguru.tests; + +import com.vinsguru.listener.TestListener; +import com.vinsguru.util.Config; +import com.vinsguru.util.Constants; +import io.github.bonigarcia.wdm.WebDriverManager; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.firefox.FirefoxOptions; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeSuite; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Listeners; + +import java.net.MalformedURLException; +import java.net.URL; + +@Listeners({TestListener.class}) +public abstract class AbstractTest { + + private static final Logger log = LoggerFactory.getLogger(AbstractTest.class); + + protected WebDriver driver; + + @BeforeSuite + public void setupConfig(){ + Config.initialize(); + } + + @BeforeTest + public void setDriver(ITestContext ctx) throws MalformedURLException { + this.driver = Boolean.parseBoolean(Config.get(Constants.GRID_ENABLED)) ? getRemoteDriver() : getLocalDriver(); + ctx.setAttribute(Constants.DRIVER, this.driver); + } + + private WebDriver getRemoteDriver() throws MalformedURLException { + Capabilities capabilities = new ChromeOptions(); + if(Constants.FIREFOX.equalsIgnoreCase(Config.get(Constants.BROWSER))){ + capabilities = new FirefoxOptions(); + } + String urlFormat = Config.get(Constants.GRID_URL_FORMAT); + String hubHost = Config.get(Constants.GRID_HUB_HOST); + String url = String.format(urlFormat, hubHost); + log.info("grid url: {}", url); + return new RemoteWebDriver(new URL(url), capabilities); + } + + private WebDriver getLocalDriver(){ + WebDriverManager.chromedriver().setup(); + return new ChromeDriver(); + } + + @AfterTest + public void quitDriver(){ + this.driver.quit(); + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/FlightReservationTest.java b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/FlightReservationTest.java new file mode 100644 index 0000000..9069254 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/FlightReservationTest.java @@ -0,0 +1,67 @@ +package com.vinsguru.tests.flightreservation; + +import com.vinsguru.pages.flightreservation.*; +import com.vinsguru.tests.AbstractTest; +import com.vinsguru.tests.flightreservation.model.FlightReservationTestData; +import com.vinsguru.util.Config; +import com.vinsguru.util.Constants; +import com.vinsguru.util.JsonUtil; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +public class FlightReservationTest extends AbstractTest { + + private FlightReservationTestData testData; + + @BeforeTest + @Parameters("testDataPath") + public void setParameters(String testDataPath){ + this.testData = JsonUtil.getTestData(testDataPath, FlightReservationTestData.class); + } + + @Test + public void userRegistrationTest(){ + RegistrationPage registrationPage = new RegistrationPage(driver); + registrationPage.goTo(Config.get(Constants.FLIGHT_RESERVATION_URL)); + Assert.assertTrue(registrationPage.isAt()); + + registrationPage.enterUserDetails(testData.firstName(), testData.lastName()); + registrationPage.enterUserCredentials(testData.email(), testData.password()); + registrationPage.enterAddress(testData.street(), testData.city(), testData.zip()); + registrationPage.register(); + } + + @Test(dependsOnMethods = "userRegistrationTest") + public void registrationConfirmationTest(){ + RegistrationConfirmationPage registrationConfirmationPage = new RegistrationConfirmationPage(driver); + Assert.assertTrue(registrationConfirmationPage.isAt()); + Assert.assertEquals(registrationConfirmationPage.getFirstName(), testData.firstName()); + registrationConfirmationPage.goToFlightsSearch(); + } + + @Test(dependsOnMethods = "registrationConfirmationTest") + public void flightsSearchTest(){ + FlightsSearchPage flightsSearchPage = new FlightsSearchPage(driver); + Assert.assertTrue(flightsSearchPage.isAt()); + flightsSearchPage.selectPassengers(testData.passengersCount()); + flightsSearchPage.searchFlights(); + } + + @Test(dependsOnMethods = "flightsSearchTest") + public void flightsSelectionTest(){ + FlightsSelectionPage flightsSelectionPage = new FlightsSelectionPage(driver); + Assert.assertTrue(flightsSelectionPage.isAt()); + flightsSelectionPage.selectFlights(); + flightsSelectionPage.confirmFlights(); + } + + @Test(dependsOnMethods = "flightsSelectionTest") + public void flightReservationConfirmationTest(){ + FlightConfirmationPage flightConfirmationPage = new FlightConfirmationPage(driver); + Assert.assertTrue(flightConfirmationPage.isAt()); + Assert.assertEquals(flightConfirmationPage.getPrice(), testData.expectedPrice()); + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/model/FlightReservationTestData.java b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/model/FlightReservationTestData.java new file mode 100644 index 0000000..ce9675a --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/model/FlightReservationTestData.java @@ -0,0 +1,12 @@ +package com.vinsguru.tests.flightreservation.model; + +public record FlightReservationTestData(String firstName, + String lastName, + String email, + String password, + String street, + String city, + String zip, + String passengersCount, + String expectedPrice) { +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/VendorPortalTest.java b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/VendorPortalTest.java new file mode 100644 index 0000000..8644a19 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/VendorPortalTest.java @@ -0,0 +1,57 @@ +package com.vinsguru.tests.vendorportal; + +import com.vinsguru.pages.vendorportal.DashboardPage; +import com.vinsguru.pages.vendorportal.LoginPage; +import com.vinsguru.tests.AbstractTest; +import com.vinsguru.tests.vendorportal.model.VendorPortalTestData; +import com.vinsguru.util.Config; +import com.vinsguru.util.Constants; +import com.vinsguru.util.JsonUtil; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +public class VendorPortalTest extends AbstractTest { + + private LoginPage loginPage; + private DashboardPage dashboardPage; + private VendorPortalTestData testData; + + @BeforeTest + @Parameters("testDataPath") + public void setPageObjects(String testDataPath){ + this.loginPage = new LoginPage(driver); + this.dashboardPage = new DashboardPage(driver); + this.testData = JsonUtil.getTestData(testDataPath, VendorPortalTestData.class); + } + + @Test + public void loginTest(){ + loginPage.goTo(Config.get(Constants.VENDOR_PORTAL_URL)); + Assert.assertTrue(loginPage.isAt()); + loginPage.login(testData.username(), testData.password()); + } + + @Test(dependsOnMethods = "loginTest") + public void dashboardTest(){ + Assert.assertTrue(dashboardPage.isAt()); + + // finance metrics + Assert.assertEquals(dashboardPage.getMonthlyEarning(), testData.monthlyEarning()); + Assert.assertEquals(dashboardPage.getAnnualEarning(), testData.annualEarning()); + Assert.assertEquals(dashboardPage.getProfitMargin(), testData.profitMargin()); + Assert.assertEquals(dashboardPage.getAvailableInventory(), testData.availableInventory()); + + // order history search + dashboardPage.searchOrderHistoryBy(testData.searchKeyword()); + Assert.assertEquals(dashboardPage.getSearchResultsCount(), testData.searchResultsCount()); + } + + @Test(dependsOnMethods = "dashboardTest") + public void logoutTest(){ + dashboardPage.logout(); + Assert.assertTrue(loginPage.isAt()); + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/model/VendorPortalTestData.java b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/model/VendorPortalTestData.java new file mode 100644 index 0000000..aaf4473 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/model/VendorPortalTestData.java @@ -0,0 +1,92 @@ +package com.vinsguru.tests.vendorportal.model; + +public record VendorPortalTestData(String username, + String password, + String monthlyEarning, + String annualEarning, + String profitMargin, + String availableInventory, + String searchKeyword, + int searchResultsCount) { +} + +/* +public class VendorPortalTestData { + + private String username; + private String password; + private String monthlyEarning; + private String annualEarning; + private String profitMargin; + private String availableInventory; + private String searchKeyword; + private int searchResultsCount; + + public VendorPortalTestData() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getMonthlyEarning() { + return monthlyEarning; + } + + public void setMonthlyEarning(String monthlyEarning) { + this.monthlyEarning = monthlyEarning; + } + + public String getAnnualEarning() { + return annualEarning; + } + + public void setAnnualEarning(String annualEarning) { + this.annualEarning = annualEarning; + } + + public String getProfitMargin() { + return profitMargin; + } + + public void setProfitMargin(String profitMargin) { + this.profitMargin = profitMargin; + } + + public String getAvailableInventory() { + return availableInventory; + } + + public void setAvailableInventory(String availableInventory) { + this.availableInventory = availableInventory; + } + + public String getSearchKeyword() { + return searchKeyword; + } + + public void setSearchKeyword(String searchKeyword) { + this.searchKeyword = searchKeyword; + } + + public int getSearchResultsCount() { + return searchResultsCount; + } + + public void setSearchResultsCount(int searchResultsCount) { + this.searchResultsCount = searchResultsCount; + } +} +*/ \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/Config.java b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/Config.java new file mode 100644 index 0000000..1c42d27 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/Config.java @@ -0,0 +1,52 @@ +package com.vinsguru.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.util.Properties; + +public class Config { + + private static final Logger log = LoggerFactory.getLogger(Config.class); + private static final String DEFAULT_PROPERTIES = "config/default.properties"; + private static Properties properties; + + public static void initialize(){ + + // load default properties + properties = loadProperties(); + + // check for any override + for(String key: properties.stringPropertyNames()){ + if(System.getProperties().containsKey(key)){ + properties.setProperty(key, System.getProperty(key)); + } + } + + // print + log.info("Test Properties"); + log.info("-----------------"); + for(String key: properties.stringPropertyNames()){ + log.info("{}={}", key, properties.getProperty(key)); + } + log.info("-----------------"); + + } + + public static String get(String key){ + return properties.getProperty(key); + } + + private static Properties loadProperties(){ + Properties properties = new Properties(); + try(InputStream stream = ResourceLoader.getResource(DEFAULT_PROPERTIES)){ + properties.load(stream); + }catch (Exception e){ + log.error("unable to read the property file {}", DEFAULT_PROPERTIES, e); + } + return properties; + } + + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/Constants.java b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/Constants.java new file mode 100644 index 0000000..edd8c2d --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/Constants.java @@ -0,0 +1,17 @@ +package com.vinsguru.util; + +public class Constants { + + public static final String GRID_ENABLED = "selenium.grid.enabled"; + public static final String GRID_URL_FORMAT = "selenium.grid.urlFormat"; + public static final String GRID_HUB_HOST = "selenium.grid.hubHost"; + + public static final String BROWSER = "browser"; + public static final String CHROME = "chrome"; + public static final String FIREFOX = "firefox"; + public static final String DRIVER = "driver"; + + public static final String FLIGHT_RESERVATION_URL = "flightReservation.url"; + public static final String VENDOR_PORTAL_URL = "vendorPortal.url"; + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/JsonUtil.java b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/JsonUtil.java new file mode 100644 index 0000000..1f53d36 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/JsonUtil.java @@ -0,0 +1,25 @@ +package com.vinsguru.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.vinsguru.tests.vendorportal.model.VendorPortalTestData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; + +public class JsonUtil { + + private static final Logger log = LoggerFactory.getLogger(JsonUtil.class); + private static final ObjectMapper mapper = new ObjectMapper(); + + public static T getTestData(String path, Class type){ + try(InputStream stream = ResourceLoader.getResource(path)){ + return mapper.readValue(stream, type); + }catch (Exception e){ + log.error("unable to read test data {}", path, e); + } + return null; + } + + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/ResourceLoader.java b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/ResourceLoader.java new file mode 100644 index 0000000..be14a26 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/java/com/vinsguru/util/ResourceLoader.java @@ -0,0 +1,29 @@ +package com.vinsguru.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +/* + A simple utility to read file. + first we check the classpath. if found, it is used. + if not, then we check the filesystem + */ +public class ResourceLoader { + + private static final Logger log = LoggerFactory.getLogger(ResourceLoader.class); + + public static InputStream getResource(String path) throws Exception { + log.info("reading resource from location: {}", path); + InputStream stream = ResourceLoader.class.getClassLoader().getResourceAsStream(path); + if(Objects.nonNull(stream)){ + return stream; + } + return Files.newInputStream(Path.of(path)); + } + +} diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/resources/config/default.properties b/06-jenkins-ci-cd/selenium-docker/src/test/resources/config/default.properties new file mode 100644 index 0000000..f7cef6e --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/resources/config/default.properties @@ -0,0 +1,11 @@ +# selenium grid +selenium.grid.enabled=false +selenium.grid.urlFormat=http://%s:4444/wd/hub +selenium.grid.hubHost=localhost + +# browser +browser=chrome + +# application +flightReservation.url=https://d1uh9e7cu07ukd.cloudfront.net/selenium-docker/reservation-app/index.html +vendorPortal.url=https://d1uh9e7cu07ukd.cloudfront.net/selenium-docker/vendor-app/index.html \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-1.json b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-1.json new file mode 100644 index 0000000..af109c9 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-1.json @@ -0,0 +1,11 @@ +{ + "firstName": "michael", + "lastName": "jackson", + "email": "mj@pop.com", + "password": "mj", + "street": "123 main street", + "city": "atlanta", + "zip": "30001", + "passengersCount": "1", + "expectedPrice": "$584 USD" +} \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-2.json b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-2.json new file mode 100644 index 0000000..fdc436b --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-2.json @@ -0,0 +1,11 @@ +{ + "firstName": "marshall", + "lastName": "mathers", + "email": "eminem@hiphop.com", + "password": "m&m", + "street": "456 main street", + "city": "detroit", + "zip": "40001", + "passengersCount": "2", + "expectedPrice": "$1169 USD" +} \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-3.json b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-3.json new file mode 100644 index 0000000..f7432d9 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-3.json @@ -0,0 +1,11 @@ +{ + "firstName": "cheb", + "lastName": "khaled", + "email": "khaled@rai.com", + "password": "didi", + "street": "101 non main street", + "city": "las vegas", + "zip": "50001", + "passengersCount": "3", + "expectedPrice": "$1753 USD" +} \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-4.json b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-4.json new file mode 100644 index 0000000..81b8f13 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-4.json @@ -0,0 +1,11 @@ +{ + "firstName": "dr", + "lastName": "dre", + "email": "dr@dre.com", + "password": "dre", + "street": "455 main street", + "city": "detroit", + "zip": "40001", + "passengersCount": "4", + "expectedPrice": "$2338 USD" +} \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/vendor-portal/john.json b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/vendor-portal/john.json new file mode 100644 index 0000000..83bfa62 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/vendor-portal/john.json @@ -0,0 +1,10 @@ +{ + "username": "john", + "password": "john", + "monthlyEarning": "$3,453", + "annualEarning": "$34,485", + "profitMargin": "-16%", + "availableInventory": "67", + "searchKeyword": "2024/01/01", + "searchResultsCount": 0 +} \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/vendor-portal/mike.json b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/vendor-portal/mike.json new file mode 100644 index 0000000..2ad690e --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/vendor-portal/mike.json @@ -0,0 +1,10 @@ +{ + "username": "mike", + "password": "mike", + "monthlyEarning": "$55,000", + "annualEarning": "$563,300", + "profitMargin": "80%", + "availableInventory": "45", + "searchKeyword": "miami", + "searchResultsCount": 10 +} \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/vendor-portal/sam.json b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/vendor-portal/sam.json new file mode 100644 index 0000000..b82ffc2 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-data/vendor-portal/sam.json @@ -0,0 +1,10 @@ +{ + "username": "sam", + "password": "sam", + "monthlyEarning": "$40,000", + "annualEarning": "$215,000", + "profitMargin": "50%", + "availableInventory": "18", + "searchKeyword": "adams", + "searchResultsCount": 8 +} \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-suites/flight-reservation.xml b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-suites/flight-reservation.xml new file mode 100644 index 0000000..3f0f879 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-suites/flight-reservation.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-suites/vendor-portal.xml b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-suites/vendor-portal.xml new file mode 100644 index 0000000..f335e13 --- /dev/null +++ b/06-jenkins-ci-cd/selenium-docker/src/test/resources/test-suites/vendor-portal.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/08-final-projects/selenium-docker/.gitignore b/08-final-projects/selenium-docker/.gitignore new file mode 100644 index 0000000..6ce9f50 --- /dev/null +++ b/08-final-projects/selenium-docker/.gitignore @@ -0,0 +1,49 @@ +# Created by .ignore support plugin (hsz.mobi) +### Java template +# Compiled class file +**/*.class + +# Log file +**/*.log + +# BlueJ files +**/*.ctxt + +# Mobile Tools for Java (J2ME) +**/.mtj.tmp/ + +# Package Files # +**/*.jar +**/*.war +**/*.nar +**/*.ear +**/*.zip +**/*.tar.gz +**/*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +**/hs_err_pid* + +### Maven template +**/target/ +**/pom.xml.tag +**/pom.xml.releaseBackup +**/pom.xml.versionsBackup +**/pom.xml.next +**/release.properties +**/dependency-reduced-pom.xml +**/buildNumber.properties +**/.mvn/timing.properties +**/.mvn/wrapper/maven-wrapper.jar + +**/*.iml + +**/.idea/ + + +**/HELP.md +**/.mvn/ +**/mvnw +**/mvnw.cmd +**/node_modules/ + diff --git a/08-final-projects/selenium-docker/Dockerfile b/08-final-projects/selenium-docker/Dockerfile new file mode 100644 index 0000000..a32e382 --- /dev/null +++ b/08-final-projects/selenium-docker/Dockerfile @@ -0,0 +1,14 @@ +FROM java47 + +# Install curl jq +RUN apk add curl jq + +# workspace +WORKDIR /home/selenium-docker + +# Add the required files +ADD target/docker-resources ./ +ADD runner.sh runner.sh + +# Start the runner.sh +ENTRYPOINT sh runner.sh \ No newline at end of file diff --git a/08-final-projects/selenium-docker/Jenkinsfile b/08-final-projects/selenium-docker/Jenkinsfile new file mode 100644 index 0000000..19a4d0f --- /dev/null +++ b/08-final-projects/selenium-docker/Jenkinsfile @@ -0,0 +1,39 @@ +pipeline{ + + agent any + + stages{ + + stage('Build Jar'){ + steps{ + sh 'mvn clean package -DskipTests' + } + } + + stage('Build Image'){ + steps{ + sh 'docker build -t=vinsdocker/selenium:latest .' + } + } + + stage('Push Image'){ + environment{ + DOCKER_HUB = credentials('dockerhub-creds') + } + steps{ + sh 'echo ${DOCKER_HUB_PSW} | docker login -u ${DOCKER_HUB_USR} --password-stdin' + sh 'docker push vinsdocker/selenium:latest' + sh "docker tag vinsdocker/selenium:latest vinsdocker/selenium:${env.BUILD_NUMBER}" + sh "docker push vinsdocker/selenium:${env.BUILD_NUMBER}" + } + } + + } + + post { + always { + sh 'docker logout' + } + } + +} \ No newline at end of file diff --git a/08-final-projects/selenium-docker/pom.xml b/08-final-projects/selenium-docker/pom.xml new file mode 100644 index 0000000..a2b5017 --- /dev/null +++ b/08-final-projects/selenium-docker/pom.xml @@ -0,0 +1,159 @@ + + + + 4.0.0 + + com.vinsguru + selenium-docker + 1.0-SNAPSHOT + + + 4.11.0 + 1.4.8 + 5.4.0 + 7.8.0 + 2.12.0 + + 3.11.0 + 3.6.0 + 3.3.0 + 3.1.2 + 3.3.1 + + ${project.build.directory}/docker-resources + + + + org.seleniumhq.selenium + selenium-java + ${selenium.java.version} + + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + io.github.bonigarcia + webdrivermanager + ${webdriver.manager.version} + test + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + test + + + + org.testng + testng + ${testng.version} + test + + + + selenium-docker + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.version} + + 17 + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.version} + + + + firefox + true + + + src/test/resources/test-suites/vendor-portal.xml + src/test/resources/test-suites/flight-reservation.xml + + 4 + target/test-output + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven.dependency.version} + + + copy-dependencies + prepare-package + + copy-dependencies + + + + ${package.directory}/libs + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.version} + + ${package.directory}/libs + + + + + test-jar + + + **/*.class + + + + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven.resources.plugin} + + + copy-resources + prepare-package + + copy-resources + + + ${package.directory} + + + src/test/resources + + + + + + + + + + + \ No newline at end of file diff --git a/08-final-projects/selenium-docker/runner.sh b/08-final-projects/selenium-docker/runner.sh new file mode 100644 index 0000000..84c888d --- /dev/null +++ b/08-final-projects/selenium-docker/runner.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +#------------------------------------------------------------------- +# This script expects the following environment variables +# HUB_HOST +# BROWSER +# THREAD_COUNT +# TEST_SUITE +#------------------------------------------------------------------- + +# Let's print what we have received +echo "-------------------------------------------" +echo "HUB_HOST : ${HUB_HOST:-hub}" +echo "BROWSER : ${BROWSER:-chrome}" +echo "THREAD_COUNT : ${THREAD_COUNT:-1}" +echo "TEST_SUITE : ${TEST_SUITE}" +echo "-------------------------------------------" + +# Do not start the tests immediately. Hub has to be ready with browser nodes +echo "Checking if hub is ready..!" +count=0 +while [ "$( curl -s http://${HUB_HOST:-hub}:4444/status | jq -r .value.ready )" != "true" ] +do + count=$((count+1)) + echo "Attempt: ${count}" + if [ "$count" -ge 30 ] + then + echo "**** HUB IS NOT READY WITHIN 30 SECONDS ****" + exit 1 + fi + sleep 1 +done + +# At this point, selenium grid should be up! +echo "Selenium Grid is up and running. Running the test...." + +# Start the java command +java -cp 'libs/*' \ + -Dselenium.grid.enabled=true \ + -Dselenium.grid.hubHost="${HUB_HOST:-hub}" \ + -Dbrowser="${BROWSER:-chrome}" \ + org.testng.TestNG \ + -threadcount "${THREAD_COUNT:-1}" \ + test-suites/"${TEST_SUITE}" \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/AbstractPage.java b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/AbstractPage.java new file mode 100644 index 0000000..56e702e --- /dev/null +++ b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/AbstractPage.java @@ -0,0 +1,22 @@ +package com.vinsguru.pages; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; + +public abstract class AbstractPage { + + protected final WebDriver driver; + protected final WebDriverWait wait; + + public AbstractPage(WebDriver driver){ + this.driver = driver; + this.wait = new WebDriverWait(driver, Duration.ofSeconds(30)); + PageFactory.initElements(driver, this); + } + + public abstract boolean isAt(); + +} diff --git a/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightConfirmationPage.java b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightConfirmationPage.java new file mode 100644 index 0000000..f38810c --- /dev/null +++ b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightConfirmationPage.java @@ -0,0 +1,39 @@ +package com.vinsguru.pages.flightreservation; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FlightConfirmationPage extends AbstractPage { + + private static final Logger log = LoggerFactory.getLogger(FlightConfirmationPage.class); + + @FindBy(css = "#flights-confirmation-section .card-body .row:nth-child(1) .col:nth-child(2)") + private WebElement flightConfirmationElement; + + @FindBy(css = "#flights-confirmation-section .card-body .row:nth-child(3) .col:nth-child(2)") + private WebElement totalPriceElement; + + public FlightConfirmationPage(WebDriver driver) { + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.flightConfirmationElement)); + return this.flightConfirmationElement.isDisplayed(); + } + + public String getPrice(){ + String confirmation = this.flightConfirmationElement.getText(); + String price = this.totalPriceElement.getText(); + log.info("Flight confirmation# : {}", confirmation); + log.info("Total price : {}", price); + return price; + } + +} diff --git a/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSearchPage.java b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSearchPage.java new file mode 100644 index 0000000..a26ff1a --- /dev/null +++ b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSearchPage.java @@ -0,0 +1,37 @@ +package com.vinsguru.pages.flightreservation; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.Select; + +public class FlightsSearchPage extends AbstractPage { + + @FindBy(id = "passengers") + private WebElement passengerSelect; + + @FindBy(id = "search-flights") + private WebElement searchFlightsButton; + + public FlightsSearchPage(WebDriver driver) { + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.passengerSelect)); + return this.passengerSelect.isDisplayed(); + } + + public void selectPassengers(String noOfPassengers){ + Select passengers = new Select(this.passengerSelect); + passengers.selectByValue(noOfPassengers); + } + + public void searchFlights(){ + this.searchFlightsButton.click(); + } + +} diff --git a/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSelectionPage.java b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSelectionPage.java new file mode 100644 index 0000000..09b5227 --- /dev/null +++ b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/FlightsSelectionPage.java @@ -0,0 +1,43 @@ +package com.vinsguru.pages.flightreservation; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class FlightsSelectionPage extends AbstractPage { + + @FindBy(name = "departure-flight") + private List departureFlightsOptions; + + @FindBy(name = "arrival-flight") + private List arrivalFlightsOptions; + + @FindBy(id = "confirm-flights") + private WebElement confirmFlightsButton; + + public FlightsSelectionPage(WebDriver driver) { + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.confirmFlightsButton)); + return this.confirmFlightsButton.isDisplayed(); + } + + public void selectFlights(){ + int random = ThreadLocalRandom.current().nextInt(0, departureFlightsOptions.size()); + this.departureFlightsOptions.get(random).click(); + this.arrivalFlightsOptions.get(random).click(); + } + + public void confirmFlights(){ + this.confirmFlightsButton.click(); + } + +} diff --git a/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationConfirmationPage.java b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationConfirmationPage.java new file mode 100644 index 0000000..e52c174 --- /dev/null +++ b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationConfirmationPage.java @@ -0,0 +1,35 @@ +package com.vinsguru.pages.flightreservation; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; + +public class RegistrationConfirmationPage extends AbstractPage { + + @FindBy(id = "go-to-flights-search") + private WebElement goToFlightsSearchButton; + + @FindBy(css = "#registration-confirmation-section p b") + private WebElement firstNameElement; + + public RegistrationConfirmationPage(WebDriver driver){ + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.goToFlightsSearchButton)); + return this.goToFlightsSearchButton.isDisplayed(); + } + + public String getFirstName(){ + return this.firstNameElement.getText(); + } + + public void goToFlightsSearch(){ + this.goToFlightsSearchButton.click(); + } + +} diff --git a/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationPage.java b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationPage.java new file mode 100644 index 0000000..0934a10 --- /dev/null +++ b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/flightreservation/RegistrationPage.java @@ -0,0 +1,69 @@ +package com.vinsguru.pages.flightreservation; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; + +public class RegistrationPage extends AbstractPage { + + @FindBy(id = "firstName") + private WebElement firstNameInput; + + @FindBy(id = "lastName") + private WebElement lastNameInput; + + @FindBy(id = "email") + private WebElement emailInput; + + @FindBy(id = "password") + private WebElement passwordInput; + + @FindBy(name = "street") + private WebElement streetInput; + + @FindBy(name = "city") + private WebElement cityInput; + + @FindBy(name = "zip") + private WebElement zipInput; + + @FindBy(id = "register-btn") + private WebElement registerButton; + + public RegistrationPage(WebDriver driver){ + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.firstNameInput)); + return this.firstNameInput.isDisplayed(); + } + + public void goTo(String url){ + this.driver.get(url); + } + + public void enterUserDetails(String firstName, String lastName){ + this.firstNameInput.sendKeys(firstName); + this.lastNameInput.sendKeys(lastName); + } + + public void enterUserCredentials(String email, String password){ + this.emailInput.sendKeys(email); + this.passwordInput.sendKeys(password); + } + + public void enterAddress(String street, String city, String zip){ + this.streetInput.sendKeys(street); + this.cityInput.sendKeys(city); + this.zipInput.sendKeys(zip); + } + + public void register(){ + this.registerButton.click(); + } + +} diff --git a/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/DashboardPage.java b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/DashboardPage.java new file mode 100644 index 0000000..297f892 --- /dev/null +++ b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/DashboardPage.java @@ -0,0 +1,102 @@ +package com.vinsguru.pages.vendorportal; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DashboardPage extends AbstractPage { + + private static final Logger log = LoggerFactory.getLogger(DashboardPage.class); + + @FindBy(id = "monthly-earning") + private WebElement monthlyEarningElement; + + @FindBy(id = "annual-earning") + private WebElement annualEarningElement; + + @FindBy(id = "profit-margin") + private WebElement profitMarginElement; + + @FindBy(id = "available-inventory") + private WebElement availableInventoryElement; + + @FindBy(css = "#dataTable_filter input") + private WebElement searchInput; + + @FindBy(id = "dataTable_info") + private WebElement searchResultsCountElement; + + @FindBy(css = "img.img-profile") + private WebElement userProfilePictureElement; + + // prefer id / name / css + @FindBy(linkText = "Logout") + private WebElement logoutLink; + + @FindBy(css = "#logoutModal a") + private WebElement modalLogoutButton; + + public DashboardPage(WebDriver driver) { + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.monthlyEarningElement)); + return this.monthlyEarningElement.isDisplayed(); + } + + public String getMonthlyEarning(){ + return this.monthlyEarningElement.getText(); + } + + public String getAnnualEarning(){ + return this.annualEarningElement.getText(); + } + + public String getProfitMargin(){ + return this.profitMarginElement.getText(); + } + + public String getAvailableInventory(){ + return this.availableInventoryElement.getText(); + } + + public void searchOrderHistoryBy(String keyword){ + this.searchInput.sendKeys(keyword); + } + + /* + Showing 1 to 10 of 32 entries (filtered from 99 total entries) + arr[0] = "Showing" + arr[1] = "1" + arr[2] = "to" + arr[3] = "10" + arr[4] = "of" + arr[5] = "32" + ... + ... + */ + public int getSearchResultsCount(){ + String resultsText = this.searchResultsCountElement.getText(); + String[] arr = resultsText.split(" "); + // if we do not have 5th item, it would throw exception. + // that's fine. we would want our test to fail anyway in that case! + int count = Integer.parseInt(arr[5]); + log.info("Results count: {}", count); + return count; + } + + public void logout(){ + this.userProfilePictureElement.click(); + this.wait.until(ExpectedConditions.visibilityOf(this.logoutLink)); + this.logoutLink.click(); + this.wait.until(ExpectedConditions.visibilityOf(this.modalLogoutButton)); + this.modalLogoutButton.click(); + } + +} diff --git a/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/LoginPage.java b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/LoginPage.java new file mode 100644 index 0000000..2e1b5c8 --- /dev/null +++ b/08-final-projects/selenium-docker/src/main/java/com/vinsguru/pages/vendorportal/LoginPage.java @@ -0,0 +1,40 @@ +package com.vinsguru.pages.vendorportal; + +import com.vinsguru.pages.AbstractPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; + +public class LoginPage extends AbstractPage { + + @FindBy(id = "username") + private WebElement usernameInput; + + @FindBy(id = "password") + private WebElement passwordInput; + + @FindBy(id = "login") + private WebElement loginButton; + + public LoginPage(WebDriver driver) { + super(driver); + } + + @Override + public boolean isAt() { + this.wait.until(ExpectedConditions.visibilityOf(this.loginButton)); + return this.loginButton.isDisplayed(); + } + + public void goTo(String url){ + this.driver.get(url); + } + + public void login(String username, String password){ + this.usernameInput.sendKeys(username); + this.passwordInput.sendKeys(password); + this.loginButton.click(); + } + +} diff --git a/08-final-projects/selenium-docker/src/main/resources/logback.xml b/08-final-projects/selenium-docker/src/main/resources/logback.xml new file mode 100644 index 0000000..28aa43e --- /dev/null +++ b/08-final-projects/selenium-docker/src/main/resources/logback.xml @@ -0,0 +1,21 @@ + + + + + %d{HH:mm:ss.SSS} %-5level [%15.15t] %cyan(%-30.30logger{30}) : %m%n + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/test/java/com/vinsguru/listener/TestListener.java b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/listener/TestListener.java new file mode 100644 index 0000000..68c1812 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/listener/TestListener.java @@ -0,0 +1,21 @@ +package com.vinsguru.listener; + +import com.vinsguru.util.Constants; +import org.openqa.selenium.OutputType; +import org.openqa.selenium.TakesScreenshot; +import org.testng.ITestListener; +import org.testng.ITestResult; +import org.testng.Reporter; + +public class TestListener implements ITestListener { + + @Override + public void onTestFailure(ITestResult result) { + TakesScreenshot driver = (TakesScreenshot) result.getTestContext().getAttribute(Constants.DRIVER); + String screenshot = driver.getScreenshotAs(OutputType.BASE64); + String htmlImageFormat = ""; + String htmlImage = String.format(htmlImageFormat, screenshot); + Reporter.log(htmlImage); + } + +} diff --git a/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/AbstractTest.java b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/AbstractTest.java new file mode 100644 index 0000000..7b2a10e --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/AbstractTest.java @@ -0,0 +1,64 @@ +package com.vinsguru.tests; + +import com.vinsguru.listener.TestListener; +import com.vinsguru.util.Config; +import com.vinsguru.util.Constants; +import io.github.bonigarcia.wdm.WebDriverManager; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.firefox.FirefoxOptions; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeSuite; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Listeners; + +import java.net.MalformedURLException; +import java.net.URL; + +@Listeners({TestListener.class}) +public abstract class AbstractTest { + + private static final Logger log = LoggerFactory.getLogger(AbstractTest.class); + + protected WebDriver driver; + + @BeforeSuite + public void setupConfig(){ + Config.initialize(); + } + + @BeforeTest + public void setDriver(ITestContext ctx) throws MalformedURLException { + this.driver = Boolean.parseBoolean(Config.get(Constants.GRID_ENABLED)) ? getRemoteDriver() : getLocalDriver(); + ctx.setAttribute(Constants.DRIVER, this.driver); + } + + private WebDriver getRemoteDriver() throws MalformedURLException { + Capabilities capabilities = new ChromeOptions(); + if(Constants.FIREFOX.equalsIgnoreCase(Config.get(Constants.BROWSER))){ + capabilities = new FirefoxOptions(); + } + String urlFormat = Config.get(Constants.GRID_URL_FORMAT); + String hubHost = Config.get(Constants.GRID_HUB_HOST); + String url = String.format(urlFormat, hubHost); + log.info("grid url: {}", url); + return new RemoteWebDriver(new URL(url), capabilities); + } + + private WebDriver getLocalDriver(){ + WebDriverManager.chromedriver().setup(); + return new ChromeDriver(); + } + + @AfterTest + public void quitDriver(){ + this.driver.quit(); + } + +} diff --git a/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/FlightReservationTest.java b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/FlightReservationTest.java new file mode 100644 index 0000000..9069254 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/FlightReservationTest.java @@ -0,0 +1,67 @@ +package com.vinsguru.tests.flightreservation; + +import com.vinsguru.pages.flightreservation.*; +import com.vinsguru.tests.AbstractTest; +import com.vinsguru.tests.flightreservation.model.FlightReservationTestData; +import com.vinsguru.util.Config; +import com.vinsguru.util.Constants; +import com.vinsguru.util.JsonUtil; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +public class FlightReservationTest extends AbstractTest { + + private FlightReservationTestData testData; + + @BeforeTest + @Parameters("testDataPath") + public void setParameters(String testDataPath){ + this.testData = JsonUtil.getTestData(testDataPath, FlightReservationTestData.class); + } + + @Test + public void userRegistrationTest(){ + RegistrationPage registrationPage = new RegistrationPage(driver); + registrationPage.goTo(Config.get(Constants.FLIGHT_RESERVATION_URL)); + Assert.assertTrue(registrationPage.isAt()); + + registrationPage.enterUserDetails(testData.firstName(), testData.lastName()); + registrationPage.enterUserCredentials(testData.email(), testData.password()); + registrationPage.enterAddress(testData.street(), testData.city(), testData.zip()); + registrationPage.register(); + } + + @Test(dependsOnMethods = "userRegistrationTest") + public void registrationConfirmationTest(){ + RegistrationConfirmationPage registrationConfirmationPage = new RegistrationConfirmationPage(driver); + Assert.assertTrue(registrationConfirmationPage.isAt()); + Assert.assertEquals(registrationConfirmationPage.getFirstName(), testData.firstName()); + registrationConfirmationPage.goToFlightsSearch(); + } + + @Test(dependsOnMethods = "registrationConfirmationTest") + public void flightsSearchTest(){ + FlightsSearchPage flightsSearchPage = new FlightsSearchPage(driver); + Assert.assertTrue(flightsSearchPage.isAt()); + flightsSearchPage.selectPassengers(testData.passengersCount()); + flightsSearchPage.searchFlights(); + } + + @Test(dependsOnMethods = "flightsSearchTest") + public void flightsSelectionTest(){ + FlightsSelectionPage flightsSelectionPage = new FlightsSelectionPage(driver); + Assert.assertTrue(flightsSelectionPage.isAt()); + flightsSelectionPage.selectFlights(); + flightsSelectionPage.confirmFlights(); + } + + @Test(dependsOnMethods = "flightsSelectionTest") + public void flightReservationConfirmationTest(){ + FlightConfirmationPage flightConfirmationPage = new FlightConfirmationPage(driver); + Assert.assertTrue(flightConfirmationPage.isAt()); + Assert.assertEquals(flightConfirmationPage.getPrice(), testData.expectedPrice()); + } + +} diff --git a/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/model/FlightReservationTestData.java b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/model/FlightReservationTestData.java new file mode 100644 index 0000000..ce9675a --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/flightreservation/model/FlightReservationTestData.java @@ -0,0 +1,12 @@ +package com.vinsguru.tests.flightreservation.model; + +public record FlightReservationTestData(String firstName, + String lastName, + String email, + String password, + String street, + String city, + String zip, + String passengersCount, + String expectedPrice) { +} diff --git a/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/VendorPortalTest.java b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/VendorPortalTest.java new file mode 100644 index 0000000..8644a19 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/VendorPortalTest.java @@ -0,0 +1,57 @@ +package com.vinsguru.tests.vendorportal; + +import com.vinsguru.pages.vendorportal.DashboardPage; +import com.vinsguru.pages.vendorportal.LoginPage; +import com.vinsguru.tests.AbstractTest; +import com.vinsguru.tests.vendorportal.model.VendorPortalTestData; +import com.vinsguru.util.Config; +import com.vinsguru.util.Constants; +import com.vinsguru.util.JsonUtil; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +public class VendorPortalTest extends AbstractTest { + + private LoginPage loginPage; + private DashboardPage dashboardPage; + private VendorPortalTestData testData; + + @BeforeTest + @Parameters("testDataPath") + public void setPageObjects(String testDataPath){ + this.loginPage = new LoginPage(driver); + this.dashboardPage = new DashboardPage(driver); + this.testData = JsonUtil.getTestData(testDataPath, VendorPortalTestData.class); + } + + @Test + public void loginTest(){ + loginPage.goTo(Config.get(Constants.VENDOR_PORTAL_URL)); + Assert.assertTrue(loginPage.isAt()); + loginPage.login(testData.username(), testData.password()); + } + + @Test(dependsOnMethods = "loginTest") + public void dashboardTest(){ + Assert.assertTrue(dashboardPage.isAt()); + + // finance metrics + Assert.assertEquals(dashboardPage.getMonthlyEarning(), testData.monthlyEarning()); + Assert.assertEquals(dashboardPage.getAnnualEarning(), testData.annualEarning()); + Assert.assertEquals(dashboardPage.getProfitMargin(), testData.profitMargin()); + Assert.assertEquals(dashboardPage.getAvailableInventory(), testData.availableInventory()); + + // order history search + dashboardPage.searchOrderHistoryBy(testData.searchKeyword()); + Assert.assertEquals(dashboardPage.getSearchResultsCount(), testData.searchResultsCount()); + } + + @Test(dependsOnMethods = "dashboardTest") + public void logoutTest(){ + dashboardPage.logout(); + Assert.assertTrue(loginPage.isAt()); + } + +} diff --git a/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/model/VendorPortalTestData.java b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/model/VendorPortalTestData.java new file mode 100644 index 0000000..aaf4473 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/tests/vendorportal/model/VendorPortalTestData.java @@ -0,0 +1,92 @@ +package com.vinsguru.tests.vendorportal.model; + +public record VendorPortalTestData(String username, + String password, + String monthlyEarning, + String annualEarning, + String profitMargin, + String availableInventory, + String searchKeyword, + int searchResultsCount) { +} + +/* +public class VendorPortalTestData { + + private String username; + private String password; + private String monthlyEarning; + private String annualEarning; + private String profitMargin; + private String availableInventory; + private String searchKeyword; + private int searchResultsCount; + + public VendorPortalTestData() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getMonthlyEarning() { + return monthlyEarning; + } + + public void setMonthlyEarning(String monthlyEarning) { + this.monthlyEarning = monthlyEarning; + } + + public String getAnnualEarning() { + return annualEarning; + } + + public void setAnnualEarning(String annualEarning) { + this.annualEarning = annualEarning; + } + + public String getProfitMargin() { + return profitMargin; + } + + public void setProfitMargin(String profitMargin) { + this.profitMargin = profitMargin; + } + + public String getAvailableInventory() { + return availableInventory; + } + + public void setAvailableInventory(String availableInventory) { + this.availableInventory = availableInventory; + } + + public String getSearchKeyword() { + return searchKeyword; + } + + public void setSearchKeyword(String searchKeyword) { + this.searchKeyword = searchKeyword; + } + + public int getSearchResultsCount() { + return searchResultsCount; + } + + public void setSearchResultsCount(int searchResultsCount) { + this.searchResultsCount = searchResultsCount; + } +} +*/ \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/Config.java b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/Config.java new file mode 100644 index 0000000..1c42d27 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/Config.java @@ -0,0 +1,52 @@ +package com.vinsguru.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.util.Properties; + +public class Config { + + private static final Logger log = LoggerFactory.getLogger(Config.class); + private static final String DEFAULT_PROPERTIES = "config/default.properties"; + private static Properties properties; + + public static void initialize(){ + + // load default properties + properties = loadProperties(); + + // check for any override + for(String key: properties.stringPropertyNames()){ + if(System.getProperties().containsKey(key)){ + properties.setProperty(key, System.getProperty(key)); + } + } + + // print + log.info("Test Properties"); + log.info("-----------------"); + for(String key: properties.stringPropertyNames()){ + log.info("{}={}", key, properties.getProperty(key)); + } + log.info("-----------------"); + + } + + public static String get(String key){ + return properties.getProperty(key); + } + + private static Properties loadProperties(){ + Properties properties = new Properties(); + try(InputStream stream = ResourceLoader.getResource(DEFAULT_PROPERTIES)){ + properties.load(stream); + }catch (Exception e){ + log.error("unable to read the property file {}", DEFAULT_PROPERTIES, e); + } + return properties; + } + + +} diff --git a/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/Constants.java b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/Constants.java new file mode 100644 index 0000000..edd8c2d --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/Constants.java @@ -0,0 +1,17 @@ +package com.vinsguru.util; + +public class Constants { + + public static final String GRID_ENABLED = "selenium.grid.enabled"; + public static final String GRID_URL_FORMAT = "selenium.grid.urlFormat"; + public static final String GRID_HUB_HOST = "selenium.grid.hubHost"; + + public static final String BROWSER = "browser"; + public static final String CHROME = "chrome"; + public static final String FIREFOX = "firefox"; + public static final String DRIVER = "driver"; + + public static final String FLIGHT_RESERVATION_URL = "flightReservation.url"; + public static final String VENDOR_PORTAL_URL = "vendorPortal.url"; + +} diff --git a/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/JsonUtil.java b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/JsonUtil.java new file mode 100644 index 0000000..1f53d36 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/JsonUtil.java @@ -0,0 +1,25 @@ +package com.vinsguru.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.vinsguru.tests.vendorportal.model.VendorPortalTestData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; + +public class JsonUtil { + + private static final Logger log = LoggerFactory.getLogger(JsonUtil.class); + private static final ObjectMapper mapper = new ObjectMapper(); + + public static T getTestData(String path, Class type){ + try(InputStream stream = ResourceLoader.getResource(path)){ + return mapper.readValue(stream, type); + }catch (Exception e){ + log.error("unable to read test data {}", path, e); + } + return null; + } + + +} diff --git a/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/ResourceLoader.java b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/ResourceLoader.java new file mode 100644 index 0000000..be14a26 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/java/com/vinsguru/util/ResourceLoader.java @@ -0,0 +1,29 @@ +package com.vinsguru.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +/* + A simple utility to read file. + first we check the classpath. if found, it is used. + if not, then we check the filesystem + */ +public class ResourceLoader { + + private static final Logger log = LoggerFactory.getLogger(ResourceLoader.class); + + public static InputStream getResource(String path) throws Exception { + log.info("reading resource from location: {}", path); + InputStream stream = ResourceLoader.class.getClassLoader().getResourceAsStream(path); + if(Objects.nonNull(stream)){ + return stream; + } + return Files.newInputStream(Path.of(path)); + } + +} diff --git a/08-final-projects/selenium-docker/src/test/resources/config/default.properties b/08-final-projects/selenium-docker/src/test/resources/config/default.properties new file mode 100644 index 0000000..f7cef6e --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/resources/config/default.properties @@ -0,0 +1,11 @@ +# selenium grid +selenium.grid.enabled=false +selenium.grid.urlFormat=http://%s:4444/wd/hub +selenium.grid.hubHost=localhost + +# browser +browser=chrome + +# application +flightReservation.url=https://d1uh9e7cu07ukd.cloudfront.net/selenium-docker/reservation-app/index.html +vendorPortal.url=https://d1uh9e7cu07ukd.cloudfront.net/selenium-docker/vendor-app/index.html \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-1.json b/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-1.json new file mode 100644 index 0000000..af109c9 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-1.json @@ -0,0 +1,11 @@ +{ + "firstName": "michael", + "lastName": "jackson", + "email": "mj@pop.com", + "password": "mj", + "street": "123 main street", + "city": "atlanta", + "zip": "30001", + "passengersCount": "1", + "expectedPrice": "$584 USD" +} \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-2.json b/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-2.json new file mode 100644 index 0000000..fdc436b --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-2.json @@ -0,0 +1,11 @@ +{ + "firstName": "marshall", + "lastName": "mathers", + "email": "eminem@hiphop.com", + "password": "m&m", + "street": "456 main street", + "city": "detroit", + "zip": "40001", + "passengersCount": "2", + "expectedPrice": "$1169 USD" +} \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-3.json b/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-3.json new file mode 100644 index 0000000..f7432d9 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-3.json @@ -0,0 +1,11 @@ +{ + "firstName": "cheb", + "lastName": "khaled", + "email": "khaled@rai.com", + "password": "didi", + "street": "101 non main street", + "city": "las vegas", + "zip": "50001", + "passengersCount": "3", + "expectedPrice": "$1753 USD" +} \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-4.json b/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-4.json new file mode 100644 index 0000000..81b8f13 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/resources/test-data/flight-reservation/passenger-4.json @@ -0,0 +1,11 @@ +{ + "firstName": "dr", + "lastName": "dre", + "email": "dr@dre.com", + "password": "dre", + "street": "455 main street", + "city": "detroit", + "zip": "40001", + "passengersCount": "4", + "expectedPrice": "$2338 USD" +} \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/test/resources/test-data/vendor-portal/john.json b/08-final-projects/selenium-docker/src/test/resources/test-data/vendor-portal/john.json new file mode 100644 index 0000000..83bfa62 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/resources/test-data/vendor-portal/john.json @@ -0,0 +1,10 @@ +{ + "username": "john", + "password": "john", + "monthlyEarning": "$3,453", + "annualEarning": "$34,485", + "profitMargin": "-16%", + "availableInventory": "67", + "searchKeyword": "2024/01/01", + "searchResultsCount": 0 +} \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/test/resources/test-data/vendor-portal/mike.json b/08-final-projects/selenium-docker/src/test/resources/test-data/vendor-portal/mike.json new file mode 100644 index 0000000..2ad690e --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/resources/test-data/vendor-portal/mike.json @@ -0,0 +1,10 @@ +{ + "username": "mike", + "password": "mike", + "monthlyEarning": "$55,000", + "annualEarning": "$563,300", + "profitMargin": "80%", + "availableInventory": "45", + "searchKeyword": "miami", + "searchResultsCount": 10 +} \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/test/resources/test-data/vendor-portal/sam.json b/08-final-projects/selenium-docker/src/test/resources/test-data/vendor-portal/sam.json new file mode 100644 index 0000000..b82ffc2 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/resources/test-data/vendor-portal/sam.json @@ -0,0 +1,10 @@ +{ + "username": "sam", + "password": "sam", + "monthlyEarning": "$40,000", + "annualEarning": "$215,000", + "profitMargin": "50%", + "availableInventory": "18", + "searchKeyword": "adams", + "searchResultsCount": 8 +} \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/test/resources/test-suites/flight-reservation.xml b/08-final-projects/selenium-docker/src/test/resources/test-suites/flight-reservation.xml new file mode 100644 index 0000000..3f0f879 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/resources/test-suites/flight-reservation.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/08-final-projects/selenium-docker/src/test/resources/test-suites/vendor-portal.xml b/08-final-projects/selenium-docker/src/test/resources/test-suites/vendor-portal.xml new file mode 100644 index 0000000..f335e13 --- /dev/null +++ b/08-final-projects/selenium-docker/src/test/resources/test-suites/vendor-portal.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..0439488 --- /dev/null +++ b/Readme.md @@ -0,0 +1,6 @@ +# Scalable Test Automation With Docker, Jenkins & AWS + +This repository contains all the source code for my course in Udemy + +![](.doc/selenium-docker-aws-jenkins.png) +