├── .github ├── CODEOWNERS └── workflows │ ├── Semgrep.yml │ └── reviewing_changes.yml ├── .gitignore ├── .travis.yml ├── README.md ├── conf ├── base.conf.js ├── local-test.conf.js └── test.conf.js ├── package-lock.json ├── package.json └── tests └── specs ├── local_test.js └── test.js /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | .github/* @browserstack/asi-devs 2 | 3 | * @browserstack/automate-public-repos 4 | -------------------------------------------------------------------------------- /.github/workflows/Semgrep.yml: -------------------------------------------------------------------------------- 1 | # Name of this GitHub Actions workflow. 2 | name: Semgrep 3 | 4 | on: 5 | # Scan changed files in PRs (diff-aware scanning): 6 | # The branches below must be a subset of the branches above 7 | pull_request: 8 | branches: ["master", "main"] 9 | push: 10 | branches: ["master", "main"] 11 | schedule: 12 | - cron: '0 6 * * *' 13 | 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | semgrep: 20 | # User definable name of this GitHub Actions job. 21 | permissions: 22 | contents: read # for actions/checkout to fetch code 23 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results 24 | name: semgrep/ci 25 | # If you are self-hosting, change the following `runs-on` value: 26 | runs-on: ubuntu-latest 27 | 28 | container: 29 | # A Docker image with Semgrep installed. Do not change this. 30 | image: returntocorp/semgrep 31 | 32 | # Skip any PR created by dependabot to avoid permission issues: 33 | if: (github.actor != 'dependabot[bot]') 34 | 35 | steps: 36 | # Fetch project source with GitHub Actions Checkout. 37 | - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 38 | # Run the "semgrep ci" command on the command line of the docker image. 39 | - run: semgrep ci --sarif --output=semgrep.sarif 40 | env: 41 | # Add the rules that Semgrep uses by setting the SEMGREP_RULES environment variable. 42 | SEMGREP_RULES: p/default # more at semgrep.dev/explore 43 | 44 | - name: Upload SARIF file for GitHub Advanced Security Dashboard 45 | uses: github/codeql-action/upload-sarif@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0 46 | with: 47 | sarif_file: semgrep.sarif 48 | if: always() -------------------------------------------------------------------------------- /.github/workflows/reviewing_changes.yml: -------------------------------------------------------------------------------- 1 | # This job is to test different npm profiles in master branch against Pull Request raised 2 | # This workflow targets wdio 3 | 4 | name: NodeJS Test workflow on workflow_dispatch 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | commit_sha: 10 | description: 'The full commit id to build' 11 | required: true 12 | 13 | jobs: 14 | comment-run: 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | fail-fast: false 18 | max-parallel: 3 19 | matrix: 20 | node: ['14', '16', '18'] 21 | os: [ macos-latest, windows-latest, ubuntu-latest ] 22 | name: WebDriverIO Repo ${{ matrix.node }} - ${{ matrix.os }} Sample 23 | env: 24 | BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} 25 | BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | with: 30 | ref: ${{ github.event.inputs.commit_sha }} 31 | - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 32 | id: status-check-in-progress 33 | env: 34 | job_name: WebDriverIO Repo ${{ matrix.node }} - ${{ matrix.os }} Sample 35 | commit_sha: ${{ github.event.inputs.commit_sha }} 36 | with: 37 | github-token: ${{ github.token }} 38 | script: | 39 | const result = await github.rest.checks.create({ 40 | owner: context.repo.owner, 41 | repo: context.repo.repo, 42 | name: process.env.job_name, 43 | head_sha: process.env.commit_sha, 44 | status: 'in_progress' 45 | }).catch((err) => ({status: err.status, response: err.response})); 46 | console.log(`The status-check response : ${result.status} Response : ${JSON.stringify(result.response)}`) 47 | if (result.status !== 201) { 48 | console.log('Failed to create check run') 49 | } 50 | - name: Setup node 51 | uses: actions/setup-node@v3 52 | with: 53 | node-version: ${{ matrix.node }} 54 | 55 | - name: Install dependencies 56 | run: npm install 57 | 58 | - name: Run sample tests 59 | run: npm run test 60 | 61 | - name: Run local tests 62 | run: npm run local 63 | 64 | - if: always() 65 | uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 66 | id: status-check-completed 67 | env: 68 | conclusion: ${{ job.status }} 69 | job_name: WebDriverIO Repo ${{ matrix.node }} - ${{ matrix.os }} Sample 70 | commit_sha: ${{ github.event.inputs.commit_sha }} 71 | with: 72 | github-token: ${{ github.token }} 73 | script: | 74 | const result = await github.rest.checks.create({ 75 | owner: context.repo.owner, 76 | repo: context.repo.repo, 77 | name: process.env.job_name, 78 | head_sha: process.env.commit_sha, 79 | status: 'completed', 80 | conclusion: process.env.conclusion 81 | }).catch((err) => ({status: err.status, response: err.response})); 82 | console.log(`The status-check response : ${result.status} Response : ${JSON.stringify(result.response)}`) 83 | if (result.status !== 201) { 84 | console.log('Failed to create check run') 85 | } 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | local.log 3 | errorShots 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.3" 4 | - "4.0" 5 | - "0.12" 6 | - "0.10" 7 | 8 | before_install: 9 | - true && export BROWSERSTACK_ACCESS_KEY= 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webdriverio-browserstack 2 | [WebdriverIO](http://webdriver.io/) Integration with BrowserStack. 3 | 4 | ![BrowserStack Logo](https://d98b8t1nnulk5.cloudfront.net/production/images/layout/logo-header.png?1469004780) 5 | 6 | 7 | 8 | ## Setup 9 | * Clone the repo 10 | * Install dependencies `npm install` 11 | * You can setup environment variables for all sample repos (see Notes) or update `*.conf.js` files inside the `conf/` directory with your [BrowserStack Username and Access Key](https://www.browserstack.com/accounts/settings) 12 | 13 | ## Running your tests 14 | - To run parallel tests, run `npm run test` 15 | - To run local test, run `npm run local` 16 | 17 | Understand how many parallel sessions you need by using our [Parallel Test Calculator](https://www.browserstack.com/automate/parallel-calculator?ref=github) 18 | 19 | ## Notes 20 | * You can view your test results on the [BrowserStack automate dashboard](https://www.browserstack.com/automate) 21 | * To test on a different set of browsers, check out our [platform configurator](https://www.browserstack.com/automate/capabilities) 22 | * You can export the environment variables for the Username and Access Key of your BrowserStack account 23 | 24 | ```sh 25 | export BROWSERSTACK_USERNAME= && 26 | export BROWSERSTACK_ACCESS_KEY= 27 | ``` 28 | 29 | ## Additional Resources 30 | * [Documentation for writing automate test scripts in Node](https://www.browserstack.com/automate/node) 31 | * [Customizing your tests on BrowserStack](https://www.browserstack.com/automate/capabilities) 32 | * [Browsers & mobile devices for selenium testing on BrowserStack](https://www.browserstack.com/list-of-browsers-and-platforms?product=automate) 33 | * [Using REST API to access information about your tests via the command-line interface](https://www.browserstack.com/automate/rest-api) 34 | -------------------------------------------------------------------------------- /conf/base.conf.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | user: process.env.BROWSERSTACK_USERNAME || 'BROWSERSTACK_USERNAME', 3 | key: process.env.BROWSERSTACK_ACCESS_KEY || 'BROWSERSTACK_ACCESS_KEY', 4 | 5 | updateJob: false, 6 | specs: ['./tests/specs/test.js'], 7 | exclude: [], 8 | 9 | logLevel: 'warn', 10 | coloredLogs: true, 11 | screenshotPath: './errorShots/', 12 | baseUrl: '', 13 | waitforTimeout: 10000, 14 | connectionRetryTimeout: 120000, 15 | connectionRetryCount: 3, 16 | hostname: 'hub.browserstack.com', 17 | services: [['browserstack']], 18 | 19 | before: function () { 20 | var chai = require('chai'); 21 | global.expect = chai.expect; 22 | chai.Should(); 23 | }, 24 | framework: 'mocha', 25 | mochaOpts: { 26 | ui: 'bdd', 27 | timeout: 60000, 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /conf/local-test.conf.js: -------------------------------------------------------------------------------- 1 | const { config: baseConfig } = require('./base.conf.js'); 2 | 3 | const localConfig = { 4 | // Adding browserstackLocal to browserstack-service to initiate local binary 5 | services: [ 6 | [ 7 | 'browserstack', 8 | { 9 | browserstackLocal: true, 10 | buildIdentifier: '#${BUILD_NUMBER}', 11 | opts: { 12 | forcelocal: false, 13 | localIdentifier: 'webdriverio-browserstack-repo' 14 | } 15 | }, 16 | ], 17 | ], 18 | capabilities: [ 19 | { 20 | browserName: 'chrome', 21 | browserVersion: 'latest', 22 | 'bstack:options': { 23 | buildName: 'browserstack build', 24 | source: 'webdriverio:sample-master:v1.2' 25 | } 26 | }, 27 | ], 28 | specs: ['./tests/specs/local_test.js'], 29 | }; 30 | 31 | exports.config = { ...baseConfig, ...localConfig }; 32 | -------------------------------------------------------------------------------- /conf/test.conf.js: -------------------------------------------------------------------------------- 1 | const { config: baseConfig } = require('./base.conf.js'); 2 | 3 | const parallelConfig = { 4 | maxInstances: 10, 5 | commonCapabilities: { 6 | 'bstack:options': { 7 | buildName: 'browserstack build', 8 | source: 'webdriverio:sample-master:v1.2' 9 | } 10 | }, 11 | services: [ 12 | [ 13 | 'browserstack', 14 | { buildIdentifier: '#${BUILD_NUMBER}' }, 15 | ], 16 | ], 17 | capabilities: [ 18 | { 19 | browserName: 'chrome', 20 | browserVersion: 'latest', 21 | 'bstack:options': { 22 | os: 'Windows', 23 | osVersion: '10', 24 | }, 25 | }, 26 | { 27 | browserName: 'safari', 28 | browserVersion: 'latest', 29 | 'bstack:options': { 30 | os: 'OS X', 31 | osVersion: 'Big Sur', 32 | }, 33 | }, 34 | { 35 | browserName: 'chrome', 36 | 'bstack:options': { 37 | deviceName: 'Samsung Galaxy S20', 38 | }, 39 | }, 40 | ], 41 | }; 42 | 43 | exports.config = { ...baseConfig, ...parallelConfig }; 44 | 45 | // Code to support common capabilities 46 | exports.config.capabilities.forEach(function (caps) { 47 | for (var i in exports.config.commonCapabilities) 48 | caps[i] = { ...caps[i], ...exports.config.commonCapabilities[i]}; 49 | }); 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webdriverio-browserstack", 3 | "version": "1.0.1", 4 | "readme": "WendriverIO Integration with [BrowserStack](https://www.browserstack.com)", 5 | "description": "Selenium examples for WebdriverIO and BrowserStack Automate", 6 | "scripts": { 7 | "test": "npx wdio conf/test.conf.js", 8 | "local": "npx wdio conf/local-test.conf.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/browserstack/webdriverio-browserstack.git" 13 | }, 14 | "keywords": [ 15 | "webdriverio", 16 | "browserstack", 17 | "selenium", 18 | "tests" 19 | ], 20 | "bugs": { 21 | "url": "https://github.com/browserstack/webdriverio-browserstack/issues" 22 | }, 23 | "homepage": "https://github.com/browserstack/webdriverio-browserstack#readme", 24 | "dependencies": { 25 | "@wdio/cli": "^7", 26 | "chai": "^4.3.6", 27 | "webdriverio": "^7" 28 | }, 29 | "devDependencies": { 30 | "@wdio/browserstack-service": "^7", 31 | "@wdio/local-runner": "^7", 32 | "@wdio/mocha-framework": "^7" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/specs/local_test.js: -------------------------------------------------------------------------------- 1 | describe('BStack Local Testing', () => { 2 | it('can check tunnel working', async () => { 3 | await browser.url('http://bs-local.com:45454'); 4 | await browser.waitUntil( 5 | async () => (await browser.getTitle()).match(/BrowserStack Local/i), 6 | 5000, 7 | "Failed to connect local tunnel" 8 | ); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/specs/test.js: -------------------------------------------------------------------------------- 1 | describe("Testing with BStackDemo", () => { 2 | it("add product to cart", async () => { 3 | await browser.url("https://bstackdemo.com/"); 4 | await browser.waitUntil( 5 | async () => (await browser.getTitle()).match(/StackDemo/i), 6 | 5000, 7 | "Title didn't match with BrowserStack" 8 | ); 9 | 10 | const productOnScreen = await $('//*[@id="1"]/p'); 11 | const productOnScreenText = await productOnScreen.getText(); 12 | 13 | const addToCart = await $('//*[@id="1"]/div[4]'); 14 | await addToCart.click(); 15 | 16 | const productInCart = await $('//*[@id="__next"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]'); 17 | 18 | await browser.waitUntil(async () => ( 19 | await productInCart.getText()).match(productOnScreenText), 20 | { timeout: 5000 } 21 | ); 22 | }); 23 | }); 24 | --------------------------------------------------------------------------------