From 76c1df0fdb862f6790d75e10ea30506d3e13eff8 Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Fri, 7 Oct 2022 10:11:45 +0530 Subject: [PATCH 01/19] update: test files for sdk, remove extra files --- .env | 3 - scripts/local-test.py | 32 ++++++++++ scripts/local.py | 63 -------------------- scripts/parallel.py | 105 --------------------------------- scripts/{single.py => test.py} | 32 +++------- 5 files changed, 40 insertions(+), 195 deletions(-) delete mode 100644 .env create mode 100644 scripts/local-test.py delete mode 100644 scripts/local.py delete mode 100644 scripts/parallel.py rename scripts/{single.py => test.py} (68%) diff --git a/.env b/.env deleted file mode 100644 index 0987e7b..0000000 --- a/.env +++ /dev/null @@ -1,3 +0,0 @@ -BROWSERSTACK_USERNAME="BROWSERSTACK_USERNAME" -BROWSERSTACK_ACCESS_KEY="BROWSERSTACK_ACCESS_KEY" -URL="https://hub.browserstack.com/wd/hub" diff --git a/scripts/local-test.py b/scripts/local-test.py new file mode 100644 index 0000000..6e0a8c7 --- /dev/null +++ b/scripts/local-test.py @@ -0,0 +1,32 @@ +import json +from selenium import webdriver +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.common.by import By +from selenium.webdriver.chrome.options import Options as ChromeOptions + +options = ChromeOptions() +driver = webdriver.Remote( + command_executor='http://localhost:4444/wd/hub', + options=options) + +try: + driver.get('http://bs-local.com:45691/check') + body_text = WebDriverWait(driver, 10).until( + EC.visibility_of_element_located((By.CSS_SELECTOR, 'body'))).text + # check if local connected successfully + if body_text == 'Up and running': + # mark test as passed if Local website is accessible + driver.execute_script( + 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed", "reason": "Local Test ran successfully"}}') + else: + # mark test as failed if Local website is not accessible + driver.execute_script( + 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": "Local test setup failed"}}') +except Exception as err: + message = 'Exception: ' + str(err.__class__) + str(err.msg) + driver.execute_script( + 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": ' + json.dumps(message) + '}}') + +# Stop the driver +driver.quit() diff --git a/scripts/local.py b/scripts/local.py deleted file mode 100644 index 77bfc77..0000000 --- a/scripts/local.py +++ /dev/null @@ -1,63 +0,0 @@ -from dotenv import load_dotenv -import os -from selenium import webdriver -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.chrome.options import Options as ChromeOptions -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.common.by import By -from browserstack.local import Local -from selenium.common.exceptions import NoSuchElementException - -load_dotenv() -BROWSERSTACK_USERNAME = os.environ.get("BROWSERSTACK_USERNAME") or "BROWSERSTACK_USERNAME" -BROWSERSTACK_ACCESS_KEY = os.environ.get("BROWSERSTACK_ACCESS_KEY") or "BROWSERSTACK_ACCESS_KEY" -URL = os.environ.get("URL") or "https://hub.browserstack.com/wd/hub" - -# Creates an instance of Local -bs_local = Local() - -# You can also set an environment variable - "BROWSERSTACK_ACCESS_KEY". -bs_local_args = { "key": BROWSERSTACK_ACCESS_KEY } - -# Starts the Local instance with the required arguments -bs_local.start(**bs_local_args) - -# Check if BrowserStack local instance is running -print("Local binary connected: ", bs_local.isRunning()) - -desired_cap = { - "os" : "OS X", - "osVersion" : "Sierra", - "buildName" : "browserstack-build-1", - "sessionName" : "BStack local python", - "local" : "true", - "userName": BROWSERSTACK_USERNAME, - "accessKey": BROWSERSTACK_ACCESS_KEY -} -desired_cap["source"] = "python:sample-main:v1.0" -options = ChromeOptions() -options.set_capability('bstack:options', desired_cap) -driver = webdriver.Remote( - command_executor=URL, - options=options) -try: - driver.get("http://bs-local.com:45691/check") - body_text = WebDriverWait(driver, 10).until( - EC.visibility_of_element_located((By.CSS_SELECTOR, 'body'))).text - # check if local connected successfully - if body_text == "Up and running": - # mark test as passed if Local is accessible - driver.execute_script( - 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed", "reason": "Local Test ran successfully"}}') - else: - # mark test as failed if Local not accessible - driver.execute_script( - 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": "Local test setup failed"}}') -except Exception as err: - message = "Exception: " + str(err.__class__) + str(err.msg) - driver.execute_script( - 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": ' + json.dumps(message) + '}}') - bs_local.stop() -# Stop the driver -driver.quit() -bs_local.stop() diff --git a/scripts/parallel.py b/scripts/parallel.py deleted file mode 100644 index 31c2757..0000000 --- a/scripts/parallel.py +++ /dev/null @@ -1,105 +0,0 @@ -from dotenv import load_dotenv -import os -from selenium import webdriver -from selenium.webdriver.chrome.options import Options as ChromeOptions -from selenium.webdriver.firefox.options import Options as FirefoxOptions -from selenium.webdriver.safari.options import Options as SafariOptions -from selenium.webdriver.edge.options import Options as EdgeOptions -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.common.by import By -from selenium.common.exceptions import NoSuchElementException -from threading import Thread - -load_dotenv() -BROWSERSTACK_USERNAME = os.environ.get("BROWSERSTACK_USERNAME") or "BROWSERSTACK_USERNAME" -BROWSERSTACK_ACCESS_KEY = os.environ.get("BROWSERSTACK_ACCESS_KEY") or "BROWSERSTACK_ACCESS_KEY" -URL = os.environ.get("URL") or "https://hub.browserstack.com/wd/hub" - -capabilities = [ - { - "os": "OS X", - "osVersion": "Monterey", - "buildName" : "browserstack-build-1", - "sessionName" : "BStack parallel python", - "browserName": "chrome", - "browserVersion": "latest" - }, - { - "os": "Windows", - "osVersion": "11", - "buildName" : "browserstack-build-1", - "sessionName" : "BStack parallel python", - "browserName": "firefox", - "browserVersion": "latest" - }, - { - "osVersion": "10", - "deviceName" : "Samsung Galaxy S20", - "buildName" : "browserstack-build-1", - "sessionName" : "BStack parallel python", - "browserName": "chrome", - }, -] - -def get_browser_option(browser): - switcher = { - "chrome": ChromeOptions(), - "firefox": FirefoxOptions(), - "edge": EdgeOptions(), - "safari": SafariOptions(), - } - return switcher.get(browser, ChromeOptions()) - -def run_session(cap): - bstack_options = { - "osVersion" : cap["osVersion"], - "buildName" : cap["buildName"], - "sessionName" : cap["sessionName"], - "userName": BROWSERSTACK_USERNAME, - "accessKey": BROWSERSTACK_ACCESS_KEY - } - if "os" in cap: - bstack_options["os"] = cap["os"] - bstack_options["source"] = "python:sample-main:v1.0" - options = get_browser_option(cap["browserName"].lower()) - if "browserVersion" in cap: - options.browser_version = cap["browserVersion"] - options.set_capability('bstack:options', bstack_options) - - driver = webdriver.Remote( - command_executor=URL, - options=options) - try: - driver.get("https://bstackdemo.com/") - WebDriverWait(driver, 10).until(EC.title_contains("StackDemo")) - # Get text of an product - iPhone 12 - item_on_page = WebDriverWait(driver, 10).until( - EC.visibility_of_element_located((By.XPATH, '//*[@id="1"]/p'))).text - # Click the 'Add to cart' button if it is visible - WebDriverWait(driver, 10).until(EC.visibility_of_element_located( - (By.XPATH, '//*[@id="1"]/div[4]'))).click() - # Check if the Cart pane is visible - WebDriverWait(driver, 10).until(EC.visibility_of_element_located( - (By.CLASS_NAME, "float-cart__content"))) - ## Get text of product in cart - item_in_cart = WebDriverWait(driver, 10).until(EC.visibility_of_element_located( - (By.XPATH, '//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]'))).text - # Verify whether the product (iPhone 12) is added to cart - if item_on_page == item_in_cart: - # Set the status of test as 'passed' or 'failed' based on the condition; if item is added to cart - driver.execute_script( - 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed", "reason": "iPhone 12 has been successfully added to the cart!"}}') - except NoSuchElementException as err: - message = "Exception: " + str(err.__class__) + str(err.msg) - driver.execute_script( - 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": ' + json.dumps(message) + '}}') - except Exception as err: - message = "Exception: " + str(err.__class__) + str(err.msg) - driver.execute_script( - 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": ' + json.dumps(message) + '}}') - # Stop the driver - driver.quit() - -for cap in capabilities: - Thread(target=run_session, args=(cap,)).start() diff --git a/scripts/single.py b/scripts/test.py similarity index 68% rename from scripts/single.py rename to scripts/test.py index b8c4e5a..c6e00e3 100644 --- a/scripts/single.py +++ b/scripts/test.py @@ -1,35 +1,19 @@ -from dotenv import load_dotenv -import os import json from selenium import webdriver from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.chrome.options import Options as ChromeOptions from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException +from selenium.webdriver.chrome.options import Options as ChromeOptions -load_dotenv() -BROWSERSTACK_USERNAME = os.environ.get("BROWSERSTACK_USERNAME") or "BROWSERSTACK_USERNAME" -BROWSERSTACK_ACCESS_KEY = os.environ.get("BROWSERSTACK_ACCESS_KEY") or "BROWSERSTACK_ACCESS_KEY" -URL = os.environ.get("URL") or "https://hub.browserstack.com/wd/hub" - -bstack_options = { - "os" : "OS X", - "osVersion" : "Monterey", - "buildName" : "browserstack-build-1", - "sessionName" : "BStack single python", - "userName": BROWSERSTACK_USERNAME, - "accessKey": BROWSERSTACK_ACCESS_KEY -} -bstack_options["source"] = "python:sample-main:v1.0" options = ChromeOptions() -options.set_capability('bstack:options', bstack_options) driver = webdriver.Remote( - command_executor=URL, + command_executor='http://localhost:4444/wd/hub', options=options) + try: - driver.get("https://bstackdemo.com/") - WebDriverWait(driver, 10).until(EC.title_contains("StackDemo")) + driver.get('https://bstackdemo.com/') + WebDriverWait(driver, 10).until(EC.title_contains('StackDemo')) # Get text of an product - iPhone 12 item_on_page = WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.XPATH, '//*[@id="1"]/p'))).text @@ -38,7 +22,7 @@ (By.XPATH, '//*[@id="1"]/div[4]'))).click() # Check if the Cart pane is visible WebDriverWait(driver, 10).until(EC.visibility_of_element_located( - (By.CLASS_NAME, "float-cart__content"))) + (By.CLASS_NAME, 'float-cart__content'))) ## Get text of product in cart item_in_cart = WebDriverWait(driver, 10).until(EC.visibility_of_element_located( (By.XPATH, '//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]'))).text @@ -52,11 +36,11 @@ driver.execute_script( 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": "iPhone 12 not added to the cart!"}}') except NoSuchElementException as err: - message = "Exception: " + str(err.__class__) + str(err.msg) + message = 'Exception: ' + str(err.__class__) + str(err.msg) driver.execute_script( 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": ' + json.dumps(message) + '}}') except Exception as err: - message = "Exception: " + str(err.__class__) + str(err.msg) + message = 'Exception: ' + str(err.__class__) + str(err.msg) driver.execute_script( 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": ' + json.dumps(message) + '}}') # Stop the driver From b89285551c248dc17dc644a3ccc1c4530517ddbb Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Fri, 7 Oct 2022 10:12:14 +0530 Subject: [PATCH 02/19] add: config file and requirements --- browserstack.yml | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 4 ++-- 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 browserstack.yml diff --git a/browserstack.yml b/browserstack.yml new file mode 100644 index 0000000..02b1910 --- /dev/null +++ b/browserstack.yml @@ -0,0 +1,62 @@ +# ============================= +# Set BrowserStack Credentials +# ============================= +# Add your BrowserStack userName and acccessKey here or set BROWSERSTACK_USERNAME and +# BROWSERSTACK_ACCESS_KEY as env variables +userName: YOUR_USERNAME +accessKey: YOUR_ACCESS_KEY + +# ====================== +# Organizing your tests +# ====================== +# Use `projectName`, `buildName`, `name` capabilities to organise your tests +# `name` is the name of your test sessions and is automatically picked from your +# test name and doesn't need to be set manually when using BrowserStack SDK +# `buildName` is used to name your CI/CD job or the execution of your test suite. +# Ensure you add a dynamic identifier, like an incremental build number from your +# CI/CD or timestamp at the end of every build; otherwise tests from different +# executions will be grouped together on BrowserStack +buildName: browserstack-build-1 +# Use `projectName` to set the name of your project. Example, Marketing Website +projectName: BrowserStack Samples + +# ======================================= +# 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 + browser: Chrome + browserVersion: latest + - os: Windows + osVersion: 10 + browser: Edge + browserVersion: latest + - device: iPhone 13 + browserName: Safari + osVersion: 15 + - device: Samsung Galaxy S22 Ultra + browserName: chrome # Try 'samsung' for Samsung browser + osVersion: 12.0 + +# ========================================== +# 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) +# browserStackLocalOptions: +# Options to be passed to BrowserStack local in-case of advanced configurations + # 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 availabe 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 (Available levels: `disable`, `errors`, `warnings`, `info`, `verbose`, Default: errors) diff --git a/requirements.txt b/requirements.txt index b05ee02..6bb0568 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -python-dotenv browserstack-local >= 1.2.3 -selenium == 4.1.0 +selenium +browserstack-sdk From 858d286e8f021b5b3dc46837ae66e75b2a19a547 Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Fri, 7 Oct 2022 13:22:33 +0530 Subject: [PATCH 03/19] chore: update list of platforms --- browserstack.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/browserstack.yml b/browserstack.yml index 02b1910..c3c534b 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -34,9 +34,6 @@ platforms: osVersion: 10 browser: Edge browserVersion: latest - - device: iPhone 13 - browserName: Safari - osVersion: 15 - device: Samsung Galaxy S22 Ultra browserName: chrome # Try 'samsung' for Samsung browser osVersion: 12.0 From 2a7bccd147f1a5ed8ea17d19bc0b1be19ba3126d Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Fri, 7 Oct 2022 15:49:09 +0530 Subject: [PATCH 04/19] add: readme for sdk, rename files --- README.md | 70 +++++++++++++++----------------- {scripts => tests}/local-test.py | 0 {scripts => tests}/test.py | 0 3 files changed, 33 insertions(+), 37 deletions(-) rename {scripts => tests}/local-test.py (100%) rename {scripts => tests}/test.py (100%) diff --git a/README.md b/README.md index 66a8624..0b2d5c6 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,44 @@ # python-selenium-browserstack +Run python tests on browserstack using the SDK. ## Prerequisite ``` -python3 and pip3 should be installed +python3 should be installed ``` -## Steps to run test session - -- Install packages through requirements.txt -``` -pip3 install -r requirements.txt +## Setup +* Clone the repo ``` -- Update your credentials in .env file -```dotenv -BROWSERSTACK_USERNAME="BROWSERSTACK_USERNAME" -BROWSERSTACK_ACCESS_KEY="BROWSERSTACK_ACCESS_KEY" -URL="https://hub.browserstack.com/wd/hub" +git clone -b sdk https://github.com/browserstack/python-selenium-browserstack.git +``` +* Install packages through requirements.txt ``` -- Change the capabilities if you wish: -(For single test session, Navigate to ./scripts/single.py) -```python -desired_cap = { - ... - 'browserName': 'iPhone', - 'device': 'iPhone 11', - 'realMobile': 'true', - 'os_version': '14.0', - 'name': 'BStack-[Python] Sample Test', # test name - 'build': 'BStack Build Number 1' # CI/CD job or build name - ... -} +pip3 install -r requirements.txt ``` -- Run tests +## Set BrowserStack Credentials +* Add your BrowserStack username and access key in the `browserstack.yml` config fle. +* You can also export them as environment variables, `BROWSERSTACK_USERNAME` and `BROWSERSTACK_ACCESS_KEY`: + + #### For Linux/MacOS + ``` + export BROWSERSTACK_USERNAME= + export BROWSERSTACK_ACCESS_KEY= + ``` + #### For Windows + ``` + setx BROWSERSTACK_USERNAME= + setx BROWSERSTACK_ACCESS_KEY= + ``` - a. For single - ``` - python3 ./scripts/single.py - ``` - b. For local - ``` - python3 ./scripts/local.py - ``` - c. For parallel - ``` - python3 ./scripts/parallel.py - ``` +## Running tests +* Run sample test: + - To run the sample test across platforms defined in the `browserstack.yml` file, run: + ``` + browserstack-sdk ./scripts/test.py + ``` +* Run tests on locally hosted website: + - To run the local test across platforms defined in the `browserstack.yml` file, run: + ``` + browserstack-sdk ./scripts/local-test.py + ``` diff --git a/scripts/local-test.py b/tests/local-test.py similarity index 100% rename from scripts/local-test.py rename to tests/local-test.py diff --git a/scripts/test.py b/tests/test.py similarity index 100% rename from scripts/test.py rename to tests/test.py From 20cde6db67aeab3d2b58754a08e9f05168c88235 Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Fri, 21 Oct 2022 11:38:37 +0530 Subject: [PATCH 05/19] chore: add comments --- tests/local-test.py | 4 ++++ tests/test.py | 16 ++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/local-test.py b/tests/local-test.py index 6e0a8c7..3d31daa 100644 --- a/tests/local-test.py +++ b/tests/local-test.py @@ -6,6 +6,10 @@ from selenium.webdriver.chrome.options import Options as ChromeOptions options = ChromeOptions() + +# The webdriver management will be handled by the browserstack-sdk +# so this will be overridden and tests will run browserstack - +# without any changes to the test files! driver = webdriver.Remote( command_executor='http://localhost:4444/wd/hub', options=options) diff --git a/tests/test.py b/tests/test.py index c6e00e3..0426dba 100644 --- a/tests/test.py +++ b/tests/test.py @@ -4,12 +4,11 @@ from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException -from selenium.webdriver.chrome.options import Options as ChromeOptions -options = ChromeOptions() -driver = webdriver.Remote( - command_executor='http://localhost:4444/wd/hub', - options=options) +# The webdriver management will be handled by the browserstack-sdk +# so this will be overridden and tests will run browserstack - +# without any changes to the test files! +driver = webdriver.Chrome() try: driver.get('https://bstackdemo.com/') @@ -23,7 +22,7 @@ # Check if the Cart pane is visible WebDriverWait(driver, 10).until(EC.visibility_of_element_located( (By.CLASS_NAME, 'float-cart__content'))) - ## Get text of product in cart + # Get text of product in cart item_in_cart = WebDriverWait(driver, 10).until(EC.visibility_of_element_located( (By.XPATH, '//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]'))).text # Verify whether the product (iPhone 12) is added to cart @@ -43,5 +42,6 @@ message = 'Exception: ' + str(err.__class__) + str(err.msg) driver.execute_script( 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed", "reason": ' + json.dumps(message) + '}}') -# Stop the driver -driver.quit() +finally: + # Stop the driver + driver.quit() From 4acec786fc6807a03dbe6f5b8fcc4f11925f79d4 Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Thu, 3 Nov 2022 12:46:15 +0530 Subject: [PATCH 06/19] chore: fix command in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0b2d5c6..0d1124f 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,10 @@ pip3 install -r requirements.txt * Run sample test: - To run the sample test across platforms defined in the `browserstack.yml` file, run: ``` - browserstack-sdk ./scripts/test.py + browserstack-sdk ./tests/test.py ``` * Run tests on locally hosted website: - To run the local test across platforms defined in the `browserstack.yml` file, run: ``` - browserstack-sdk ./scripts/local-test.py + browserstack-sdk ./tests/local-test.py ``` From 60415b40fa2625e93474c10e82a70a5d6485058c Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Wed, 9 Nov 2022 11:15:10 +0530 Subject: [PATCH 07/19] update: modify local test with default localIdentifier --- tests/local-test.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/local-test.py b/tests/local-test.py index 3d31daa..65b1402 100644 --- a/tests/local-test.py +++ b/tests/local-test.py @@ -15,11 +15,10 @@ options=options) try: - driver.get('http://bs-local.com:45691/check') - body_text = WebDriverWait(driver, 10).until( - EC.visibility_of_element_located((By.CSS_SELECTOR, 'body'))).text + driver.get('http://bs-local.com:45454') + page_title = driver.title # check if local connected successfully - if body_text == 'Up and running': + if page_title == 'BrowserStack Local': # mark test as passed if Local website is accessible driver.execute_script( 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed", "reason": "Local Test ran successfully"}}') From 61b6fcfdf1e08a05c016075fb40c559041b2d287 Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Thu, 10 Nov 2022 10:52:11 +0530 Subject: [PATCH 08/19] update: title match condition --- tests/local-test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/local-test.py b/tests/local-test.py index 65b1402..b4cf1c5 100644 --- a/tests/local-test.py +++ b/tests/local-test.py @@ -18,7 +18,7 @@ driver.get('http://bs-local.com:45454') page_title = driver.title # check if local connected successfully - if page_title == 'BrowserStack Local': + if 'BrowserStack Local' in page_title: # mark test as passed if Local website is accessible driver.execute_script( 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed", "reason": "Local Test ran successfully"}}') From 84a2f47c930b22a73bafe148164ca5536ff9aa1a Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Thu, 10 Nov 2022 12:41:46 +0530 Subject: [PATCH 09/19] chore: remove unused import --- .gitignore | 1 + tests/local-test.py | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index ca91b7a..0f2dc0c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /local.log +env diff --git a/tests/local-test.py b/tests/local-test.py index b4cf1c5..5271b2b 100644 --- a/tests/local-test.py +++ b/tests/local-test.py @@ -1,8 +1,5 @@ import json from selenium import webdriver -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Options as ChromeOptions options = ChromeOptions() From ac90002af9c67f864ce86909d3e3afedcdef97a4 Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Wed, 28 Dec 2022 17:44:06 +0530 Subject: [PATCH 10/19] update: sample yml --- browserstack.yml | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/browserstack.yml b/browserstack.yml index c3c534b..d698962 100644 --- a/browserstack.yml +++ b/browserstack.yml @@ -1,24 +1,25 @@ # ============================= # Set BrowserStack Credentials # ============================= -# Add your BrowserStack userName and acccessKey here or set BROWSERSTACK_USERNAME and +# 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 # ====================== -# Organizing your tests +# BrowserStack Reporting # ====================== -# Use `projectName`, `buildName`, `name` capabilities to organise your tests -# `name` is the name of your test sessions and is automatically picked from your -# test name and doesn't need to be set manually when using BrowserStack SDK -# `buildName` is used to name your CI/CD job or the execution of your test suite. -# Ensure you add a dynamic identifier, like an incremental build number from your -# CI/CD or timestamp at the end of every build; otherwise tests from different -# executions will be grouped together on BrowserStack -buildName: browserstack-build-1 -# Use `projectName` to set the name of your project. Example, Marketing Website -projectName: BrowserStack Samples +# The following capabilities are used to set up reporting on BrowserStack: +# Set 'projectName' to the name of your project. Example, Marketing Website +projectName: BrowserStack Samples +# 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) @@ -28,13 +29,13 @@ projectName: BrowserStack Samples platforms: - os: OS X osVersion: Big Sur - browser: Chrome + browserName: Chrome browserVersion: latest - os: Windows osVersion: 10 - browser: Edge + browserName: Edge browserVersion: latest - - device: Samsung Galaxy S22 Ultra + - deviceName: Samsung Galaxy S22 Ultra browserName: chrome # Try 'samsung' for Samsung browser osVersion: 12.0 @@ -49,11 +50,14 @@ browserstackLocal: true # (Default false) # Options to be passed to BrowserStack local in-case of advanced configurations # 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 availabe here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections + # Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections + +source: python-browserstack:sample-sdk:v1.0 # =================== # 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 (Available levels: `disable`, `errors`, `warnings`, `info`, `verbose`, Default: errors) +consoleLogs: errors # Remote browser's console debug levels to be printed (Default: errors) +# Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) From f37a249a9a83e3bd8a95408b63cac5e07135ef9b Mon Sep 17 00:00:00 2001 From: Neha Agarwal Date: Mon, 10 Apr 2023 18:26:24 +0530 Subject: [PATCH 11/19] Added github action --- .github/CODEOWNERS | 1 + .github/workflows/reviewing_changes.yml | 88 +++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/reviewing_changes.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..8f0fab7 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +.github/workflows/*.yml @browserstack/asi-devs diff --git a/.github/workflows/reviewing_changes.yml b/.github/workflows/reviewing_changes.yml new file mode 100644 index 0000000..403eb0f --- /dev/null +++ b/.github/workflows/reviewing_changes.yml @@ -0,0 +1,88 @@ +# This job is to test different profiles in sdk branch against Pull Requests raised +# This workflow targets python-selenium + +name: Python-selenium Test workflow for Python from workflow_dispatch + +on: + workflow_dispatch: + inputs: + pull_request_number: + description: 'The pull request number to build' + required: true + +jobs: + comment-run: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + max-parallel: 3 + matrix: + python: ['3.7', '3.10', '3.11'] + os: [ macos-latest, windows-latest, ubuntu-latest ] + name: Python-selenium Repo ${{ matrix.python }} - ${{ matrix.os }} Sample + env: + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + + steps: + - uses: actions/checkout@v3 + with: + ref: refs/pull/${{ github.event.inputs.pull_request_number }}/head + - name: Fetch Commit SHA + run: | + git log -1 --format='%H' + echo "commit_sha=$(git log -1 --format='%H')" >> $GITHUB_ENV + echo "commit_sha=$(git log -1 --format='%H')" >> $env:GITHUB_ENV + - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 + id: status-check-in-progress + env: + job_name: Python-selenium Repo ${{ matrix.python }} - ${{ matrix.os }} Sample + with: + github-token: ${{ github.token }} + script: | + const result = await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: process.env.job_name, + head_sha: process.env.commit_sha, + status: 'in_progress' + }).catch((err) => ({status: err.status, response: err.response})); + console.log(`The status-check response : ${result.status} Response : ${JSON.stringify(result.response)}`) + if (result.status !== 201) { + console.log('Failed to create check run') + } + - name: Setup python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python }} + + - name: Install dependencies + run: python -m pip install -r requirements.txt + + - name: run tests in parallel + run: browserstack-sdk ./tests/test.py + + - name: run local tests in parallel + run: browserstack-sdk ./tests/local-test.py + + - if: always() + uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 + id: status-check-completed + env: + conclusion: ${{ job.status }} + job_name: Python-selenium Repo ${{ matrix.python }} - ${{ matrix.os }} Sample + with: + github-token: ${{ github.token }} + script: | + const result = await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: process.env.job_name, + head_sha: process.env.commit_sha, + status: 'completed', + conclusion: process.env.conclusion + }).catch((err) => ({status: err.status, response: err.response})); + console.log(`The status-check response : ${result.status} Response : ${JSON.stringify(result.response)}`) + if (result.status !== 201) { + console.log('Failed to create check run') + } From 0369bf24099ca102586bee4dbb4761b7368271b6 Mon Sep 17 00:00:00 2001 From: Neha Agarwal Date: Tue, 11 Apr 2023 10:59:07 +0530 Subject: [PATCH 12/19] fix codeowners --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8f0fab7..7e1f1b4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -.github/workflows/*.yml @browserstack/asi-devs +.github/* @browserstack/asi-devs From c5c66528bf2afb1cdf54041f3fdcd19d2ad1db4c Mon Sep 17 00:00:00 2001 From: Neha Agarwal Date: Tue, 11 Apr 2023 12:58:36 +0530 Subject: [PATCH 13/19] fix name in workflow file --- .github/workflows/reviewing_changes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reviewing_changes.yml b/.github/workflows/reviewing_changes.yml index 403eb0f..aa17fe0 100644 --- a/.github/workflows/reviewing_changes.yml +++ b/.github/workflows/reviewing_changes.yml @@ -1,7 +1,7 @@ # This job is to test different profiles in sdk branch against Pull Requests raised # This workflow targets python-selenium -name: Python-selenium Test workflow for Python from workflow_dispatch +name: Python SDK Test workflow on workflow_dispatch on: workflow_dispatch: From 52866f8573feddcd4385ed7a00190c33674eb78e Mon Sep 17 00:00:00 2001 From: Neha Agarwal Date: Mon, 19 Jun 2023 12:01:10 +0530 Subject: [PATCH 14/19] Added sessionName --- tests/local-test.py | 2 +- tests/test.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/local-test.py b/tests/local-test.py index 5271b2b..0a324b6 100644 --- a/tests/local-test.py +++ b/tests/local-test.py @@ -3,7 +3,7 @@ from selenium.webdriver.chrome.options import Options as ChromeOptions options = ChromeOptions() - +options.set_capability('sessionName', 'BStack Local Test') # The webdriver management will be handled by the browserstack-sdk # so this will be overridden and tests will run browserstack - # without any changes to the test files! diff --git a/tests/test.py b/tests/test.py index 0426dba..c242b14 100644 --- a/tests/test.py +++ b/tests/test.py @@ -4,11 +4,14 @@ from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException +from selenium.webdriver.chrome.options import Options as ChromeOptions # The webdriver management will be handled by the browserstack-sdk # so this will be overridden and tests will run browserstack - # without any changes to the test files! -driver = webdriver.Chrome() +options = ChromeOptions() +options.set_capability('sessionName', 'BStack Sample Test') +driver = webdriver.Chrome(options=options) try: driver.get('https://bstackdemo.com/') From 0f53e6c281c949749fc22955799f9cb8973f1f45 Mon Sep 17 00:00:00 2001 From: Neha Agarwal Date: Mon, 19 Jun 2023 12:02:18 +0530 Subject: [PATCH 15/19] small fix --- tests/local-test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/local-test.py b/tests/local-test.py index 0a324b6..2478a77 100644 --- a/tests/local-test.py +++ b/tests/local-test.py @@ -4,6 +4,7 @@ options = ChromeOptions() options.set_capability('sessionName', 'BStack Local Test') + # The webdriver management will be handled by the browserstack-sdk # so this will be overridden and tests will run browserstack - # without any changes to the test files! From 1d5910e3874c5e256b369ea2fbe442efd9fd707c Mon Sep 17 00:00:00 2001 From: Karan Shah <64479353+karanshah-browserstack@users.noreply.github.com> Date: Tue, 20 Jun 2023 12:21:12 +0530 Subject: [PATCH 16/19] Revert "small fix" This reverts commit 0f53e6c281c949749fc22955799f9cb8973f1f45. --- tests/local-test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/local-test.py b/tests/local-test.py index 2478a77..0a324b6 100644 --- a/tests/local-test.py +++ b/tests/local-test.py @@ -4,7 +4,6 @@ options = ChromeOptions() options.set_capability('sessionName', 'BStack Local Test') - # The webdriver management will be handled by the browserstack-sdk # so this will be overridden and tests will run browserstack - # without any changes to the test files! From 328942bf380ca12a573e201e0b8657fdccf99c41 Mon Sep 17 00:00:00 2001 From: Karan Shah <64479353+karanshah-browserstack@users.noreply.github.com> Date: Tue, 20 Jun 2023 12:21:12 +0530 Subject: [PATCH 17/19] Revert "Added sessionName" This reverts commit 52866f8573feddcd4385ed7a00190c33674eb78e. --- tests/local-test.py | 2 +- tests/test.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/local-test.py b/tests/local-test.py index 0a324b6..5271b2b 100644 --- a/tests/local-test.py +++ b/tests/local-test.py @@ -3,7 +3,7 @@ from selenium.webdriver.chrome.options import Options as ChromeOptions options = ChromeOptions() -options.set_capability('sessionName', 'BStack Local Test') + # The webdriver management will be handled by the browserstack-sdk # so this will be overridden and tests will run browserstack - # without any changes to the test files! diff --git a/tests/test.py b/tests/test.py index c242b14..0426dba 100644 --- a/tests/test.py +++ b/tests/test.py @@ -4,14 +4,11 @@ from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException -from selenium.webdriver.chrome.options import Options as ChromeOptions # The webdriver management will be handled by the browserstack-sdk # so this will be overridden and tests will run browserstack - # without any changes to the test files! -options = ChromeOptions() -options.set_capability('sessionName', 'BStack Sample Test') -driver = webdriver.Chrome(options=options) +driver = webdriver.Chrome() try: driver.get('https://bstackdemo.com/') From 9af5c5458ec062679a40ac445d0aa5401b966e53 Mon Sep 17 00:00:00 2001 From: Karan Shah <64479353+karanshah-browserstack@users.noreply.github.com> Date: Tue, 20 Jun 2023 12:21:22 +0530 Subject: [PATCH 18/19] Revert "Revert "Added sessionName"" --- tests/local-test.py | 1 + tests/test.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/local-test.py b/tests/local-test.py index 5271b2b..2478a77 100644 --- a/tests/local-test.py +++ b/tests/local-test.py @@ -3,6 +3,7 @@ from selenium.webdriver.chrome.options import Options as ChromeOptions options = ChromeOptions() +options.set_capability('sessionName', 'BStack Local Test') # The webdriver management will be handled by the browserstack-sdk # so this will be overridden and tests will run browserstack - diff --git a/tests/test.py b/tests/test.py index 0426dba..c242b14 100644 --- a/tests/test.py +++ b/tests/test.py @@ -4,11 +4,14 @@ from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException +from selenium.webdriver.chrome.options import Options as ChromeOptions # The webdriver management will be handled by the browserstack-sdk # so this will be overridden and tests will run browserstack - # without any changes to the test files! -driver = webdriver.Chrome() +options = ChromeOptions() +options.set_capability('sessionName', 'BStack Sample Test') +driver = webdriver.Chrome(options=options) try: driver.get('https://bstackdemo.com/') From 240c2a985c21dc20eb97dfdb3d9fdbe2111df5b7 Mon Sep 17 00:00:00 2001 From: Neha Agarwal Date: Mon, 10 Jul 2023 13:24:33 +0530 Subject: [PATCH 19/19] delete sdk workflow --- .github/workflows/reviewing_changes.yml | 88 ------------------------- 1 file changed, 88 deletions(-) delete mode 100644 .github/workflows/reviewing_changes.yml diff --git a/.github/workflows/reviewing_changes.yml b/.github/workflows/reviewing_changes.yml deleted file mode 100644 index aa17fe0..0000000 --- a/.github/workflows/reviewing_changes.yml +++ /dev/null @@ -1,88 +0,0 @@ -# This job is to test different profiles in sdk branch against Pull Requests raised -# This workflow targets python-selenium - -name: Python SDK Test workflow on workflow_dispatch - -on: - workflow_dispatch: - inputs: - pull_request_number: - description: 'The pull request number to build' - required: true - -jobs: - comment-run: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - max-parallel: 3 - matrix: - python: ['3.7', '3.10', '3.11'] - os: [ macos-latest, windows-latest, ubuntu-latest ] - name: Python-selenium Repo ${{ matrix.python }} - ${{ matrix.os }} Sample - env: - BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} - BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - - steps: - - uses: actions/checkout@v3 - with: - ref: refs/pull/${{ github.event.inputs.pull_request_number }}/head - - name: Fetch Commit SHA - run: | - git log -1 --format='%H' - echo "commit_sha=$(git log -1 --format='%H')" >> $GITHUB_ENV - echo "commit_sha=$(git log -1 --format='%H')" >> $env:GITHUB_ENV - - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 - id: status-check-in-progress - env: - job_name: Python-selenium Repo ${{ matrix.python }} - ${{ matrix.os }} Sample - with: - github-token: ${{ github.token }} - script: | - const result = await github.rest.checks.create({ - owner: context.repo.owner, - repo: context.repo.repo, - name: process.env.job_name, - head_sha: process.env.commit_sha, - status: 'in_progress' - }).catch((err) => ({status: err.status, response: err.response})); - console.log(`The status-check response : ${result.status} Response : ${JSON.stringify(result.response)}`) - if (result.status !== 201) { - console.log('Failed to create check run') - } - - name: Setup python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python }} - - - name: Install dependencies - run: python -m pip install -r requirements.txt - - - name: run tests in parallel - run: browserstack-sdk ./tests/test.py - - - name: run local tests in parallel - run: browserstack-sdk ./tests/local-test.py - - - if: always() - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 - id: status-check-completed - env: - conclusion: ${{ job.status }} - job_name: Python-selenium Repo ${{ matrix.python }} - ${{ matrix.os }} Sample - with: - github-token: ${{ github.token }} - script: | - const result = await github.rest.checks.create({ - owner: context.repo.owner, - repo: context.repo.repo, - name: process.env.job_name, - head_sha: process.env.commit_sha, - status: 'completed', - conclusion: process.env.conclusion - }).catch((err) => ({status: err.status, response: err.response})); - console.log(`The status-check response : ${result.status} Response : ${JSON.stringify(result.response)}`) - if (result.status !== 201) { - console.log('Failed to create check run') - }