From 28173b26f1119ed262425c86e369dd909cbb060c Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Tue, 20 Sep 2022 16:29:10 +0530 Subject: [PATCH 01/29] initial commit on init --- .gitignore | 12 + LICENSE | 2 +- README.md | 271 +++++++++++++++++- pom.xml | 142 +++++++++ src/test/java/Runner/CucumberTest.java | 130 +++++++++ .../StepDefinitions/ApplicationHooks.java | 28 ++ src/test/java/StepDefinitions/E2ESteps.java | 99 +++++++ .../java/StepDefinitions/OffersSteps.java | 50 ++++ src/test/java/StepDefinitions/UsersSteps.java | 73 +++++ src/test/java/com/pages/bsCheckout.java | 45 +++ src/test/java/com/pages/bsConfirmation.java | 34 +++ src/test/java/com/pages/bsHome.java | 128 +++++++++ src/test/java/com/pages/bsOffers.java | 37 +++ src/test/java/com/pages/bsOrders.java | 38 +++ src/test/java/com/pages/bsSignin.java | 46 +++ .../util/BrowserstackTestStatusListener.java | 38 +++ .../java/com/qa/util/CapabilityReader.java | 43 +++ src/test/java/com/qa/util/SuiteAlterer.java | 63 ++++ src/test/resources/Features/E2E.feature | 19 ++ src/test/resources/Features/Offers.feature | 16 ++ src/test/resources/Features/Users.feature | 35 +++ src/test/resources/Reports/Report.txt | 3 + .../Run_Local_Parallel/local.parallel.json | 48 ++++ .../localParallel.testng.xml | 32 +++ .../conf/Run_Local_Test/local.conf.json | 26 ++ .../conf/Run_Local_Test/local.testng.xml | 14 + .../conf/Run_Parallel_Test/parallel.conf.json | 48 ++++ .../Run_Parallel_Test/parallel.testng.xml | 32 +++ .../conf/Run_Single_Test/single.conf.json | 26 ++ .../conf/Run_Single_Test/single.testng.xml | 14 + src/test/resources/extent-config.xml | 41 +++ src/test/resources/extent.properties | 14 + testng.xml | 15 + 33 files changed, 1660 insertions(+), 2 deletions(-) create mode 100644 pom.xml create mode 100644 src/test/java/Runner/CucumberTest.java create mode 100644 src/test/java/StepDefinitions/ApplicationHooks.java create mode 100644 src/test/java/StepDefinitions/E2ESteps.java create mode 100644 src/test/java/StepDefinitions/OffersSteps.java create mode 100644 src/test/java/StepDefinitions/UsersSteps.java create mode 100644 src/test/java/com/pages/bsCheckout.java create mode 100644 src/test/java/com/pages/bsConfirmation.java create mode 100644 src/test/java/com/pages/bsHome.java create mode 100644 src/test/java/com/pages/bsOffers.java create mode 100644 src/test/java/com/pages/bsOrders.java create mode 100644 src/test/java/com/pages/bsSignin.java create mode 100644 src/test/java/com/qa/util/BrowserstackTestStatusListener.java create mode 100644 src/test/java/com/qa/util/CapabilityReader.java create mode 100644 src/test/java/com/qa/util/SuiteAlterer.java create mode 100644 src/test/resources/Features/E2E.feature create mode 100644 src/test/resources/Features/Offers.feature create mode 100644 src/test/resources/Features/Users.feature create mode 100644 src/test/resources/Reports/Report.txt create mode 100644 src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json create mode 100644 src/test/resources/browserstack/conf/Run_Local_Parallel/localParallel.testng.xml create mode 100644 src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json create mode 100644 src/test/resources/browserstack/conf/Run_Local_Test/local.testng.xml create mode 100644 src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json create mode 100644 src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml create mode 100644 src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json create mode 100644 src/test/resources/browserstack/conf/Run_Single_Test/single.testng.xml create mode 100644 src/test/resources/extent-config.xml create mode 100644 src/test/resources/extent.properties create mode 100644 testng.xml diff --git a/.gitignore b/.gitignore index a1c2a23..0d631f2 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,15 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +src/test/resources/Reports/* +target/ +build/ +.project +.settings/ +.classpath +.idea/ +.gradle/ +*.iml +.DS_Store +!gradle/wrapper/* diff --git a/LICENSE b/LICENSE index 261eeb9..bc8e1b9 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright (c) 2021, BrowserStack Limited. https://www.browserstack.com Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 378560c..6d84572 100644 --- a/README.md +++ b/README.md @@ -1 +1,270 @@ -# browserstack-examples-cucumber-testng \ No newline at end of file +![Logo](https://www.browserstack.com/images/static/header-logo.jpg) + +# BrowserStack Examples Cucumber TestNG Selenium 4 Cucumber TestNG + +## Introduction + +This BrowserStack Example repository demonstrates a Selenium 4 [Page Factory Model](https://www.browserstack.com/guide/page-object-model-in-selenium) wrapped under Cucumber and TestNG with parallel testing capabilities. The Selenium test scripts are written for the open source [BrowserStack Demo web application](https://bstackdemo.com) ([Github](https://github.com/browserstack/browserstack-demo-app)). This BrowserStack Demo App is an e-commerce web application which showcases multiple real-world user scenarios, written in Next and React. The app is bundled with offers data, orders data and products data that contains everything you need to start using the app and run tests out-of-the-box. + +The Selenium tests are run on different platforms like on-prem and BrowserStack using various run configurations and test capabilities. + +## :pushpin: Key Features + +:globe_with_meridians: Empowered to run on various platforms including **on-premise browsers**, browsers running on a remote selenium grid such as + **[BrowserStack Automate](https://www.browserstack.com/automate)** + +:rocket: Enables concurrent execution of cucumber scenarios at scenario and feature level across different platforms. + +:bulb: Run the builds with max concurrency as per the parallel quota on your BrowserStack Account. + +:computer: Control concurrency of the tests by setting thread count from terminal. + +:white_check_mark: Three distinct layers of framework implementaion viz. Feature File, Step Definitions, Page Factory Classes to maintain the border between Business users and developers. + +:recycle: Can be ran on any installed browsers onprem without downloading the respective driver executables. + +:zap: Single Runner class required to configure all parameters and plugins. + +:bar_chart: HTML and PDF reports generation with screenshots. + +--- + +## Repository setup + +- Clone the repository +- Ensure you have the following dependencies installed on the machine + - Java >= 8 + - Maven >= 3.1+ + + + Maven: + ```sh + mvn install + ``` +--- +## About the tests in this repository + +This repository contains the following Cucumber Scenario tests: + +| Features | Tags | Test Name | Description | +|--|--|--|--| +| E2E | @e2e |End to End Scenario|This test scenario verifies successful product purchase lifecycle end-to-end with verifying that 9 Apple products are only shown if the Apple vendor filter option is applied and verifies that the product prices are in ascending order when the product sort “Lowest to Highest” is applied. It demonstrates the [Page Factory Model design pattern](https://www.browserstack.com/guide/page-object-model-in-selenium)| +|User|@locked|Login as Locked User|This test verifies the login workflow error for a locked user.| +|User|@noimage, @fail|Login as User with no image loaded| This test verifies that the product images load for user: “image_not_loading_user” on the e-commerce application. Since the images do not load, the test case assertion fails.| +|User|@orders|Login as User with existing Orders| This test verifies that existing orders are shown for user: “existing_orders_user”| +|Offers|@offers|Offers for Mumbai location|This test mocks the GPS location for Mumbai and verifies that the product offers applicable for the Mumbai location are shown. | + + - `@test` will run all scenarios in **Offers** feature file. + - `@users` will run all scenarios in **Users** feature file. + - `@e2e` will run all scenarios in **E2E** feature file. + - `@regression` will run all scenarios in all the feature files. + +--- + +## Test infrastructure environments + +- [On-premise/self-hosted](#On-Premise-or-Self-Hosted) +- [BrowserStack](#browserstack) + +--- +## Configuring the maximum parallel test threads for this repository + +For all the parallel run configuration profiles, tests will run with max parallels in your BrowserStack account, or you can configure the parallel test threads from the terminal + + "-DthreadCount=xx" + +|BrowserStack| onPrem | +|--|--| +| browserstack/conf/Run_Single_Test/single.testng.xml|testng.xml +browserstack/conf/Run_Parallel_Test/parallel.testng.xml | | +|browserstack/conf/Run_Local_Test/local.testng.xml| +|browserstack/conf/Run_Parallel_Test/parallel.testng.xml + + + +--- +# Test Reporting + +- [Extent reports](https://ghchirp.tech/2098/) +- Reports in HTML and PDF formats are generated in the [Reports](src/test/resources/Reports) with TimeStamp. +--- +# On Premise or Self Hosted + +This infrastructure points to running the tests on your own machine using any browser (e.g. Chrome) using the browser's driver executables (e.g. ChromeDriver for Chrome). This driver dependency is resolved using the WebDriverManager and eliminates the need to download the webdriver on the local machine. + +## Running Your Tests + +### Run a specific test/entire suite on your own machine + +- How to run the test? + + To run any test scenario (e.g. End to End Scenario) on your own machine, use the following command, you can specify any cucumber tag from the feature files to run the scenarios at feature/scenario outline level in this Maven Profile. + + Eg. "@e2e", "@noimage", "@offers" or any of the other test scenario tags, as outlined in [About the tests in this repository](#About-the-tests-in-this-repository) section. +`"-Dcucumber.filter.tags=@e2e"` + + User can also specify the choice of browser in the `` tags of the profile to run the suite/scenarios in that browser. + Eg. `firefox` + + thread-count can be set for the tests from the terminal + `"-DthreadCount=5"` + + Maven: + ```sh + mvn test -P scenario-onprem + ``` + +- Output + + This run profile executes a specific test scenario/suite in single/parallel on the stated browser instance on your own machine. + +--- +# BrowserStack + +[BrowserStack](https://browserstack.com) provides instant access to 2,000+ real mobile devices and browsers on a highly reliable cloud infrastructure that effortlessly scales as testing needs grow. + +## Prerequisites + +- Create a new [BrowserStack account](https://www.browserstack.com/users/sign_up) or use an existing one. +- Identify your BrowserStack username and access key from the [BrowserStack Automate Dashboard](https://automate.browserstack.com/) and export them as environment variables using the below commands. + + - For \*nix based and Mac machines: + + ```sh + export BROWSERSTACK_USERNAME= && + export BROWSERSTACK_ACCESS_KEY= + ``` + + - For Windows: + + ```shell + set BROWSERSTACK_USERNAME= + set BROWSERSTACK_ACCESS_KEY= + ``` + + Alternatively, you can also hardcode username and access_key objects in the respective json files: + - [single.conf.json](src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json) file + - [parallel.conf.json](src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json) file + - [local.conf.json](src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json) file + - [local.parallel.json](src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json) file + + +Note: +- We have configured a list of test capabilities in the json files. You can certainly update them based on your device / browser test requirements. +- The exact test capability values can be easily identified using the [Browserstack W3C Capability Generator](https://www.browserstack.com/automate/capabilities?tag=selenium-4) + + +## Running Your Tests + +### Run a specific test/entire test suite in parallel on a single BrowserStack browser + +In this section, we will run the tests in parallel on a single browser on Browserstack. Refer to [single.conf.json](src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json) file to change test capabilities for this configuration. + +- How to run the test? + + To run the tests in parallel on a single BrowserStack browser, use the following command: + + Maven: + ```sh + mvn test -P scenario-bs + ``` + + You can mention any scenario from the feature files using the `-Dcucumber.filter.tags`, tags defined at Feature level Eg. `@users` will run all the scenarios in the [Users Feature](src/test/resources/Features/Users.feature) file in parallel. Likewise `@regression` will run all the scenarios from all the Feature files in parallel. + +- Output + + This run profile executes any scenario/entire test suite in parallel on a single BrowserStack browser. Please refer to your [BrowserStack dashboard](https://automate.browserstack.com/) for test results. + + - Note: By default, this execution would run maximum test threads based on the parallel quota on your BrowserStack Account. Thread count can be configured as below based on your requirements. + + ```sh + mvn test -P scenario-bs "-Dcucumber.filter.tags=@regresssion" "-DthreadCount=3" + ``` + + +### Run the entire test suite in parallel on multiple BrowserStack browsers + +In this section, we will run the tests in parallel on multiple browsers on Browserstack. Refer to the [parallel.conf.json](src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json) file to change test capabilities for this configuration. + +- How to run the test? + + To run the entire test suite in parallel on multiple BrowserStack browsers/devices, use the following command: + + Maven: + ```sh + mvn test -P suite-cross-bs + ``` + + You can mention any scenario from the feature files using the `-Dcucumber.filter.tags`, tags defined at Feature level Eg. `@users` will run all the scenarios in the [Users Feature](src/test/resources/Features/Users.feature) file in parallel. Likewise `@regression` will run all the scenarios from all the Feature files in parallel across multiple browsers/devices. +--- +### [Web application hosted on internal environment] Running your tests on BrowserStack using BrowserStackLocal + +#### Prerequisites + +- Clone the [BrowserStack demo application](https://github.com/browserstack/browserstack-demo-app) repository. + ```sh + git clone https://github.com/browserstack/browserstack-demo-app + ``` +- Please follow the README.md on the BrowserStack demo application repository to install and start the dev server on localhost. +- In this section, we will run a single test case to test the BrowserStack Demo app hosted on your local machine i.e. localhost. Refer to the [local.conf.json](src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json) file to change test capabilities for this configuration. +- Note: You may need to provide additional BrowserStackLocal arguments to successfully connect your localhost environment with BrowserStack infrastructure. (e.g if you are behind firewalls, proxy or VPN). +- Further details for successfully creating a BrowserStackLocal connection can be found here: + + - [Local Testing with Automate](https://www.browserstack.com/local-testing/automate) + - [BrowserStackLocal Java GitHub](https://github.com/browserstack/browserstack-local-java) + + +### [Web application hosted on internal environment] Run a specific test/entire test suite in parallel on a single BrowserStack browser/device using BrowserStackLocal + +- How to run the test? + + - To run any test scenario (e.g. End to End Scenario) on a single BrowserStack browser using BrowserStackLocal, use the following command: + + Maven: + ```sh + mvn test -P local-bs + ``` + +You can mention any scenario from the feature files using the `-Dcucumber.filter.tags`, tags defined at Feature level Eg. `@users` will run all the scenarios in the [Users Feature](src/test/resources/Features/Users.feature) file in parallel. Likewise `@regression` will run all the scenarios from all the Feature files in parallel. + +- Output + + This run profile executes a single test/entire suite on an internally hosted web application on a single browser on BrowserStack in parallel. Please refer to your [BrowserStack dashboard](https://automate.browserstack.com/) for test results. + + +### [Web application hosted on internal environment] Run the entire test suite in parallel on multiple BrowserStack browser using BrowserStackLocal + +In this section, we will run the test cases to test the internally hosted website in parallel on multiple browsers/devices on Browserstack. Refer to the [local.parallel.json](src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json) file to change test capabilities for this configuration. + +- How to run the test? + + To run the entire test suite in parallel on multiple devices and browsers using BrowserStackLocal, use the following command: + + Maven: + ```sh + mvn test -P local-cross-bs + ``` + +- Output + + This run profile executes the entire test suite on an internally hosted web application on multiple browsers on BrowserStack. Please refer to your [BrowserStack dashboard](https://automate.browserstack.com/) for test results. + +- Note: By default, this execution would run maximum test threads based on the parallel quota on your BrowserStack Account. Thread count can be configured as below based on your requirements. + ```sh + mvn test -P local-cross-bs "-Dcucumber.filter.tags=@e2e" "-DthreadCount=3" + ``` + + +## Additional Resources + +- View your test results on the [BrowserStack Automate dashboard](https://www.browserstack.com/automate) +- Documentation for writing [Automate test scripts in Java](https://www.browserstack.com/automate/java) +- Customising your tests capabilities for Selenium 4 on BrowserStack using our [test capability generator](https://www.browserstack.com/automate/capabilities?tag=selenium-4) +- [List of Browsers & mobile devices](https://www.browserstack.com/list-of-browsers-and-platforms?product=automate) for automation testing on BrowserStack +- [Using Automate REST API](https://www.browserstack.com/automate/rest-api) to access information about your tests via the command-line interface +- Understand how many parallel sessions you need by using our [Parallel Test Calculator](https://www.browserstack.com/automate/parallel-calculator?ref=github) +- For testing public web applications behind IP restriction, [Inbound IP Whitelisting](https://www.browserstack.com/local-testing/inbound-ip-whitelisting) can be enabled with the [BrowserStack Enterprise](https://www.browserstack.com/enterprise) offering + + +## Open Issues +- The PDF report breaks for the multiple scenarios executed in parallel on multiple devices/browsers. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..edddd83 --- /dev/null +++ b/pom.xml @@ -0,0 +1,142 @@ + + + 4.0.0 + + org.BrowserStack + CucumberJava_Automate + 1.0-SNAPSHOT + + + 7.2.2 + UTF-8 + 1.8 + 1.8 + 7.4.0 + 3.0.0-M5 + + + https://bstackdemo.com/ + + + + + io.cucumber + cucumber-java + ${cucumber.version} + + + io.cucumber + cucumber-testng + ${cucumber.version} + test + + + org.testng + testng + ${testng.version} + test + + + io.cucumber + cucumber-picocontainer + ${cucumber.version} + test + + + org.seleniumhq.selenium + selenium-java + 4.1.0 + + + io.github.bonigarcia + webdrivermanager + 5.0.3 + + + commons-io + commons-io + 2.11.0 + + + tech.grasshopper + extentreports-cucumber7-adapter + 1.1.0 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + com.browserstack + browserstack-local-java + 1.0.6 + + + com.squareup.okhttp3 + okhttp + 5.0.0-alpha.10 + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.version} + + + ${suiteXmlFile} + + + ${browser-type} + ${Application_url} + + + + + + + + + scenario-onprem + + chrome + testng.xml + + + + scenario-bs + + remote + src/test/resources/browserstack/conf/Run_Single_Test/single.testng.xml + + + + suite-cross-bs + + remote + src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml + + + + local-bs + + remote + http://localhost:3000/ + src/test/resources/browserstack/conf/Run_Local_Test/local.testng.xml + + + + local-cross-bs + + remote + http://localhost:3000/ + src/test/resources/browserstack/conf/Run_Local_Parallel/localParallel.testng.xml + + + + \ No newline at end of file diff --git a/src/test/java/Runner/CucumberTest.java b/src/test/java/Runner/CucumberTest.java new file mode 100644 index 0000000..fa7602b --- /dev/null +++ b/src/test/java/Runner/CucumberTest.java @@ -0,0 +1,130 @@ +package Runner; + +import com.browserstack.local.Local; +import com.qa.util.BrowserstackTestStatusListener; +import com.qa.util.CapabilityReader; +import io.cucumber.testng.CucumberOptions; +import io.cucumber.testng.FeatureWrapper; +import io.cucumber.testng.PickleWrapper; +import io.cucumber.testng.TestNGCucumberRunner; +import io.github.bonigarcia.wdm.WebDriverManager; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.openqa.selenium.safari.SafariDriver; +import org.testng.annotations.*; + +import java.io.FileReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@CucumberOptions(features = "src/test/resources/Features", glue = {"StepDefinitions"}, + plugin = {"pretty", + "com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"}, + monochrome = true + ) +@Listeners(BrowserstackTestStatusListener.class) +public class CucumberTest { + private TestNGCucumberRunner testNGCucumberRunner; + public static ThreadLocal tlDriver = new ThreadLocal<>(); + private Local l; + public static String buildname; + @Parameters(value = {"config"}) + @BeforeSuite + public void localStart(String config_file) throws Exception { + JSONParser parser = new JSONParser(); + if(!config_file.isEmpty()) { + JSONObject config = (JSONObject) parser.parse(new FileReader("src/test/resources/browserstack/conf/" + config_file)); + JSONObject local = (JSONObject) config.get("capabilities"); + boolean localKey = local.containsKey("local"); + if (localKey && local.get("local").toString().contains("true")) { + l = new Local(); + Map options = new HashMap(); + if (System.getenv("BROWSERSTACK_ACCESS_KEY") == null) + options.put("key", (String) config.get("key")); + else + options.put("key", System.getenv("BROWSERSTACK_ACCESS_KEY")); + System.out.println("Starting Local"); + l.start(options); + } + SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.YY hh.mm"); + buildname = local.get("projectName") + "-" + sdf.format(new Date()); + } + } + @BeforeClass(alwaysRun = true) + public void setUpClass() { + testNGCucumberRunner = new TestNGCucumberRunner(this.getClass()); + } + @Parameters(value = { "config", "environment" }) + @BeforeMethod() + public void setUpHooks(String config_file, String environment) throws Exception { + DesiredCapabilities capabilities = new DesiredCapabilities(); + String browser=System.getProperty("browser-type").toLowerCase(); + if (browser.equalsIgnoreCase("chrome")) { + WebDriverManager.chromedriver().setup(); + tlDriver.set(new ChromeDriver()); + } else if (browser.equalsIgnoreCase("firefox")) { + WebDriverManager.firefoxdriver().setup(); + tlDriver.set(new FirefoxDriver()); + } else if (browser.equalsIgnoreCase("safari")) { + WebDriverManager.safaridriver().setup(); + tlDriver.set(new SafariDriver()); + } else if (browser.equalsIgnoreCase("remote")) { + JSONParser parser = new JSONParser(); + JSONObject config; + JSONObject envs; + config = (JSONObject) parser.parse(new FileReader("src/test/resources/browserstack/conf/"+config_file)); + envs = (JSONObject) config.get("environments"); + Object env = envs.get(environment); + capabilities = CapabilityReader.getCapability((Map) env, config); + String username = (String) config.get("user"); + if (username == null || username.isEmpty()){ + username = System.getenv("BROWSERSTACK_USERNAME"); + } + String accessKey = (String) config.get("key"); + if (accessKey == null || accessKey.isEmpty()) { + accessKey = System.getenv("BROWSERSTACK_ACCESS_KEY"); + } + tlDriver.set( new RemoteWebDriver( + new URL("https://" + username + ":" + accessKey + "@" + config.get("server") + "/wd/hub"), capabilities)); + }else + throw new AssertionError("Invalid input for browser"); + if(!capabilities.toString().contains("realMobile")) + getDriver().manage().window().maximize(); + } + + public static synchronized WebDriver getDriver(){ + return tlDriver.get(); + } + + @Test(dataProvider = "scenarios") + public void scenario(PickleWrapper pickleWrapper, FeatureWrapper featureWrapper) throws MalformedURLException { + JavascriptExecutor jse = (JavascriptExecutor)getDriver(); + if(System.getProperty("browser-type").equalsIgnoreCase("remote")) + jse.executeScript("browserstack_executor: {\"action\": \"setSessionName\", \"arguments\": {\"name\":\" "+ pickleWrapper.getPickle().getName() +" \" }}"); + testNGCucumberRunner.runScenario(pickleWrapper.getPickle()); + } + @DataProvider(parallel = true) + public Object[][] scenarios(){ + return testNGCucumberRunner.provideScenarios(); + } + + @AfterClass(alwaysRun = true) + public void tearDownClass() { + testNGCucumberRunner.finish(); + } + @AfterSuite + public void shutLocal() throws Exception { + System.out.println("Stopping Local"); + if(l != null) l.stop(); + } +} diff --git a/src/test/java/StepDefinitions/ApplicationHooks.java b/src/test/java/StepDefinitions/ApplicationHooks.java new file mode 100644 index 0000000..153b056 --- /dev/null +++ b/src/test/java/StepDefinitions/ApplicationHooks.java @@ -0,0 +1,28 @@ +package StepDefinitions; + +import Runner.CucumberTest; +import io.cucumber.java.After; +import io.cucumber.java.Scenario; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.OutputType; +import org.openqa.selenium.TakesScreenshot; +import org.openqa.selenium.WebDriver; + +public class ApplicationHooks extends CucumberTest { + public WebDriver driver = CucumberTest.getDriver(); + @After(order=1) + public void screenshot(Scenario sc){ + String screenshotName = sc.getName().replaceAll(" ","_"); + byte[] sourcePath = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES); + sc.attach(sourcePath,"image/png",screenshotName); + } + + private void markTestStatus(String status, String reason, WebDriver driver) { + try { + JavascriptExecutor jse = (JavascriptExecutor) driver; + jse.executeScript("browserstack_executor: {\"action\": \"setSessionStatus\", \"arguments\": {\"status\": \"" + status + "\", \"reason\": \"" + reason + "\"}}"); + } catch (Exception e) { + System.out.print("Error executing javascript" + e); + } + } +} diff --git a/src/test/java/StepDefinitions/E2ESteps.java b/src/test/java/StepDefinitions/E2ESteps.java new file mode 100644 index 0000000..6295f58 --- /dev/null +++ b/src/test/java/StepDefinitions/E2ESteps.java @@ -0,0 +1,99 @@ +package StepDefinitions; + +import com.pages.bsCheckout; +import com.pages.bsConfirmation; +import com.pages.bsHome; +import com.pages.bsSignin; +import io.cucumber.java.Before; +import io.cucumber.java.Scenario; +import io.cucumber.java.en.And; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import org.openqa.selenium.JavascriptExecutor; +import org.testng.Assert; + +import java.util.HashMap; +import java.util.Map; + +public class E2ESteps { + ApplicationHooks hooks ; + private final bsHome bshome; + private final bsSignin bssignin; + private final bsCheckout bschecokout; + private final bsConfirmation bsconfirm ; + public E2ESteps(ApplicationHooks hooks){ + this.hooks = hooks; + bshome = new bsHome(hooks.driver); + bssignin = new bsSignin(hooks.driver); + bschecokout = new bsCheckout(hooks.driver); + bsconfirm = new bsConfirmation(hooks.driver); + } + Scenario sc; + + @Before + public void getScenario(Scenario scenario){ + this.sc = scenario; + } + @Given("User is on home page") + public void user_is_on_home_page() { + JavascriptExecutor jse = (JavascriptExecutor) hooks.driver; + Map deviceInfo = new HashMap<>(); + try{ + deviceInfo = (Map) jse.executeScript("mobile:deviceInfo"); + }catch(Exception e){ + deviceInfo = null; + } + if(deviceInfo !=null && deviceInfo.get("model").toLowerCase().matches(".*(iphone|ipad).*")) + hooks.driver.get(System.getProperties().getProperty("Application_url").replaceAll("localhost","bs-local.com")); + else + hooks.driver.get(System.getProperties().getProperty("Application_url")); + } + @When("User clicks on sign in link") + public void user_clicks_on_sign_in_link() { + bshome.userSignIn(); + } + @And("User enters {string} and {string} and clicks on sign in") + public void SigninDetails(String user, String pass) throws InterruptedException { + bssignin.userCredentials(user,pass); + } + @And("{string} is signed in to the App") + public void user_is_signed_in_to_the_app(String user) { + Boolean success = bshome.verifySignin(); + if(success) System.out.println(user + " signed in successfully"); + else{ + Assert.fail(user+" login failed"); + } + } + @And("User clicks on Apple in Vendor filter") + public void user_clicks_on_apple_in_vendor_filter() { + bshome.searchProduct(); + } + @And("User sorts the search result by Price: Low to High") + public void sorts_the_search_result_by_price_low_to_high() throws InterruptedException { + bshome.sortResult(); + } + @And("User can see the list of Apple products with Price in ascending order") + public void user_can_see_the_list_of_apple_products_with_price_in_ascending_order() { + Map iphonelist = bshome.searchResult(); + //System.out.println("Details of Apple Products:"); + sc.log("Details of Apple Products:"); + //iphonelist.forEach((k,v)->System.out.println(k+" $"+v)); + iphonelist.forEach((k,v)->sc.log(k+" $"+v)); + } + @And("User clicks on Add to cart for the iPhone XR and clicks on CHECKOUT") + public void user_clicks_on_add_to_cart_for_the_i_phone_xr_and_clicks_on_checkout() { + bshome.AddtoCart(); + } + @And("User enters {string},{string},{string},{string},{int} and clicks SUBMIT") + public void user_enters_and_clicks_submit(String first, String last, String add, String state, Integer pin) { + bschecokout.fillAddress(first,last,add,state,pin); + } + @Then("User can see the order id for the product") + public void user_can_see_the_order_id_for_the_product() { + String orderid = bsconfirm.verifyPurchase(); + Assert.assertNotEquals("",orderid,"Order ID confirmation: "+orderid); + sc.log("Order ID confirmation: "+orderid); + } + +} \ No newline at end of file diff --git a/src/test/java/StepDefinitions/OffersSteps.java b/src/test/java/StepDefinitions/OffersSteps.java new file mode 100644 index 0000000..6e52a2f --- /dev/null +++ b/src/test/java/StepDefinitions/OffersSteps.java @@ -0,0 +1,50 @@ +package StepDefinitions; + +import com.pages.bsHome; +import com.pages.bsOffers; +import io.cucumber.java.Before; +import io.cucumber.java.Scenario; +import io.cucumber.java.en.And; +import io.cucumber.java.en.Then; +import org.junit.Assert; + +import java.util.Iterator; +import java.util.List; + +public class OffersSteps { + ApplicationHooks hooks; + private bsHome bshome; + private bsOffers bsoffer; + public OffersSteps(ApplicationHooks hooks){ + this.hooks = hooks; + bshome = new bsHome(hooks.driver); + bsoffer = new bsOffers(hooks.driver); + } + Scenario sc; + + @Before + public void getScenario(Scenario scenario){ + this.sc = scenario; + } + + @And("User Clicks on Offers") + public void user_clicks_on_offers() { + bshome.openOffers(); + } + @Then("User can see offers list for Mumbai location") + public void user_can_see_offers_list_for_mumbai_location() { + List offerDetails = bsoffer.getOffers(); + if(offerDetails.isEmpty()){ + Assert.fail("No offers found for your location"); + }else{ + Iterator t = offerDetails.iterator(); + //System.out.println("Offers for your location:"); + sc.log("Offers for your location:"); + while(t.hasNext()){ + //System.out.println(t.next()); + sc.log((String) t.next()); + } + } + } + +} diff --git a/src/test/java/StepDefinitions/UsersSteps.java b/src/test/java/StepDefinitions/UsersSteps.java new file mode 100644 index 0000000..234ddbf --- /dev/null +++ b/src/test/java/StepDefinitions/UsersSteps.java @@ -0,0 +1,73 @@ +package StepDefinitions; + +import com.pages.bsHome; +import com.pages.bsOrders; +import com.pages.bsSignin; +import io.cucumber.java.Before; +import io.cucumber.java.Scenario; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; + +import java.util.Iterator; +import java.util.List; + +public class UsersSteps { + + public UsersSteps(ApplicationHooks hooks){ + this.hooks = hooks; + bshome = new bsHome(hooks.driver); + bsorder = new bsOrders(hooks.driver); + bssign = new bsSignin(hooks.driver); + } + + ApplicationHooks hooks; + private final bsHome bshome; + private final bsOrders bsorder; + private final bsSignin bssign ; + Scenario sc; + + @Before + public void beforeScenario(Scenario scenario){ + this.sc = scenario; + } + + @Then("Product images are not loaded for the {string}") + public void product_images_are_not_loaded_for_the_user(String user) { + boolean imageload = bshome.verifyimageLoaded(); + if (imageload){ + System.out.println("Product images loaded for user"); + sc.log("Product images loaded for user"); + }else + throw new AssertionError("Product images not loaded for "+ user); + } + @When("User clicks on Orders") + public void user_clicks_on_orders() { + bshome.NavigatetoOrder(); + } + @Then("Existing orders are shown for the {string}") + public void existing_orders_are_shown_for_the_user(String user) { + List orders = bsorder.VerifyOrders(); + if (orders.isEmpty()){ + throw new AssertionError("No orders found for the "+user); + }else{ + //System.out.println("Details of Orders for "+user+":"); + sc.log("Details of Orders for "+user+":"); + Iterator t = orders.iterator(); + while(t.hasNext()){ + //System.out.println(t.next()); + sc.log(t.next().toString()); + } + } + } + @Then("{string} gets Your account has been locked.") + public void gets_your_account_has_been_locked(String user) throws InterruptedException { + boolean verify = bssign.verifyLockedUsersign(); + if(verify){ + //System.out.println(user+" was not able to login"); + sc.log(user+" was not able to login"); + }else + throw new AssertionError("Locked user "+user+" logged in!"); + + } + +} diff --git a/src/test/java/com/pages/bsCheckout.java b/src/test/java/com/pages/bsCheckout.java new file mode 100644 index 0000000..d2b2ed2 --- /dev/null +++ b/src/test/java/com/pages/bsCheckout.java @@ -0,0 +1,45 @@ +package com.pages; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; + +public class bsCheckout { + + WebDriver driver; + WebDriverWait wait; + public bsCheckout(WebDriver driver){ + this.driver = driver; + wait = new WebDriverWait(driver, Duration.ofSeconds(10)); + PageFactory.initElements(driver,this); + } + + @FindBy(id = "firstNameInput") + WebElement txtName; + @FindBy(id="lastNameInput") + WebElement txtLast; + @FindBy(id = "addressLine1Input") + WebElement txtAddress; + @FindBy(id = "provinceInput") + WebElement txtState; + @FindBy(id="postCodeInput") + WebElement txtPostal; + @FindBy(xpath = "//button[text()='Submit']") + WebElement btnSubmit; + + public void fillAddress(String first, String last, String address, String state, Integer pin){ + wait.until(ExpectedConditions.elementToBeClickable(txtName)); + txtName.sendKeys(first); + txtLast.sendKeys(last); + txtAddress.sendKeys(address); + txtState.sendKeys(state); + txtPostal.sendKeys(Integer.toString(pin)); + btnSubmit.submit(); + + } +} diff --git a/src/test/java/com/pages/bsConfirmation.java b/src/test/java/com/pages/bsConfirmation.java new file mode 100644 index 0000000..61400b0 --- /dev/null +++ b/src/test/java/com/pages/bsConfirmation.java @@ -0,0 +1,34 @@ +package com.pages; + +import org.junit.Assert; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; + +public class bsConfirmation { + WebDriver driver; + WebDriverWait wait; + public bsConfirmation(WebDriver driver){ + this.driver = driver; + wait = new WebDriverWait(driver, Duration.ofSeconds(10)); + PageFactory.initElements(driver,this); + } + + @FindBy(xpath="//button[contains(text(),'Continue Shopping')]") + WebElement btnContinue; + @FindBy(xpath ="//div/strong") + WebElement lblOrder; + + public String verifyPurchase(){ + wait.until(ExpectedConditions.elementToBeClickable(btnContinue)); + Assert.assertTrue("Order not placed!",driver.getPageSource().contains("Your Order has been successfully placed.")); + String orderid; + orderid = lblOrder.getAttribute("innerText"); + return orderid; + } +} diff --git a/src/test/java/com/pages/bsHome.java b/src/test/java/com/pages/bsHome.java new file mode 100644 index 0000000..5aa861e --- /dev/null +++ b/src/test/java/com/pages/bsHome.java @@ -0,0 +1,128 @@ +package com.pages; + +import org.junit.Assert; +import org.openqa.selenium.*; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.How; +import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.ui.*; + +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class bsHome { + + WebDriver driver; + WebDriverWait wait; + @FindBy(how = How.XPATH, using="//span[text()='Apple']") + WebElement lblApple ; + @FindBy(xpath="//select") + WebElement ddPrice ; + @FindBy(id="signin") + WebElement lnksignIn; + @FindBy(xpath="//div[text()='Checkout']") + WebElement lnkCheckout; + @FindBy(id = "offers") + WebElement lnkOffers; + @FindBy(id="logout") + WebElement lnkLogout; + @FindBy(xpath = "(//div[@class='shelf-item__thumb']/img)[1]") + WebElement image; + @FindBy(id="orders") + WebElement lnkOrder; + + public bsHome(WebDriver driver){ + this.driver = driver; + wait = new WebDriverWait(driver, Duration.ofSeconds(10)); + PageFactory.initElements(driver,this); + } + public void searchProduct(){ + lblApple.click(); + } + public void sortResult() throws InterruptedException { + Select sort = new Select(ddPrice); + sort.selectByValue("lowestprice"); + Thread.sleep(5000); + + } + public Map searchResult(){ + List iphones = driver.findElements(By.cssSelector("div.shelf-item")); + Assert.assertEquals("9 products not shown as expected",9,iphones.size()); + String title,price; + Map iphoneDetails= new HashMap(); + for (int i = 0;i 0", image); + + boolean loaded = false; + if (result instanceof Boolean) { + loaded = (Boolean) result; + return loaded; + } + return loaded; + } + + public void AddtoCart(){ + WebElement cart = driver.findElement(By.xpath("(//div[text()='Add to cart'])[1]")); + wait.until(ExpectedConditions.elementToBeClickable(cart)); + cart.click(); + wait.until(ExpectedConditions.elementToBeClickable(lnkCheckout)); + lnkCheckout.click(); + } + public void NavigatetoOrder(){ + wait.until(ExpectedConditions.elementToBeClickable(lnkOrder)); + lnkOrder.click(); + } + +} diff --git a/src/test/java/com/pages/bsOffers.java b/src/test/java/com/pages/bsOffers.java new file mode 100644 index 0000000..7e29dfd --- /dev/null +++ b/src/test/java/com/pages/bsOffers.java @@ -0,0 +1,37 @@ +package com.pages; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +public class bsOffers { + private WebDriver driver; + private WebDriverWait wait; + public bsOffers(WebDriver driver){ + this.driver = driver; + wait = new WebDriverWait(driver, Duration.ofSeconds(10)); + PageFactory.initElements(driver,this); + } + + public List getOffers(){ + List offers= null; + List offerDetails = new ArrayList<>(); + try { + wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[contains(@class,'offers-listing')]"))); + offers = driver.findElements(By.xpath("//div[contains(@class,'offers-listing')]/div/div")); + }catch(Exception e){ + return offerDetails; + } + for (WebElement offer : offers){ + offerDetails.add(offer.getText()); + } + return offerDetails; + } +} diff --git a/src/test/java/com/pages/bsOrders.java b/src/test/java/com/pages/bsOrders.java new file mode 100644 index 0000000..4f14a62 --- /dev/null +++ b/src/test/java/com/pages/bsOrders.java @@ -0,0 +1,38 @@ +package com.pages; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +public class bsOrders { + + public bsOrders(WebDriver driver){ + this.driver = driver; + wait = new WebDriverWait(driver, Duration.ofSeconds(10)); + PageFactory.initElements(driver,this); + } + WebDriver driver; + WebDriverWait wait; + + @FindBy(xpath = "//div[@id='__next']//div[contains(@class,'top-medium')]") + WebElement paneOrders; + + public List VerifyOrders(){ + wait.until(ExpectedConditions.visibilityOf(paneOrders)); + List orderTitle = driver.findElements(By.xpath("//strong[text()='Title:']/..")); + List orderPrice = driver.findElements(By.xpath("//span[contains(@class,'price')]")); + List Allorders = new ArrayList<>(); + for(int i=0;i 254) ? message.substring(0, 254) : message; + markTestStatus("failed", reason.replaceAll("[^a-zA-Z0-9._-]", " "), driver); + driver.quit(); + } + +} diff --git a/src/test/java/com/qa/util/CapabilityReader.java b/src/test/java/com/qa/util/CapabilityReader.java new file mode 100644 index 0000000..a300c03 --- /dev/null +++ b/src/test/java/com/qa/util/CapabilityReader.java @@ -0,0 +1,43 @@ +package com.qa.util; + +import Runner.CucumberTest; +import com.browserstack.local.Local; +import org.json.simple.JSONObject; +import org.openqa.selenium.remote.DesiredCapabilities; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public class CapabilityReader { + protected static HashMap browserstackOptions = new HashMap<>(); + + public static synchronized DesiredCapabilities getCapability(Map envCapabilities, JSONObject config) throws Exception { + DesiredCapabilities capabilities = new DesiredCapabilities(); + Iterator it = envCapabilities.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + if (pair.getKey().toString().toLowerCase().contains("envoptions")) { + browserstackOptions = (HashMap) pair.getValue(); + } else + capabilities.setCapability(pair.getKey().toString(), pair.getValue().toString()); + } + Map commonCapabilities = (Map) config.get("capabilities"); + it = commonCapabilities.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + browserstackOptions.put(pair.getKey().toString(), pair.getValue().toString()); + } + // Set the build name for tests + browserstackOptions.put("buildName",CucumberTest.buildname); + if (System.getenv("BROWSERSTACK_BUILD_NAME") != null) { + browserstackOptions.put("buildName", System.getenv("BROWSERSTACK_BUILD_NAME")); + } + + capabilities.setCapability("bstack:options", browserstackOptions); + + return capabilities; + } +} diff --git a/src/test/java/com/qa/util/SuiteAlterer.java b/src/test/java/com/qa/util/SuiteAlterer.java new file mode 100644 index 0000000..383ec0d --- /dev/null +++ b/src/test/java/com/qa/util/SuiteAlterer.java @@ -0,0 +1,63 @@ +package com.qa.util; + +import org.apache.commons.codec.binary.Base64; +import org.json.JSONObject; +import org.testng.IAlterSuiteListener; +import org.testng.xml.XmlSuite; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.List; + +public class SuiteAlterer implements IAlterSuiteListener { + @Override + public void alter(List suites) { + int count=5; + if(System.getProperty("threadCount")==null && System.getProperty("browser-type").equalsIgnoreCase("remote")) { + // This will fetch the parallel_sessions_max_allowed for the account and set the threadCount to it + URL url = null; + try { + url = new URL("https://api.browserstack.com/automate/plan.json"); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + String auth = System.getenv("BROWSERSTACK_USERNAME") + ":" + System.getenv("BROWSERSTACK_ACCESS_KEY"); + byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(StandardCharsets.UTF_8)); + String authHeaderValue = "Basic " + new String(encodedAuth); + HttpURLConnection http = null; + try { + http = (HttpURLConnection) url.openConnection (); + } catch (IOException e) { + e.printStackTrace(); + } + http.setRequestProperty("Authorization", authHeaderValue); + BufferedReader in = null; + try { + in = new BufferedReader(new InputStreamReader(http.getInputStream())); + } catch (IOException e) { + e.printStackTrace(); + } + String inputLine = ""; + StringBuffer response = new StringBuffer(); + while (true) { + try { + if (!((inputLine = in.readLine()) != null)) break; + } catch (IOException e) { + e.printStackTrace(); + } + response.append(inputLine); + } + JSONObject myResponse = new JSONObject(response.toString()); + count = (int) myResponse.get("parallel_sessions_max_allowed"); + }else if(System.getProperty("threadCount")!=null) + //Sets the thread count value via CLI + count = Integer.parseInt(System.getProperty("threadCount")); + XmlSuite suite = suites.get(0); + suite.setDataProviderThreadCount(count); + } +} diff --git a/src/test/resources/Features/E2E.feature b/src/test/resources/Features/E2E.feature new file mode 100644 index 0000000..52d509c --- /dev/null +++ b/src/test/resources/Features/E2E.feature @@ -0,0 +1,19 @@ +#Author: Abdul Qadir Khan +#Date: 06/12/21 +#Description: Demonstrate Cucumber Java framework with BS Automate product +@e2e @regression +Feature: E2E Flow + Scenario Outline: Signed in User makes a Purchase for a product with Price and Vendor filters + Given User is on home page + When User clicks on sign in link + And User enters and and clicks on sign in + And is signed in to the App + And User clicks on Apple in Vendor filter + And User sorts the search result by Price: Low to High + And User can see the list of Apple products with Price in ascending order + And User clicks on Add to cart for the iPhone XR and clicks on CHECKOUT + And User enters ,,
,, and clicks SUBMIT + Then User can see the order id for the product + Examples: + |username|password|first|last|address|state|postal| + |"demouser"|"testingisfun99"|"first"|"last"|"test"|"test state"|1234| \ No newline at end of file diff --git a/src/test/resources/Features/Offers.feature b/src/test/resources/Features/Offers.feature new file mode 100644 index 0000000..b181f7b --- /dev/null +++ b/src/test/resources/Features/Offers.feature @@ -0,0 +1,16 @@ +#Author: Abdul Qadir Khan +#Date: 14/12/21 +#Description: Demonstrate Cucumber Java framework with BS Automate product + @test @regression + Feature: Offers for user in Mumbai area + @offers + Scenario Outline: Signed in user can see promotional offers under Offers for Mumbai Location + Given User is on home page + When User clicks on sign in link + And User enters and and clicks on sign in + And is signed in to the App + And User Clicks on Offers + Then User can see offers list for Mumbai location + Examples: + | username | password | + |"fav_user"|"testingisfun99"| \ No newline at end of file diff --git a/src/test/resources/Features/Users.feature b/src/test/resources/Features/Users.feature new file mode 100644 index 0000000..dc3aec0 --- /dev/null +++ b/src/test/resources/Features/Users.feature @@ -0,0 +1,35 @@ +#Author: Abdul Qadir Khan +#Date: 20/12/21 +#Description: Demonstrate Cucumber Java framework with BS Automate product +@users @regression +Feature: Different Users use cases in BStackDemo + Background: + Given User is on home page + When User clicks on sign in link + + @noimage @fail + Scenario Outline: Login as User with no image loaded + When User enters and and clicks on sign in + And is signed in to the App + Then Product images are not loaded for the + Examples: + | username | password | + |"image_not_loading_user"|"testingisfun99"| + + @orders + Scenario Outline: Login as existing user to verify orders + When User enters and and clicks on sign in + And is signed in to the App + And User clicks on Orders + Then Existing orders are shown for the + Examples: + | username | password | + |"existing_orders_user"|"testingisfun99"| + + @locked + Scenario Outline: Login as a locked User + When User enters and and clicks on sign in + Then gets Your account has been locked. + Examples: + | username | password | + |"locked_user"|"testingisfun99"| diff --git a/src/test/resources/Reports/Report.txt b/src/test/resources/Reports/Report.txt new file mode 100644 index 0000000..86ec0ed --- /dev/null +++ b/src/test/resources/Reports/Report.txt @@ -0,0 +1,3 @@ +Reports will be generated based on timestamp and will have pdf and html formats. +The screenshots are attached in the same directory and can be zipped and shared over email without losing the screenshots in the folder. +These configurations can be changed from the extent.properties file. \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json b/src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json new file mode 100644 index 0000000..801495b --- /dev/null +++ b/src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json @@ -0,0 +1,48 @@ +{ + "server": "hub-cloud.browserstack.com", + "user": "", + "key": "", + + "capabilities": { + "projectName": "Local BDD", + "buildName": "BDD-Local-Parallel-7", + "sessionName": "BS Suite", + "local": "true", + "debug": "true", + "networkLogs": "true", + "consoleLogs": "errors" + }, + + "environments": { + "env1": { + "browser": "Safari", + "browser_version": "15.3", + "envOptions": { + "os": "OS X", + "osVersion": "Monterey" + } + }, + "env2":{ + "browser": "Edge", + "browser_version": "latest", + "envOptions": { + "os": "Windows", + "osVersion": "11" + } + },"env3":{ + "envOptions": { + "osVersion": "15", + "deviceName": "iPhone 13 Pro Max", + "realMobile": "true" + } + }, + "env4":{ + "envOptions": { + "osVersion": "12.0", + "browserName": "samsung", + "deviceName": "Samsung Galaxy S22 Ultra", + "realMobile": "true" + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Local_Parallel/localParallel.testng.xml b/src/test/resources/browserstack/conf/Run_Local_Parallel/localParallel.testng.xml new file mode 100644 index 0000000..fcb49a1 --- /dev/null +++ b/src/test/resources/browserstack/conf/Run_Local_Parallel/localParallel.testng.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json b/src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json new file mode 100644 index 0000000..34f464a --- /dev/null +++ b/src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json @@ -0,0 +1,26 @@ +{ + "server": "hub-cloud.browserstack.com", + "user": "", + "key": "", + + "capabilities": { + "projectName": "Local BDD", + "buildName": "Local_BS_BDD_2", + "sessionName": "Local_Test", + "debug": "true", + "networkLogs": "true", + "local": true , + "consoleLogs":"errors" + }, + + "environments": { + "env": { + "browserName": "Chrome", + "browserVersion": "latest", + "envOptions": { + "os": "Windows", + "osVersion": "11" + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Local_Test/local.testng.xml b/src/test/resources/browserstack/conf/Run_Local_Test/local.testng.xml new file mode 100644 index 0000000..4d85002 --- /dev/null +++ b/src/test/resources/browserstack/conf/Run_Local_Test/local.testng.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json new file mode 100644 index 0000000..a1fc52e --- /dev/null +++ b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json @@ -0,0 +1,48 @@ +{ + "server": "hub-cloud.browserstack.com", + "user": "", + "key": "", + + "capabilities": { + "projectName": "Cucumber Java", + "buildName": "BDDParallel-4", + "sessionName": "BS Suite", + "debug": "true", + "networkLogs": "true", + "seleniumVersion": "4.0.0", + "consoleLogs": "errors" + }, + + "environments": { + "env1": { + "browser": "Edge", + "browser_version": "latest", + "envOptions": { + "os": "Windows", + "osVersion": "11" + } + }, + "env2":{ + "browser": "Safari", + "browser_version": "15.3", + "envOptions": { + "os": "OS X", + "osVersion": "Monterey" + } + },"env3":{ + "envOptions": { + "osVersion": "15", + "deviceName": "iPhone 13 Pro Max", + "realMobile": "true" + } + }, + "env4":{ + "envOptions": { + "osVersion": "12.0", + "browserName": "chrome", + "deviceName": "Samsung Galaxy S22 Ultra", + "realMobile": "true" + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml new file mode 100644 index 0000000..2931e53 --- /dev/null +++ b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json b/src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json new file mode 100644 index 0000000..21be2da --- /dev/null +++ b/src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json @@ -0,0 +1,26 @@ +{ + "server": "hub-cloud.browserstack.com", + "user": "", + "key": "", + + "capabilities": { + "projectName": "Cucumber Java", + "buildName": "BStackDemo BDD", + "sessionName": "BS Suite", + "debug": "true", + "networkLogs": "true", + "seleniumVersion": "4.0.0", + "consoleLogs": "errors" + }, + + "environments": { + "env": { + "browser": "Chrome", + "browser_version": "latest", + "envOptions": { + "os": "OS X", + "osVersion": "Monterey" + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Single_Test/single.testng.xml b/src/test/resources/browserstack/conf/Run_Single_Test/single.testng.xml new file mode 100644 index 0000000..56d05e6 --- /dev/null +++ b/src/test/resources/browserstack/conf/Run_Single_Test/single.testng.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/extent-config.xml b/src/test/resources/extent-config.xml new file mode 100644 index 0000000..8b640a6 --- /dev/null +++ b/src/test/resources/extent-config.xml @@ -0,0 +1,41 @@ + + + + + standard + + + UTF-8 + + + https + + + Cucumber TestNG Report + + + BS BDD Demo Report + + + yyyy-MM-dd + + + HH:mm:ss + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/extent.properties b/src/test/resources/extent.properties new file mode 100644 index 0000000..4c7f509 --- /dev/null +++ b/src/test/resources/extent.properties @@ -0,0 +1,14 @@ +extent.reporter.spark.start=true +extent.reporter.spark.config=src/test/resources/extent-config.xml + +basefolder.name=src/test/resources/Reports/SparkReport +basefolder.datetimepattern=MMM-d-YY HH-mm-ss + +extent.reporter.spark.vieworder=dashboard,test,category,exception,author,device,log +extent.reporter.spark.out=Reports/Spark.html + +screenshot.dir=Screenshots/ +screenshot.rel.path=../Screenshots/ + +extent.reporter.pdf.start=true +extent.reporter.pdf.out=Reports/Extent.pdf \ No newline at end of file diff --git a/testng.xml b/testng.xml new file mode 100644 index 0000000..df49ee9 --- /dev/null +++ b/testng.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file From a0fa6667b3625547e24a6a0a26557ca1a8c752fe Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Wed, 16 Aug 2023 15:29:55 +0530 Subject: [PATCH 02/29] added java sdk --- browserstack.yml | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ pom.xml | 28 +++++++++++++----- 2 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 browserstack.yml diff --git a/browserstack.yml b/browserstack.yml new file mode 100644 index 0000000..577087d --- /dev/null +++ b/browserstack.yml @@ -0,0 +1,75 @@ +# ============================= +# Set BrowserStack Credentials +# ============================= +# Add your BrowserStack userName and accessKey here or set BROWSERSTACK_USERNAME and +# BROWSERSTACK_ACCESS_KEY as env variables +userName: YOUR_USERNAME +accessKey: YOUR_ACCESS_KEY + +# ====================== +# BrowserStack Reporting +# ====================== +# The following capabilities are used to set up reporting on BrowserStack: +# Set 'projectName' to the name of your project. Example, Marketing Website +projectName: BrowserStack Sample +# Set `buildName` as the name of the job / testsuite being run +buildName: browserstack build +# `buildIdentifier` is a unique id to differentiate every execution that gets appended to +# buildName. Choose your buildIdentifier format from the available expressions: +# ${BUILD_NUMBER} (Default): Generates an incremental counter with every execution +# ${DATE_TIME}: Generates a Timestamp with every execution. Eg. 05-Nov-19:30 +# Read more about buildIdentifiers here -> https://www.browserstack.com/docs/automate/selenium/organize-tests +buildIdentifier: '#${BUILD_NUMBER}' # Supports strings along with either/both ${expression} + +# ======================================= +# Platforms (Browsers / Devices to test) +# ======================================= +# Platforms object contains all the browser / device combinations you want to test on. +# Entire list available here -> (https://www.browserstack.com/list-of-browsers-and-platforms/automate) +platforms: + - os: OS X + osVersion: Big Sur + browserName: Chrome + browserVersion: latest + - os: Windows + osVersion: 10 + browserName: Edge + browserVersion: latest + - deviceName: Samsung Galaxy S22 Ultra + browserName: chrome # Try 'samsung' for Samsung browser + osVersion: 12.0 + +# ======================= +# Parallels per Platform +# ======================= +# The number of parallel threads to be used for each platform set. +# BrowserStack's SDK runner will select the best strategy based on the configured value +# +# Example 1 - If you have configured 3 platforms and set `parallelsPerPlatform` as 2, a total of 6 (2 * 3) parallel threads will be used on BrowserStack +# +# Example 2 - If you have configured 1 platform and set `parallelsPerPlatform` as 5, a total of 5 (1 * 5) parallel threads will be used on BrowserStack +parallelsPerPlatform: 1 + +source: java:intellij:v1.1.2 +staticWebDriver: true +# ========================================== +# BrowserStack Local +# (For localhost, staging/private websites) +# ========================================== +# Set browserStackLocal to true if your website under test is not accessible publicly over the internet +# Learn more about how BrowserStack Local works here -> https://www.browserstack.com/docs/automate/selenium/local-testing-introduction +browserstackLocal: false # (Default false) + +# Options to be passed to BrowserStack local in-case of advanced configurations +# browserStackLocalOptions: + # localIdentifier: # (Default: null) Needed if you need to run multiple instances of local. + # forceLocal: true # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. + # Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections + +# =================== +# Debugging features +# =================== +debug: false # # Set to true if you need screenshots for every selenium command ran +networkLogs: false # Set to true to enable HAR logs capturing +consoleLogs: errors # Remote browser's console debug levels to be printed (Default: errors) +# Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) diff --git a/pom.xml b/pom.xml index edddd83..30b7fec 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,4 @@ - - + 4.0.0 org.BrowserStack @@ -13,10 +10,9 @@ UTF-8 1.8 1.8 + 3.0.0-M5 7.4.0 3.0.0-M5 - - https://bstackdemo.com/ @@ -79,6 +75,12 @@ okhttp 5.0.0-alpha.10 + + com.browserstack + browserstack-java-sdk + LATEST + compile + @@ -90,13 +92,25 @@ ${suiteXmlFile} - + -javaagent:"${com.browserstack:browserstack-java-sdk:jar}" ${browser-type} ${Application_url} + + maven-dependency-plugin + 3.3.0 + + + getClasspathFilenames + + properties + + + + From 3e1be76348e3768eff48c683268b6e23c1505199 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Fri, 18 Aug 2023 19:57:19 +0530 Subject: [PATCH 03/29] refactor the xml --- .../Run_Local_Parallel/local.parallel.json | 48 ------------------- .../localParallel.testng.xml | 32 ------------- .../conf/Run_Local_Test/local.conf.json | 26 ---------- .../conf/Run_Local_Test/local.testng.xml | 14 ------ .../conf/Run_Parallel_Test/parallel.conf.json | 22 --------- .../Run_Parallel_Test/parallel.testng.xml | 18 ------- 6 files changed, 160 deletions(-) delete mode 100644 src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json delete mode 100644 src/test/resources/browserstack/conf/Run_Local_Parallel/localParallel.testng.xml delete mode 100644 src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json delete mode 100644 src/test/resources/browserstack/conf/Run_Local_Test/local.testng.xml diff --git a/src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json b/src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json deleted file mode 100644 index 801495b..0000000 --- a/src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "server": "hub-cloud.browserstack.com", - "user": "", - "key": "", - - "capabilities": { - "projectName": "Local BDD", - "buildName": "BDD-Local-Parallel-7", - "sessionName": "BS Suite", - "local": "true", - "debug": "true", - "networkLogs": "true", - "consoleLogs": "errors" - }, - - "environments": { - "env1": { - "browser": "Safari", - "browser_version": "15.3", - "envOptions": { - "os": "OS X", - "osVersion": "Monterey" - } - }, - "env2":{ - "browser": "Edge", - "browser_version": "latest", - "envOptions": { - "os": "Windows", - "osVersion": "11" - } - },"env3":{ - "envOptions": { - "osVersion": "15", - "deviceName": "iPhone 13 Pro Max", - "realMobile": "true" - } - }, - "env4":{ - "envOptions": { - "osVersion": "12.0", - "browserName": "samsung", - "deviceName": "Samsung Galaxy S22 Ultra", - "realMobile": "true" - } - } - } -} \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Local_Parallel/localParallel.testng.xml b/src/test/resources/browserstack/conf/Run_Local_Parallel/localParallel.testng.xml deleted file mode 100644 index fcb49a1..0000000 --- a/src/test/resources/browserstack/conf/Run_Local_Parallel/localParallel.testng.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json b/src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json deleted file mode 100644 index 34f464a..0000000 --- a/src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "server": "hub-cloud.browserstack.com", - "user": "", - "key": "", - - "capabilities": { - "projectName": "Local BDD", - "buildName": "Local_BS_BDD_2", - "sessionName": "Local_Test", - "debug": "true", - "networkLogs": "true", - "local": true , - "consoleLogs":"errors" - }, - - "environments": { - "env": { - "browserName": "Chrome", - "browserVersion": "latest", - "envOptions": { - "os": "Windows", - "osVersion": "11" - } - } - } -} \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Local_Test/local.testng.xml b/src/test/resources/browserstack/conf/Run_Local_Test/local.testng.xml deleted file mode 100644 index 4d85002..0000000 --- a/src/test/resources/browserstack/conf/Run_Local_Test/local.testng.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json index a1fc52e..0fdf3d3 100644 --- a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json +++ b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json @@ -21,28 +21,6 @@ "os": "Windows", "osVersion": "11" } - }, - "env2":{ - "browser": "Safari", - "browser_version": "15.3", - "envOptions": { - "os": "OS X", - "osVersion": "Monterey" - } - },"env3":{ - "envOptions": { - "osVersion": "15", - "deviceName": "iPhone 13 Pro Max", - "realMobile": "true" - } - }, - "env4":{ - "envOptions": { - "osVersion": "12.0", - "browserName": "chrome", - "deviceName": "Samsung Galaxy S22 Ultra", - "realMobile": "true" - } } } } \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml index 2931e53..b21f994 100644 --- a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml +++ b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml @@ -11,22 +11,4 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file From e0a50e73f2c78a7ef5750b73e012d0ad22c60f3c Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Fri, 18 Aug 2023 20:02:25 +0530 Subject: [PATCH 04/29] Changes to config --- browserstack.yml | 12 ++++++--- pom.xml | 27 +++++-------------- src/test/java/Runner/CucumberTest.java | 3 ++- .../conf/Run_Parallel_Test/parallel.conf.json | 20 +++++++++----- .../Run_Parallel_Test/parallel.testng.xml | 2 +- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/browserstack.yml b/browserstack.yml index 577087d..c954063 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -38,6 +38,8 @@ platforms: - deviceName: Samsung Galaxy S22 Ultra browserName: chrome # Try 'samsung' for Samsung browser osVersion: 12.0 + - deviceName: iPhone 13 Pro Max + osVersion: 15 # ======================= # Parallels per Platform @@ -49,8 +51,8 @@ platforms: # # Example 2 - If you have configured 1 platform and set `parallelsPerPlatform` as 5, a total of 5 (1 * 5) parallel threads will be used on BrowserStack parallelsPerPlatform: 1 - -source: java:intellij:v1.1.2 +framework: cucumber-testng #junit,testng,java,cucumber-testng,serenity,cucumber-junit +source: cucumber-testng:intellij:v1.1.2 staticWebDriver: true # ========================================== # BrowserStack Local @@ -58,7 +60,7 @@ staticWebDriver: true # ========================================== # Set browserStackLocal to true if your website under test is not accessible publicly over the internet # Learn more about how BrowserStack Local works here -> https://www.browserstack.com/docs/automate/selenium/local-testing-introduction -browserstackLocal: false # (Default false) +browserstackLocal: true # (Default false) # Options to be passed to BrowserStack local in-case of advanced configurations # browserStackLocalOptions: @@ -73,3 +75,7 @@ debug: false # # Set to true if you need screenshots for every seleniu networkLogs: false # Set to true to enable HAR logs capturing consoleLogs: errors # Remote browser's console debug levels to be printed (Default: errors) # Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) + +# Test Observability is an intelligent test reporting & debugging product. It collects data using the SDK. Read more about what data is collected at https://www.browserstack.com/docs/test-observability/references/terms-and-conditions +# Visit observability.browserstack.com to see your test reports and insights. To disable test observability, specify `testObservability: false` in the key below. +testObservability: true diff --git a/pom.xml b/pom.xml index 30b7fec..91c9cbe 100644 --- a/pom.xml +++ b/pom.xml @@ -76,11 +76,11 @@ 5.0.0-alpha.10 - com.browserstack - browserstack-java-sdk - LATEST - compile - + com.browserstack + browserstack-java-sdk + LATEST + compile + @@ -129,27 +129,12 @@ src/test/resources/browserstack/conf/Run_Single_Test/single.testng.xml - - suite-cross-bs - - remote - src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml - - local-bs remote http://localhost:3000/ - src/test/resources/browserstack/conf/Run_Local_Test/local.testng.xml - - - - local-cross-bs - - remote - http://localhost:3000/ - src/test/resources/browserstack/conf/Run_Local_Parallel/localParallel.testng.xml + src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml diff --git a/src/test/java/Runner/CucumberTest.java b/src/test/java/Runner/CucumberTest.java index fa7602b..2c56452 100644 --- a/src/test/java/Runner/CucumberTest.java +++ b/src/test/java/Runner/CucumberTest.java @@ -120,7 +120,8 @@ public Object[][] scenarios(){ @AfterClass(alwaysRun = true) public void tearDownClass() { - testNGCucumberRunner.finish(); + //testNGCucumberRunner.finish(); //Conflict with the SDK Runner.CucumberTest.tearDownClass + // NoSuchMethod org.yaml.snakeyaml.constructor.Constructor. } @AfterSuite public void shutLocal() throws Exception { diff --git a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json index 0fdf3d3..9f3c6f9 100644 --- a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json +++ b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json @@ -15,12 +15,20 @@ "environments": { "env1": { - "browser": "Edge", - "browser_version": "latest", - "envOptions": { - "os": "Windows", - "osVersion": "11" - } + "browser": "Edge", + "browser_version": "latest", + "envOptions": { + "os": "Windows", + "osVersion": "11" + } + }, + "env2":{ + "browser": "Safari", + "browser_version": "15.3", + "envOptions": { + "os": "OS X", + "osVersion": "Monterey" + } } } } \ No newline at end of file diff --git a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml index b21f994..28058f2 100644 --- a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml +++ b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.testng.xml @@ -1,6 +1,6 @@ - + From 679d751c67fc29dfb286fcd62aed42ba9286934a Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:30:18 +0530 Subject: [PATCH 05/29] Update README.md --- README.md | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 6d84572..084f399 100644 --- a/README.md +++ b/README.md @@ -67,18 +67,16 @@ This repository contains the following Cucumber Scenario tests: - [BrowserStack](#browserstack) --- -## Configuring the maximum parallel test threads for this repository +## Configuring the maximum parallel test threads for this repository[SDK] -For all the parallel run configuration profiles, tests will run with max parallels in your BrowserStack account, or you can configure the parallel test threads from the terminal +For all the parallel run configuration profiles, tests will run with max parallels in your BrowserStack account, or you can configure the parallel test threads from the browserstack.yml file - "-DthreadCount=xx" + parallelsPerPlatform |BrowserStack| onPrem | |--|--| | browserstack/conf/Run_Single_Test/single.testng.xml|testng.xml -browserstack/conf/Run_Parallel_Test/parallel.testng.xml | | -|browserstack/conf/Run_Local_Test/local.testng.xml| -|browserstack/conf/Run_Parallel_Test/parallel.testng.xml +browserstack/conf/Run_Parallel_Test/parallel.testng.xml | @@ -142,11 +140,8 @@ This infrastructure points to running the tests on your own machine using any br set BROWSERSTACK_ACCESS_KEY= ``` - Alternatively, you can also hardcode username and access_key objects in the respective json files: - - [single.conf.json](src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json) file - - [parallel.conf.json](src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json) file - - [local.conf.json](src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json) file - - [local.parallel.json](src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json) file + Alternatively, you can also hardcode username and access_key objects in the respective file: + - [browserstack.yml](browserstack.yml) file Note: @@ -158,7 +153,7 @@ Note: ### Run a specific test/entire test suite in parallel on a single BrowserStack browser -In this section, we will run the tests in parallel on a single browser on Browserstack. Refer to [single.conf.json](src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json) file to change test capabilities for this configuration. +In this section, we will run the tests in parallel on a single browser on Browserstack. Refer to [browserstack.yml](browserstack.yml) file to change test capabilities for this configuration. - How to run the test? @@ -178,13 +173,13 @@ In this section, we will run the tests in parallel on a single browser on Browse - Note: By default, this execution would run maximum test threads based on the parallel quota on your BrowserStack Account. Thread count can be configured as below based on your requirements. ```sh - mvn test -P scenario-bs "-Dcucumber.filter.tags=@regresssion" "-DthreadCount=3" + mvn test -P scenario-bs "-Dcucumber.filter.tags=@regresssion" ``` ### Run the entire test suite in parallel on multiple BrowserStack browsers -In this section, we will run the tests in parallel on multiple browsers on Browserstack. Refer to the [parallel.conf.json](src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json) file to change test capabilities for this configuration. +In this section, we will run the tests in parallel on multiple browsers on Browserstack. Refer to the [browserstack.yml](browserstack.yml) file to change test capabilities for this configuration. - How to run the test? @@ -192,7 +187,7 @@ In this section, we will run the tests in parallel on multiple browsers on Brows Maven: ```sh - mvn test -P suite-cross-bs + mvn test -P scenario-bs ``` You can mention any scenario from the feature files using the `-Dcucumber.filter.tags`, tags defined at Feature level Eg. `@users` will run all the scenarios in the [Users Feature](src/test/resources/Features/Users.feature) file in parallel. Likewise `@regression` will run all the scenarios from all the Feature files in parallel across multiple browsers/devices. @@ -206,7 +201,7 @@ In this section, we will run the tests in parallel on multiple browsers on Brows git clone https://github.com/browserstack/browserstack-demo-app ``` - Please follow the README.md on the BrowserStack demo application repository to install and start the dev server on localhost. -- In this section, we will run a single test case to test the BrowserStack Demo app hosted on your local machine i.e. localhost. Refer to the [local.conf.json](src/test/resources/browserstack/conf/Run_Local_Test/local.conf.json) file to change test capabilities for this configuration. +- In this section, we will run a single test case to test the BrowserStack Demo app hosted on your local machine i.e. localhost. Refer to the [browserstack.yml](browserstack.yml) file to change test capabilities for this configuration. - Note: You may need to provide additional BrowserStackLocal arguments to successfully connect your localhost environment with BrowserStack infrastructure. (e.g if you are behind firewalls, proxy or VPN). - Further details for successfully creating a BrowserStackLocal connection can be found here: @@ -234,7 +229,7 @@ You can mention any scenario from the feature files using the `-Dcucumber.filter ### [Web application hosted on internal environment] Run the entire test suite in parallel on multiple BrowserStack browser using BrowserStackLocal -In this section, we will run the test cases to test the internally hosted website in parallel on multiple browsers/devices on Browserstack. Refer to the [local.parallel.json](src/test/resources/browserstack/conf/Run_Local_Parallel/local.parallel.json) file to change test capabilities for this configuration. +In this section, we will run the test cases to test the internally hosted website in parallel on multiple browsers/devices on Browserstack. Refer to the [browserstack.yml](browserstack.yml) file to change test capabilities for this configuration. - How to run the test? @@ -242,7 +237,7 @@ In this section, we will run the test cases to test the internally hosted websit Maven: ```sh - mvn test -P local-cross-bs + mvn test -P local-bs ``` - Output @@ -251,7 +246,7 @@ In this section, we will run the test cases to test the internally hosted websit - Note: By default, this execution would run maximum test threads based on the parallel quota on your BrowserStack Account. Thread count can be configured as below based on your requirements. ```sh - mvn test -P local-cross-bs "-Dcucumber.filter.tags=@e2e" "-DthreadCount=3" + mvn test -P local-bs "-Dcucumber.filter.tags=@e2e" ``` From 2e9bb91c22bfb25b8702a5d324a773c1f61a1a58 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Thu, 5 Oct 2023 14:37:14 +0530 Subject: [PATCH 06/29] e Please enter the commit message for your changes. Lines starting :wq Removed listeners and js executors duplicates :wq --- browserstack.yml | 4 ++-- src/test/java/Runner/CucumberTest.java | 4 ++-- src/test/java/StepDefinitions/ApplicationHooks.java | 8 -------- .../java/com/qa/util/BrowserstackTestStatusListener.java | 2 +- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/browserstack.yml b/browserstack.yml index c954063..8c8cd84 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -63,9 +63,9 @@ staticWebDriver: true browserstackLocal: true # (Default false) # Options to be passed to BrowserStack local in-case of advanced configurations -# browserStackLocalOptions: +browserStackLocalOptions: # localIdentifier: # (Default: null) Needed if you need to run multiple instances of local. - # forceLocal: true # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. + forceLocal: true # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. # Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections # =================== diff --git a/src/test/java/Runner/CucumberTest.java b/src/test/java/Runner/CucumberTest.java index 2c56452..84f5e57 100644 --- a/src/test/java/Runner/CucumberTest.java +++ b/src/test/java/Runner/CucumberTest.java @@ -109,8 +109,8 @@ public static synchronized WebDriver getDriver(){ @Test(dataProvider = "scenarios") public void scenario(PickleWrapper pickleWrapper, FeatureWrapper featureWrapper) throws MalformedURLException { JavascriptExecutor jse = (JavascriptExecutor)getDriver(); - if(System.getProperty("browser-type").equalsIgnoreCase("remote")) - jse.executeScript("browserstack_executor: {\"action\": \"setSessionName\", \"arguments\": {\"name\":\" "+ pickleWrapper.getPickle().getName() +" \" }}"); + //if(System.getProperty("browser-type").equalsIgnoreCase("remote")) + //jse.executeScript("browserstack_executor: {\"action\": \"setSessionName\", \"arguments\": {\"name\":\" "+ pickleWrapper.getPickle().getName() +" \" }}"); testNGCucumberRunner.runScenario(pickleWrapper.getPickle()); } @DataProvider(parallel = true) diff --git a/src/test/java/StepDefinitions/ApplicationHooks.java b/src/test/java/StepDefinitions/ApplicationHooks.java index 153b056..99f5eb1 100644 --- a/src/test/java/StepDefinitions/ApplicationHooks.java +++ b/src/test/java/StepDefinitions/ApplicationHooks.java @@ -17,12 +17,4 @@ public void screenshot(Scenario sc){ sc.attach(sourcePath,"image/png",screenshotName); } - private void markTestStatus(String status, String reason, WebDriver driver) { - try { - JavascriptExecutor jse = (JavascriptExecutor) driver; - jse.executeScript("browserstack_executor: {\"action\": \"setSessionStatus\", \"arguments\": {\"status\": \"" + status + "\", \"reason\": \"" + reason + "\"}}"); - } catch (Exception e) { - System.out.print("Error executing javascript" + e); - } - } } diff --git a/src/test/java/com/qa/util/BrowserstackTestStatusListener.java b/src/test/java/com/qa/util/BrowserstackTestStatusListener.java index 77ed674..79b324a 100644 --- a/src/test/java/com/qa/util/BrowserstackTestStatusListener.java +++ b/src/test/java/com/qa/util/BrowserstackTestStatusListener.java @@ -11,7 +11,7 @@ public class BrowserstackTestStatusListener implements ITestListener { private void markTestStatus(String status, String reason, WebDriver driver) { try { JavascriptExecutor jse = (JavascriptExecutor) driver; - jse.executeScript("browserstack_executor: {\"action\": \"setSessionStatus\", \"arguments\": {\"status\": \"" + status + "\", \"reason\": \"" + reason + "\"}}"); + //jse.executeScript("browserstack_executor: {\"action\": \"setSessionStatus\", \"arguments\": {\"status\": \"" + status + "\", \"reason\": \"" + reason + "\"}}"); } catch (Exception e) { System.out.print("Error executing javascript" + e); } From f9915f03cedcde23eda446a2e8dbfabfe3c3c8dc Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Mon, 29 Jul 2024 13:29:03 +0530 Subject: [PATCH 07/29] a11y o11y --- browserstack.yml | 30 +++++++++++++++++++------- pom.xml | 11 +++++----- src/test/java/Runner/CucumberTest.java | 5 +++-- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/browserstack.yml b/browserstack.yml index 8c8cd84..e532ead 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -11,7 +11,7 @@ accessKey: YOUR_ACCESS_KEY # ====================== # The following capabilities are used to set up reporting on BrowserStack: # Set 'projectName' to the name of your project. Example, Marketing Website -projectName: BrowserStack Sample +projectName: check QG on #BrowserStack Sample # Automate - Map SDK # Set `buildName` as the name of the job / testsuite being run buildName: browserstack build # `buildIdentifier` is a unique id to differentiate every execution that gets appended to @@ -20,7 +20,8 @@ buildName: browserstack build # ${DATE_TIME}: Generates a Timestamp with every execution. Eg. 05-Nov-19:30 # Read more about buildIdentifiers here -> https://www.browserstack.com/docs/automate/selenium/organize-tests buildIdentifier: '#${BUILD_NUMBER}' # Supports strings along with either/both ${expression} - +buildTag: "regresson" +selfHeal: true # ======================================= # Platforms (Browsers / Devices to test) # ======================================= @@ -31,16 +32,19 @@ platforms: osVersion: Big Sur browserName: Chrome browserVersion: latest + accessibility: true - os: Windows osVersion: 10 browserName: Edge browserVersion: latest + accessibility: false - deviceName: Samsung Galaxy S22 Ultra browserName: chrome # Try 'samsung' for Samsung browser osVersion: 12.0 + accessibility: false - deviceName: iPhone 13 Pro Max osVersion: 15 - + accessibility: false # ======================= # Parallels per Platform # ======================= @@ -50,32 +54,42 @@ platforms: # Example 1 - If you have configured 3 platforms and set `parallelsPerPlatform` as 2, a total of 6 (2 * 3) parallel threads will be used on BrowserStack # # Example 2 - If you have configured 1 platform and set `parallelsPerPlatform` as 5, a total of 5 (1 * 5) parallel threads will be used on BrowserStack -parallelsPerPlatform: 1 +parallelsPerPlatform: 3 framework: cucumber-testng #junit,testng,java,cucumber-testng,serenity,cucumber-junit source: cucumber-testng:intellij:v1.1.2 staticWebDriver: true +browserstackAutomation: true # ========================================== # BrowserStack Local # (For localhost, staging/private websites) # ========================================== # Set browserStackLocal to true if your website under test is not accessible publicly over the internet # Learn more about how BrowserStack Local works here -> https://www.browserstack.com/docs/automate/selenium/local-testing-introduction -browserstackLocal: true # (Default false) +browserstackLocal: false # (Default false) # Options to be passed to BrowserStack local in-case of advanced configurations browserStackLocalOptions: # localIdentifier: # (Default: null) Needed if you need to run multiple instances of local. - forceLocal: true # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. + forceLocal: false # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. # Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections # =================== # Debugging features # =================== -debug: false # # Set to true if you need screenshots for every selenium command ran -networkLogs: false # Set to true to enable HAR logs capturing +debug: true # # Set to true if you need screenshots for every selenium command ran +networkLogs: true # Set to true to enable HAR logs capturing consoleLogs: errors # Remote browser's console debug levels to be printed (Default: errors) # Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) # Test Observability is an intelligent test reporting & debugging product. It collects data using the SDK. Read more about what data is collected at https://www.browserstack.com/docs/test-observability/references/terms-and-conditions # Visit observability.browserstack.com to see your test reports and insights. To disable test observability, specify `testObservability: false` in the key below. testObservability: true + +accessibilityOptions: + wcagVersion: wcag21aaa # Default: wcag21aa + includeIssueType: + bestPractice: true # Default: false + needsReview: true # Default: true + experimental: true # Default: true + #includeTagsInTestingScope: [ '@users' ] # Tags for test cases to include + #excludeTagsInTestingScope: [ 'old', 'deprecated' ] # Tags for test cases to exclude \ No newline at end of file diff --git a/pom.xml b/pom.xml index 91c9cbe..70dc3b2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 1.0-SNAPSHOT - 7.2.2 + 7.3.0 UTF-8 1.8 1.8 @@ -43,17 +43,18 @@ org.seleniumhq.selenium selenium-java - 4.1.0 + 4.22.0 io.github.bonigarcia webdrivermanager - 5.0.3 + LATEST + test commons-io commons-io - 2.11.0 + LATEST tech.grasshopper @@ -118,7 +119,7 @@ scenario-onprem - chrome + firefox testng.xml diff --git a/src/test/java/Runner/CucumberTest.java b/src/test/java/Runner/CucumberTest.java index 84f5e57..5f0ae7e 100644 --- a/src/test/java/Runner/CucumberTest.java +++ b/src/test/java/Runner/CucumberTest.java @@ -28,8 +28,9 @@ import java.util.Map; @CucumberOptions(features = "src/test/resources/Features", glue = {"StepDefinitions"}, - plugin = {"pretty", - "com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"}, + plugin = {"pretty","json:target/cucumber-reports/Cucumber.json" + //"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:" + }, monochrome = true ) @Listeners(BrowserstackTestStatusListener.class) From 2ef6a9e4c1217f1927a70074f03ebbd685584d8a Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Tue, 10 Sep 2024 12:31:25 +0530 Subject: [PATCH 08/29] lighthouse and percy --- browserstack.yml | 17 ++++++++++------- src/test/java/StepDefinitions/E2ESteps.java | 5 +++++ .../qa/util/BrowserstackTestStatusListener.java | 2 +- src/test/resources/Features/E2E.feature | 2 +- src/test/resources/Features/Users.feature | 6 +++--- .../conf/Run_Parallel_Test/parallel.conf.json | 1 - .../conf/Run_Single_Test/single.conf.json | 1 - 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/browserstack.yml b/browserstack.yml index e532ead..7c6bc90 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -11,7 +11,7 @@ accessKey: YOUR_ACCESS_KEY # ====================== # The following capabilities are used to set up reporting on BrowserStack: # Set 'projectName' to the name of your project. Example, Marketing Website -projectName: check QG on #BrowserStack Sample # Automate - Map SDK +projectName: BrowserStack Sample # Automate - Map SDK # Set `buildName` as the name of the job / testsuite being run buildName: browserstack build # `buildIdentifier` is a unique id to differentiate every execution that gets appended to @@ -54,7 +54,7 @@ platforms: # Example 1 - If you have configured 3 platforms and set `parallelsPerPlatform` as 2, a total of 6 (2 * 3) parallel threads will be used on BrowserStack # # Example 2 - If you have configured 1 platform and set `parallelsPerPlatform` as 5, a total of 5 (1 * 5) parallel threads will be used on BrowserStack -parallelsPerPlatform: 3 +parallelsPerPlatform: 2 framework: cucumber-testng #junit,testng,java,cucumber-testng,serenity,cucumber-junit source: cucumber-testng:intellij:v1.1.2 staticWebDriver: true @@ -70,9 +70,9 @@ browserstackLocal: false # (Default false) # Options to be passed to BrowserStack local in-case of advanced configurations browserStackLocalOptions: # localIdentifier: # (Default: null) Needed if you need to run multiple instances of local. - forceLocal: false # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. + # forceLocal: false # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. # Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections - + # =================== # Debugging features # =================== @@ -84,12 +84,15 @@ consoleLogs: errors # Remote browser's console debug levels to be print # Test Observability is an intelligent test reporting & debugging product. It collects data using the SDK. Read more about what data is collected at https://www.browserstack.com/docs/test-observability/references/terms-and-conditions # Visit observability.browserstack.com to see your test reports and insights. To disable test observability, specify `testObservability: false` in the key below. testObservability: true - accessibilityOptions: wcagVersion: wcag21aaa # Default: wcag21aa includeIssueType: bestPractice: true # Default: false needsReview: true # Default: true experimental: true # Default: true - #includeTagsInTestingScope: [ '@users' ] # Tags for test cases to include - #excludeTagsInTestingScope: [ 'old', 'deprecated' ] # Tags for test cases to exclude \ No newline at end of file + #includeTagsInTestingScope: [ '@users' ] # Tags for test cases to include + #excludeTagsInTestingScope: [ 'old', 'deprecated' ] # Tags for test cases to exclude + +performance: assert +percy: true +percyCaptureMode: auto \ No newline at end of file diff --git a/src/test/java/StepDefinitions/E2ESteps.java b/src/test/java/StepDefinitions/E2ESteps.java index 6295f58..15515a0 100644 --- a/src/test/java/StepDefinitions/E2ESteps.java +++ b/src/test/java/StepDefinitions/E2ESteps.java @@ -48,6 +48,11 @@ public void user_is_on_home_page() { hooks.driver.get(System.getProperties().getProperty("Application_url").replaceAll("localhost","bs-local.com")); else hooks.driver.get(System.getProperties().getProperty("Application_url")); + + jse.executeScript("browserstack_executor: {\"action\":\"lighthouseAudit\",\"arguments\":{\"url\":\""+hooks.driver.getCurrentUrl()+"\",\"executorOutput\":\"json\"}}"); + + jse.executeScript("browserstack_executor: {\"action\":\"lighthouseAudit\",\"arguments\":{\"url\":\""+hooks.driver.getCurrentUrl()+"\",\"assertResult\":{\"categories\":{\"performance\":40,\"best-practices\":50},\"metrics\":{\"first-contentful-paint\":{\"moreThan\":50,\"metricUnit\":\"score\"},\"largest-contentful-paint\":{\"lessThan\":4000,\"metricUnit\":\"numeric\"},\"total-blocking-time\":{\"lessThan\":600,\"metricUnit\":\"numeric\"},\"cumulative-layout-shift\":{\"moreThan\":50,\"metricUnit\":\"score\"}}}}}"); + } @When("User clicks on sign in link") public void user_clicks_on_sign_in_link() { diff --git a/src/test/java/com/qa/util/BrowserstackTestStatusListener.java b/src/test/java/com/qa/util/BrowserstackTestStatusListener.java index 79b324a..77ed674 100644 --- a/src/test/java/com/qa/util/BrowserstackTestStatusListener.java +++ b/src/test/java/com/qa/util/BrowserstackTestStatusListener.java @@ -11,7 +11,7 @@ public class BrowserstackTestStatusListener implements ITestListener { private void markTestStatus(String status, String reason, WebDriver driver) { try { JavascriptExecutor jse = (JavascriptExecutor) driver; - //jse.executeScript("browserstack_executor: {\"action\": \"setSessionStatus\", \"arguments\": {\"status\": \"" + status + "\", \"reason\": \"" + reason + "\"}}"); + jse.executeScript("browserstack_executor: {\"action\": \"setSessionStatus\", \"arguments\": {\"status\": \"" + status + "\", \"reason\": \"" + reason + "\"}}"); } catch (Exception e) { System.out.print("Error executing javascript" + e); } diff --git a/src/test/resources/Features/E2E.feature b/src/test/resources/Features/E2E.feature index 52d509c..f18735d 100644 --- a/src/test/resources/Features/E2E.feature +++ b/src/test/resources/Features/E2E.feature @@ -3,7 +3,7 @@ #Description: Demonstrate Cucumber Java framework with BS Automate product @e2e @regression Feature: E2E Flow - Scenario Outline: Signed in User makes a Purchase for a product with Price and Vendor filters + Scenario Outline: TC-1480 Signed in User makes a Purchase for a product with Price and Vendor filters Given User is on home page When User clicks on sign in link And User enters and and clicks on sign in diff --git a/src/test/resources/Features/Users.feature b/src/test/resources/Features/Users.feature index dc3aec0..7bf8cbe 100644 --- a/src/test/resources/Features/Users.feature +++ b/src/test/resources/Features/Users.feature @@ -8,7 +8,7 @@ Feature: Different Users use cases in BStackDemo When User clicks on sign in link @noimage @fail - Scenario Outline: Login as User with no image loaded + Scenario Outline: TC-1665 Login as User with no image loaded When User enters and and clicks on sign in And is signed in to the App Then Product images are not loaded for the @@ -17,7 +17,7 @@ Feature: Different Users use cases in BStackDemo |"image_not_loading_user"|"testingisfun99"| @orders - Scenario Outline: Login as existing user to verify orders + Scenario Outline: TC-1666 Login as existing user to verify orders When User enters and and clicks on sign in And is signed in to the App And User clicks on Orders @@ -27,7 +27,7 @@ Feature: Different Users use cases in BStackDemo |"existing_orders_user"|"testingisfun99"| @locked - Scenario Outline: Login as a locked User + Scenario Outline: TC-1667 Login as a locked User When User enters and and clicks on sign in Then gets Your account has been locked. Examples: diff --git a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json index 9f3c6f9..ae18182 100644 --- a/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json +++ b/src/test/resources/browserstack/conf/Run_Parallel_Test/parallel.conf.json @@ -9,7 +9,6 @@ "sessionName": "BS Suite", "debug": "true", "networkLogs": "true", - "seleniumVersion": "4.0.0", "consoleLogs": "errors" }, diff --git a/src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json b/src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json index 21be2da..590f319 100644 --- a/src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json +++ b/src/test/resources/browserstack/conf/Run_Single_Test/single.conf.json @@ -9,7 +9,6 @@ "sessionName": "BS Suite", "debug": "true", "networkLogs": "true", - "seleniumVersion": "4.0.0", "consoleLogs": "errors" }, From 46552c859a0b1e73c5b4145eb79164650ae84d7c Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Mon, 16 Sep 2024 13:27:28 +0530 Subject: [PATCH 09/29] lighthouse and percy --- browserstack.yml | 14 ++++++++------ pom.xml | 1 - src/test/java/Runner/CucumberTest.java | 11 +++++++++-- src/test/java/StepDefinitions/E2ESteps.java | 7 ++++--- .../qa/util/BrowserstackTestStatusListener.java | 4 ++-- src/test/resources/Features/E2E.feature | 2 +- 6 files changed, 24 insertions(+), 15 deletions(-) diff --git a/browserstack.yml b/browserstack.yml index 7c6bc90..5ee46a1 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -38,12 +38,13 @@ platforms: browserName: Edge browserVersion: latest accessibility: false - - deviceName: Samsung Galaxy S22 Ultra + - deviceName: Samsung Galaxy S2* browserName: chrome # Try 'samsung' for Samsung browser - osVersion: 12.0 + osVersion: [12131415] accessibility: false - - deviceName: iPhone 13 Pro Max - osVersion: 15 + - deviceName: iPhone 1* + browserName: safari + osVersion: [15161718] accessibility: false # ======================= # Parallels per Platform @@ -68,7 +69,7 @@ browserstackAutomation: true browserstackLocal: false # (Default false) # Options to be passed to BrowserStack local in-case of advanced configurations -browserStackLocalOptions: +#browserStackLocalOptions: # localIdentifier: # (Default: null) Needed if you need to run multiple instances of local. # forceLocal: false # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. # Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections @@ -95,4 +96,5 @@ accessibilityOptions: performance: assert percy: true -percyCaptureMode: auto \ No newline at end of file +percyCaptureMode: auto +#logLevel: debug \ No newline at end of file diff --git a/pom.xml b/pom.xml index 70dc3b2..8667b3e 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,6 @@ com.browserstack browserstack-java-sdk LATEST - compile diff --git a/src/test/java/Runner/CucumberTest.java b/src/test/java/Runner/CucumberTest.java index 5f0ae7e..4efc161 100644 --- a/src/test/java/Runner/CucumberTest.java +++ b/src/test/java/Runner/CucumberTest.java @@ -31,7 +31,7 @@ plugin = {"pretty","json:target/cucumber-reports/Cucumber.json" //"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:" }, - monochrome = true + monochrome = true,publish = true ) @Listeners(BrowserstackTestStatusListener.class) public class CucumberTest { @@ -99,8 +99,15 @@ public void setUpHooks(String config_file, String environment) throws Exception new URL("https://" + username + ":" + accessKey + "@" + config.get("server") + "/wd/hub"), capabilities)); }else throw new AssertionError("Invalid input for browser"); - if(!capabilities.toString().contains("realMobile")) + Map deviceInfo = new HashMap<>(); + JavascriptExecutor jse = (JavascriptExecutor) getDriver(); + try{ + deviceInfo = (Map) jse.executeScript("mobile:deviceInfo"); + }catch(Exception e){ + } + if(deviceInfo.isEmpty()) getDriver().manage().window().maximize(); + } public static synchronized WebDriver getDriver(){ diff --git a/src/test/java/StepDefinitions/E2ESteps.java b/src/test/java/StepDefinitions/E2ESteps.java index 15515a0..55d379d 100644 --- a/src/test/java/StepDefinitions/E2ESteps.java +++ b/src/test/java/StepDefinitions/E2ESteps.java @@ -49,10 +49,11 @@ public void user_is_on_home_page() { else hooks.driver.get(System.getProperties().getProperty("Application_url")); - jse.executeScript("browserstack_executor: {\"action\":\"lighthouseAudit\",\"arguments\":{\"url\":\""+hooks.driver.getCurrentUrl()+"\",\"executorOutput\":\"json\"}}"); - - jse.executeScript("browserstack_executor: {\"action\":\"lighthouseAudit\",\"arguments\":{\"url\":\""+hooks.driver.getCurrentUrl()+"\",\"assertResult\":{\"categories\":{\"performance\":40,\"best-practices\":50},\"metrics\":{\"first-contentful-paint\":{\"moreThan\":50,\"metricUnit\":\"score\"},\"largest-contentful-paint\":{\"lessThan\":4000,\"metricUnit\":\"numeric\"},\"total-blocking-time\":{\"lessThan\":600,\"metricUnit\":\"numeric\"},\"cumulative-layout-shift\":{\"moreThan\":50,\"metricUnit\":\"score\"}}}}}"); + if(System.getenv("BROWSERSTACK_AUTOMATION").equalsIgnoreCase("true")) { + jse.executeScript("browserstack_executor: {\"action\":\"lighthouseAudit\",\"arguments\":{\"url\":\"" + hooks.driver.getCurrentUrl() + "\",\"executorOutput\":\"json\"}}"); + jse.executeScript("browserstack_executor: {\"action\":\"lighthouseAudit\",\"arguments\":{\"url\":\"" + hooks.driver.getCurrentUrl() + "\",\"assertResult\":{\"categories\":{\"performance\":40,\"best-practices\":50},\"metrics\":{\"first-contentful-paint\":{\"moreThan\":50,\"metricUnit\":\"score\"},\"largest-contentful-paint\":{\"lessThan\":4000,\"metricUnit\":\"numeric\"},\"total-blocking-time\":{\"lessThan\":600,\"metricUnit\":\"numeric\"},\"cumulative-layout-shift\":{\"moreThan\":50,\"metricUnit\":\"score\"}}}}}"); + } } @When("User clicks on sign in link") public void user_clicks_on_sign_in_link() { diff --git a/src/test/java/com/qa/util/BrowserstackTestStatusListener.java b/src/test/java/com/qa/util/BrowserstackTestStatusListener.java index 77ed674..194ac8b 100644 --- a/src/test/java/com/qa/util/BrowserstackTestStatusListener.java +++ b/src/test/java/com/qa/util/BrowserstackTestStatusListener.java @@ -21,7 +21,7 @@ private void markTestStatus(String status, String reason, WebDriver driver) { public void onTestSuccess(ITestResult result) { Object currentClass = result.getInstance(); WebDriver driver = ((CucumberTest) currentClass).getDriver(); - markTestStatus("passed", "", driver); + //markTestStatus("passed", "", driver); driver.quit(); } @@ -31,7 +31,7 @@ public void onTestFailure(ITestResult result) { WebDriver driver = ((CucumberTest) currentClass).getDriver(); String message = result.getThrowable().getMessage(); String reason = (message != null && message.length() > 254) ? message.substring(0, 254) : message; - markTestStatus("failed", reason.replaceAll("[^a-zA-Z0-9._-]", " "), driver); + //markTestStatus("failed", reason.replaceAll("[^a-zA-Z0-9._-]", " "), driver); driver.quit(); } diff --git a/src/test/resources/Features/E2E.feature b/src/test/resources/Features/E2E.feature index f18735d..f14fd22 100644 --- a/src/test/resources/Features/E2E.feature +++ b/src/test/resources/Features/E2E.feature @@ -3,7 +3,7 @@ #Description: Demonstrate Cucumber Java framework with BS Automate product @e2e @regression Feature: E2E Flow - Scenario Outline: TC-1480 Signed in User makes a Purchase for a product with Price and Vendor filters + Scenario Outline: TC-1856 Signed in User makes a Purchase for a product with Price and Vendor filters Given User is on home page When User clicks on sign in link And User enters and and clicks on sign in From c76995b811735947ae8d061708442952236aa4aa Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Thu, 10 Oct 2024 17:04:58 +0530 Subject: [PATCH 10/29] platform a11y --- browserstack.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/browserstack.yml b/browserstack.yml index 5ee46a1..25bde8e 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -32,20 +32,16 @@ platforms: osVersion: Big Sur browserName: Chrome browserVersion: latest - accessibility: true - os: Windows osVersion: 10 browserName: Edge browserVersion: latest - accessibility: false - deviceName: Samsung Galaxy S2* browserName: chrome # Try 'samsung' for Samsung browser osVersion: [12131415] - accessibility: false - deviceName: iPhone 1* browserName: safari osVersion: [15161718] - accessibility: false # ======================= # Parallels per Platform # ======================= @@ -85,6 +81,7 @@ consoleLogs: errors # Remote browser's console debug levels to be print # Test Observability is an intelligent test reporting & debugging product. It collects data using the SDK. Read more about what data is collected at https://www.browserstack.com/docs/test-observability/references/terms-and-conditions # Visit observability.browserstack.com to see your test reports and insights. To disable test observability, specify `testObservability: false` in the key below. testObservability: true +accessibility: true accessibilityOptions: wcagVersion: wcag21aaa # Default: wcag21aa includeIssueType: From 41299d1933d0b904588a0e6515cd8448266ccb05 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Wed, 16 Oct 2024 14:24:52 +0530 Subject: [PATCH 11/29] yaml reader --- src/test/java/StepDefinitions/E2ESteps.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/test/java/StepDefinitions/E2ESteps.java b/src/test/java/StepDefinitions/E2ESteps.java index 55d379d..9a84208 100644 --- a/src/test/java/StepDefinitions/E2ESteps.java +++ b/src/test/java/StepDefinitions/E2ESteps.java @@ -12,7 +12,11 @@ import io.cucumber.java.en.When; import org.openqa.selenium.JavascriptExecutor; import org.testng.Assert; +import org.yaml.snakeyaml.Yaml; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; import java.util.HashMap; import java.util.Map; @@ -36,7 +40,7 @@ public void getScenario(Scenario scenario){ this.sc = scenario; } @Given("User is on home page") - public void user_is_on_home_page() { + public void user_is_on_home_page() throws FileNotFoundException { JavascriptExecutor jse = (JavascriptExecutor) hooks.driver; Map deviceInfo = new HashMap<>(); try{ @@ -48,8 +52,10 @@ public void user_is_on_home_page() { hooks.driver.get(System.getProperties().getProperty("Application_url").replaceAll("localhost","bs-local.com")); else hooks.driver.get(System.getProperties().getProperty("Application_url")); - - if(System.getenv("BROWSERSTACK_AUTOMATION").equalsIgnoreCase("true")) { + Yaml yaml = new Yaml(); + InputStream inputStream = new FileInputStream("browserstack.yml"); + HashMap yamlMap = yaml.load(inputStream); + if(yamlMap.get("browserstackAutomation").toString().equalsIgnoreCase("true")) { jse.executeScript("browserstack_executor: {\"action\":\"lighthouseAudit\",\"arguments\":{\"url\":\"" + hooks.driver.getCurrentUrl() + "\",\"executorOutput\":\"json\"}}"); jse.executeScript("browserstack_executor: {\"action\":\"lighthouseAudit\",\"arguments\":{\"url\":\"" + hooks.driver.getCurrentUrl() + "\",\"assertResult\":{\"categories\":{\"performance\":40,\"best-practices\":50},\"metrics\":{\"first-contentful-paint\":{\"moreThan\":50,\"metricUnit\":\"score\"},\"largest-contentful-paint\":{\"lessThan\":4000,\"metricUnit\":\"numeric\"},\"total-blocking-time\":{\"lessThan\":600,\"metricUnit\":\"numeric\"},\"cumulative-layout-shift\":{\"moreThan\":50,\"metricUnit\":\"score\"}}}}}"); From c08023e756305f367c1ebfebc7386bab7b1ce252 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Fri, 24 Jan 2025 23:05:21 +0530 Subject: [PATCH 12/29] Update E2E.feature TC mapping --- src/test/resources/Features/E2E.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/resources/Features/E2E.feature b/src/test/resources/Features/E2E.feature index f14fd22..90055d7 100644 --- a/src/test/resources/Features/E2E.feature +++ b/src/test/resources/Features/E2E.feature @@ -3,7 +3,7 @@ #Description: Demonstrate Cucumber Java framework with BS Automate product @e2e @regression Feature: E2E Flow - Scenario Outline: TC-1856 Signed in User makes a Purchase for a product with Price and Vendor filters + Scenario Outline: TC-3294 Signed in User makes a Purchase for a product with Price and Vendor filters Given User is on home page When User clicks on sign in link And User enters and and clicks on sign in @@ -16,4 +16,4 @@ Feature: E2E Flow Then User can see the order id for the product Examples: |username|password|first|last|address|state|postal| - |"demouser"|"testingisfun99"|"first"|"last"|"test"|"test state"|1234| \ No newline at end of file + |"demouser"|"testingisfun99"|"first"|"last"|"test"|"test state"|1234| From e238f7261cd01a7664e929a667f3896a14f4bd53 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 5 Feb 2025 18:51:41 +0530 Subject: [PATCH 13/29] Web AI Sef Heal --- browserstack.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/browserstack.yml b/browserstack.yml index 25bde8e..7139724 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -31,11 +31,11 @@ platforms: - os: OS X osVersion: Big Sur browserName: Chrome - browserVersion: latest + browserVersion: 131.0 - os: Windows osVersion: 10 browserName: Edge - browserVersion: latest + browserVersion: 131.0 - deviceName: Samsung Galaxy S2* browserName: chrome # Try 'samsung' for Samsung browser osVersion: [12131415] @@ -94,4 +94,4 @@ accessibilityOptions: performance: assert percy: true percyCaptureMode: auto -#logLevel: debug \ No newline at end of file +#logLevel: debug From f4546da5dee7eada6f5b4c1bb641033f91bc6851 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Wed, 12 Mar 2025 16:49:42 +0530 Subject: [PATCH 14/29] jdk update --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8667b3e..c69281d 100644 --- a/pom.xml +++ b/pom.xml @@ -8,8 +8,8 @@ 7.3.0 UTF-8 - 1.8 - 1.8 + 11 + 11 3.0.0-M5 7.4.0 3.0.0-M5 From 6b46a183c58c7de14eaad354e99b79de019f54bf Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Wed, 28 May 2025 20:43:41 +0530 Subject: [PATCH 15/29] change in proj caps for a11y --- browserstack.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/browserstack.yml b/browserstack.yml index 25bde8e..585e259 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -11,7 +11,7 @@ accessKey: YOUR_ACCESS_KEY # ====================== # The following capabilities are used to set up reporting on BrowserStack: # Set 'projectName' to the name of your project. Example, Marketing Website -projectName: BrowserStack Sample # Automate - Map SDK +projectName: BrowserStack Cucumber TestNG # Automate - Map SDK # Set `buildName` as the name of the job / testsuite being run buildName: browserstack build # `buildIdentifier` is a unique id to differentiate every execution that gets appended to @@ -32,6 +32,7 @@ platforms: osVersion: Big Sur browserName: Chrome browserVersion: latest + accessibility: true - os: Windows osVersion: 10 browserName: Edge @@ -39,7 +40,7 @@ platforms: - deviceName: Samsung Galaxy S2* browserName: chrome # Try 'samsung' for Samsung browser osVersion: [12131415] - - deviceName: iPhone 1* + - deviceName: iPhone 1* browserName: safari osVersion: [15161718] # ======================= From 0a02c60e3604f4dc3e317c214d00d453c8161470 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 17 Sep 2025 13:13:15 +0530 Subject: [PATCH 16/29] Create check-quality-gate.sh TRA Quality Gates --- check-quality-gate.sh | 75 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 check-quality-gate.sh diff --git a/check-quality-gate.sh b/check-quality-gate.sh new file mode 100644 index 0000000..ee3d94f --- /dev/null +++ b/check-quality-gate.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# Update values here +project_name="BrowserStack Cucumber TestNG" # Replace with your project name +build_name="$BROWSERSTACK_BUILD_NAME" # Replace with your build name use $BROWSERSTACK_BUILD_NAME if you use the BrowserStack Jenkins Plugin + +# Read secrets from environment variables +username="$BROWSERSTACK_USERNAME" +access_key="$BROWSERSTACK_ACCESS_KEY" + +max_attempts=20 # Replace with your max retry attempt +build_tags="{Insert Value Here}" # Optional - Replace with custom build tags if any +# Script Functions - REFRAIN FROM MODIFYING THE SCRIPT BELOW +# Function to sanitize project and build names +sanitize_name() { +local name="$1" +sanitized_name=$(echo "$name" | sed 's/ /%20/g') +echo "$sanitized_name" +} +# Function to get the UUID of the latest build +get_latest_build_uuid() { +local sanitized_project_name=$(sanitize_name "$project_name") +local sanitized_build_name=$(sanitize_name "$build_name") +local sanitized_build_tags=$(sanitize_name "$build_tags") +# Fetch and sanitize the JSON response +if [ -n "$sanitized_build_tags" ]; then + response=$(curl -s --retry 3 --connect-timeout 10 -u "$username:$access_key" "https://api-automation.browserstack.com/ext/v1/builds/latest?project_name=$sanitized_project_name&build_name=$sanitized_build_name&user_name=$username&build_tags=$sanitized_build_tags") +else + response=$(curl -s --retry 3 --connect-timeout 10 -u "$username:$access_key" "https://api-automation.browserstack.com/ext/v1/builds/latest?project_name=$sanitized_project_name&build_name=$sanitized_build_name&user_name=$username") +fi +response=$(echo "$response" | perl -pe 's/([[:cntrl:]])/sprintf("\\x{%02X}", ord($1))/eg') +local build_uuid=$(echo "$response" | jq -r '.build_id') +echo "$build_uuid" +} +# Function to hit Quality Gates API and get the result +get_quality_gate_result() { +quality_gate_result=$(curl -s -H --retry 3 --connect-timeout 10 -u "$username:$access_key" https://api-automation.browserstack.com/ext/v1/quality-gates/$build_uuid) +echo "$quality_gate_result" +} +# Function to poll the API until the results of the Quality Gate are received +poll_quality_gate_api() { +local attempt=0 +local max_time=600 +local total_time=0 +while [[ $attempt -lt $max_attempts ]] && [[ $total_time -lt max_time ]]; do +quality_gate_result=$(get_quality_gate_result "$1" "$2" "$build_uuid") +local result=$(echo "$quality_gate_result" | jq -r '.status') +if [ "$result" != "running" ]; then +echo "$quality_gate_result" +exit 0 +fi +sleep 30 # Poll every 30 seconds +((attempt++)) +total_time=$((attempt * 30)) # Total elapsed time in seconds +done +echo "Timed out waiting for Quality Gate results" +exit 1 +} +# Function to assert the API response and throw pass/fail exit code +assert_quality_gate_result() { +local result=$(echo "$quality_gate_result" | jq -r '.status') +# Replace the condition below with the actual condition to determine pass/fail +if [ "$result" == "passed" ]; then +echo "Quality Gate passed" +exit 0 +else +echo "Quality Gate failed" +exit 1 +fi +} +# Quality Gates API Poll +build_uuid=\$(get_latest_build_uuid) +sleep 20 +quality_gate_result=\$(poll_quality_gate_api) +echo "Quality Gate Result: \$quality_gate_result" +assert_quality_gate_result From 2ab7e1d259ee86a595df8df999681a738f8f8b80 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:01:20 +0530 Subject: [PATCH 17/29] Update check-quality-gate.sh --- check-quality-gate.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/check-quality-gate.sh b/check-quality-gate.sh index ee3d94f..65037a6 100644 --- a/check-quality-gate.sh +++ b/check-quality-gate.sh @@ -70,6 +70,6 @@ fi # Quality Gates API Poll build_uuid=\$(get_latest_build_uuid) sleep 20 -quality_gate_result=\$(poll_quality_gate_api) -echo "Quality Gate Result: \$quality_gate_result" +quality_gate_result=$(poll_quality_gate_api) +echo "Quality Gate Result: $quality_gate_result" assert_quality_gate_result From 6948bc73340f34903752cceee2440488b0a4f21f Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:09:42 +0530 Subject: [PATCH 18/29] Update check-quality-gate.sh --- check-quality-gate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/check-quality-gate.sh b/check-quality-gate.sh index 65037a6..6482576 100644 --- a/check-quality-gate.sh +++ b/check-quality-gate.sh @@ -68,7 +68,7 @@ exit 1 fi } # Quality Gates API Poll -build_uuid=\$(get_latest_build_uuid) +build_uuid=$(get_latest_build_uuid) sleep 20 quality_gate_result=$(poll_quality_gate_api) echo "Quality Gate Result: $quality_gate_result" From ae0dcf6b8ff546f5fd35bc1c72d0667a75560177 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:18:01 +0530 Subject: [PATCH 19/29] debug check-quality-gate.sh --- check-quality-gate.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/check-quality-gate.sh b/check-quality-gate.sh index 6482576..2e3441e 100644 --- a/check-quality-gate.sh +++ b/check-quality-gate.sh @@ -1,14 +1,14 @@ #!/bin/bash # Update values here project_name="BrowserStack Cucumber TestNG" # Replace with your project name -build_name="$BROWSERSTACK_BUILD_NAME" # Replace with your build name use $BROWSERSTACK_BUILD_NAME if you use the BrowserStack Jenkins Plugin +build_name="azure-BrowserStack_Platform_SDK-CI-355" # "$BROWSERSTACK_BUILD_NAME" # Replace with your build name use $BROWSERSTACK_BUILD_NAME if you use the BrowserStack Jenkins Plugin # Read secrets from environment variables username="$BROWSERSTACK_USERNAME" access_key="$BROWSERSTACK_ACCESS_KEY" max_attempts=20 # Replace with your max retry attempt -build_tags="{Insert Value Here}" # Optional - Replace with custom build tags if any +build_tags="" # Optional - Replace with custom build tags if any # Script Functions - REFRAIN FROM MODIFYING THE SCRIPT BELOW # Function to sanitize project and build names sanitize_name() { @@ -27,8 +27,10 @@ if [ -n "$sanitized_build_tags" ]; then else response=$(curl -s --retry 3 --connect-timeout 10 -u "$username:$access_key" "https://api-automation.browserstack.com/ext/v1/builds/latest?project_name=$sanitized_project_name&build_name=$sanitized_build_name&user_name=$username") fi -response=$(echo "$response" | perl -pe 's/([[:cntrl:]])/sprintf("\\x{%02X}", ord($1))/eg') -local build_uuid=$(echo "$response" | jq -r '.build_id') +# Use perl to strip ALL non-printable control characters that can break jq. +# This is a more robust fix than just replacing \n and \t. +local sanitized_response=$(echo "$response" | perl -pe 's/[^[:print:]]//g') +local build_uuid=$(echo "$sanitized_response" | jq -r '.build_id') echo "$build_uuid" } # Function to hit Quality Gates API and get the result From ebe09aa09fbc98062d8ca2cae7fde5c31d455f95 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:47:37 +0530 Subject: [PATCH 20/29] Debug check-quality-gate.sh --- check-quality-gate.sh | 141 ++++++++++++++++++++++++++---------------- 1 file changed, 88 insertions(+), 53 deletions(-) diff --git a/check-quality-gate.sh b/check-quality-gate.sh index 2e3441e..5a1528d 100644 --- a/check-quality-gate.sh +++ b/check-quality-gate.sh @@ -1,77 +1,112 @@ #!/bin/bash # Update values here -project_name="BrowserStack Cucumber TestNG" # Replace with your project name -build_name="azure-BrowserStack_Platform_SDK-CI-355" # "$BROWSERSTACK_BUILD_NAME" # Replace with your build name use $BROWSERSTACK_BUILD_NAME if you use the BrowserStack Jenkins Plugin +project_name="BrowserStack Cucumber TestNG" +build_name="azure-BrowserStack_Platform_SDK-CI-355" # Read secrets from environment variables -username="$BROWSERSTACK_USERNAME" -access_key="$BROWSERSTACK_ACCESS_KEY" +username="$BROWSERSTACK_USERNAME" +access_key="$BROWSERSTACK_ACCESS_KEY" -max_attempts=20 # Replace with your max retry attempt -build_tags="" # Optional - Replace with custom build tags if any -# Script Functions - REFRAIN FROM MODIFYING THE SCRIPT BELOW +max_attempts=20 +build_tags="" # Optional + +echo "[DEBUG] --- Initial Configuration ---" +echo "[DEBUG] Project Name: $project_name" +echo "[DEBUG] Build Name (from env): $build_name" +echo "[DEBUG] Username: $username" +echo "[DEBUG] Build Tags: $build_tags" +echo "[DEBUG] ---------------------------" + +# Script Functions # Function to sanitize project and build names sanitize_name() { -local name="$1" -sanitized_name=$(echo "$name" | sed 's/ /%20/g') -echo "$sanitized_name" + local name="$1" + sanitized_name=$(echo "$name" | sed 's/ /%20/g') + echo "$sanitized_name" } + # Function to get the UUID of the latest build get_latest_build_uuid() { -local sanitized_project_name=$(sanitize_name "$project_name") -local sanitized_build_name=$(sanitize_name "$build_name") -local sanitized_build_tags=$(sanitize_name "$build_tags") -# Fetch and sanitize the JSON response -if [ -n "$sanitized_build_tags" ]; then - response=$(curl -s --retry 3 --connect-timeout 10 -u "$username:$access_key" "https://api-automation.browserstack.com/ext/v1/builds/latest?project_name=$sanitized_project_name&build_name=$sanitized_build_name&user_name=$username&build_tags=$sanitized_build_tags") -else - response=$(curl -s --retry 3 --connect-timeout 10 -u "$username:$access_key" "https://api-automation.browserstack.com/ext/v1/builds/latest?project_name=$sanitized_project_name&build_name=$sanitized_build_name&user_name=$username") -fi -# Use perl to strip ALL non-printable control characters that can break jq. -# This is a more robust fix than just replacing \n and \t. -local sanitized_response=$(echo "$response" | perl -pe 's/[^[:print:]]//g') -local build_uuid=$(echo "$sanitized_response" | jq -r '.build_id') -echo "$build_uuid" + local sanitized_project_name=$(sanitize_name "$project_name") + local sanitized_build_name=$(sanitize_name "$build_name") + local sanitized_build_tags=$(sanitize_name "$build_tags") + + echo "[DEBUG] Sanitized Project Name: $sanitized_project_name" + echo "[DEBUG] Sanitized Build Name: $sanitized_build_name" + + local url="https://api-automation.browserstack.com/ext/v1/builds/latest?project_name=$sanitized_project_name&build_name=$sanitized_build_name&user_name=$username" + if [ -n "$sanitized_build_tags" ]; then + url="$url&build_tags=$sanitized_build_tags" + fi + + echo "[DEBUG] Querying URL: $url" + + # Fetch the JSON response + response=$(curl -s --retry 3 --connect-timeout 10 -u "$username:$access_key" "$url") + + echo "[DEBUG] Raw API Response for Build UUID: $response" + + # Use perl to strip ALL non-printable control characters that can break jq. + local sanitized_response=$(echo "$response" | perl -pe 's/[^[:print:]]//g') + + # Use the corrected "sanitized_response" variable + local build_uuid=$(echo "$sanitized_response" | jq -r '.build_id') + echo "[DEBUG] Extracted Build UUID: $build_uuid" + echo "$build_uuid" } + # Function to hit Quality Gates API and get the result get_quality_gate_result() { -quality_gate_result=$(curl -s -H --retry 3 --connect-timeout 10 -u "$username:$access_key" https://api-automation.browserstack.com/ext/v1/quality-gates/$build_uuid) -echo "$quality_gate_result" + echo "[DEBUG] Checking Quality Gate for Build UUID: $build_uuid" + local qg_url="https://api-automation.browserstack.com/ext/v1/quality-gates/$build_uuid" + echo "[DEBUG] Querying Quality Gate URL: $qg_url" + quality_gate_result=$(curl -s -H --retry 3 --connect-timeout 10 -u "$username:$access_key" "$qg_url") + echo "[DEBUG] Raw Quality Gate Response: $quality_gate_result" + echo "$quality_gate_result" } + # Function to poll the API until the results of the Quality Gate are received poll_quality_gate_api() { -local attempt=0 -local max_time=600 -local total_time=0 -while [[ $attempt -lt $max_attempts ]] && [[ $total_time -lt max_time ]]; do -quality_gate_result=$(get_quality_gate_result "$1" "$2" "$build_uuid") -local result=$(echo "$quality_gate_result" | jq -r '.status') -if [ "$result" != "running" ]; then -echo "$quality_gate_result" -exit 0 -fi -sleep 30 # Poll every 30 seconds -((attempt++)) -total_time=$((attempt * 30)) # Total elapsed time in seconds -done -echo "Timed out waiting for Quality Gate results" -exit 1 + local attempt=0 + local max_time=600 + local total_time=0 + while [[ $attempt -lt $max_attempts ]] && [[ $total_time -lt max_time ]]; do + echo "[DEBUG] Polling attempt #$((attempt + 1))..." + quality_gate_result=$(get_quality_gate_result "$1" "$2" "$build_uuid") + local result=$(echo "$quality_gate_result" | jq -r '.status') + echo "[DEBUG] Current status is: '$result'" + if [ "$result" != "running" ]; then + echo "$quality_gate_result" + exit 0 + fi + sleep 30 + ((attempt++)) + total_time=$((attempt * 30)) + done + echo "Timed out waiting for Quality Gate results" + exit 1 } + # Function to assert the API response and throw pass/fail exit code assert_quality_gate_result() { -local result=$(echo "$quality_gate_result" | jq -r '.status') -# Replace the condition below with the actual condition to determine pass/fail -if [ "$result" == "passed" ]; then -echo "Quality Gate passed" -exit 0 -else -echo "Quality Gate failed" -exit 1 -fi + local result=$(echo "$quality_gate_result" | jq -r '.status') + if [ "$result" == "passed" ]; then + echo "Quality Gate passed" + exit 0 + else + echo "Quality Gate failed" + exit 1 + fi } -# Quality Gates API Poll + +# --- Main Script Execution --- build_uuid=$(get_latest_build_uuid) +if [ "$build_uuid" == "null" ] || [ -z "$build_uuid" ]; then + echo "[ERROR] Failed to retrieve a valid Build UUID. Please check the project and build names in the logs above." + exit 1 +fi + sleep 20 quality_gate_result=$(poll_quality_gate_api) -echo "Quality Gate Result: $quality_gate_result" +echo "Final Quality Gate Result: $quality_gate_result" assert_quality_gate_result From 39caf325ed14f5542ac2aabe0e39f175f3b7cf89 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 17 Sep 2025 18:03:11 +0530 Subject: [PATCH 21/29] Debug check-quality-gate.sh --- check-quality-gate.sh | 104 ++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 59 deletions(-) diff --git a/check-quality-gate.sh b/check-quality-gate.sh index 5a1528d..dd7a1c2 100644 --- a/check-quality-gate.sh +++ b/check-quality-gate.sh @@ -10,6 +10,10 @@ access_key="$BROWSERSTACK_ACCESS_KEY" max_attempts=20 build_tags="" # Optional +# --- Redirect debug logs to stderr to avoid corrupting JSON variables --- +exec 3>&1 # Save original stdout +exec 1>&2 # Redirect stdout to stderr for debug logs + echo "[DEBUG] --- Initial Configuration ---" echo "[DEBUG] Project Name: $project_name" echo "[DEBUG] Build Name (from env): $build_name" @@ -18,95 +22,77 @@ echo "[DEBUG] Build Tags: $build_tags" echo "[DEBUG] ---------------------------" # Script Functions -# Function to sanitize project and build names sanitize_name() { local name="$1" sanitized_name=$(echo "$name" | sed 's/ /%20/g') echo "$sanitized_name" } -# Function to get the UUID of the latest build get_latest_build_uuid() { local sanitized_project_name=$(sanitize_name "$project_name") local sanitized_build_name=$(sanitize_name "$build_name") - local sanitized_build_tags=$(sanitize_name "$build_tags") echo "[DEBUG] Sanitized Project Name: $sanitized_project_name" echo "[DEBUG] Sanitized Build Name: $sanitized_build_name" - local url="https://api-automation.browserstack.com/ext/v1/builds/latest?project_name=$sanitized_project_name&build_name=$sanitized_build_name&user_name=$username" - if [ -n "$sanitized_build_tags" ]; then - url="$url&build_tags=$sanitized_build_tags" - fi + # REMOVED user_name parameter for a more reliable query + local url="https://api-automation.browserstack.com/ext/v1/builds/latest?project_name=$sanitized_project_name&build_name=$sanitized_build_name" echo "[DEBUG] Querying URL: $url" - # Fetch the JSON response + # Fetch the JSON response and send it to the original stdout response=$(curl -s --retry 3 --connect-timeout 10 -u "$username:$access_key" "$url") - - echo "[DEBUG] Raw API Response for Build UUID: $response" - - # Use perl to strip ALL non-printable control characters that can break jq. - local sanitized_response=$(echo "$response" | perl -pe 's/[^[:print:]]//g') - - # Use the corrected "sanitized_response" variable - local build_uuid=$(echo "$sanitized_response" | jq -r '.build_id') - echo "[DEBUG] Extracted Build UUID: $build_uuid" - echo "$build_uuid" + echo "$response" >&3 } -# Function to hit Quality Gates API and get the result get_quality_gate_result() { echo "[DEBUG] Checking Quality Gate for Build UUID: $build_uuid" local qg_url="https://api-automation.browserstack.com/ext/v1/quality-gates/$build_uuid" echo "[DEBUG] Querying Quality Gate URL: $qg_url" + + # Fetch the JSON response and send it to the original stdout quality_gate_result=$(curl -s -H --retry 3 --connect-timeout 10 -u "$username:$access_key" "$qg_url") - echo "[DEBUG] Raw Quality Gate Response: $quality_gate_result" - echo "$quality_gate_result" + echo "$quality_gate_result" >&3 } -# Function to poll the API until the results of the Quality Gate are received -poll_quality_gate_api() { - local attempt=0 - local max_time=600 - local total_time=0 - while [[ $attempt -lt $max_attempts ]] && [[ $total_time -lt max_time ]]; do - echo "[DEBUG] Polling attempt #$((attempt + 1))..." - quality_gate_result=$(get_quality_gate_result "$1" "$2" "$build_uuid") - local result=$(echo "$quality_gate_result" | jq -r '.status') - echo "[DEBUG] Current status is: '$result'" - if [ "$result" != "running" ]; then - echo "$quality_gate_result" - exit 0 - fi - sleep 30 - ((attempt++)) - total_time=$((attempt * 30)) - done - echo "Timed out waiting for Quality Gate results" - exit 1 -} +# --- Main Script Execution --- -# Function to assert the API response and throw pass/fail exit code -assert_quality_gate_result() { - local result=$(echo "$quality_gate_result" | jq -r '.status') - if [ "$result" == "passed" ]; then - echo "Quality Gate passed" - exit 0 - else - echo "Quality Gate failed" - exit 1 - fi -} +# Restore stdout for the main logic +exec 1>&3 + +build_uuid_json=$(get_latest_build_uuid) +echo "[DEBUG] Raw API Response for Build UUID: $build_uuid_json" +sanitized_response=$(echo "$build_uuid_json" | perl -pe 's/[^[:print:]]//g') +build_uuid=$(echo "$sanitized_response" | jq -r '.build_id') +echo "[DEBUG] Extracted Build UUID: $build_uuid" -# --- Main Script Execution --- -build_uuid=$(get_latest_build_uuid) if [ "$build_uuid" == "null" ] || [ -z "$build_uuid" ]; then - echo "[ERROR] Failed to retrieve a valid Build UUID. Please check the project and build names in the logs above." + echo "[ERROR] Failed to retrieve a valid Build UUID. Please check the project and build names in the logs above." >&2 exit 1 fi sleep 20 -quality_gate_result=$(poll_quality_gate_api) -echo "Final Quality Gate Result: $quality_gate_result" -assert_quality_gate_result + +# Polling loop +attempt=0 +while [[ $attempt -lt $max_attempts ]]; do + echo "[DEBUG] Polling attempt #$((attempt + 1))..." >&2 + quality_gate_result_json=$(get_quality_gate_result) + result=$(echo "$quality_gate_result_json" | jq -r '.status') + echo "[DEBUG] Current status is: '$result'" >&2 + if [ "$result" != "running" ]; then + break + fi + sleep 30 + ((attempt++)) +done + +if [ "$result" != "passed" ]; then + echo "Final Quality Gate Result: $quality_gate_result_json" + echo "Quality Gate failed" >&2 + exit 1 +else + echo "Final Quality Gate Result: $quality_gate_result_json" + echo "Quality Gate passed" >&2 + exit 0 +fi From 14bda668b6170c4ab315c3379c2b798e4fe4c6bf Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 17 Sep 2025 18:15:36 +0530 Subject: [PATCH 22/29] Update check-quality-gate.sh --- check-quality-gate.sh | 75 ++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 41 deletions(-) diff --git a/check-quality-gate.sh b/check-quality-gate.sh index dd7a1c2..75beaaf 100644 --- a/check-quality-gate.sh +++ b/check-quality-gate.sh @@ -1,73 +1,63 @@ #!/bin/bash # Update values here project_name="BrowserStack Cucumber TestNG" -build_name="azure-BrowserStack_Platform_SDK-CI-355" +build_name="$BROWSERSTACK_BUILD_NAME" # Read secrets from environment variables username="$BROWSERSTACK_USERNAME" access_key="$BROWSERSTACK_ACCESS_KEY" max_attempts=20 -build_tags="" # Optional +build_tags="" -# --- Redirect debug logs to stderr to avoid corrupting JSON variables --- -exec 3>&1 # Save original stdout -exec 1>&2 # Redirect stdout to stderr for debug logs - -echo "[DEBUG] --- Initial Configuration ---" -echo "[DEBUG] Project Name: $project_name" -echo "[DEBUG] Build Name (from env): $build_name" -echo "[DEBUG] Username: $username" -echo "[DEBUG] Build Tags: $build_tags" -echo "[DEBUG] ---------------------------" +# --- Print initial config to stderr --- +echo "[DEBUG] --- Initial Configuration ---" >&2 +echo "[DEBUG] Project Name: $project_name" >&2 +echo "[DEBUG] Build Name (from env): $build_name" >&2 +echo "[DEBUG] Username: $username" >&2 +echo "[DEBUG] Build Tags: $build_tags" >&2 +echo "[DEBUG] ---------------------------" >&2 # Script Functions sanitize_name() { local name="$1" - sanitized_name=$(echo "$name" | sed 's/ /%20/g') - echo "$sanitized_name" + echo "$name" | sed 's/ /%20/g' } get_latest_build_uuid() { local sanitized_project_name=$(sanitize_name "$project_name") local sanitized_build_name=$(sanitize_name "$build_name") - echo "[DEBUG] Sanitized Project Name: $sanitized_project_name" - echo "[DEBUG] Sanitized Build Name: $sanitized_build_name" + echo "[DEBUG] Sanitized Project Name: $sanitized_project_name" >&2 + echo "[DEBUG] Sanitized Build Name: $sanitized_build_name" >&2 # REMOVED user_name parameter for a more reliable query local url="https://api-automation.browserstack.com/ext/v1/builds/latest?project_name=$sanitized_project_name&build_name=$sanitized_build_name" - - echo "[DEBUG] Querying URL: $url" + echo "[DEBUG] Querying URL: $url" >&2 - # Fetch the JSON response and send it to the original stdout - response=$(curl -s --retry 3 --connect-timeout 10 -u "$username:$access_key" "$url") - echo "$response" >&3 + # Fetch the JSON response. This goes to stdout. + curl -s --retry 3 --connect-timeout 10 -u "$username:$access_key" "$url" } get_quality_gate_result() { - echo "[DEBUG] Checking Quality Gate for Build UUID: $build_uuid" + echo "[DEBUG] Checking Quality Gate for Build UUID: $build_uuid" >&2 local qg_url="https://api-automation.browserstack.com/ext/v1/quality-gates/$build_uuid" - echo "[DEBUG] Querying Quality Gate URL: $qg_url" - - # Fetch the JSON response and send it to the original stdout - quality_gate_result=$(curl -s -H --retry 3 --connect-timeout 10 -u "$username:$access_key" "$qg_url") - echo "$quality_gate_result" >&3 + echo "[DEBUG] Querying Quality Gate URL: $qg_url" >&2 + + # Fetch the JSON response. This goes to stdout. + curl -s -H --retry 3 --connect-timeout 10 -u "$username:$access_key" "$qg_url" } # --- Main Script Execution --- - -# Restore stdout for the main logic -exec 1>&3 - build_uuid_json=$(get_latest_build_uuid) -echo "[DEBUG] Raw API Response for Build UUID: $build_uuid_json" -sanitized_response=$(echo "$build_uuid_json" | perl -pe 's/[^[:print:]]//g') +echo "[DEBUG] Raw API Response for Build UUID: $build_uuid_json" >&2 + +sanitized_response=$(echo "$build_uuid_json" | perl -pe 's/[^[:print:]\n]//g') build_uuid=$(echo "$sanitized_response" | jq -r '.build_id') -echo "[DEBUG] Extracted Build UUID: $build_uuid" +echo "[DEBUG] Extracted Build UUID: $build_uuid" >&2 if [ "$build_uuid" == "null" ] || [ -z "$build_uuid" ]; then - echo "[ERROR] Failed to retrieve a valid Build UUID. Please check the project and build names in the logs above." >&2 + echo "[ERROR] Failed to retrieve a valid Build UUID. Check API response above." >&2 exit 1 fi @@ -78,8 +68,11 @@ attempt=0 while [[ $attempt -lt $max_attempts ]]; do echo "[DEBUG] Polling attempt #$((attempt + 1))..." >&2 quality_gate_result_json=$(get_quality_gate_result) + echo "[DEBUG] Raw Quality Gate Response: $quality_gate_result_json" >&2 + result=$(echo "$quality_gate_result_json" | jq -r '.status') echo "[DEBUG] Current status is: '$result'" >&2 + if [ "$result" != "running" ]; then break fi @@ -87,12 +80,12 @@ while [[ $attempt -lt $max_attempts ]]; do ((attempt++)) done -if [ "$result" != "passed" ]; then - echo "Final Quality Gate Result: $quality_gate_result_json" - echo "Quality Gate failed" >&2 - exit 1 -else - echo "Final Quality Gate Result: $quality_gate_result_json" +echo "Final Quality Gate Result: $quality_gate_result_json" + +if [ "$result" == "passed" ]; then echo "Quality Gate passed" >&2 exit 0 +else + echo "Quality Gate failed" >&2 + exit 1 fi From 06fd387f2068b0a11561cba3ce450513e454bd49 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 17 Sep 2025 18:17:35 +0530 Subject: [PATCH 23/29] Update check-quality-gate.sh --- check-quality-gate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/check-quality-gate.sh b/check-quality-gate.sh index 75beaaf..07ee2ad 100644 --- a/check-quality-gate.sh +++ b/check-quality-gate.sh @@ -1,7 +1,7 @@ #!/bin/bash # Update values here project_name="BrowserStack Cucumber TestNG" -build_name="$BROWSERSTACK_BUILD_NAME" +build_name="azure-BrowserStack_Platform_SDK-CI-355" # Read secrets from environment variables username="$BROWSERSTACK_USERNAME" From 2cfb5b9575f168ac907b1d1759fb7a785b912d27 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 17 Sep 2025 18:33:22 +0530 Subject: [PATCH 24/29] Update check-quality-gate.sh --- check-quality-gate.sh | 104 +++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/check-quality-gate.sh b/check-quality-gate.sh index 07ee2ad..553cc83 100644 --- a/check-quality-gate.sh +++ b/check-quality-gate.sh @@ -1,91 +1,99 @@ #!/bin/bash -# Update values here -project_name="BrowserStack Cucumber TestNG" -build_name="azure-BrowserStack_Platform_SDK-CI-355" +set -e # Exit immediately if a command exits with a non-zero status. -# Read secrets from environment variables +# --- Configuration --- +# These values are read from Azure DevOps Pipeline variables. +project_name="BrowserStack Cucumber TestNG" +build_name="$BROWSERSTACK_BUILD_NAME" username="$BROWSERSTACK_USERNAME" access_key="$BROWSERSTACK_ACCESS_KEY" -max_attempts=20 -build_tags="" +# --- Script Parameters --- +max_attempts=20 # Max number of times to poll the API (20 attempts * 30s = 10 minutes) +build_tags="" # Optional: Set build tags if needed -# --- Print initial config to stderr --- -echo "[DEBUG] --- Initial Configuration ---" >&2 -echo "[DEBUG] Project Name: $project_name" >&2 -echo "[DEBUG] Build Name (from env): $build_name" >&2 -echo "[DEBUG] Username: $username" >&2 -echo "[DEBUG] Build Tags: $build_tags" >&2 -echo "[DEBUG] ---------------------------" >&2 +# --- Script Functions --- -# Script Functions +# Function to URL-encode a string sanitize_name() { local name="$1" echo "$name" | sed 's/ /%20/g' } +# Function to get the UUID of the latest build get_latest_build_uuid() { - local sanitized_project_name=$(sanitize_name "$project_name") - local sanitized_build_name=$(sanitize_name "$build_name") - - echo "[DEBUG] Sanitized Project Name: $sanitized_project_name" >&2 - echo "[DEBUG] Sanitized Build Name: $sanitized_build_name" >&2 + local sanitized_project_name + local sanitized_build_name + sanitized_project_name=$(sanitize_name "$project_name") + sanitized_build_name=$(sanitize_name "$build_name") - # REMOVED user_name parameter for a more reliable query + # API call relies on project and build name, which is most reliable in CI. local url="https://api-automation.browserstack.com/ext/v1/builds/latest?project_name=$sanitized_project_name&build_name=$sanitized_build_name" - echo "[DEBUG] Querying URL: $url" >&2 - # Fetch the JSON response. This goes to stdout. + # Add optional build tags to the query if they are set + if [ -n "$build_tags" ]; then + local sanitized_build_tags + sanitized_build_tags=$(sanitize_name "$build_tags") + url="$url&build_tags=$sanitized_build_tags" + fi + + echo "Querying for build..." >&2 + + # Fetch the JSON response. This goes to standard output. curl -s --retry 3 --connect-timeout 10 -u "$username:$access_key" "$url" } +# Function to get the status of the Quality Gate for a given build UUID get_quality_gate_result() { - echo "[DEBUG] Checking Quality Gate for Build UUID: $build_uuid" >&2 + local build_uuid="$1" local qg_url="https://api-automation.browserstack.com/ext/v1/quality-gates/$build_uuid" - echo "[DEBUG] Querying Quality Gate URL: $qg_url" >&2 - # Fetch the JSON response. This goes to stdout. + # Fetch the JSON response. This goes to standard output. curl -s -H --retry 3 --connect-timeout 10 -u "$username:$access_key" "$qg_url" } # --- Main Script Execution --- -build_uuid_json=$(get_latest_build_uuid) -echo "[DEBUG] Raw API Response for Build UUID: $build_uuid_json" >&2 -sanitized_response=$(echo "$build_uuid_json" | perl -pe 's/[^[:print:]\n]//g') -build_uuid=$(echo "$sanitized_response" | jq -r '.build_id') -echo "[DEBUG] Extracted Build UUID: $build_uuid" >&2 +echo "--- BrowserStack Quality Gate Check ---" >&2 + +build_info_json=$(get_latest_build_uuid) +build_uuid=$(echo "$build_info_json" | jq -r '.build_id') if [ "$build_uuid" == "null" ] || [ -z "$build_uuid" ]; then - echo "[ERROR] Failed to retrieve a valid Build UUID. Check API response above." >&2 + echo "Error: Failed to retrieve a valid Build UUID from the API." >&2 + echo "API Response: $build_info_json" >&2 exit 1 fi +echo "Successfully found Build UUID: $build_uuid" >&2 +echo "Waiting 20 seconds before polling Quality Gate..." >&2 sleep 20 # Polling loop attempt=0 while [[ $attempt -lt $max_attempts ]]; do - echo "[DEBUG] Polling attempt #$((attempt + 1))..." >&2 - quality_gate_result_json=$(get_quality_gate_result) - echo "[DEBUG] Raw Quality Gate Response: $quality_gate_result_json" >&2 + attempt=$((attempt + 1)) + echo "Polling attempt #$attempt..." >&2 + + quality_gate_json=$(get_quality_gate_result "$build_uuid") + status=$(echo "$quality_gate_json" | jq -r '.status') - result=$(echo "$quality_gate_result_json" | jq -r '.status') - echo "[DEBUG] Current status is: '$result'" >&2 + echo "Current status is: '$status'" >&2 - if [ "$result" != "running" ]; then - break + if [ "$status" != "running" ]; then + echo "Final Quality Gate Result: $quality_gate_json" + + if [ "$status" == "passed" ]; then + echo "✅ Quality Gate Passed" >&2 + exit 0 + else + echo "❌ Quality Gate Failed" >&2 + exit 1 + fi fi + sleep 30 - ((attempt++)) done -echo "Final Quality Gate Result: $quality_gate_result_json" - -if [ "$result" == "passed" ]; then - echo "Quality Gate passed" >&2 - exit 0 -else - echo "Quality Gate failed" >&2 - exit 1 -fi +echo "Error: Timed out waiting for Quality Gate results after $max_attempts attempts." >&2 +exit 1 From 74daff1945e887ab235cb3017aaebefd030b0e78 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Tue, 4 Nov 2025 17:29:23 +0530 Subject: [PATCH 25/29] Add browser profiling and test orchestration options --- browserstack.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/browserstack.yml b/browserstack.yml index 7ca1a9d..09eb28c 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -94,4 +94,14 @@ accessibilityOptions: performance: assert percy: true percyCaptureMode: auto -#logLevel: debug \ No newline at end of file +#logLevel: debug +browserProfiling: true + +testOrchestrationOptions: + retryTestsOnFailure: + enabled: true + maxRetries: 2 + runSmartSelection: + enabled: true + mode: relevantFirst + runPreviouslyFailedFirst: true From 3f6a470e3d7fd96252122fd7b706396f663822c3 Mon Sep 17 00:00:00 2001 From: abdul-qadir92 Date: Wed, 26 Nov 2025 19:57:01 +0530 Subject: [PATCH 26/29] Add accessibility assertions and GitHub Action workflow --- .../workflows/browserstack-accessibility.yml | 28 ++++++++++++ browserstack.yml | 6 +-- src/test/java/StepDefinitions/UsersSteps.java | 43 +++++++++++++++++++ 3 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/browserstack-accessibility.yml diff --git a/.github/workflows/browserstack-accessibility.yml b/.github/workflows/browserstack-accessibility.yml new file mode 100644 index 0000000..12cb032 --- /dev/null +++ b/.github/workflows/browserstack-accessibility.yml @@ -0,0 +1,28 @@ +name: BrowserStack Accessibility Tests (On-Prem) + +on: + # Manual trigger only + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + + # Ensure this only runs for the sdk branch + if: github.ref == 'refs/heads/sdk' + + steps: + - name: Checkout sdk branch + uses: actions/checkout@v4 + with: + ref: sdk + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '11' + cache: maven + + - name: Run Accessibility tests + run: mvn test -P scenario-onprem -B \ No newline at end of file diff --git a/browserstack.yml b/browserstack.yml index 09eb28c..c9d8746 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -38,10 +38,10 @@ platforms: browserVersion: 131.0 - deviceName: Samsung Galaxy S2* browserName: chrome # Try 'samsung' for Samsung browser - osVersion: [12131415] + osVersion: "[12131415]" - deviceName: iPhone 1* browserName: safari - osVersion: [15161718] + osVersion: "[15161718]" # ======================= # Parallels per Platform # ======================= @@ -51,7 +51,7 @@ platforms: # Example 1 - If you have configured 3 platforms and set `parallelsPerPlatform` as 2, a total of 6 (2 * 3) parallel threads will be used on BrowserStack # # Example 2 - If you have configured 1 platform and set `parallelsPerPlatform` as 5, a total of 5 (1 * 5) parallel threads will be used on BrowserStack -parallelsPerPlatform: 2 +parallelsPerPlatform: 4 framework: cucumber-testng #junit,testng,java,cucumber-testng,serenity,cucumber-junit source: cucumber-testng:intellij:v1.1.2 staticWebDriver: true diff --git a/src/test/java/StepDefinitions/UsersSteps.java b/src/test/java/StepDefinitions/UsersSteps.java index 234ddbf..55e1dd3 100644 --- a/src/test/java/StepDefinitions/UsersSteps.java +++ b/src/test/java/StepDefinitions/UsersSteps.java @@ -8,8 +8,14 @@ import io.cucumber.java.en.Then; import io.cucumber.java.en.When; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; + +import org.testng.Assert; + +import com.browserstack.accessibility.AccessibilityUtils; public class UsersSteps { @@ -68,6 +74,43 @@ public void gets_your_account_has_been_locked(String user) throws InterruptedExc }else throw new AssertionError("Locked user "+user+" logged in!"); + // Accessibility assertion using BrowserStack Accessibility results summary + // Documentation reference: + // https://www.browserstack.com/docs/accessibility/automated-tests/add-accessibility-assertions + Map summary = AccessibilityUtils.getResultsSummary(hooks.driver); + + // Debug logging to understand NPE / null values + System.out.println("Accessibility summary: " + summary); + sc.log("Accessibility summary: " + String.valueOf(summary)); + + if (summary == null) { + sc.log("Accessibility summary is null, skipping accessibility assertions."); + return; + } + + Object severityMapObj = summary.get("issueCountBySeverity"); + if (!(severityMapObj instanceof Map)) { + sc.log("issueCountBySeverity is missing or not a Map in accessibility summary, skipping assertions."); + return; + } + + Map severityMap = (Map) severityMapObj; + Object criticalObj = severityMap.get("critical"); + sc.log("Critical issue count (raw): " + String.valueOf(criticalObj)); + + if (criticalObj == null) { + sc.log("Critical issue count is null in accessibility summary, skipping assertions."); + return; + } + + // Fetch and log detailed accessibility results as well + ArrayList> results = AccessibilityUtils.getResults(hooks.driver); + System.out.println("Accessibility detailed results: " + results); + sc.log("Accessibility detailed results: " + String.valueOf(results)); + + int criticalIssueCount = Integer.parseInt(String.valueOf(criticalObj)); + Assert.assertTrue(criticalIssueCount < 1, "Critical issue count breached the threshold!"); + } } From f1bf5a1258320afb04bd8acc2a386e0431d8fadc Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 26 Nov 2025 20:13:33 +0530 Subject: [PATCH 27/29] Update BrowserStack accessibility workflow Removed conditional check for sdk branch in workflow. --- .github/workflows/browserstack-accessibility.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/browserstack-accessibility.yml b/.github/workflows/browserstack-accessibility.yml index 12cb032..7425362 100644 --- a/.github/workflows/browserstack-accessibility.yml +++ b/.github/workflows/browserstack-accessibility.yml @@ -1,16 +1,12 @@ name: BrowserStack Accessibility Tests (On-Prem) on: - # Manual trigger only workflow_dispatch: jobs: test: runs-on: ubuntu-latest - # Ensure this only runs for the sdk branch - if: github.ref == 'refs/heads/sdk' - steps: - name: Checkout sdk branch uses: actions/checkout@v4 @@ -25,4 +21,4 @@ jobs: cache: maven - name: Run Accessibility tests - run: mvn test -P scenario-onprem -B \ No newline at end of file + run: mvn test -P scenario-onprem -B From a15c817f3d0b559aac8c280d61f7eb8798011fbd Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Wed, 26 Nov 2025 20:18:43 +0530 Subject: [PATCH 28/29] Change parallelsPerPlatform from 4 to 2 --- browserstack.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browserstack.yml b/browserstack.yml index c9d8746..4dced8c 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -51,7 +51,7 @@ platforms: # Example 1 - If you have configured 3 platforms and set `parallelsPerPlatform` as 2, a total of 6 (2 * 3) parallel threads will be used on BrowserStack # # Example 2 - If you have configured 1 platform and set `parallelsPerPlatform` as 5, a total of 5 (1 * 5) parallel threads will be used on BrowserStack -parallelsPerPlatform: 4 +parallelsPerPlatform: 2 framework: cucumber-testng #junit,testng,java,cucumber-testng,serenity,cucumber-junit source: cucumber-testng:intellij:v1.1.2 staticWebDriver: true From be987a4aaa53bd8db7d71dcf1b78295af380d1db Mon Sep 17 00:00:00 2001 From: abdul-qadir92 <92802404+abdul-qadir92@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:08:31 +0530 Subject: [PATCH 29/29] Update scenario outlines for user login tests --- src/test/resources/Features/Users.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/resources/Features/Users.feature b/src/test/resources/Features/Users.feature index 7bf8cbe..22b3911 100644 --- a/src/test/resources/Features/Users.feature +++ b/src/test/resources/Features/Users.feature @@ -8,7 +8,7 @@ Feature: Different Users use cases in BStackDemo When User clicks on sign in link @noimage @fail - Scenario Outline: TC-1665 Login as User with no image loaded + Scenario Outline: TC-5883 Login as User with no image loaded When User enters and and clicks on sign in And is signed in to the App Then Product images are not loaded for the @@ -17,7 +17,7 @@ Feature: Different Users use cases in BStackDemo |"image_not_loading_user"|"testingisfun99"| @orders - Scenario Outline: TC-1666 Login as existing user to verify orders + Scenario Outline: TC-5884 Login as existing user to verify orders When User enters and and clicks on sign in And is signed in to the App And User clicks on Orders @@ -27,7 +27,7 @@ Feature: Different Users use cases in BStackDemo |"existing_orders_user"|"testingisfun99"| @locked - Scenario Outline: TC-1667 Login as a locked User + Scenario Outline: TC-5885 Login as a locked User When User enters and and clicks on sign in Then gets Your account has been locked. Examples: