diff --git a/README.md b/README.md index e53af9c..6225d81 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # jest-environment-browserstack -[![Actions Status](https://github.com/taktakpeops/jest-environment-browserstack/workflows/Node%20CI/badge.svg)](https://github.com/taktakpeops/jest-environment-browserstack/actions) [![npm version](http://img.shields.io/npm/v/jest-environment-browserstack.svg?style=flat)](https://npmjs.org/package/jest-environment-browserstack "View this project on npm") - +[![Actions Status](https://github.com/taktakpeops/jest-environment-browserstack/workflows/Node%20CI/badge.svg)](https://github.com/taktakpeops/jest-environment-browserstack/actions) [![npm version](http://img.shields.io/npm/v/jest-environment-browserstack.svg?style=flat)](https://npmjs.org/package/jest-environment-browserstack 'View this project on npm') Use Jest as test-runner for running your visual-tests and more using Browserstack. @@ -90,13 +89,30 @@ my-visual-test.spec.js: /** * @jest-environment browserstack */ -import { until, By } from 'selenium-webdriver'; +import { By } from 'selenium-webdriver'; describe('my visual test', () => { - it('test something', () => { - global.__driver__.get('https://mysuperurl.ltd'); - // do something - // do test + let driver; + + beforeAll(async () => { + // you can override the default configuration + driver = await global.__driver__({ + 'bstack:options': { + sessionName: 'my test', + }, + }); + driver.get('https://mysuperurl.ltd'); + }); + + afterAll(async () => { + // can be omitted + await driver.quit(); + }); + + it('test something', async () => { + const myElement = await driver.findElement(By.css('.super.class')); + const text = await myElement.getText(); + expect(text).toBe('super text'); }); }); ``` @@ -121,7 +137,7 @@ The `test` script will run a basic e2e tests, a visual tests making a snapshot o # Known limitations -For now, only one browser can be defined. +For now, cannot override Browserstack parameters at runtime. # Bug and more diff --git a/examples/with-bt-local/package.json b/examples/with-bt-local/package.json index ed40369..0b8cc6a 100644 --- a/examples/with-bt-local/package.json +++ b/examples/with-bt-local/package.json @@ -67,6 +67,9 @@ "start": "PORT=8080 node scripts/start.js", "build": "node scripts/build.js", "test:jest": "jest --ci", + "test:visual": "jest --testNamePattern=VisualTest --update-snapshot", + "test:e2e": "jest --testNamePattern=UiTest", + "test:unit": "jest --testNamePattern=UnitTest", "test": "npm-run-all -p -r start test:jest" }, "eslintConfig": { diff --git a/examples/with-bt-local/src/App.e2e.test.js b/examples/with-bt-local/src/App.e2e.test.js index bec88ab..c19c461 100644 --- a/examples/with-bt-local/src/App.e2e.test.js +++ b/examples/with-bt-local/src/App.e2e.test.js @@ -4,12 +4,24 @@ const { By } = require('selenium-webdriver'); -describe('ui testing', () => { - const driver = global.__driver__; +describe('ui testing UiTest', () => { + let driver; + + beforeAll(async () => { + driver = await global.__driver__({ + 'bstack:options': { + sessionName: 'ui testing', + }, + }); - it('load the app', async () => { await driver.get('http://localhost:8080'); + }, 10000); + afterAll(async () => { + await driver.quit(); + }); + + it('load the app', async () => { const title = await driver.getTitle(); expect(title).toBe('React App'); diff --git a/examples/with-bt-local/src/App.test.js b/examples/with-bt-local/src/App.test.js index a754b20..1b9fb83 100644 --- a/examples/with-bt-local/src/App.test.js +++ b/examples/with-bt-local/src/App.test.js @@ -2,7 +2,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; -it('renders without crashing', () => { +it('renders without crashing UnitTest', () => { const div = document.createElement('div'); ReactDOM.render(, div); ReactDOM.unmountComponentAtNode(div); diff --git a/examples/with-bt-local/src/__visual_testing__/App.test.js b/examples/with-bt-local/src/__visual_testing__/App.test.js index 51e05de..9dd39ad 100644 --- a/examples/with-bt-local/src/__visual_testing__/App.test.js +++ b/examples/with-bt-local/src/__visual_testing__/App.test.js @@ -5,12 +5,24 @@ const { toMatchImageSnapshot } = require('jest-image-snapshot'); expect.extend({ toMatchImageSnapshot }); -describe('ui testing', () => { - const driver = global.__driver__; +describe('visual testing VisualTest', () => { + let driver; + + beforeAll(async () => { + driver = await global.__driver__({ + 'bstack:options': { + sessionName: 'visual testing', + }, + }); - it('load the app', async () => { await driver.get('http://localhost:8080'); + }, 10000); + + afterAll(async () => { + await driver.quit(); + }); + it('check the app', async () => { const title = await driver.getTitle(); expect(title).toBe('React App'); diff --git a/examples/with-bt-local/src/__visual_testing__/__image_snapshots__/app-test-js-visual-testing-visual-test-take-and-screenshot-and-compare-1-snap.png b/examples/with-bt-local/src/__visual_testing__/__image_snapshots__/app-test-js-visual-testing-visual-test-take-and-screenshot-and-compare-1-snap.png new file mode 100644 index 0000000..3a18ae9 Binary files /dev/null and b/examples/with-bt-local/src/__visual_testing__/__image_snapshots__/app-test-js-visual-testing-visual-test-take-and-screenshot-and-compare-1-snap.png differ diff --git a/examples/with-bt-local/yarn.lock b/examples/with-bt-local/yarn.lock index 8cbdc87..3f55424 100644 --- a/examples/with-bt-local/yarn.lock +++ b/examples/with-bt-local/yarn.lock @@ -1173,6 +1173,18 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== +"@types/lodash.merge@~4.6.6": + version "4.6.6" + resolved "https://registry.yarnpkg.com/@types/lodash.merge/-/lodash.merge-4.6.6.tgz#b84b403c1d31bc42d51772d1cd5557fa008cd3d6" + integrity sha512-IB90krzMf7YpfgP3u/EvZEdXVvm4e3gJbUvh5ieuI+o+XqiNEt6fCzqNRaiLlPVScLI59RxIGZMQ3+Ko/DJ8vQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.141" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.141.tgz#d81f4d0c562abe28713406b571ffb27692a82ae6" + integrity sha512-v5NYIi9qEbFEUpCyikmnOYe4YlP8BMUdTcNCAquAKzu+FA7rZ1onj9x80mbnDdOW/K5bFf3Tv5kJplP33+gAbQ== + "@types/q@^1.5.1": version "1.5.2" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" @@ -5256,9 +5268,11 @@ jest-each@^24.9.0: version "1.0.0" dependencies: "@jest/types" "~24.9.0" + "@types/lodash.merge" "~4.6.6" "@types/selenium-webdriver" "~4.0.3" browserstack-local "~1.4.2" jest-environment-node "~24.9.0" + lodash.merge "~4.6.2" selenium-webdriver "~4.0.0-alpha.5" jest-environment-jsdom-fourteen@0.1.0: @@ -5923,6 +5937,11 @@ lodash.memoize@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.merge@~4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" diff --git a/package-lock.json b/package-lock.json index 6aec602..aaf7bb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -437,6 +437,19 @@ "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==", "dev": true }, + "@types/lodash": { + "version": "4.14.141", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.141.tgz", + "integrity": "sha512-v5NYIi9qEbFEUpCyikmnOYe4YlP8BMUdTcNCAquAKzu+FA7rZ1onj9x80mbnDdOW/K5bFf3Tv5kJplP33+gAbQ==" + }, + "@types/lodash.merge": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.6.tgz", + "integrity": "sha512-IB90krzMf7YpfgP3u/EvZEdXVvm4e3gJbUvh5ieuI+o+XqiNEt6fCzqNRaiLlPVScLI59RxIGZMQ3+Ko/DJ8vQ==", + "requires": { + "@types/lodash": "*" + } + }, "@types/node": { "version": "12.7.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.7.tgz", @@ -3552,6 +3565,11 @@ "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", diff --git a/package.json b/package.json index 1107170..3f64714 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jest-environment-browserstack", - "version": "1.0.0", + "version": "1.1.0", "description": "an environment for using Browserstack with Jest", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -31,9 +31,11 @@ "license": "MIT", "dependencies": { "@jest/types": "~24.9.0", + "@types/lodash.merge": "~4.6.6", "@types/selenium-webdriver": "~4.0.3", "browserstack-local": "~1.4.2", "jest-environment-node": "~24.9.0", + "lodash.merge": "~4.6.2", "selenium-webdriver": "~4.0.0-alpha.5" }, "devDependencies": { diff --git a/src/__tests__/github.spec.ts b/src/__tests__/github.spec.ts new file mode 100644 index 0000000..2ef1a12 --- /dev/null +++ b/src/__tests__/github.spec.ts @@ -0,0 +1,32 @@ +import { WebDriver, By } from 'selenium-webdriver'; + +describe('Github', () => { + let driver: WebDriver; + + beforeAll(async () => { + // eslint-disable-next-line + // @ts-ignore + driver = await global.__driver__({ + 'bstack:options': { + sessionName: 'github', + }, + }); + await driver.get('https://github.com/taktakpeops/jest-environment-browserstack'); + }, 10000); + + afterAll(async () => { + await driver.quit(); + }); + + it('has an author', async () => { + const authorSpan = await driver.findElement(By.css('.author')); + const author = await authorSpan.getText(); + expect(author).toBe('taktakpeops'); + }); + + it('has a repository name', async () => { + const repoSpan = await driver.findElement(By.css('h1.public strong')); + const repo = await repoSpan.getText(); + expect(repo).toBe('jest-environment-browserstack'); + }); +}); diff --git a/src/__tests__/index.spec.ts b/src/__tests__/index.spec.ts deleted file mode 100644 index 2b820ac..0000000 --- a/src/__tests__/index.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { WebDriver } from 'selenium-webdriver'; - -describe('BrowserstackEnvironment', () => { - // eslint-disable-next-line - // @ts-ignore - const driver = global.__driver__ as WebDriver; - - it('connects to NPM', async () => { - await driver.get('https://www.npmjs.com/package/jest-environment-browserstack'); - - const title = await driver.getTitle(); - expect(title).toBe('jest-environment-browserstack - npm'); - }); -}); diff --git a/src/__tests__/npm.spec.ts b/src/__tests__/npm.spec.ts new file mode 100644 index 0000000..8f8d997 --- /dev/null +++ b/src/__tests__/npm.spec.ts @@ -0,0 +1,31 @@ +import { WebDriver, By } from 'selenium-webdriver'; + +describe('NPM', () => { + let driver: WebDriver; + + beforeAll(async () => { + // eslint-disable-next-line + // @ts-ignore + driver = await global.__driver__({ + 'bstack:options': { + sessionName: 'npm', + }, + }); + await driver.get('https://www.npmjs.com/package/jest-environment-browserstack'); + }, 10000); + + afterAll(async () => { + await driver.quit(); + }); + + it('get title from NPM', async () => { + const title = await driver.getTitle(); + expect(title).toBe('jest-environment-browserstack - npm'); + }); + + it('get name of the module', async () => { + const titleBlock = await driver.findElement(By.css('.w-100.ph0-l.ph3.ph4-m span[title="jest-environment-browserstack"]')); + const title = await titleBlock.getText(); + expect(title).toBe('jest-environment-browserstack'); + }); +}); diff --git a/src/index.ts b/src/index.ts index 95d38cf..45cda15 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import { Options, Local } from 'browserstack-local'; import { Builder, WebDriver } from 'selenium-webdriver'; import { randomBytes } from 'crypto'; import { Script } from 'vm'; +import merge from 'lodash.merge'; import { BrowserstackCapabilities } from './types'; @@ -18,6 +19,8 @@ export default class BrowserstackEnvironment extends NodeEnvironment { private btTunnelOpts: Options; + private readonly drivers: WebDriver[]; + constructor(config: Config.ProjectConfig) { super(config); @@ -54,6 +57,7 @@ export default class BrowserstackEnvironment extends NodeEnvironment { } this.selHubUrl = seleniumHubUrl; + this.drivers = new Array(); } async setup(): Promise { @@ -63,13 +67,21 @@ export default class BrowserstackEnvironment extends NodeEnvironment { await this.createBTTunnel(); } - this.global.__driver__ = await this.createWDDriver(); + this.global.__driver__ = this.createWDDriver.bind(this); } async teardown(): Promise { await super.teardown(); - await this.global.__driver__.quit(); + await Promise.all( + this.drivers.map(async driver => { + try { + await driver.quit(); + } catch (_) { + return Promise.resolve(); + } + }), + ); await this.closeBTTunnel(); } @@ -77,10 +89,13 @@ export default class BrowserstackEnvironment extends NodeEnvironment { return super.runScript(script); } - private async createWDDriver(): Promise { - const driverFactory = new Builder().usingServer(this.selHubUrl).withCapabilities(this.btCapabilities); + private async createWDDriver(capabilities?: BrowserstackCapabilities): Promise { + const driverFactory = new Builder().usingServer(this.selHubUrl).withCapabilities(merge(this.btCapabilities, capabilities)); + const driver = await driverFactory.build(); + + this.drivers.push(driver); - return await driverFactory.build(); + return driver; } private createBTTunnel(): Promise {