Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ browserstack.err
**/.DS_Store
.vscode/
.env
package-lock.json
2 changes: 2 additions & 0 deletions browserstack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ buildIdentifier: '#${BUILD_NUMBER}' # Supports strings along with either/both ${
# Set `source` in the syntax `<fw-name>:sample-<branch-name>:<version-number>
source: cucumber-js:sample-master:v1.0

CUSTOM_TAG_1: "bstack_sample"

# =======================================
# Platforms (Browsers / Devices to test)
# =======================================
Expand Down
3 changes: 3 additions & 0 deletions cucumber.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
default: '--require "module*/features/step_definitions/steps.js" --require features/support/*.js --retry 2 --retryTagFilter @retry'
};
31 changes: 22 additions & 9 deletions features/support/hooks.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use strict';

const { Builder, Capabilities } = require("selenium-webdriver");
const { Before, After } = require("@cucumber/cucumber");
const { Before, After, BeforeAll, AfterAll } = require("@cucumber/cucumber");
const assert = require('assert');

var createBrowserStackSession = function(){
return new Builder().
Expand All @@ -10,14 +11,26 @@ var createBrowserStackSession = function(){
build();
}

Before(function (scenario, callback) {
var world = this;
world.driver = createBrowserStackSession();
callback();
let globalDriver;

BeforeAll(async function () {
globalDriver = createBrowserStackSession();
await globalDriver.get('https://bstackdemo.com/');
const title = await globalDriver.getTitle();
assert.match(title, /StackDemo/i, 'Title does not match /StackDemo/i');
});

After(function(scenario, callback){
this.driver.quit().then(function(){
callback();
});
AfterAll(async function(){
if (globalDriver) {
await globalDriver.quit();
}
});

// Optionally, make globalDriver available in steps via World
const { setWorldConstructor } = require('@cucumber/cucumber');
class CustomWorld {
get driver() {
return globalDriver;
}
}
setWorldConstructor(CustomWorld);
68 changes: 68 additions & 0 deletions moduleA/features/step_definitions/steps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use strict';

const { Given, When, Then, Before, BeforeStep } = require('@cucumber/cucumber');
const assert = require('assert');

Given('module A - flaky test - random product selection', async function () {
const randomProductIndex = Math.random() > 0.5 ? "1" : "2000";
const productOnScreen = await this.driver.findElement({ xpath: `//*[@id="${randomProductIndex}"]/p` });
const productOnScreenText = await productOnScreen.getText();

const addToCart = await this.driver.findElement({ xpath: `//*[@id="${randomProductIndex}"]/div[4]` });
await addToCart.click();

const productInCart = await this.driver.findElement({ xpath: '//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]' });
let textMatch = false;
for (let i = 0; i < 10; i++) {
const cartText = await productInCart.getText();
if (cartText.match(productOnScreenText)) {
textMatch = true;
break;
}
await new Promise(res => setTimeout(res, 500));
}
assert.ok(textMatch, 'Cart product text did not match selected product');
});

Given('module A - always failing test - missing element 1', async function () {
await this.driver.findElement({ xpath: '//*[@id="non-existent-1"]/p' }).then(el => el.click());
});

Given('module A - always failing test - same stacktrace 1', async function () {
await this.driver.findElement({ xpath: '//*[@id="common-error"]/p' }).then(el => el.click());
});

Given('module A - always failing test - same stacktrace 2', async function () {
await this.driver.findElement({ xpath: '//*[@id="common-error"]/p' }).then(el => el.click());
});

Given('module A - always passing test - example B', async function () {
assert(true);
});
Given('module A - always passing test - example C', async function () {
assert(true);
});
Given('module A - always passing test - example D', async function () {
assert(true);
});
Given('module A - always passing test - example E', async function () {
assert(true);
});

Given('module A - passing test - verify page title', async function () {
const title = await this.driver.getTitle();
assert.match(title, /StackDemo/i);
});

Given('module A - Test with framework-level retry - 2 retries configured', async function () {
assert(Math.random() > 0.7, "Test failed, retrying...");
});

Given('module A - Another Test with framework-level retry - 2 retries configured', async function () {
assert(Math.random() > 0.7, "Test failed, retrying...");
});

Given('module A - always passing test - example A', async function () {
const url = await this.driver.getCurrentUrl();
assert.ok(url.includes("bstackdemo"));
});
43 changes: 43 additions & 0 deletions moduleA/features/testsA.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Feature: ModuleA sample tests

@regression @p1
Scenario: flaky test - random product selection
Given module A - flaky test - random product selection

Scenario: always failing test - missing element 1
Given module A - always failing test - missing element 1

@regression
Scenario: always failing test - same stacktrace 1
Given module A - always failing test - same stacktrace 1

Scenario: always failing test - same stacktrace 2
Given module A - always failing test - same stacktrace 2

Scenario: passing test - verify page title
Given module A - passing test - verify page title

@retry @regression
Scenario: Test with framework-level retry - 2 retries configured
Given module A - Test with framework-level retry - 2 retries configured

@retry
Scenario: Another Test with framework-level retry - 2 retries configured
Given module A - Another Test with framework-level retry - 2 retries configured

Scenario: always passing test - example A
Given module A - always passing test - example A

@must-pass
Scenario: always passing test - example B
Given module A - always passing test - example B

Scenario: always passing test - example C
Given module A - always passing test - example C

Scenario: always passing test - example D
Given module A - always passing test - example D

@must-pass
Scenario: always passing test - example E
Given module A - always passing test - example E
89 changes: 89 additions & 0 deletions moduleB/features/step_definitions/steps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use strict';

const { Given, When, Then, Before, BeforeStep } = require('@cucumber/cucumber');
const assert = require('assert');

Given('module B - Flaky test - random product selection', async function () {
const randomProductIndex = Math.random() > 0.7 ? "1" : "2000";
const productOnScreen = await this.driver.findElement({ xpath: `//*[@id="${randomProductIndex}"]/p` });
const productOnScreenText = await productOnScreen.getText();

const addToCart = await this.driver.findElement({ xpath: `//*[@id="${randomProductIndex}"]/div[4]` });
await addToCart.click();

const productInCart = await this.driver.findElement({ xpath: '//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]' });
let textMatch = false;
for (let i = 0; i < 10; i++) {
const cartText = await productInCart.getText();
if (cartText.match(productOnScreenText)) {
textMatch = true;
break;
}
await new Promise(res => setTimeout(res, 500));
}
assert.ok(textMatch, 'Cart product text did not match selected product');
});

Given('module B - Another Test with framework-level retry - 2 retries configured', async function () {
assert(Math.random() > 0.7, "Test failed, retrying...");
});

Given('module B - Failing test - same stacktrace', async function () {
await this.driver.findElement({ xpath: '//*[@id="common-error"]/p' }).then(el => el.click());
});

Given('module B - Failing test - same stacktrace 2', async function () {
await this.driver.findElement({ xpath: '//*[@id="common-error"]/p' }).then(el => el.click());
});

Given('module B - Always Passing test - example F', async function () {
assert(true);
});

Given('module B - Always Passing test - example G', async function () {
assert(true);
});

Given('module B - Always Passing test - example H', async function () {
assert(true);
});

Given('module B - Always Passing test - example I', async function () {
assert(true);
});

Given('module B - Passing test - verify page title', async function () {
const title = await this.driver.getTitle();
assert.match(title, /StackDemo/i);
});

Given('module B - Always passing test', async function () {
const result = 6 + 3;
assert.strictEqual(result, 9);
});

Given('module B - Always passing test - example B', async function () {
const result = 1000 * 2;
assert.strictEqual(result, 2000);
});

Given('module B - Always passing test - example C', async function () {
const result = 1000 * 2;
assert.strictEqual(result, 2000);
});

Given('module B - Always passing test - example D', async function () {
const str1 = "BrowserStack is better than LambdaTest";
const str2 = str1.substring(3, 10);
assert.strictEqual(str2, "wserSta");
});

Given('module B - Always passing test - example E', async function () {
const str1 = "BrowserStack is better than LambdaTest";
const str2 = str1.substring(3, 11);
assert.strictEqual(str2, "wserStac");
});

Given('module B - Test with framework-level retry - 2 retries configured', async function () {
assert(Math.random() > 0.7, "Test failed, retrying...");
});
50 changes: 50 additions & 0 deletions moduleB/features/testsB.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
Feature: ModuleB sample tests

Scenario: Flaky test - random product selection
Given module B - Flaky test - random product selection

@regression @p1
Scenario: Failing test - same stacktrace
Given module B - Failing test - same stacktrace

Scenario: Failing test - same stacktrace 2
Given module B - Failing test - same stacktrace 2

@must-pass
Scenario: Passing test - verify page title
Given module B - Passing test - verify page title

Scenario: Always passing test
Given module B - Always passing test

Scenario: Always passing test - example B
Given module B - Always passing test - example B

Scenario: Always passing test - example C
Given module B - Always passing test - example C

Scenario: Always passing test - example D
Given module B - Always passing test - example D

Scenario: Always passing test - example E
Given module B - Always passing test - example E

Scenario: Always passing test - example F
Given module B - Always passing test - example F

Scenario: Always passing test - example G
Given module B - Always passing test - example G

Scenario: Always passing test - example H
Given module B - Always passing test - example H

Scenario: Always passing test - example I
Given module B - Always passing test - example I

@retry
Scenario: Test with framework-level retry - 2 retries configured
Given module B - Test with framework-level retry - 2 retries configured

@retry
Scenario: Another Test with framework-level retry - 2 retries configured
Given module B - Another Test with framework-level retry - 2 retries configured
66 changes: 66 additions & 0 deletions moduleC/features/step_definitions/steps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
'use strict';

const { Given, When, Then, Before, BeforeStep } = require('@cucumber/cucumber');
const assert = require('assert');

Given('module C - flaky test - random product selection', async function () {
const randomProductIndex = Math.random() > 0.5 ? "1" : "2000";
const productOnScreen = await this.driver.findElement({ xpath: `//*[@id="${randomProductIndex}"]/p` });
const productOnScreenText = await productOnScreen.getText();

const addToCart = await this.driver.findElement({ xpath: `//*[@id="${randomProductIndex}"]/div[4]` });
await addToCart.click();

const productInCart = await this.driver.findElement({ xpath: '//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]' });
let textMatch = false;
for (let i = 0; i < 10; i++) {
const cartText = await productInCart.getText();
if (cartText.match(productOnScreenText)) {
textMatch = true;
break;
}
await new Promise(res => setTimeout(res, 500));
}
assert.ok(textMatch, 'Cart product text did not match selected product');
});

Given('module C - always failing test - missing element 1', async function () {
await this.driver.findElement({ xpath: '//*[@id="non-existent-1"]/p' }).then(el => el.click());
});

Given('module C - always failing test - same stacktrace 1', async function () {
await this.driver.findElement({ xpath: '//*[@id="common-error"]/p' }).then(el => el.click());
});

Given('module C - always failing test - same stacktrace 2', async function () {
await this.driver.findElement({ xpath: '//*[@id="common-error"]/p' }).then(el => el.click());
});

Given('module C - Always Passing Test - example F', async function () {
assert(true);
});

Given('module C - Always Passing Test - example G', async function () {
assert(true);
});

Given('module C - Always Passing Test - example H', async function () {
assert(true);
});

Given('module C - Always Passing Test - example I', async function () {
assert(true);
});

Given('module C - passing test - verify page title', async function () {
const title = await this.driver.getTitle();
assert.match(title, /StackDemo/i);
});

Given('module C - Test with framework-level retry - 2 retries configured', async function () {
assert(Math.random() > 0.7, "Test failed, retrying...");
});

Given('module C - Another Test with framework-level retry - 2 retries configured', async function () {
assert(Math.random() > 0.7, "Test failed, retrying...");
});
Loading