From fd315496529357389fde8b852e205206b69bdf7c Mon Sep 17 00:00:00 2001 From: Aakash Hotchandani Date: Wed, 3 Jun 2026 16:59:42 +0530 Subject: [PATCH 1/5] feat: add BrowserStack SDK sample (Node.js / Playwright / Cucumber-JS / Automate) Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 9 ++++ README.md | 57 ++++++++++++++++++++++- browserstack.yml | 26 +++++++++++ features/local.feature | 5 ++ features/single.feature | 6 +++ features/step_definitions/bstack-steps.js | 33 +++++++++++++ features/step_definitions/local-steps.js | 17 +++++++ features/support/env.js | 5 ++ features/support/hooks.js | 26 +++++++++++ package.json | 22 +++++++++ 10 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 browserstack.yml create mode 100644 features/local.feature create mode 100644 features/single.feature create mode 100644 features/step_definitions/bstack-steps.js create mode 100644 features/step_definitions/local-steps.js create mode 100644 features/support/env.js create mode 100644 features/support/hooks.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..943cc8e --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +node_modules +package-lock.json +.env +local.log +browserstack.err +log/ +screenshots +**/.DS_Store +.vscode/ diff --git a/README.md b/README.md index 5830aae..6ab691d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,55 @@ -# cucumber-js-playwright-browserstack -Creating a sample repo for different Playwright languages and runners +# Cucumber-JS (Playwright) with BrowserStack + +Run your Cucumber-JS + Playwright tests on the BrowserStack cloud using the [BrowserStack Node SDK](https://www.browserstack.com/docs/automate/selenium/sdk-overview). This sample tests on BrowserStack Automate. + +## Prerequisites + +- A [BrowserStack](https://www.browserstack.com/) account (username + access key). +- [Node.js](https://nodejs.org/) (>= 14) and npm installed. +- Your BrowserStack credentials, available from your [account settings](https://www.browserstack.com/accounts/settings). + +## Setup + +1. Clone this repository: + + ```bash + git clone https://github.com/browserstack/cucumber-js-playwright-browserstack.git + cd cucumber-js-playwright-browserstack + ``` + +2. Install the dependencies: + + ```bash + npm install + ``` + +3. Set your BrowserStack credentials as environment variables: + + ```bash + export BROWSERSTACK_USERNAME="YOUR_USERNAME" + export BROWSERSTACK_ACCESS_KEY="YOUR_ACCESS_KEY" + ``` + + Alternatively, edit `browserstack.yml` and replace `YOUR_USERNAME` and `YOUR_ACCESS_KEY` with your credentials. + +## Run Sample Test + +Runs the sample test against the public BrowserStack demo website: + +```bash +npx browserstack-node-sdk cucumber-js features/single.feature +``` + +## Run Local Test + +Runs the local test through the BrowserStack Local tunnel (`browserstackLocal: true` in `browserstack.yml`): + +```bash +npx browserstack-node-sdk cucumber-js features/local.feature +``` + +## Notes + +- View your test runs and debug results on the [BrowserStack Automate dashboard](https://automate.browserstack.com/). +- Platforms, parallelism, and product capabilities (such as `testObservability`) are configured in `browserstack.yml`. +- The BrowserStack Node SDK patches the Playwright library at runtime and routes browser sessions to BrowserStack — no changes to your test code are required. diff --git a/browserstack.yml b/browserstack.yml new file mode 100644 index 0000000..c1815aa --- /dev/null +++ b/browserstack.yml @@ -0,0 +1,26 @@ +userName: YOUR_USERNAME +accessKey: YOUR_ACCESS_KEY +projectName: BrowserStack Samples +buildName: browserstack build +buildIdentifier: '#${BUILD_NUMBER}' +framework: cucumber-js +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 + osVersion: 12.0 +parallelsPerPlatform: 1 +browserstackAutomation: true +browserstackLocal: true +source: cucumber-js-playwright-browserstack:sample-sdk:v1.0 +testObservability: true +debug: false +networkLogs: false +consoleLogs: errors diff --git a/features/local.feature b/features/local.feature new file mode 100644 index 0000000..439514d --- /dev/null +++ b/features/local.feature @@ -0,0 +1,5 @@ +Feature: BrowserStack Local Testing + + Scenario: BStack Local Test - Can check tunnel working + When I open dashboard + Then I should see "BrowserStack Local" diff --git a/features/single.feature b/features/single.feature new file mode 100644 index 0000000..25abb5b --- /dev/null +++ b/features/single.feature @@ -0,0 +1,6 @@ +Feature: Browserstack test + + Scenario: BStack Sample Test - Can add a product to the cart + Given I visit bstackdemo website + When I add a product to the cart + Then I should see the same product in the cart section diff --git a/features/step_definitions/bstack-steps.js b/features/step_definitions/bstack-steps.js new file mode 100644 index 0000000..df86753 --- /dev/null +++ b/features/step_definitions/bstack-steps.js @@ -0,0 +1,33 @@ +'use strict'; + +const assert = require('assert'); +const { Given, When, Then } = require('@cucumber/cucumber'); + +let productText = ''; + +Given(/^I visit bstackdemo website/, async function () { + await this.page.goto('https://bstackdemo.com/'); +}); + +When(/^I add a product to the cart/, async function () { + await this.page.waitForSelector('//*[@id="1"]/p'); + productText = await this.page.locator('//*[@id="1"]/p').innerText(); + await this.page.locator('//*[@id="1"]/div[4]').click(); +}); + +Then(/^I should see the same product in the cart section/, async function () { + await this.page.waitForSelector('.float-cart__content'); + + await this.page.waitForSelector( + '//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]' + ); + const productCartText = await this.page + .locator('//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]') + .innerText(); + + assert.strictEqual( + productCartText, + productText, + 'Expected product to be ' + productText + ); +}); diff --git a/features/step_definitions/local-steps.js b/features/step_definitions/local-steps.js new file mode 100644 index 0000000..f09d482 --- /dev/null +++ b/features/step_definitions/local-steps.js @@ -0,0 +1,17 @@ +'use strict'; + +const assert = require('assert'); +const { When, Then } = require('@cucumber/cucumber'); + +When(/^I open dashboard$/, async function () { + await this.page.goto('http://bs-local.com:45454'); +}); + +Then(/^I should see "([^"]*)"$/, async function (sourceMatch) { + const title = await this.page.title(); + assert.strictEqual( + title, + sourceMatch, + 'Expected page title to be ' + sourceMatch + ); +}); diff --git a/features/support/env.js b/features/support/env.js new file mode 100644 index 0000000..11f84d3 --- /dev/null +++ b/features/support/env.js @@ -0,0 +1,5 @@ +'use strict'; + +const { setDefaultTimeout } = require('@cucumber/cucumber'); + +setDefaultTimeout(60 * 1000); diff --git a/features/support/hooks.js b/features/support/hooks.js new file mode 100644 index 0000000..d60f846 --- /dev/null +++ b/features/support/hooks.js @@ -0,0 +1,26 @@ +'use strict'; + +const { chromium } = require('playwright'); +const { Before, After } = require('@cucumber/cucumber'); + +// When run through `browserstack-node-sdk cucumber-js`, the BrowserStack Node +// SDK patches the Playwright library at import time and routes the browser +// session to BrowserStack using the capabilities declared in browserstack.yml. +// No CDP wsEndpoint or credentials are needed in the test code. +Before(async function () { + this.browser = await chromium.launch(); + this.context = await this.browser.newContext(); + this.page = await this.context.newPage(); +}); + +After(async function () { + if (this.page) { + await this.page.close(); + } + if (this.context) { + await this.context.close(); + } + if (this.browser) { + await this.browser.close(); + } +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..ddd3f9d --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "cucumber-js-playwright-browserstack", + "version": "0.1.0", + "description": "Playwright examples for Cucumber-JS and BrowserStack Automate using the BrowserStack Node SDK", + "repository": { + "type": "git", + "url": "git+https://github.com/browserstack/cucumber-js-playwright-browserstack.git" + }, + "scripts": { + "test": "browserstack-node-sdk cucumber-js", + "sample-test": "browserstack-node-sdk cucumber-js features/single.feature", + "sample-local-test": "browserstack-node-sdk cucumber-js features/local.feature" + }, + "devDependencies": { + "@cucumber/cucumber": "^10.0.1", + "playwright": "^1.57.0" + }, + "dependencies": { + "browserstack-local": "^1.5.5", + "browserstack-node-sdk": "latest" + } +} From 81050f8bc02d2a9a7a7391521c16cbcd79acf20d Mon Sep 17 00:00:00 2001 From: Aakash Hotchandani Date: Wed, 3 Jun 2026 22:36:56 +0530 Subject: [PATCH 2/5] fix(sdk): depend on @playwright/test and import from it Same root cause as jest-playwright: SDK patches @playwright/test's BrowserType. Resolves the preInitialize compareVersions crash. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 8 ++++++++ features/support/hooks.js | 2 +- package.json | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 943cc8e..99221b8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,11 @@ log/ screenshots **/.DS_Store .vscode/ +.venv +venv +__pycache__ +bin/ +obj/ +target/ +*.log +.pytest_cache diff --git a/features/support/hooks.js b/features/support/hooks.js index d60f846..32547c5 100644 --- a/features/support/hooks.js +++ b/features/support/hooks.js @@ -1,6 +1,6 @@ 'use strict'; -const { chromium } = require('playwright'); +const { chromium } = require('@playwright/test'); const { Before, After } = require('@cucumber/cucumber'); // When run through `browserstack-node-sdk cucumber-js`, the BrowserStack Node diff --git a/package.json b/package.json index ddd3f9d..8984cb0 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@cucumber/cucumber": "^10.0.1", - "playwright": "^1.57.0" + "@playwright/test": "^1.57.0" }, "dependencies": { "browserstack-local": "^1.5.5", From 24cc8a1cd5529e1119f679eef94bd7584f7acf9d Mon Sep 17 00:00:00 2001 From: Aakash Hotchandani Date: Wed, 3 Jun 2026 23:06:31 +0530 Subject: [PATCH 3/5] fix: pin browserstack-node-sdk 1.49.3 for cucumber-js+Playwright (1.55.x regression) Verified live: sessions run on BrowserStack (Windows/Edge + Android passed, macOS done). Same 1.55.x launch-patcher regression as jest; aligned to the working reference (cucumber-js) deps: @playwright/test 1.55.0, selenium-webdriver. Co-Authored-By: Claude Opus 4.8 (1M context) --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8984cb0..183b7d1 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,11 @@ }, "devDependencies": { "@cucumber/cucumber": "^10.0.1", - "@playwright/test": "^1.57.0" + "@playwright/test": "1.55.0", + "browserstack-node-sdk": "1.49.3" }, "dependencies": { "browserstack-local": "^1.5.5", - "browserstack-node-sdk": "latest" + "selenium-webdriver": "4.28.1" } } From e28e2bddc18ff3cb753a1fa2a03118241cc7ac1a Mon Sep 17 00:00:00 2001 From: Aakash Hotchandani Date: Fri, 12 Jun 2026 12:09:55 +0530 Subject: [PATCH 4/5] Add BrowserStack SDK sample-test GitHub Actions workflow (workflow_dispatch) --- .github/workflows/sdk-sample-test.yml | 68 +++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/sdk-sample-test.yml diff --git a/.github/workflows/sdk-sample-test.yml b/.github/workflows/sdk-sample-test.yml new file mode 100644 index 0000000..1433801 --- /dev/null +++ b/.github/workflows/sdk-sample-test.yml @@ -0,0 +1,68 @@ +# Runs the BrowserStack SDK sample against a given commit and reports a status check. +# Trigger: Actions tab -> "Cucumber-JS + Playwright SDK sample test" -> Run workflow -> paste the PR's full commit SHA. +# Requires repo secrets: BROWSERSTACK_USERNAME, BROWSERSTACK_ACCESS_KEY. +name: Cucumber-JS + Playwright SDK sample test + +on: + workflow_dispatch: + inputs: + commit_sha: + description: 'The full commit id to build' + required: true + +jobs: + sdk-sample: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + max-parallel: 3 + matrix: + os: [ubuntu-latest] + node: ['18', '20', '22'] + name: cucumber-js-playwright Node ${{ matrix.node }} 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 }} + - name: Mark status check in_progress + uses: actions/github-script@v7 + env: + job_name: cucumber-js-playwright Node ${{ matrix.node }} sample + commit_sha: ${{ github.event.inputs.commit_sha }} + with: + github-token: ${{ github.token }} + script: | + 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(e => console.log('check create failed:', e.status)); + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + - name: Install + run: | + npm install + - name: Run sample test + run: | + npm run sample-test + - name: Run sample-local test + run: | + npm run sample-local-test + - name: Mark status check completed + if: always() + uses: actions/github-script@v7 + env: + conclusion: ${{ job.status }} + job_name: cucumber-js-playwright Node ${{ matrix.node }} sample + commit_sha: ${{ github.event.inputs.commit_sha }} + with: + github-token: ${{ github.token }} + script: | + 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(e => console.log('check create failed:', e.status)); From 53ef03d1a6ab78130d7f32de8e8c460db4f18897 Mon Sep 17 00:00:00 2001 From: Aakash Hotchandani Date: Thu, 18 Jun 2026 10:35:38 +0530 Subject: [PATCH 5/5] Add least-privilege permissions to sample-test workflow (CodeQL: limit GITHUB_TOKEN) --- .github/workflows/sdk-sample-test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/sdk-sample-test.yml b/.github/workflows/sdk-sample-test.yml index 1433801..12aa359 100644 --- a/.github/workflows/sdk-sample-test.yml +++ b/.github/workflows/sdk-sample-test.yml @@ -10,6 +10,10 @@ on: description: 'The full commit id to build' required: true +permissions: + contents: read # checkout + checks: write # github-script creates the status check + jobs: sdk-sample: runs-on: ${{ matrix.os }}