This repository has been archived by the owner on May 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 37
/
wdio-selenium-docker-service.js
135 lines (114 loc) · 4.91 KB
/
wdio-selenium-docker-service.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* eslint-disable class-methods-use-this */
const path = require('path');
const util = require('util');
const childProcess = require('child_process');
const { SevereServiceError } = require('webdriverio');
const { Logger } = require('@cerner/terra-cli');
const logger = new Logger({ prefix: '[terra-functional-testing:wdio-selenium-docker-service]' });
const exec = util.promisify(childProcess.exec);
class SeleniumDockerService {
/**
* Service constructor.
* @param {Object} _options - The options specific to this service.
* @param {Object} _capabilities - The list of capabilities details.
* @param {Object} config - The object containing the wdio configuration and options defined in the terra-cli test runner.
*/
constructor(_options, _capabilities, config = {}) {
const { launcherOptions, serviceOptions } = config;
const { disableSeleniumService, keepAliveSeleniumDockerService } = launcherOptions || {};
const { seleniumVersion } = serviceOptions || {};
this.disableSeleniumService = disableSeleniumService === true;
this.keepAliveSeleniumDockerService = keepAliveSeleniumDockerService === true;
this.seleniumVersion = seleniumVersion;
}
/**
* Gets executed once before all workers get launched.
*/
async onPrepare() {
if (!this.disableSeleniumService) {
try {
// Verify docker is installed.
await exec('docker -v');
await this.startSeleniumHub();
} catch (error) {
throw new SevereServiceError(error);
}
}
}
/**
* Waits for a command to complete successfully.
* @param {string} command - The shell command to run.
* @param {func} callback - A callback function to accept or reject the result of the command. Must return a promise.
* @param {number} retries - The number of times to retry the command. Defaults to 30 retries.
* @param {number} interval - The timeout between commands in milliseconds. Defaults to every two seconds.
* @returns {Promise} - A promise that resolves when the callback accepts the command response.
*/
async pollCommand(command, callback, retries = 30, interval = 2000) {
return new Promise((resolve, reject) => {
let retryCount = 0;
let pollTimeout = null;
const poll = async () => {
if (retryCount >= retries) {
clearTimeout(pollTimeout);
pollTimeout = null;
reject(Error(`Timeout. Exceeded retry count for ${command}.`));
}
try {
const result = await exec(command);
await callback(result).then(() => resolve());
} catch (error) {
retryCount += 1;
pollTimeout = setTimeout(poll, interval);
}
};
pollTimeout = setTimeout(poll, interval);
});
}
/**
* Retrieves the docker compose filepath.
* @returns {string} - The docker compose file path.
*/
getDockerComposeFilePath() {
return path.resolve(__dirname, '../docker/docker-compose.yml');
}
/**
* Starts the docker selenium hub and waits for the grid to become ready.
*/
async startSeleniumHub() {
logger.info('Starting the docker selenium hub...');
const envVars = this.seleniumVersion ? `TERRA_SELENIUM_DOCKER_VERSION=${this.seleniumVersion} ` : '';
await exec(`${envVars}docker-compose -f ${this.getDockerComposeFilePath()} up -d`);
await this.waitForSeleniumHubReady();
logger.info('Successfully started the docker selenium hub.');
}
/**
* Waits for the docker selenium hub to become healthy.
*/
async waitForSeleniumHubReady() {
await this.pollCommand("docker inspect --format='{{json .State.Health.Status}}' selenium-hub", (result) => (
new Promise((resolve, reject) => {
const { stdout } = result;
if (stdout && stdout.trim() === '"healthy"') {
resolve();
} else {
reject();
}
// A two minute timeout for the selenium service to become available.
// This helps account for pulling the docker images for the very first time.
})), 60, 2000);
}
/**
* Gets executed after all workers have shut down and the process is about to exit.
* An error thrown in the `onComplete` hook will result in the test run failing.
*/
async onComplete() {
// When multiple test sessions are executed sequentially as specified in the WDIO script in the package.json file, the keepAliveSeleniumDockerService cli option
// is used to indicate not to remove the currently deployed docker stack upon test completion as it will be used again for the next test session.
// The docker stack is expected to be removed by the last test session when no keepAliveSeleniumDockerService cli option is specified.
if (!this.keepAliveSeleniumDockerService && !this.disableSeleniumService) {
logger.info('Shutting down the docker selenium hub...');
await exec(`docker-compose -f ${this.getDockerComposeFilePath()} down`);
}
}
}
module.exports = SeleniumDockerService;