Skip to content

Commit f9595b2

Browse files
author
Thomas Schoemaecker
committed
Initial commit
0 parents  commit f9595b2

30 files changed

Lines changed: 8028 additions & 0 deletions

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules/
2+
reports/
3+
test-results/
4+
*.log
5+
.env
6+
local.log

README.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# cucumber-playwright-browserstack
2+
3+
Cucumber.js + Playwright integration with BrowserStack for E2E functional testing, using the `browserstack-node-sdk`.
4+
5+
## Project Structure
6+
7+
```
8+
├── browserstack.yml # BrowserStack configuration (platforms, credentials, etc.)
9+
├── cucumber.js # Cucumber configuration
10+
├── features/
11+
│ ├── search.feature # Sample product search / navigation tests
12+
│ ├── cart.feature # Sample add-to-cart test
13+
│ ├── local.feature # BrowserStack Local tunnel test
14+
│ ├── step_definitions/
15+
│ │ ├── search-steps.js # Step definitions for search scenarios
16+
│ │ ├── cart-steps.js # Step definitions for cart scenarios
17+
│ │ └── local-steps.js # Step definitions for local testing
18+
│ └── support/
19+
│ ├── world.js # Custom World: Playwright browser lifecycle + BrowserStack CDP
20+
│ └── hooks.js # Before/After hooks (browser setup, screenshot on failure)
21+
├── package.json
22+
└── README.md
23+
```
24+
25+
## Setup
26+
27+
1. Clone the repo and install dependencies:
28+
29+
```bash
30+
npm install
31+
```
32+
33+
2. Set your BrowserStack credentials as environment variables:
34+
35+
```bash
36+
export BROWSERSTACK_USERNAME="your_username"
37+
export BROWSERSTACK_ACCESS_KEY="your_access_key"
38+
```
39+
40+
Or update `userName` and `accessKey` in `browserstack.yml`.
41+
42+
## Running Tests
43+
44+
### Run sample tests on BrowserStack
45+
46+
Runs the search and product features against [bstackdemo.com](https://bstackdemo.com/) on the platforms defined in `browserstack.yml`:
47+
48+
```bash
49+
npm run sample-test
50+
```
51+
52+
### Run local tests on BrowserStack
53+
54+
Verifies that the BrowserStack Local tunnel is working:
55+
56+
```bash
57+
npm run sample-local-test
58+
```
59+
60+
### Run tests locally (without BrowserStack)
61+
62+
If you want to run the Cucumber tests directly (connects to BrowserStack CDP by default — you can modify `world.js` to launch a local browser instead):
63+
64+
```bash
65+
npm test
66+
```
67+
68+
## How It Works
69+
70+
- **Cucumber.js** provides the BDD test structure with Gherkin feature files.
71+
- **Playwright** handles all browser automation (navigation, clicks, assertions).
72+
- **BrowserStack SDK** (`browserstack-node-sdk`) wraps the Cucumber runner to manage platform distribution, parallel execution, BrowserStack Local tunneling, and test reporting.
73+
- The custom `World` class in `features/support/world.js` connects Playwright to BrowserStack via the CDP WebSocket endpoint (`wss://cdp.browserstack.com/playwright`).
74+
75+
## Configuration
76+
77+
### Platforms
78+
79+
Edit `browserstack.yml` to change which browsers / OS combinations to test on. See the [full platform list](https://www.browserstack.com/list-of-browsers-and-platforms/automate).
80+
81+
### Timeouts
82+
83+
The default Cucumber timeout is set to 60 seconds in `features/support/world.js`. Adjust via `setDefaultTimeout()`.
84+
85+
## Viewing Results
86+
87+
- View test results on the [BrowserStack Automate Dashboard](https://automate.browserstack.com/).
88+
- An HTML report is generated at `reports/cucumber-report.html` after each run.

browserstack.err

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[object Object]

browserstack.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# =============================
2+
# Set BrowserStack Credentials
3+
# =============================
4+
# Add your BrowserStack userName and accessKey here or set BROWSERSTACK_USERNAME and
5+
# BROWSERSTACK_ACCESS_KEY as environment variables
6+
userName: YOUR_USERNAME
7+
accessKey: YOUR_ACCESS_KEY
8+
9+
# ======================
10+
# BrowserStack Reporting
11+
# ======================
12+
projectName: Cucumber Playwright BrowserStack
13+
buildName: cucumber-playwright build
14+
buildIdentifier: '#${BUILD_NUMBER}'
15+
16+
# =======================================
17+
# Platforms (Browsers / Devices to test)
18+
# =======================================
19+
# Entire list available here -> (https://www.browserstack.com/list-of-browsers-and-platforms/automate)
20+
platforms:
21+
- os: Windows
22+
osVersion: 11
23+
browserName: chrome
24+
browserVersion: latest
25+
- os: OS X
26+
osVersion: Ventura
27+
browserName: playwright-webkit
28+
browserVersion: latest
29+
- os: Windows
30+
osVersion: 11
31+
browserName: playwright-firefox
32+
browserVersion: latest
33+
34+
# =======================
35+
# Parallels per Platform
36+
# =======================
37+
parallelsPerPlatform: 1
38+
39+
# ==========================================
40+
# BrowserStack Local
41+
# (For localhost, staging/private websites)
42+
# ==========================================
43+
browserstackLocal: true
44+
45+
framework: playwright
46+
source: cucumber-playwright-browserstack:sample-sdk:v1.0
47+
48+
# ===================
49+
# Debugging features
50+
# ===================
51+
debug: false
52+
networkLogs: false
53+
consoleLogs: errors
54+
55+
# Test Observability
56+
testObservability: true

cucumber.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = {
2+
default: {
3+
require: ['features/support/*.js', 'features/step_definitions/*.js'],
4+
format: ['progress-bar', 'html:reports/cucumber-report.html'],
5+
formatOptions: { snippetInterface: 'async-await' },
6+
},
7+
};

features/cart.feature

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Feature: BrowserStack Demo Cart
2+
3+
Scenario: Can add a product to the cart
4+
Given I am on the bstackdemo home page
5+
When I add the first product to the cart
6+
Then I should see the cart badge showing "1"
7+
And I should see the product in the cart sidebar

features/local.feature

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Feature: BrowserStack Local Testing
2+
3+
Scenario: BStack Local tunnel is working
4+
Given I open the BrowserStack Local page
5+
Then I should see the title containing "BrowserStack Local"

features/search.feature

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Feature: BrowserStack Demo Search
2+
3+
Scenario: Can find and interact with products on BStackDemo
4+
Given I am on the bstackdemo home page
5+
Then I should see the page title "StackDemo"
6+
When I select the vendor "Apple" from the filter
7+
Then all products should contain "iPhone" or "iPad"
8+
9+
Scenario: Can navigate to the orders page
10+
Given I am on the bstackdemo home page
11+
When I click on "Orders" in the navigation
12+
Then I should see the sign in page
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
const { When, Then } = require('@cucumber/cucumber');
4+
const { expect } = require('@playwright/test');
5+
6+
let addedProductName = '';
7+
8+
When('I add the first product to the cart', async function () {
9+
await this.page.locator('.shelf-item').first().waitFor();
10+
11+
addedProductName = await this.page
12+
.locator('.shelf-item__title')
13+
.first()
14+
.textContent();
15+
16+
await this.page.locator('.shelf-item__buy-btn').first().click();
17+
await this.page.waitForTimeout(1000);
18+
});
19+
20+
Then(
21+
'I should see the cart badge showing {string}',
22+
async function (expectedCount) {
23+
const badge = this.page.locator('.bag__quantity');
24+
await expect(badge).toHaveText(expectedCount);
25+
}
26+
);
27+
28+
Then('I should see the product in the cart sidebar', async function () {
29+
const cartContent = this.page.locator('.float-cart__content');
30+
await cartContent.waitFor({ state: 'visible' });
31+
32+
const cartProductName = await this.page
33+
.locator('.shelf-item__details .title')
34+
.first()
35+
.textContent();
36+
37+
expect(cartProductName.trim()).toBe(addedProductName.trim());
38+
});

0 commit comments

Comments
 (0)