@wdio/crossbrowsertesting-service
Version:
A WebdriverIO service that manages local tunnel and job metadata for CrossBrowserTesting users.
180 lines (179 loc) • 6.39 kB
JavaScript
import got from 'got';
import logger from '@wdio/logger';
const log = logger('@wdio/crossbrowsertesting-service');
const jobDataProperties = ['name', 'tags', 'public', 'build', 'extra'];
export default class CrossBrowserTestingService {
_config;
_capabilities;
_browser;
_testCnt = 0;
_failures = 0;
_isServiceEnabled;
_suiteTitle;
_cbtUsername;
_cbtAuthkey;
constructor(_config, _capabilities) {
this._config = _config;
this._capabilities = _capabilities;
this._cbtUsername = this._config.user;
this._cbtAuthkey = this._config.key;
this._isServiceEnabled = !!(this._cbtUsername && this._cbtAuthkey);
}
before(caps, specs, browser) {
this._browser = browser;
}
/**
* Before suite
* @param {object} suite Suite
*/
beforeSuite(suite) {
this._suiteTitle = suite.title;
}
/**
* Before test
* @param {object} test Test
*/
beforeTest(test) {
if (!this._isServiceEnabled) {
return;
}
/**
* in jasmine we get Jasmine__TopLevel__Suite as title since service using test
* framework hooks in order to execute async functions.
* This tweak allows us to set the real suite name for jasmine jobs.
*/
/* istanbul ignore if */
if (this._suiteTitle === 'Jasmine__TopLevel__Suite') {
this._suiteTitle = test.fullName.slice(0, test.fullName.indexOf(test.title) - 1);
}
}
afterSuite(suite) {
if (Object.prototype.hasOwnProperty.call(suite, 'error')) {
++this._failures;
}
}
/**
* After test
* @param {object} test Test
*/
afterTest(test, context, results) {
if (!results.passed) {
++this._failures;
}
}
/**
* For CucumberJS
*
* Before feature
* @param {string} uri
* @param {object} feature
*/
beforeFeature(uri, feature) {
if (!this._isServiceEnabled) {
return;
}
this._suiteTitle = feature.name;
}
/**
*
* Runs before a Cucumber Scenario.
* @param {ITestCaseHookParameter} world world object containing information on pickle and test step
* @param {object} result results object containing scenario results
* @param {boolean} result.passed true if scenario has passed
* @param {string} result.error error stack if scenario failed
* @param {number} result.duration duration of scenario in milliseconds
*/
afterScenario(world, result) {
// check if scenario has failed
if (!result.passed) {
++this._failures;
}
}
/**
* Update info
* @return {Promise} Promsie with result of updateJob method call
*/
after(result) {
if (!this._isServiceEnabled || !this._browser) {
return;
}
let failures = this._failures;
/**
* set failures if user has bail option set in which case afterTest and
* afterSuite aren't executed before after hook
*/
if (this._config.mochaOpts && this._config.mochaOpts.bail && Boolean(result)) {
failures = 1;
}
const status = 'status: ' + (failures > 0 ? 'failing' : 'passing');
if (!this._browser.isMultiremote) {
log.info(`Update job with sessionId ${this._browser.sessionId}, ${status}`);
return this.updateJob(this._browser.sessionId, failures);
}
const browser = this._browser;
return Promise.all(Object.keys(this._capabilities).map((browserName) => {
log.info(`Update multiremote job for browser "${browserName}" and sessionId ${browser.getInstance(browserName).sessionId}, ${status}`);
return this.updateJob(browser.getInstance(browserName).sessionId, failures, false, browserName);
}));
}
onReload(oldSessionId, newSessionId) {
if (!this._isServiceEnabled || !this._browser) {
return;
}
const status = 'status: ' + (this._failures > 0 ? 'failing' : 'passing');
if (!this._browser.isMultiremote) {
log.info(`Update (reloaded) job with sessionId ${oldSessionId}, ${status}`);
return this.updateJob(oldSessionId, this._failures, true);
}
const browserName = this._browser.instances.filter((browserName) => this._browser.getInstance(browserName).sessionId === newSessionId)[0];
log.info(`Update (reloaded) multiremote job for browser "${browserName}" and sessionId ${oldSessionId}, ${status}`);
return this.updateJob(oldSessionId, this._failures, true, browserName);
}
async updateJob(sessionId, failures, calledOnReload = false, browserName) {
const json = this.getBody(failures, calledOnReload, browserName);
this._failures = 0;
const response = await got.put(this.getRestUrl(sessionId), {
json,
responseType: 'json',
username: this._cbtUsername,
password: this._cbtAuthkey
});
return response.body;
}
/**
*
* @param {string} sessionId Session id
* @returns {String}
*/
getRestUrl(sessionId) {
return `https://crossbrowsertesting.com/api/v3/selenium/${sessionId}`;
}
getBody(failures, calledOnReload = false, browserName) {
const body = { test: {} };
/**
* set default values
*/
body.test.name = this._suiteTitle;
/**
* add reload count to title if reload is used
*/
if ((calledOnReload || this._testCnt) && this._browser) {
let testCnt = ++this._testCnt;
if (this._browser.isMultiremote) {
testCnt = Math.ceil(testCnt / this._browser.instances.length);
}
body.test.name += ` (${testCnt})`;
}
for (const prop of jobDataProperties) {
if (!this._capabilities[prop]) {
continue;
}
body.test[prop] = this._capabilities[prop];
}
if (browserName) {
body.test.name = `${browserName}: ${body.test.name}`;
}
body.test.success = failures === 0 ? '1' : '0';
return body;
}
}