diff --git a/.browserstack b/.browserstack deleted file mode 100644 index 2eb6b4b..0000000 --- a/.browserstack +++ /dev/null @@ -1,3 +0,0 @@ -[credentials] -username = BROWSERSTACK_USERNAME -key = BROWSERSTACK_ACCESS_KEY diff --git a/.gitignore b/.gitignore index c723d19..ac13aa0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .vscode __pycache__ .pytest_cache -local.log env +local.log diff --git a/README.md b/README.md index 59795ee..a016396 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PyTest with Browserstack -PyTest Integration with BrowserStack. +PyTest Integration with BrowserStack using SDK. ![BrowserStack Logo](https://d98b8t1nnulk5.cloudfront.net/production/images/layout/logo-header.png?1469004780) ## Prerequisite @@ -8,19 +8,27 @@ PyTest Integration with BrowserStack. ## Setup -* Clone the repo +* Clone the repo with `git clone -b sdk https://github.com/browserstack/pytest-browserstack.git` +* It is recommended to use a virtual environment to install dependencies. To create a virtual environment: + ``` + python3 -m venv env + source env/bin/activate # on Mac + env\Scripts\activate # on Windows + ``` * Install dependencies `pip install -r requirements.txt` -* To run your automated tests using BrowserStack, you must provide a valid username and access key. This can be done either by using a .browserstack configuration file in the working directory or your home directory, by setting the BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY environment variables, or by adding user & key to config file. - - -## Run tests on locally hosted websites -* To run a local test, (if you have not set the BROWSERSTACK_ACCESS_KEY environment variable) first go to config/local.json then edit key on line 3 -* Run `paver run local` - -## Run sample tests -* To run parallel tests, run `paver run sample` - - Understand how many parallel sessions you need by using our [Parallel Test Calculator](https://www.browserstack.com/automate/parallel-calculator?ref=github) +* To run your automated tests using BrowserStack, you must provide a valid username and access key. This can be done either by providing your username and access key in the `browserstack.yml` configuration file, or by setting the `BROWSERSTACK_USERNAME` and `BROWSERSTACK_ACCESS_KEY` environment variables. + +## Run sample test in parallel: +* To run the sample test across platforms defined in the configuration file run: +``` + browserstack-sdk pytest -s tests/bstack-sample-test.py +``` + +## Run a sample local test: +* To run the sample local test across platforms defined in the configuration file, you just need to set `browserstackLocal` key as true in the `browserstack.yml` file and run: +``` + browserstack-sdk pytest -s tests/bstack-local-test.py +``` ## Notes * You can view your test results on the [BrowserStack Automate dashboard](https://www.browserstack.com/automate) diff --git a/browserstack.yml b/browserstack.yml new file mode 100644 index 0000000..fa0c5b6 --- /dev/null +++ b/browserstack.yml @@ -0,0 +1,77 @@ +# ============================= +# 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 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} +# Set `framework` of your test suite. Example, `testng`, `cucumber`, `cucumber-testng` +# This property is needed to send test context to BrowserStack (test name, status) +framework: pytest + +# ======================================= +# 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 + +# ========================================== +# 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 available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections + +source: pytest-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 (Default: errors) +# Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) diff --git a/config/local.json b/config/local.json deleted file mode 100644 index e490cb8..0000000 --- a/config/local.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "user": "BROWSERSTACK_USERNAME", - "key": "BROWSERSTACK_ACCESS_KEY", - "capabilities": { - "bstack:options": { - "projectName": "Pytest Browserstack", - "buildName": "browserstack-build-1", - "sessionName": "BStack local pytest", - "local": true, - "debug": "true", - "networkLogs": "true" - } - }, - "environments": [ - { - "bstack:options": { - "os": "Windows", - "osVersion": "11" - }, - "browserName": "chrome", - "browserVersion": "latest" - } - ] -} diff --git a/config/sample.json b/config/sample.json deleted file mode 100644 index 2bfb4ab..0000000 --- a/config/sample.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "user": "BROWSERSTACK_USERNAME", - "key": "BROWSERSTACK_ACCESS_KEY", - "capabilities": { - "bstack:options": { - "projectName": "Pytest Browserstack", - "buildName": "browserstack-build-1", - "sessionName": "BStack sample pytest", - "local": false, - "debug": "true", - "networkLogs": "true" - } - }, - "environments": [ - { - "bstack:options": { - "os": "Windows", - "osVersion": "11" - }, - "browserName": "chrome", - "browserVersion": "latest" - }, - { - "bstack:options": { - "os": "OS X", - "osVersion": "Monterey" - }, - "browserName": "firefox", - "browserVersion": "latest" - }, - { - "bstack:options": { - "osVersion": "10.0", - "deviceName": "Samsung Galaxy S20" - } - } - ] -} diff --git a/conftest.py b/conftest.py deleted file mode 100644 index 821e950..0000000 --- a/conftest.py +++ /dev/null @@ -1,41 +0,0 @@ -import pytest -from browserstack.local import Local -import os, json -from jsonmerge import merge -CONFIG_FILE = os.environ['CONFIG_FILE'] if 'CONFIG_FILE' in os.environ else 'config/single.json' -TASK_ID = int(os.environ['TASK_ID']) if 'TASK_ID' in os.environ else 0 - -with open(CONFIG_FILE) as data_file: - CONFIG = json.load(data_file) - -bs_local = None - -BROWSERSTACK_USERNAME = os.environ['BROWSERSTACK_USERNAME'] if 'BROWSERSTACK_USERNAME' in os.environ else CONFIG["user"] -BROWSERSTACK_ACCESS_KEY = os.environ['BROWSERSTACK_ACCESS_KEY'] if 'BROWSERSTACK_ACCESS_KEY' in os.environ else CONFIG["key"] - -def start_local(): - """Code to start browserstack local before start of test.""" - global bs_local - bs_local = Local() - bs_local_args = { "key": BROWSERSTACK_ACCESS_KEY or "access_key", "forcelocal": "true" } - bs_local.start(**bs_local_args) - -def stop_local(): - """Code to stop browserstack local after end of test.""" - global bs_local - if bs_local is not None: - bs_local.stop() - -@pytest.fixture(scope='session') -def session_capabilities(): - capabilities = merge(CONFIG['environments'][TASK_ID],CONFIG["capabilities"]) - capabilities['bstack:options']['userName'] = BROWSERSTACK_USERNAME - capabilities['bstack:options']['accessKey'] = BROWSERSTACK_ACCESS_KEY - capabilities['bstack:options']['source'] = 'pytest:sample-main:v1.0' - if "local" in capabilities['bstack:options'] and capabilities['bstack:options']['local']: - start_local() - return capabilities - - -def pytest_sessionfinish(session, exitstatus): - stop_local() diff --git a/pavement.py b/pavement.py deleted file mode 100644 index 3accb7c..0000000 --- a/pavement.py +++ /dev/null @@ -1,37 +0,0 @@ -from paver.easy import * -from paver.setuputils import setup -from multiprocess import Process -import platform -import json - -setup( - name = "pytest-browserstack", - version = "0.1.0", - author = "BrowserStack", - author_email = "support@browserstack.com", - description = ("PyTest Integration with BrowserStack"), - license = "MIT", - keywords = "example selenium browserstack", - url = "https://github.com/browserstack/pytest-browserstack", - packages=['tests'] -) - -def run_py_test(config, task_id=0): - if platform.system() == "Windows": - sh('cmd /C "set CONFIG_FILE=config/%s.json && set TASK_ID=%s && pytest -s tests/test_%s.py --driver Browserstack"' % (config, task_id, config)) - else: - sh('CONFIG_FILE=config/%s.json TASK_ID=%s pytest -s tests/test_%s.py --driver Browserstack' % (config, task_id, config)) - -@task -@consume_nargs(1) -def run(args): - """Run single, local and parallel test using different config.""" - jobs = [] - config_file = 'config/%s.json' % (args[0]) - with open(config_file) as data_file: - CONFIG = json.load(data_file) - environments = CONFIG['environments'] - for i in range(len(environments)): - p = Process(target=run_py_test, args=(args[0], i)) - jobs.append(p) - p.start() diff --git a/requirements.txt b/requirements.txt index 8acdc13..0bf126d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,5 @@ browserstack-local -jsonmerge -multiprocess -paver -selenium==4.* -psutil -pytest -pytest-variables +selenium>=3.14 +pytest==7.4.4 pytest-selenium -pytest-xdist +browserstack-sdk diff --git a/tests/bstack-local-test.py b/tests/bstack-local-test.py new file mode 100644 index 0000000..2e76538 --- /dev/null +++ b/tests/bstack-local-test.py @@ -0,0 +1,6 @@ +import pytest + +def test_local(selenium): + selenium.get('http://bs-local.com:45454') + local_page_title = selenium.title + assert local_page_title == 'BrowserStack Local' diff --git a/tests/test_sample.py b/tests/bstack-sample-test.py similarity index 60% rename from tests/test_sample.py rename to tests/bstack-sample-test.py index 7ba9692..3a21d93 100644 --- a/tests/test_sample.py +++ b/tests/bstack-sample-test.py @@ -1,5 +1,3 @@ -from browserstack.local import Local -from selenium import webdriver import pytest from selenium.webdriver.common.by import By @@ -18,8 +16,5 @@ def test_example(selenium): # locating product in cart and getting name of the product in cart productCartText = selenium.find_element(By.XPATH, '//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]').text - # checking whether product has been added to cart by comparing product name and marking test pass or fail - if productText == productCartText: - selenium.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed","reason": "Test Passed Successfully"}}') - else: - selenium.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed","reason": "Product added to the cart not same as selected"}}') + # checking whether product has been added to cart by comparing product name + assert productCartText == productText diff --git a/tests/test_local.py b/tests/test_local.py deleted file mode 100644 index 5d4d72f..0000000 --- a/tests/test_local.py +++ /dev/null @@ -1,10 +0,0 @@ -import pytest -from selenium.webdriver.common.by import By - -def test_local(selenium): - selenium.get('http://bs-local.com:45691/check') - - if selenium.find_element(By.CSS_SELECTOR, 'body').text == 'Up and running': - selenium.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed","reason": "Local is Running"}}') - else: - selenium.execute_script('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"failed","reason": "Local is Not Running"}}')