jest-environment-browserstack
Version:
an environment for using Browserstack with Jest
159 lines (158 loc) • 6.79 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const jest_environment_node_1 = __importDefault(require("jest-environment-node"));
const browserstack_local_1 = require("browserstack-local");
const crypto_1 = require("crypto");
/**
* Browserstack environment
*/
class BrowserstackEnvironment extends jest_environment_node_1.default {
constructor(config) {
super(config);
// get the configurations from the config in the main package.json
this.btCapabilities = config.globals.browserstack;
const { capabilities: { 'bstack:options': opts }, driver, seleniumHubUrl = 'https://hub-cloud.browserstack.com/wd/hub', } = this.btCapabilities;
// if no Browserstack credentials were found in the package.json
// look into the environment for BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY
if (!opts.accessKey || !opts.userName) {
const { BROWSERSTACK_USERNAME: userName = '', BROWSERSTACK_ACCESS_KEY: accessKey = '' } = process.env;
if (!userName || !accessKey) {
throw new Error('valid credentials for Browserstack are required');
}
opts.accessKey = accessKey;
opts.userName = userName;
}
this.key = opts.accessKey;
if (opts.local) {
// if local identifier wasn't generate a random 28 chars string
if (!opts.localIdentifier) {
this.localIdentifier = Buffer.from(crypto_1.randomBytes(28)).toString('hex');
opts.localIdentifier = this.localIdentifier;
}
else {
this.localIdentifier = opts.localIdentifier;
}
opts.localIdentifier = this.localIdentifier;
this.btTunnelOpts = this.btCapabilities.localTesting;
}
this.selHubUrl = seleniumHubUrl;
if (!driver) {
throw new Error('a driver is needed for interacting with Browserstack');
}
this.pluginDriver = driver;
this.drivers = new Array();
}
/**
* override the original setup environment for the Browserstack one
*/
setup() {
const _super = Object.create(null, {
setup: { get: () => super.setup }
});
return __awaiter(this, void 0, void 0, function* () {
yield _super.setup.call(this);
if (this.btTunnelOpts) {
yield this.createBTTunnel();
}
// the plugin for the driver is loaded dynamically
const mod = yield Promise.resolve().then(() => __importStar(require(this.pluginDriver)));
// look for the exported member having a createWdDriver method
const clazz = Object.values(mod).find((fct) => Reflect.has(fct.prototype, 'createWdDriver'));
if (!clazz) {
throw new Error('the plugin is not extending @jest-browserstack-environment/plugin');
}
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
this.plugin = new clazz();
const driver = yield this.plugin.createWdDriver(this.btCapabilities.capabilities, this.selHubUrl);
this.global.__driver__ = ((caps) => __awaiter(this, void 0, void 0, function* () {
if (caps) {
caps['bstack:options'].accessKey = this.key;
caps['bstack:options'].userName = this.btCapabilities.capabilities['bstack:options'].userName;
}
return (yield driver.build(caps || this.btCapabilities.capabilities));
})).bind(this);
this.drivers.push(this.global.__driver__);
});
}
/**
* environment teardown - kill all driver instance and close the tunnel
*/
teardown() {
const _super = Object.create(null, {
teardown: { get: () => super.teardown }
});
return __awaiter(this, void 0, void 0, function* () {
yield _super.teardown.call(this);
yield Promise.all(this.drivers.map((driver) => __awaiter(this, void 0, void 0, function* () {
try {
yield driver.quit();
}
catch (_) {
return Promise.resolve();
}
})));
yield this.closeBTTunnel();
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
runScript(script) {
return super.runScript(script);
}
/**
* Create the Browserstack tunnel
* @returns {Promise} resolve if the tunnel was correctly created
*/
createBTTunnel() {
if (this.localIdentifier) {
this.btTunnelOpts.localIdentifier = this.localIdentifier;
}
this.btTunnelOpts.key = this.key;
this.global.__local__ = new browserstack_local_1.Local();
return new Promise((resolve, reject) => {
this.global.__local__.start(this.btTunnelOpts, (err) => {
if (err) {
return reject(err);
}
return resolve();
});
});
}
/**
* Browserstack tunnel teardown
* @returns {Promise} resolve if everything went well
*/
closeBTTunnel() {
if (!this.global.__local__) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
this.global.__local__.stop((err) => {
if (err) {
return reject(err);
}
resolve();
});
});
}
}
exports.default = BrowserstackEnvironment;