-
Notifications
You must be signed in to change notification settings - Fork 0
Add BrowserStack SDK + Playwright behave sample #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2ff9314
0f337d2
fa69904
5030da4
4392c39
943aefd
9f41152
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # Runs the behave + Playwright sample against BrowserStack SDK on workflow_dispatch. | ||
| # Mirrors the pattern from browserstack/cucumber-java-playwright-browserstack: | ||
| # triggered manually with a commit SHA, posts check statuses back to that SHA so | ||
| # results show up on PRs without binding to push/pull_request triggers. | ||
|
|
||
| name: Behave Playwright SDK Test workflow on workflow_dispatch | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| commit_sha: | ||
| description: 'The full commit id to build' | ||
| required: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| sample-run: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| checks: write | ||
| name: Behave Playwright Sample | ||
| env: | ||
| BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} | ||
| BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ github.event.inputs.commit_sha }} | ||
|
|
||
| - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 | ||
| id: status-check-in-progress | ||
| env: | ||
| job_name: Behave Playwright Sample | ||
| commit_sha: ${{ github.event.inputs.commit_sha }} | ||
| 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: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.10' | ||
| cache: pip | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| pip install -r requirements.txt | ||
| playwright install chromium | ||
|
|
||
| - name: Run sample on BrowserStack | ||
| run: browserstack-sdk behave features/sample.feature | ||
|
|
||
| - if: always() | ||
| uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 | ||
| id: status-check-completed | ||
| env: | ||
| conclusion: ${{ job.status }} | ||
| job_name: Behave Playwright Sample | ||
| commit_sha: ${{ github.event.inputs.commit_sha }} | ||
| 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') | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| .venv/ | ||
| __pycache__/ | ||
| *.pyc | ||
| log/ | ||
| local.log |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,77 @@ | ||
| # behave-playwright-browserstack | ||
| Sample repo for customers | ||
| behave-playwright-browserstack (BrowserStack SDK + Playwright) | ||
| =============================================================== | ||
|
|
||
| This repo shows how to run [behave](https://behave.readthedocs.io/) tests on BrowserStack using the [BrowserStack Python SDK](https://pypi.org/project/browserstack-sdk/) and [Playwright Python](https://playwright.dev/python/). The SDK handles capability injection, BrowserStack routing for Playwright launches, parallelization, and BrowserStack Local for you — you describe platforms once in `browserstack.yml` and run the test command unchanged. | ||
|
|
||
| ## Setup | ||
| * Clone this repo | ||
| * Install dependencies (creates the BrowserStack SDK CLI on `PATH` and downloads Playwright Chromium) | ||
| ```sh | ||
| pip install -r requirements.txt | ||
| playwright install chromium | ||
| ``` | ||
| * Set `BROWSERSTACK_USERNAME` and `BROWSERSTACK_ACCESS_KEY` as environment variables, or replace `userName` and `accessKey` directly in `browserstack.yml` with your [BrowserStack Username and Access Key](https://www.browserstack.com/accounts/settings). Env vars take precedence. | ||
|
|
||
| ### Running your tests | ||
| There are two sample scenarios in `features/`: | ||
|
|
||
| * **`features/sample.feature`** — drives `https://www.bstackdemo.com` (a public site) and adds a product to the cart. | ||
| * **`features/local.feature`** — drives `http://bs-local.com:45454/` through the BrowserStack Local tunnel; verifies the page title contains "BrowserStack Local". | ||
|
|
||
| `browserstack.yml` enables `browserstackLocal: true`, so the SDK starts and stops the BrowserStack Local tunnel for you on every run — no manual binary lifecycle. | ||
|
|
||
| #### Sample test (public site) | ||
| Runs in parallel across the 3 Playwright browser engines (chromium / firefox / webkit) declared in `browserstack.yml`: | ||
|
|
||
| ```sh | ||
| browserstack-sdk behave features/sample.feature | ||
| ``` | ||
|
|
||
| #### Local test (private / localhost host) | ||
| Start a local HTTP server first — `features/local-html/` contains a tiny page titled "BrowserStack Local Test Page": | ||
|
|
||
| ```sh | ||
| python3 -m http.server 45454 --directory features/local-html | ||
| ``` | ||
|
|
||
| Then in a separate terminal: | ||
|
|
||
| ```sh | ||
| browserstack-sdk behave features/local.feature | ||
| ``` | ||
|
|
||
| `bs-local.com` is a hostname BrowserStack Local resolves to your machine inside the remote browser — for your own app, point your scenarios at `http://bs-local.com:<port>/` instead of a public URL. | ||
|
|
||
| Understand how many parallel sessions you need by using our [Parallel Test Calculator](https://www.browserstack.com/automate/parallel-calculator?ref=github). | ||
|
|
||
| Alternatively the variables can be set in the environment using env or your CI framework (like GitHub Actions or Jenkins). See `.github/workflows/build.yml` for a GitHub Actions example — it runs on `workflow_dispatch` (manual trigger) with a commit SHA input and posts a status check back to that commit. | ||
|
|
||
| ### How the SDK changes things | ||
| - **One `browserstack.yml`** declares platforms; the SDK picks them up automatically. | ||
| - **The SDK runs platforms in parallel for you** — no hand-rolled parallel runner; the SDK forks one behave run per `(platform × parallelsPerPlatform)` cell. | ||
| - **The SDK monkeypatches Playwright's browser launches** — the test code calls `chromium.launch()` and the SDK transparently routes the launch to the per-platform browser configured in `browserstack.yml` (chromium, firefox, or webkit). No `chromium.connect(wss_url)` plumbing is needed in customer code. | ||
| - **The CLI is `browserstack-sdk behave …`** — wraps `behave` and injects the SDK at runtime. | ||
|
|
||
| ### Repo layout | ||
| ``` | ||
| . | ||
| ├── browserstack.yml # SDK config: credentials, 3 parallel platforms, Local toggle, reporting | ||
| ├── requirements.txt | ||
| └── features/ | ||
| ├── sample.feature # bstackdemo add-to-cart scenario | ||
| ├── local.feature # BrowserStack Local tunnel scenario | ||
| ├── local-html/ | ||
| │ └── index.html # static page served on :45454 for local.feature | ||
| ├── environment.py # behave hooks: launch browser, hand to context.page | ||
| └── steps/ | ||
| ├── sample_steps.py | ||
| └── local_steps.py | ||
| ``` | ||
|
|
||
| ### Further Reading | ||
| - [behave](https://behave.readthedocs.io/) | ||
| - [Playwright Python](https://playwright.dev/python/) | ||
| - [BrowserStack documentation for Playwright](https://www.browserstack.com/docs/automate/playwright) | ||
| - [BrowserStack Python SDK on PyPI](https://pypi.org/project/browserstack-sdk/) | ||
|
|
||
| Happy Testing! | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| # ============================= | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please ensure that the structure is similar to: https://github.com/browserstack/junit-browserstack/blob/master/junit-5/browserstack.yml
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addressed |
||
| # 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: behave-playwright-sdk-build-1 | ||
| # `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: behave | ||
|
|
||
| source: behave-playwright-browserstack:sample-main:v1.0 | ||
|
|
||
| # ======================================= | ||
| # 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) | ||
| # The three entries below map 1:1 to the Playwright engine families (chromium / firefox / | ||
| # webkit). Customer code calls `chromium.launch()` and the SDK transparently routes the | ||
| # launch to the per-platform browser at runtime, so no per-platform branching is needed. | ||
| platforms: | ||
| - os: Windows | ||
| osVersion: 11 | ||
| browserName: chrome | ||
| browserVersion: latest | ||
| - os: Windows | ||
| osVersion: 11 | ||
| browserName: playwright-firefox | ||
| browserVersion: latest | ||
| - os: OS X | ||
| osVersion: Sonoma | ||
| browserName: playwright-webkit | ||
| browserVersion: latest | ||
|
|
||
| # ======================= | ||
| # 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 # <boolean> (Default false) | ||
|
|
||
| # Options to be passed to BrowserStack local in-case of advanced configurations | ||
| # browserStackLocalOptions: | ||
| # localIdentifier: # <string> (Default: null) Needed if you need to run multiple instances of local. | ||
| # forceLocal: true # <boolean> (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: true # <boolean> # Set to true if you need screenshots for every selenium command ran | ||
| networkLogs: false # <boolean> Set to true to enable HAR logs capturing | ||
| consoleLogs: errors # <string> Remote browser's console debug levels to be printed (Default: errors) | ||
| # Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| from playwright.sync_api import sync_playwright | ||
|
|
||
|
|
||
| def before_scenario(context, scenario): | ||
| # Customer code calls `chromium.launch()` directly. The BrowserStack SDK | ||
| # monkeypatches Playwright at runtime so this launch is routed to the | ||
| # browser configured in the platform entry the SDK is currently driving | ||
| # — works unchanged for chromium, firefox, and webkit platforms. | ||
| context.pw = sync_playwright().start() | ||
| context.browser = context.pw.chromium.launch() | ||
| context.page = context.browser.new_page() | ||
|
|
||
|
|
||
| def after_scenario(context, scenario): | ||
| try: | ||
| context.browser.close() | ||
| finally: | ||
| context.pw.stop() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="utf-8" /> | ||
| <title>BrowserStack Local Test Page</title> | ||
| </head> | ||
| <body> | ||
| <h1>BrowserStack Local Test Page</h1> | ||
| <p> | ||
| Served on <code>http://localhost:45454/</code> for the | ||
| <code>features/local.feature</code> scenario. The BrowserStack Local | ||
| tunnel resolves <code>bs-local.com:45454</code> on the remote browser | ||
| back to this page. | ||
| </p> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| Feature: Verify BrowserStack Local | ||
|
|
||
| Scenario: Navigate to a page served on localhost through the BrowserStack Local tunnel | ||
| Given I visit local app website | ||
| Then the page title should contain "BrowserStack Local" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| Feature: Browserstack test | ||
|
|
||
| Scenario: Can add the product in cart | ||
| Given I visit bstackdemo website | ||
| When I add a product to the cart | ||
| Then I should see same product in cart section |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| from behave import given, then | ||
|
|
||
|
|
||
| @given("I visit local app website") | ||
| def visit_local_app(context): | ||
| context.page.goto("http://bs-local.com:45454/") | ||
|
|
||
|
|
||
| @then('the page title should contain "{expected}"') | ||
| def verify_title_contains(context, expected): | ||
| title = context.page.title() | ||
| assert expected in title, ( | ||
| f"expected title to contain {expected!r}, got {title!r}" | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| from behave import given, when, then | ||
|
|
||
|
|
||
| @given("I visit bstackdemo website") | ||
| def visit_bstackdemo(context): | ||
| context.page.goto("https://www.bstackdemo.com/") | ||
| assert context.page.title() == "StackDemo" | ||
|
|
||
|
|
||
| @when("I add a product to the cart") | ||
| def add_product(context): | ||
| product_locator = context.page.locator('xpath=//*[@id="1"]/p') | ||
| context.product_on_page_text = product_locator.text_content() | ||
| context.page.locator('xpath=//*[@id="1"]/div[4]').click() | ||
|
|
||
|
|
||
| @then("I should see same product in cart section") | ||
| def verify_cart(context): | ||
| cart = context.page.locator('xpath=//*[@class="float-cart__content"]') | ||
| cart.wait_for(state="visible") | ||
| cart_product_locator = context.page.locator( | ||
| 'xpath=//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]' | ||
| ) | ||
| product_on_cart_text = cart_product_locator.text_content() | ||
| assert product_on_cart_text == context.product_on_page_text, ( | ||
| f"expected {context.product_on_page_text!r} in cart, got {product_on_cart_text!r}" | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| browserstack-sdk>=1.46.0 | ||
| behave>=1.2.7 | ||
| playwright==1.49.0 |
Uh oh!
There was an error while loading. Please reload this page.