diff --git a/.github/workflows/sdk-sample-test.yml b/.github/workflows/sdk-sample-test.yml new file mode 100644 index 0000000..12aa359 --- /dev/null +++ b/.github/workflows/sdk-sample-test.yml @@ -0,0 +1,72 @@ +# 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 + +permissions: + contents: read # checkout + checks: write # github-script creates the status check + +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)); diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..99221b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +node_modules +package-lock.json +.env +local.log +browserstack.err +log/ +screenshots +**/.DS_Store +.vscode/ +.venv +venv +__pycache__ +bin/ +obj/ +target/ +*.log +.pytest_cache 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..32547c5 --- /dev/null +++ b/features/support/hooks.js @@ -0,0 +1,26 @@ +'use strict'; + +const { chromium } = require('@playwright/test'); +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..183b7d1 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "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/test": "1.55.0", + "browserstack-node-sdk": "1.49.3" + }, + "dependencies": { + "browserstack-local": "^1.5.5", + "selenium-webdriver": "4.28.1" + } +}