'use strict';
const Launcher = require('@wdio/cli').default;
const dotenv = require('dotenv');
const fse = require('fs-extra');
const rootRequire = require('rpcm-root-require');
const LoggerWrapper = rootRequire('/platform-helpers/log4js-wrapper');
const RunnerOptionsValidator = rootRequire('/platform-helpers/runner-options-validator');
const Utils = rootRequire('/platform-helpers/utils');
const RunnerCapabilitiesBuilder = rootRequire('/platform-helpers/runner-capabilities-builder');
const RunnerTestsListBuilder = rootRequire('platform-helpers/runner-tests-list-builder');
const DockerHelper = rootRequire('platform-helpers/docker-helper');
const ReportGenerator = rootRequire('/platform-helpers/report-generator.js');
const RetryHelper = rootRequire('/platform-helpers/retry-helper.js');
const Constants = rootRequire('/platform-helpers/constants');
const logger = LoggerWrapper.getLogger();
rootRequire('/platform-helpers/string-extensions');
const checkReadyFolder = () => {
fse.ensureDirSync('{0}/ready'.format(rootRequire.rootPath));
};
const checkTmpFolder = () => {
// make sure the directory to store the copy of the tests (to include in the report folder) is present
fse.ensureDirSync('{0}/tmp/tests'.format(rootRequire.rootPath));
// make sure the directory to store the copy of the tests (to include in the report folder) is present
fse.ensureDirSync('{0}/tmp/tests/helpers'.format(rootRequire.rootPath));
};
/**
* Main process
*/
class RunnerHelper {
static run = async (opts) => {
logger.debug('runner-helper.js --> RunnerHelper.run called with the following options: {0}{1}'.format(Utils.getNewLine(), JSON.stringify(opts, null, 2)));
logger.trace('runner-helper.js --> processing DockerHelper.checkDockerLogFiles();');
DockerHelper.checkDockerLogFiles();
logger.trace('runner-helper.js --> processing checkReadyFolder();');
checkReadyFolder();
logger.trace('runner-helper.js --> processing checkTmpFolder();');
checkTmpFolder();
logger.trace('runner-helper.js --> processing dotenv.config();');
dotenv.config();
logger.trace('runner-helper.js --> processing RunnerOptionsValidator.validate(opts);');
RunnerOptionsValidator.validate(opts);
logger.trace('runner-helper.js --> processing RunnerCapabilitiesBuilder.build(opts);');
RunnerCapabilitiesBuilder.build(opts);
logger.trace('runner-helper.js --> processing RunnerTestsListBuilder.build(opts);');
const testsList = RunnerTestsListBuilder.build(opts);
logger.debug('runner-helper.js --> tests list: {0}{1}'.format(Utils.getNewLine(), JSON.stringify(testsList, null, 2)));
logger.info('runner-helper.js --> running {0} file/browser combination(s).'.format(testsList.length));
/**
* saucelabs run
*/
logger.trace('runner-helper.js --> processing the test overridable options object');
const overridableOptions = {
services: [['selenium-standalone', { skipSeleniumInstall: false, drivers: { chrome: true } }]]
};
if (opts.runCrossBrowser || opts.runRemotely || opts.runFullCrossBrowser) {
overridableOptions.services = [['sauce']];
overridableOptions.connectionRetryTimeout = 60000;
overridableOptions.user = process.env.SAUCE_USERNAME;
overridableOptions.key = process.env.SAUCE_ACCESS_KEY;
overridableOptions.capabilities = [];
overridableOptions.bail = 0;
overridableOptions.coloredLogs = true;
overridableOptions.screenshotPath = './errorShots/';
overridableOptions.maxInstances = 1;
overridableOptions.host = 'https://ondemand.{0}.saucelabs.com'.format(process.env.SAUCE_REGION === 'eu' ? 'eu-central-1' : 'us-west-1');
overridableOptions.region = process.env.SAUCE_REGION;
}
logger.debug('runner-helper.js --> test overridable options object: {0}{1}'.format(Utils.getNewLine(), JSON.stringify(overridableOptions, null, 2)));
logger.trace('runner-helper.js --> instantiating ReportGenerator');
const reportGenerator = new ReportGenerator({
reportTitle: opts.summaryReportTitle,
reportContext: opts.reportContext,
summaryContext: opts.summaryContext,
showInBrowser: opts.showInBrowser,
options: opts
});
logger.trace('runner-helper.js --> clearing filesystem from old report files');
reportGenerator.clean();
logger.trace('runner-helper.js --> stopping any running Docker containers...');
DockerHelper.stopSync();
logger.trace('runner-helper.js --> looping throughout the test list and processing each!');
for (let index = 0; index < testsList.length; index++) {
const testSpec = testsList[index];
logger.trace('runner-helper.js --> test {0} running with specification: {1}{2}!'.format(index + 1, Utils.getNewLine(), JSON.stringify(testSpec, null, 2)));
const capabilitiesUnref = JSON.parse(JSON.stringify(testSpec.capability));
const retryController = new RetryHelper(capabilitiesUnref['teal:runRetries']);
const useSauceConnect = capabilitiesUnref['teal:enableSauceConnect'];
const sauceConnectOverrideString = opts.enableProxy === useSauceConnect
? ''
: '(originally set to {0})'.format(opts.enableProxy ? Constants.ACTIVE : Constants.OFF);
logger.info(
('runner-helper.js --> {1}' +
'{15}Run {2} of {3}{1}' +
'{15}File : {4}{0}' +
'{15}Browser : {5}{1}' +
'{15}Trace : {6}{0}' +
'{15}Proxy : {7}{0}' +
'{15}Sauce Connect : {8} {9}{1}' +
'{15}Remote Run : {10}{0}' +
'{15}Cross Browser : {11}{0}' +
'{15}So Many Browsers : {12}{1}' +
'{15}Retries Allowed : {13}{0}' +
'{15}Show Browser Report: {16}{0}' +
'{0}{15}{14}{0}')
.format(
Utils.getNewLine(),
Utils.getNewLine(2),
index + 1,
testsList.length,
testSpec.spec,
capabilitiesUnref.browserName,
opts.enableTrace ? Constants.ACTIVE : Constants.OFF,
opts.enableProxy ? Constants.ACTIVE : Constants.OFF,
useSauceConnect ? Constants.ACTIVE : Constants.OFF,
sauceConnectOverrideString,
opts.runRemotely ? Constants.ACTIVE : Constants.OFF,
opts.runCrossBrowser ? Constants.ACTIVE : Constants.OFF,
opts.runFullCrossBrowser ? Constants.ACTIVE : Constants.OFF,
capabilitiesUnref['teal:runRetries'],
opts.enableProxy ? '' : ' Skipping Sauce Connect setup because Proxy is inactive.',
Utils.getRepeatedCharacters(' ', 10),
opts.showInBrowser ? Constants.ACTIVE : Constants.OFF)
);
try {
overridableOptions.specs = [testSpec.spec];
overridableOptions.capabilities = [capabilitiesUnref];
overridableOptions.sauceConnect = !!testSpec.sauceConnect;
logger.trace('runner-helper.js --> executing wdio test run with options: {0}{1}'.format(Utils.getNewLine(), JSON.stringify(overridableOptions, null, 2)));
let testRunResult = await new Launcher('{0}/wdio.conf.js'.format(rootRequire.rootPath), overridableOptions).run();
// 1 means there was at least one failure (likely a Sauce Labs or other infrastructure error) - retry if appropriate
while (testRunResult !== 0 && retryController.shouldRetry()) {
// spin down the docker images, if any - don't wait for this to complete before continuing
if (useSauceConnect) {
logger.trace('runner-helper.js --> RETRYING... stopping any running Docker containers...');
DockerHelper.stopSync();
}
logger.trace('runner-helper.js --> RETRYING...{0}retry {1} of {2} maximum{0}'.format(Utils.getNewLine(), retryController.incrementRetryCounter(), retryController.getMaxAttempts()));
testRunResult = await new Launcher('{0}/wdio.conf.js'.format(rootRequire.rootPath), overridableOptions).run();
}
logger.trace('runner-helper.js --> test run finished');
logger.trace('runner-helper.js --> moving files between runs for report generation');
reportGenerator.moveFilesBetweenRuns(index);
} catch (error) {
if (capabilitiesUnref['teal:enableSauceConnect']) {
logger.trace('runner-helper.js --> Stopping any running Docker containers...');
DockerHelper.stopAsync();
}
logger.error('{1}{1}{2}runner.js --> CRITICAL Fail running test!{3}{2}{4}{1}{0}'
.format(Utils.getRepeatedCharacters('*', 100), Utils.getNewLine(2), Utils.getRepeatedCharacters(' ', 10), Utils.getNewLine(), error));
}
}
logger.trace('runner-helper.js --> generating report');
reportGenerator.createReport({
config: {},
capabilities: opts.capabilities,
results: {},
'teal:fullRunList': JSON.stringify(testsList)
});
logger.trace('runner-helper.js --> Stopping any running Docker containers...');
DockerHelper.stopSync();
logger.info('runner-helper.js --> Summary report successfully generated!');
logger.debug('runner-helper.js --> RunnerHelper.run finished');
};
}
module.exports = RunnerHelper;