From ba505b7dfc96216abb21c5b244d670d9d11b9976 Mon Sep 17 00:00:00 2001 From: titusfortner Date: Tue, 4 Feb 2025 13:44:05 -0800 Subject: [PATCH] update mobile web implementation --- .github/workflows/appium-mobile-examples.yml | 35 ++++++ appium/appium-web/appium-web-examples/pom.xml | 73 ------------ .../simple_example/AndroidWebAppTest.java | 109 ------------------ .../simple_example/IOSWebAppTest.java | 106 ----------------- .../com/examples/simple_example/README.md | 78 ------------- .../src/test/java/com/helpers/Constants.java | 11 -- .../com/helpers/SauceAppiumTestWatcher.java | 32 ----- appium/mobile-web/README.md | 80 +++++++++++++ appium/mobile-web/pom.xml | 60 ++++++++++ .../com/saucedemo/AuthenticationTest.java | 49 ++++++++ .../src/test/java/com/saucedemo/CartTest.java | 91 +++++++++++++++ .../test/java/com/saucedemo/CheckoutTest.java | 72 ++++++++++++ .../java/com/saucedemo/NavigationTest.java | 70 +++++++++++ .../src/test/java/com/saucedemo/TestBase.java | 85 ++++++++++++++ .../com/saucedemo/TestConfigurations.java | 106 +++++++++++++++++ 15 files changed, 648 insertions(+), 409 deletions(-) create mode 100644 .github/workflows/appium-mobile-examples.yml delete mode 100644 appium/appium-web/appium-web-examples/pom.xml delete mode 100644 appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/AndroidWebAppTest.java delete mode 100644 appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/IOSWebAppTest.java delete mode 100644 appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/README.md delete mode 100644 appium/appium-web/appium-web-examples/src/test/java/com/helpers/Constants.java delete mode 100644 appium/appium-web/appium-web-examples/src/test/java/com/helpers/SauceAppiumTestWatcher.java create mode 100644 appium/mobile-web/README.md create mode 100644 appium/mobile-web/pom.xml create mode 100644 appium/mobile-web/src/test/java/com/saucedemo/AuthenticationTest.java create mode 100644 appium/mobile-web/src/test/java/com/saucedemo/CartTest.java create mode 100644 appium/mobile-web/src/test/java/com/saucedemo/CheckoutTest.java create mode 100644 appium/mobile-web/src/test/java/com/saucedemo/NavigationTest.java create mode 100644 appium/mobile-web/src/test/java/com/saucedemo/TestBase.java create mode 100644 appium/mobile-web/src/test/java/com/saucedemo/TestConfigurations.java diff --git a/.github/workflows/appium-mobile-examples.yml b/.github/workflows/appium-mobile-examples.yml new file mode 100644 index 00000000..d712f6be --- /dev/null +++ b/.github/workflows/appium-mobile-examples.yml @@ -0,0 +1,35 @@ +name: Real Devices + +on: + schedule: + - cron: '35 20 * * *' + workflow_dispatch: + push: + branches: [ main ] + paths: + - '**/appium/**' + pull_request: + paths: + - '**/appium/**' + +env: + SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} + SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + cloud: [ RDC, VDC ] + browser: [ chrome, safari ] + steps: + - uses: actions/checkout@v4 + - name: Set up Java + uses: actions/setup-java@v4 + with: + java-version: 11 + distribution: "temurin" + - name: Mobile Web ${{ matrix.cloud }} {{ matrix.browser }} + working-directory: ./appium/mobile-web + run: mvn test -Dsauce.cloud=${{ matrix.cloud}} -Dsauce.browser=${{ matrix.browser}} diff --git a/appium/appium-web/appium-web-examples/pom.xml b/appium/appium-web/appium-web-examples/pom.xml deleted file mode 100644 index 7c5cc5d0..00000000 --- a/appium/appium-web/appium-web-examples/pom.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - 4.0.0 - - appium-web-examples - com.saucelabs - 1.0-SNAPSHOT - - Sauce Labs Appium Web - - - 11 - 11 - UTF-8 - 3.5.2 - 9.4.0 - 4.28.1 - - - - - - - commons-logging - commons-logging - 1.3.4 - - - junit - junit - 4.13.2 - - - org.assertj - assertj-core - 3.27.3 - - - io.appium - java-client - ${appium.version} - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.13.0 - - ${maven.compiler.source} - ${maven.compiler.source} - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven.surefire.version} - - all - 30 - true - false - - - - - - \ No newline at end of file diff --git a/appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/AndroidWebAppTest.java b/appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/AndroidWebAppTest.java deleted file mode 100644 index e87e487d..00000000 --- a/appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/AndroidWebAppTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.examples.simple_example; - -import com.helpers.SauceAppiumTestWatcher; -import io.appium.java_client.AppiumDriver; -import io.appium.java_client.android.AndroidDriver; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; -import org.openqa.selenium.By; -import org.openqa.selenium.MutableCapabilities; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Arrays; -import java.util.List; - -import static com.helpers.Constants.*; - -/** - * Android Native App Tests - */ -public class AndroidWebAppTest { - @Rule - public TestName name = new TestName(); - - //This rule allows us to set test status with Junit - @Rule - public SauceAppiumTestWatcher resultReportingTestWatcher = new SauceAppiumTestWatcher(); - - private AndroidDriver driver; - - By usernameInput = By.id("user-name"); - By passwordInput = By.id("password"); - By submitButton = By.className("btn_action"); - By inventoryList = By.className("inventory_list"); - - @Before - public void setup() throws MalformedURLException { - System.out.println("Sauce Android Web App - Before hook"); - - MutableCapabilities capabilities = new MutableCapabilities(); - MutableCapabilities sauceOptions = new MutableCapabilities(); - URL url; - - url = switch (region) { - case "us" -> new URL(SAUCE_US_URL); - default -> new URL(SAUCE_EU_URL); - }; - - // For all capabilities please check - // http://appium.io/docs/en/writing-running-appium/caps/#general-capabilities - // Use the platform configuration https://saucelabs.com/platform/platform-configurator#/ - // to find the emulators/real devices names, OS versions and appium versions you can use for your testings - capabilities.setCapability("platformName", "android"); - capabilities.setCapability("appium:automationName", "UiAutomator2"); - if (rdc.equals("true")) { - capabilities.setCapability("appium:deviceName", "samsung.*"); - sauceOptions.setCapability("appiumVersion", "latest"); - } - else { - capabilities.setCapability("appium:deviceName", "Android GoogleAPI Emulator"); - sauceOptions.setCapability("appiumVersion", "latest"); - } - - capabilities.setCapability("appium:platformVersion", "13"); - capabilities.setCapability("browserName", "chrome"); - - // Sauce capabilities - sauceOptions.setCapability("name", name.getMethodName()); - sauceOptions.setCapability("build", "myWebApp-job-1"); - List tags = Arrays.asList("sauceDemo", "Android", "Demo", "Web"); - sauceOptions.setCapability("tags", tags); - sauceOptions.setCapability("username", System.getenv("SAUCE_USERNAME")); - sauceOptions.setCapability("accessKey", System.getenv("SAUCE_ACCESS_KEY")); - - capabilities.setCapability("sauce:options", sauceOptions); - - driver = new AndroidDriver(url, capabilities); - - resultReportingTestWatcher.setDriver(driver); - } - - @Test - public void loginToSwagLabsWebTestValid() { - System.out.println("Sauce - Start loginToSwagLabsWebTestValid test"); - String urlToTest = "https://www.saucedemo.com/"; - driver.get(urlToTest); - - login("standard_user", "secret_sauce"); - - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - Assert.assertTrue(isOnProductsPage()); - } - public void login(String user, String pass){ - driver.findElement(usernameInput).sendKeys(user); - driver.findElement(passwordInput).sendKeys(pass); - driver.findElement(submitButton).click(); - } - - public boolean isOnProductsPage() { - return driver.findElement(inventoryList).isDisplayed(); - } -} diff --git a/appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/IOSWebAppTest.java b/appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/IOSWebAppTest.java deleted file mode 100644 index 991d9087..00000000 --- a/appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/IOSWebAppTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.examples.simple_example; - -import com.helpers.SauceAppiumTestWatcher; -import io.appium.java_client.ios.IOSDriver; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; -import org.openqa.selenium.By; -import org.openqa.selenium.MutableCapabilities; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Arrays; -import java.util.List; - -import static com.helpers.Constants.*; - -public class IOSWebAppTest { - - @Rule - public TestName name = new TestName(); - - //This rule allows us to set test status with Junit - @Rule - public SauceAppiumTestWatcher resultReportingTestWatcher = new SauceAppiumTestWatcher(); - - private IOSDriver driver; - By usernameInput = By.id("user-name"); - By passwordInput = By.id("password"); - By submitButton = By.className("btn_action"); - By inventoryList = By.className("inventory_list"); - - @Before - public void setUp() throws MalformedURLException { - System.out.println("Sauce iOS Native App - Before hook"); - - MutableCapabilities capabilities = new MutableCapabilities(); - MutableCapabilities sauceOptions = new MutableCapabilities(); - URL url; - String appName; - - url = switch (region) { - case "us" -> new URL(SAUCE_US_URL); - default -> new URL(SAUCE_EU_URL); - }; - - // For all capabilities please check - // http://appium.io/docs/en/writing-running-appium/caps/#general-capabilities - // Use the platform configuration https://saucelabs.com/platform/platform-configurator#/ - // to find the simulators/real device names, OS versions and appium versions you can use for your testings - - capabilities.setCapability("platformName", "iOS"); - capabilities.setCapability("appium:automationName", "XCuiTest"); - if (rdc.equals("true")) { - //Allocate any avilable iPhone device with version 14 - capabilities.setCapability("appium:deviceName", "iPhone.*"); - capabilities.setCapability("appium:platformVersion", "14"); - } - else { - capabilities.setCapability("appium:deviceName", "iPhone Instant Simulator"); - capabilities.setCapability("appium:platformVersion", "current_major"); - } - capabilities.setCapability("browserName", "safari"); - - sauceOptions.setCapability("name", name.getMethodName()); - sauceOptions.setCapability("build", "myWebApp-job-1"); - List tags = Arrays.asList("sauceDemo_ios", "iOS", "Demo", "Web"); - sauceOptions.setCapability("tags", tags); - sauceOptions.setCapability("username", System.getenv("SAUCE_USERNAME")); - sauceOptions.setCapability("accessKey", System.getenv("SAUCE_ACCESS_KEY")); - capabilities.setCapability("sauce:options", sauceOptions); - - driver = new IOSDriver(url, capabilities); - - resultReportingTestWatcher.setDriver(driver); - } - - @Test - public void loginToSwagLabsWebTestValid() { - System.out.println("Sauce - Start loginToSwagLabsWebTestValid test"); - String urlToTest = "https://www.saucedemo.com/"; - driver.get(urlToTest); - - login("standard_user", "secret_sauce"); - - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - Assert.assertTrue(isOnProductsPage()); - } - - public void login(String user, String pass){ - driver.findElement(usernameInput).sendKeys(user); - driver.findElement(passwordInput).sendKeys(pass); - driver.findElement(submitButton).click(); - } - - public boolean isOnProductsPage() { - return driver.findElement(inventoryList).isDisplayed(); - } - -} diff --git a/appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/README.md b/appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/README.md deleted file mode 100644 index 0b472d7d..00000000 --- a/appium/appium-web/appium-web-examples/src/test/java/com/examples/simple_example/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# Java Web App Simple Example on Sauce Labs -This folder is a very simple example to get you started with your appium testing on Web App. -This folder contains examples for: - -- [Android Real Devices in the Sauce Labs Cloud](#run-tests-on-sauce-labs-android-real-devices) -- [Android Emulators in the Sauce Labs Cloud](#run-tests-on-sauce-labs-android-emulators) -- [iOS Real Devices in the Sauce Labs Cloud](#run-tests-on-sauce-labs-ios-real-devices) -- [iOS Simulators in the Sauce Labs Cloud](#run-tests-on-sauce-labs-ios-simulators) - -## Important information -### Environment variables for Sauce Labs -The examples in this repository use environment variables, make sure you've added the following - - # For Sauce Labs Emulators/Simulators/Real devices - export SAUCE_USERNAME=******** - export SAUCE_ACCESS_KEY=******* - - -## Run tests on Sauce Labs Android real devices -If you want to run the tests on Sauce Labs Android Real Devices then you can run the Android test with - - // If using the US DC - mvn clean test -Dtest=AndroidWebAppTest -Dregion=us - - // If using the EU DC - mvn clean test -Dtest=AndroidWebAppTest -Dregion=eu - -The tests, which can be found [here](AndroidWebAppTest.java), will be executed on: -- Any available Samsung device with OS Android 12 - -> The devices use *dynamic* allocation, meaning they will try to find an available device that matches a regular expression. -> NOTE: Make sure you are in the folder `appium-app-examples` when you execute this command - -## Run tests on Sauce Labs Android Emulators -If you want to run the tests on Sauce Labs Android emulators then you can run the Android test with - - // If using the US DC - mvn clean test -Dtest=AndroidWebAppTest -Dregion=us -Drdc=false - - // If using the EU DC - mvn clean test -Dtest=AndroidWebAppTest -Dregion=eu -Drdc=false - -The tests, which can be found [here](AndroidWebAppTest.java), will be executed on: - -- Android GoogleAPI Emulator with OS Android 12 -> NOTE: Make sure you are in the folder `appium-web-examples` when you execute this command - -## Run tests on Sauce Labs iOS real devices -If you want to run the tests on iOS Sauce Labs Real Devices then you can run the iOS test with - - // If using the US DC - mvn clean install -Dtest=IOSWebAppTest -Dregion=us - - // If using the EU DC - mvn clean install -Dtest=IOSWebAppTest -Dregion=eu - -The tests, which can be found [here](IOSWebAppTest.java), will be executed on: - -- Any available iPhone with OS 14 - -> The devices use *dynamic* allocation, meaning they will try to find an available device that matches a regular -expression. -> NOTE: Make sure you are in the folder `appium-web-examples` when you execute this command - -## Run tests on Sauce Labs iOS Simulators -If you want to run the tests on Sauce Labs iOS simulators then you can run the Android test with - - // If using the US DC - mvn clean install -Dtest=IOSWebAppTest -Dregion=us -Drdc=false - - // If using the EU DC - mvn clean install -Dtest=IOSWebAppTest -Dregion=eu -Drdc=false - -The tests, which can be found [here](IOSWebAppTest.java), will be executed on: - -- iPhone 11 Simulator with OS 14 -> NOTE: Make sure you are in the folder `appium-web-examples` when you execute this command - diff --git a/appium/appium-web/appium-web-examples/src/test/java/com/helpers/Constants.java b/appium/appium-web/appium-web-examples/src/test/java/com/helpers/Constants.java deleted file mode 100644 index 9b5aaae6..00000000 --- a/appium/appium-web/appium-web-examples/src/test/java/com/helpers/Constants.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.helpers; - -public class Constants { - public static final String region = System.getProperty("region", "eu"); - public static final String host = System.getProperty("host", "saucelabs"); - public static final String rdc = System.getProperty("rdc", "true"); - - public static final String SAUCE_EU_URL = "https://ondemand.eu-central-1.saucelabs.com/wd/hub"; - public static final String SAUCE_US_URL = "https://ondemand.us-west-1.saucelabs.com/wd/hub"; - -} diff --git a/appium/appium-web/appium-web-examples/src/test/java/com/helpers/SauceAppiumTestWatcher.java b/appium/appium-web/appium-web-examples/src/test/java/com/helpers/SauceAppiumTestWatcher.java deleted file mode 100644 index fc431012..00000000 --- a/appium/appium-web/appium-web-examples/src/test/java/com/helpers/SauceAppiumTestWatcher.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.helpers; - -import io.appium.java_client.AppiumDriver; -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; - -public class SauceAppiumTestWatcher extends TestWatcher { - private AppiumDriver driver; - - public void setDriver(AppiumDriver driver) - { - this.driver = driver; - } - - @Override - protected void succeeded(Description description) { - if(driver != null) - { - driver.executeScript("sauce:job-result=passed"); - driver.quit(); - } - } - - @Override - public void failed(Throwable e, Description description) { - if(driver != null) - { - driver.executeScript("sauce:job-result=failed"); - driver.quit(); - } - } -} diff --git a/appium/mobile-web/README.md b/appium/mobile-web/README.md new file mode 100644 index 00000000..2d3dca32 --- /dev/null +++ b/appium/mobile-web/README.md @@ -0,0 +1,80 @@ +# Testing a Mobile Website + +If you need to test your website on mobile devices with Appium, you have 5 choices. +* Chrome on Android Device on Sauce Labs Real Device Cloud +* Safari on iOS Device on Sauce Labs Real Device Cloud +* Chrome on Emulator in Sauce Labs Virtual Device Cloud +* Safari on Simulator in Sauce Labs Virtual Device Cloud +* [Chrome Mobile Emulation](../../selenium-junit5-examples/README.md#chrome-mobile-emulation) + +[Setup Instructions](https://github.com/saucelabs-training/demo-java/blob/main/README.md#%EF%B8%8Fsetupprerequisites) +and +[Contribution Information](https://github.com/saucelabs-training/demo-java/blob/main/README.md#contributing) +can be found on the [Main README](https://github.com/saucelabs-training/demo-java/blob/main/README.md). + +## Executing Examples +The Website being tested is [Swag Labs](https://www.saucedemo.com/). + +1. After cloning this repo from instructions, change to this subdirectory: + ``` + $ cd appium/mobile-web + ``` + +2. Run the following command to update any package dependencies: + ``` + $ mvn dependency:resolve + ``` +3. Then run the following command to compile your test code: + ``` + $ mvn test-compile + ``` +4. Finally, run the following test to see if you've properly configured the test environment: + ``` + $ mvn clean test + ``` + + See passing tests on [GitHub Actions](https://github.com/saucelabs-training/demo-java/actions/workflows/appium-examples.yml) + +## Configurations +This code allows toggling what cloud the tests will target. +The capabilities for these configurations can be copied from the +[Platform Configurator](https://app.saucelabs.com/platform-configurator), +and can be viewed in the [Test Configurations](src/test/java/com/saucedemo/TestConfigurations.java) file. + +### Chrome on Virtual Device Cloud (default) +Tests will execute on an Android Emulator: + ``` + $ mvn clean test -Dsauce.browser=chrome -Dsauce.cloud=vdc + ``` + +### Safari on Virtual Device Cloud +Tests will execute on an iOS Simulator: + ``` + $ mvn clean test -Dsauce.browser=safari -Dsauce.cloud=vdc + ``` + +### Chrome on Real Device Cloud +Tests will execute on a real Android device: + ``` + $ mvn clean test -Dsauce.browser=chrome -Dsauce.cloud=rdc + ``` + +### Safari on Real Device Cloud +Tests will execute on a real iOS device: + ``` + $ mvn clean test -Dsauce.browser=safari -Dsauce.cloud=rdc + ``` + +## Disclaimer + +> The code in these scripts is provided on an "AS-IS" basis without warranty of any kind, either +> express or implied, including without limitation any implied warranties of condition, +> uninterrupted +> use, merchantability, fitness for a particular purpose, or non-infringement. These scripts are +> provided for educational and demonstration purposes only and should not be used in production. +> Issues regarding these scripts should be submitted through GitHub. These scripts are maintained by +> the Technical Services team at Sauce Labs. +> +> Some examples in this repository, such as `appium-example`, `parallel-testing`, and `headless`, +> may require a different account tier beyond free trial. Please contact +> the [Sauce Labs Sales Team](https://saucelabs.com/contact) for support and information. diff --git a/appium/mobile-web/pom.xml b/appium/mobile-web/pom.xml new file mode 100644 index 00000000..4f896eab --- /dev/null +++ b/appium/mobile-web/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + com.saucelabs + mobile-web + 1.0-SNAPSHOT + + + 11 + 11 + UTF-8 + 10 + + + + + org.junit.jupiter + junit-jupiter + 5.10.3 + test + + + io.appium + java-client + 9.3.0 + test + + + com.titusfortner + selenium-logger + 2.4.0 + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.1 + + + + junit.jupiter.execution.parallel.enabled = true + junit.jupiter.execution.parallel.mode.default = concurrent + junit.jupiter.execution.parallel.mode.classes.default = concurrent + junit.jupiter.execution.parallel.config.strategy = fixed + junit.jupiter.execution.parallel.config.fixed.parallelism = ${surefire.parallel} + junit.jupiter.execution.parallel.config.fixed.max-pool-size = ${surefire.parallel} + + + + + + + \ No newline at end of file diff --git a/appium/mobile-web/src/test/java/com/saucedemo/AuthenticationTest.java b/appium/mobile-web/src/test/java/com/saucedemo/AuthenticationTest.java new file mode 100644 index 00000000..b69ca3ef --- /dev/null +++ b/appium/mobile-web/src/test/java/com/saucedemo/AuthenticationTest.java @@ -0,0 +1,49 @@ +package com.saucedemo; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +public class AuthenticationTest extends TestBase { + + @Test + public void signInUnsuccessful() { + driver.get("https://www.saucedemo.com/"); + + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("locked_out_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + + WebElement errorElement = driver.findElement(By.cssSelector("[data-test='error']")); + Assertions.assertTrue( + errorElement.getText().contains("Sorry, this user has been locked out"), "Error Not Found"); + } + + @Test + public void signInSuccessful() { + driver.get("https://www.saucedemo.com/"); + + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + + Assertions.assertEquals( + "https://www.saucedemo.com/inventory.html", driver.getCurrentUrl(), "Login Not Successful"); + } + + @Test + public void logout() throws InterruptedException { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + driver.findElement(By.id("react-burger-menu-btn")).click(); + Thread.sleep(1000); + + driver.findElement(By.id("logout_sidebar_link")).click(); + + Assertions.assertEquals( + "https://www.saucedemo.com/", driver.getCurrentUrl(), "Logout Not Successful"); + } +} diff --git a/appium/mobile-web/src/test/java/com/saucedemo/CartTest.java b/appium/mobile-web/src/test/java/com/saucedemo/CartTest.java new file mode 100644 index 00000000..1bc92a98 --- /dev/null +++ b/appium/mobile-web/src/test/java/com/saucedemo/CartTest.java @@ -0,0 +1,91 @@ +package com.saucedemo; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; + +public class CartTest extends TestBase { + + @Test + public void addFromProductPage() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + + driver + .findElement(By.cssSelector("button[data-test='add-to-cart-sauce-labs-bolt-t-shirt']")) + .click(); + + Assertions.assertEquals( + "1", + driver.findElement(By.className("shopping_cart_badge")).getText(), + "Item not correctly added to cart"); + } + + @Test + public void removeFromProductPage() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + driver + .findElement(By.cssSelector("button[data-test='add-to-cart-sauce-labs-bolt-t-shirt']")) + .click(); + + driver + .findElement(By.cssSelector("button[data-test='remove-sauce-labs-bolt-t-shirt']")) + .click(); + + Assertions.assertTrue( + driver.findElements(By.className("shopping_cart_badge")).isEmpty(), + "Item not correctly removed from cart"); + } + + @Test + public void addFromInventoryPage() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + + driver.findElement(By.cssSelector("button[data-test='add-to-cart-sauce-labs-onesie']")).click(); + + Assertions.assertEquals("1", driver.findElement(By.className("shopping_cart_badge")).getText()); + } + + @Test + public void removeFromInventoryPage() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + driver + .findElement(By.cssSelector("button[data-test='add-to-cart-sauce-labs-bike-light']")) + .click(); + + driver.findElement(By.cssSelector("button[data-test='remove-sauce-labs-bike-light']")).click(); + + Assertions.assertTrue( + driver.findElements(By.className("shopping_cart_badge")).isEmpty(), + "Shopping Cart is not empty"); + } + + @Test + public void removeFromCartPage() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + driver + .findElement(By.cssSelector("button[data-test='add-to-cart-sauce-labs-backpack']")) + .click(); + driver.findElement(By.className("shopping_cart_link")).click(); + + driver.findElement(By.cssSelector("button[data-test='remove-sauce-labs-backpack']")).click(); + + Assertions.assertTrue( + driver.findElements(By.className("shopping_cart_badge")).isEmpty(), + "Shopping Cart is not empty"); + } +} diff --git a/appium/mobile-web/src/test/java/com/saucedemo/CheckoutTest.java b/appium/mobile-web/src/test/java/com/saucedemo/CheckoutTest.java new file mode 100644 index 00000000..b8b9f1ae --- /dev/null +++ b/appium/mobile-web/src/test/java/com/saucedemo/CheckoutTest.java @@ -0,0 +1,72 @@ +package com.saucedemo; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; + +public class CheckoutTest extends TestBase { + + @Test + public void badInfo() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + driver.findElement(By.cssSelector("button[data-test='add-to-cart-sauce-labs-onesie']")).click(); + driver.findElement(By.className("shopping_cart_link")).click(); + driver.findElement(By.cssSelector("button[data-test='checkout']")).click(); + + driver.findElement(By.cssSelector("input[data-test='continue']")).click(); + + Assertions.assertTrue( + driver + .findElement(By.cssSelector("input[data-test='firstName']")) + .getAttribute("class") + .contains("error"), + "Expected error not found on page"); + } + + @Test + public void goodInfo() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + driver.findElement(By.cssSelector("button[data-test='add-to-cart-sauce-labs-onesie']")).click(); + driver.findElement(By.className("shopping_cart_link")).click(); + driver.findElement(By.cssSelector("button[data-test='checkout']")).click(); + + driver.findElement(By.cssSelector("input[data-test='firstName']")).sendKeys("Luke"); + driver.findElement(By.cssSelector("input[data-test='lastName']")).sendKeys("Perry"); + driver.findElement(By.cssSelector("input[data-test='postalCode']")).sendKeys("90210"); + + driver.findElement(By.cssSelector("input[data-test='continue']")).click(); + + Assertions.assertEquals( + "https://www.saucedemo.com/checkout-step-two.html", + driver.getCurrentUrl(), + "Information Submission Unsuccessful"); + } + + @Test + public void completeCheckout() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + driver.findElement(By.cssSelector("button[data-test='add-to-cart-sauce-labs-onesie']")).click(); + driver.findElement(By.className("shopping_cart_link")).click(); + driver.findElement(By.cssSelector("button[data-test='checkout']")).click(); + driver.findElement(By.cssSelector("input[data-test='firstName']")).sendKeys("Luke"); + driver.findElement(By.cssSelector("input[data-test='lastName']")).sendKeys("Perry"); + driver.findElement(By.cssSelector("input[data-test='postalCode']")).sendKeys("90210"); + driver.findElement(By.cssSelector("input[data-test='continue']")).click(); + + driver.findElement(By.cssSelector("button[data-test='finish']")).click(); + + Assertions.assertEquals( + "https://www.saucedemo.com/checkout-complete.html", driver.getCurrentUrl()); + + Assertions.assertTrue(driver.findElement(By.className("complete-text")).isDisplayed()); + } +} diff --git a/appium/mobile-web/src/test/java/com/saucedemo/NavigationTest.java b/appium/mobile-web/src/test/java/com/saucedemo/NavigationTest.java new file mode 100644 index 00000000..be1acdb8 --- /dev/null +++ b/appium/mobile-web/src/test/java/com/saucedemo/NavigationTest.java @@ -0,0 +1,70 @@ +package com.saucedemo; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; + +public class NavigationTest extends TestBase { + + @Test + public void cancelFromCart() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + driver.findElement(By.className("shopping_cart_link")).click(); + + driver.findElement(By.cssSelector("button[data-test='continue-shopping']")).click(); + + Assertions.assertEquals("https://www.saucedemo.com/inventory.html", driver.getCurrentUrl()); + } + + @Test + public void cancelFromInfoPage() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + driver.findElement(By.cssSelector("button[data-test='add-to-cart-sauce-labs-onesie']")).click(); + driver.findElement(By.className("shopping_cart_link")).click(); + driver.findElement(By.cssSelector("button[data-test='checkout']")).click(); + + driver.findElement(By.cssSelector("button[data-test='cancel']")).click(); + + Assertions.assertEquals("https://www.saucedemo.com/cart.html", driver.getCurrentUrl()); + } + + @Test + public void cancelFromCheckoutPage() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + driver.findElement(By.cssSelector("button[data-test='add-to-cart-sauce-labs-onesie']")).click(); + driver.findElement(By.className("shopping_cart_link")).click(); + driver.findElement(By.cssSelector("button[data-test='checkout']")).click(); + driver.findElement(By.cssSelector("input[data-test='firstName']")).sendKeys("Luke"); + driver.findElement(By.cssSelector("input[data-test='lastName']")).sendKeys("Perry"); + driver.findElement(By.cssSelector("input[data-test='postalCode']")).sendKeys("90210"); + driver.findElement(By.cssSelector("input[data-test='continue']")).click(); + + driver.findElement(By.cssSelector("button[data-test='cancel']")).click(); + + Assertions.assertEquals("https://www.saucedemo.com/inventory.html", driver.getCurrentUrl()); + } + + @Test + public void startCheckout() { + driver.get("https://www.saucedemo.com/"); + driver.findElement(By.cssSelector("input[data-test='username']")).sendKeys("standard_user"); + driver.findElement(By.cssSelector("input[data-test='password']")).sendKeys("secret_sauce"); + driver.findElement(By.cssSelector("input[data-test='login-button']")).click(); + driver.findElement(By.cssSelector("button[data-test='add-to-cart-sauce-labs-onesie']")).click(); + driver.findElement(By.className("shopping_cart_link")).click(); + + driver.findElement(By.cssSelector("button[data-test='checkout']")).click(); + + Assertions.assertEquals( + "https://www.saucedemo.com/checkout-step-one.html", driver.getCurrentUrl()); + } +} diff --git a/appium/mobile-web/src/test/java/com/saucedemo/TestBase.java b/appium/mobile-web/src/test/java/com/saucedemo/TestBase.java new file mode 100644 index 00000000..e380cca2 --- /dev/null +++ b/appium/mobile-web/src/test/java/com/saucedemo/TestBase.java @@ -0,0 +1,85 @@ +package com.saucedemo; + +import io.appium.java_client.AppiumDriver; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.extension.TestWatcher; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.remote.RemoteWebDriver; + +public class TestBase { + + static { + String buildName = "Default Build Name"; + String buildNumber = String.valueOf(System.currentTimeMillis()); + System.setProperty("build.name", buildName + ": " + buildNumber); + } + + public WebDriver driver; + @RegisterExtension public TestBase.SauceTestWatcher watcher = new SauceTestWatcher(); + public static final String DATA_CENTER = System.getProperty("sauce.region", "us"); + public static final String SAUCE_EU_URL = "https://ondemand.eu-central-1.saucelabs.com/wd/hub"; + public static final String SAUCE_US_URL = "https://ondemand.us-west-1.saucelabs.com/wd/hub"; + public static final String SAUCE_URL = DATA_CENTER.equals("us") ? SAUCE_US_URL : SAUCE_EU_URL; + + @BeforeEach + public void setup(TestInfo testInfo) throws MalformedURLException { + Capabilities capabilities = TestConfigurations.getCapabilities(testInfo); + + this.driver = new AppiumDriver(new URL(SAUCE_URL), capabilities); + this.driver.manage().timeouts().implicitlyWait(Duration.of(5, ChronoUnit.SECONDS)); + } + + public class SauceTestWatcher implements TestWatcher { + @Override + public void testSuccessful(ExtensionContext context) { + if (driver != null) { + printResults(context.getDisplayName()); + try { + ((JavascriptExecutor) driver).executeScript("sauce:job-result=passed"); + driver.quit(); + } catch (Exception e) { + System.out.println("problem with using driver: " + e); + } + } + } + + @Override + public void testFailed(ExtensionContext context, Throwable cause) { + if (driver != null) { + printResults(context.getDisplayName()); + + try { + ((JavascriptExecutor) driver).executeScript("sauce:job-result=failed"); + driver.quit(); + } catch (Exception e) { + System.out.println("problem with using driver: " + e); + } + } + } + + public void printResults(String name) { + String sessionId; + String sauceCloud = System.getProperty("sauce.cloud", "vdc"); + if (sauceCloud.equalsIgnoreCase("rdc")) { + sessionId = (String) ((RemoteWebDriver) driver).getCapabilities().getCapability("appium:jobUuid"); + } else { + sessionId = String.valueOf(((RemoteWebDriver) driver).getSessionId()); + } + + String sauceReporter = + String.format("SauceOnDemandSessionID=%s job-name=%s", sessionId, name); + String sauceTestLink = + String.format("Test Job Link: https://app.saucelabs.com/tests/%s", sessionId); + System.out.print(sauceReporter + "\n" + sauceTestLink + "\n"); + } + } +} diff --git a/appium/mobile-web/src/test/java/com/saucedemo/TestConfigurations.java b/appium/mobile-web/src/test/java/com/saucedemo/TestConfigurations.java new file mode 100644 index 00000000..77cb40d9 --- /dev/null +++ b/appium/mobile-web/src/test/java/com/saucedemo/TestConfigurations.java @@ -0,0 +1,106 @@ +package com.saucedemo; + +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.TestInfo; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.MutableCapabilities; + +public class TestConfigurations { + static final String SAUCE_CLOUD = System.getProperty("sauce.cloud", "vdc"); + static final String SAUCE_PLATFORM = System.getProperty("sauce.browser", "chrome"); + static final String BUILD_TIME = String.valueOf(System.currentTimeMillis()); + + // Best practice would put these constants in a config file and dynamically pull them. + public static Capabilities getCapabilities(TestInfo testInfo) { + if (SAUCE_PLATFORM.equalsIgnoreCase("chrome")) { + if (SAUCE_CLOUD.equalsIgnoreCase("vdc")) { + return androidVDC(testInfo); + } else if (SAUCE_CLOUD.equalsIgnoreCase("rdc")) { + return androidRDC(testInfo); + } + } + + if (SAUCE_PLATFORM.equalsIgnoreCase("safari")) { + if (SAUCE_CLOUD.equalsIgnoreCase("vdc")) { + return iosVDC(testInfo); + } else if (SAUCE_CLOUD.equalsIgnoreCase("rdc")) { + return iosRDC(testInfo); + } + } + + throw new RuntimeException("Invalid platform/cloud combination. Browser must be chrome or safari and cloud must be vdc or rdc."); + } + + private static Capabilities androidVDC(TestInfo testInfo) { + Map caps = new HashMap<>(); + caps.put("platformName", "Android"); + caps.put("browserName", "Chrome"); + caps.put("appium:deviceName", "Android GoogleAPI Emulator"); + caps.put("appium:platformVersion", "current_major"); + caps.put("appium:automationName", "UiAutomator2"); + + Map sauceOptions = new HashMap<>(); + sauceOptions.put("username", System.getenv("SAUCE_USERNAME")); + sauceOptions.put("accessKey", System.getenv("SAUCE_ACCESS_KEY")); + sauceOptions.put("name", testInfo.getDisplayName()); + sauceOptions.put("build", "Android Web VDC: " + BUILD_TIME); + caps.put("sauce:options", sauceOptions); + + return new MutableCapabilities(caps); + } + + private static Capabilities androidRDC(TestInfo testInfo) { + Map caps = new HashMap<>(); + caps.put("platformName", "Android"); + caps.put("browserName", "Chrome"); + caps.put("appium:deviceName", "Google.*"); + caps.put("appium:automationName", "UiAutomator2"); + + Map sauceOptions = new HashMap<>(); + sauceOptions.put("username", System.getenv("SAUCE_USERNAME")); + sauceOptions.put("accessKey", System.getenv("SAUCE_ACCESS_KEY")); + sauceOptions.put("name", testInfo.getDisplayName()); + sauceOptions.put("build", "Android Web RDC: " + BUILD_TIME); + sauceOptions.put("appiumVersion", "latest"); + caps.put("sauce:options", sauceOptions); + + return new MutableCapabilities(caps); + } + + private static Capabilities iosVDC(TestInfo testInfo) { + Map caps = new HashMap<>(); + caps.put("platformName", "iOS"); + caps.put("browserName", "Safari"); + caps.put("appium:deviceName", "iPhone Simulator"); + caps.put("appium:platformVersion", "current_major"); + caps.put("appium:automationName", "XCUITest"); + + Map sauceOptions = new HashMap<>(); + sauceOptions.put("username", System.getenv("SAUCE_USERNAME")); + sauceOptions.put("accessKey", System.getenv("SAUCE_ACCESS_KEY")); + sauceOptions.put("name", testInfo.getDisplayName()); + sauceOptions.put("build", "iOS Web VDC: " + BUILD_TIME); + caps.put("sauce:options", sauceOptions); + + return new MutableCapabilities(caps); + } + + private static Capabilities iosRDC(TestInfo testInfo) { + Map caps = new HashMap<>(); + caps.put("platformName", "iOS"); + caps.put("browserName", "Safari"); + caps.put("appium:deviceName", "iPhone.*"); + caps.put("appium:automationName", "XCUITest"); + + Map sauceOptions = new HashMap<>(); + sauceOptions.put("username", System.getenv("SAUCE_USERNAME")); + sauceOptions.put("accessKey", System.getenv("SAUCE_ACCESS_KEY")); + sauceOptions.put("appiumVersion", "latest"); + sauceOptions.put("name", testInfo.getDisplayName()); + sauceOptions.put("build", "iOS Web RDC: " + BUILD_TIME); + caps.put("sauce:options", sauceOptions); + + return new MutableCapabilities(caps); + } +}