@wdio/cli 7.16.16 → 7.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/package.json +4 -4
  2. package/build/@types/yarn-install.d.ts +0 -16
  3. package/build/@types/yarn-install.d.ts.map +0 -1
  4. package/build/@types/yarn-install.js +0 -1
  5. package/build/commands/config.d.ts +0 -64
  6. package/build/commands/config.d.ts.map +0 -1
  7. package/build/commands/config.js +0 -236
  8. package/build/commands/install.d.ts +0 -18
  9. package/build/commands/install.d.ts.map +0 -1
  10. package/build/commands/install.js +0 -112
  11. package/build/commands/repl.d.ts +0 -36
  12. package/build/commands/repl.d.ts.map +0 -1
  13. package/build/commands/repl.js +0 -62
  14. package/build/commands/run.d.ts +0 -172
  15. package/build/commands/run.d.ts.map +0 -1
  16. package/build/commands/run.js +0 -172
  17. package/build/constants.d.ts +0 -485
  18. package/build/constants.d.ts.map +0 -1
  19. package/build/constants.js +0 -370
  20. package/build/index.d.ts +0 -5
  21. package/build/index.d.ts.map +0 -1
  22. package/build/index.js +0 -84
  23. package/build/interface.d.ts +0 -75
  24. package/build/interface.d.ts.map +0 -1
  25. package/build/interface.js +0 -245
  26. package/build/launcher.d.ts +0 -95
  27. package/build/launcher.d.ts.map +0 -1
  28. package/build/launcher.js +0 -418
  29. package/build/templates/afterTest.ejs +0 -20
  30. package/build/templates/exampleFiles/cucumber/features/login.feature +0 -12
  31. package/build/templates/exampleFiles/cucumber/step_definitions/steps.js.ejs +0 -52
  32. package/build/templates/exampleFiles/jasmine/example.e2e.js.ejs +0 -42
  33. package/build/templates/exampleFiles/mocha/example.e2e.js.ejs +0 -42
  34. package/build/templates/exampleFiles/pageobjects/login.page.js.ejs +0 -44
  35. package/build/templates/exampleFiles/pageobjects/page.js.ejs +0 -13
  36. package/build/templates/exampleFiles/pageobjects/secure.page.js.ejs +0 -19
  37. package/build/templates/reporters.ejs +0 -14
  38. package/build/templates/wdio.conf.tpl.ejs +0 -448
  39. package/build/types.d.ts +0 -110
  40. package/build/types.d.ts.map +0 -1
  41. package/build/types.js +0 -2
  42. package/build/utils.d.ts +0 -82
  43. package/build/utils.d.ts.map +0 -1
  44. package/build/utils.js +0 -345
  45. package/build/watcher.d.ts +0 -34
  46. package/build/watcher.d.ts.map +0 -1
  47. package/build/watcher.js +0 -150
package/build/utils.js DELETED
@@ -1,345 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getDefaultFiles = exports.getPathForFileGeneration = exports.getAnswers = exports.generateTestFiles = exports.hasPackage = exports.hasFile = exports.getCapabilities = exports.validateServiceAnswers = exports.renderConfigurationFile = exports.convertPackageHashToObject = exports.addServiceDeps = exports.replaceConfig = exports.findInConfig = exports.getRunnerName = exports.runOnCompleteHook = exports.runLauncherHook = exports.runServiceHook = void 0;
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
8
- const ejs_1 = __importDefault(require("ejs"));
9
- const path_1 = __importDefault(require("path"));
10
- const inquirer_1 = __importDefault(require("inquirer"));
11
- const logger_1 = __importDefault(require("@wdio/logger"));
12
- const recursive_readdir_1 = __importDefault(require("recursive-readdir"));
13
- const webdriverio_1 = require("webdriverio");
14
- const child_process_1 = require("child_process");
15
- const util_1 = require("util");
16
- const constants_1 = require("./constants");
17
- const log = (0, logger_1.default)('@wdio/cli:utils');
18
- const TEMPLATE_ROOT_DIR = path_1.default.join(__dirname, 'templates', 'exampleFiles');
19
- const renderFile = (0, util_1.promisify)(ejs_1.default.renderFile);
20
- /**
21
- * run service launch sequences
22
- */
23
- async function runServiceHook(launcher, hookName, ...args) {
24
- const start = Date.now();
25
- return Promise.all(launcher.map(async (service) => {
26
- try {
27
- if (typeof service[hookName] === 'function') {
28
- await service[hookName](...args);
29
- }
30
- }
31
- catch (err) {
32
- const message = `A service failed in the '${hookName}' hook\n${err.stack}\n\n`;
33
- if (err instanceof webdriverio_1.SevereServiceError) {
34
- return { status: 'rejected', reason: message };
35
- }
36
- log.error(`${message}Continue...`);
37
- }
38
- })).then(results => {
39
- if (launcher.length) {
40
- log.debug(`Finished to run "${hookName}" hook in ${Date.now() - start}ms`);
41
- }
42
- const rejectedHooks = results.filter(p => p && p.status === 'rejected');
43
- if (rejectedHooks.length) {
44
- return Promise.reject(new Error(`\n${rejectedHooks.map(p => p && p.reason).join()}\n\nStopping runner...`));
45
- }
46
- });
47
- }
48
- exports.runServiceHook = runServiceHook;
49
- /**
50
- * Run hook in service launcher
51
- * @param {Array|Function} hook - can be array of functions or single function
52
- * @param {Object} config
53
- * @param {Object} capabilities
54
- */
55
- async function runLauncherHook(hook, ...args) {
56
- const catchFn = (e) => log.error(`Error in hook: ${e.stack}`);
57
- if (typeof hook === 'function') {
58
- hook = [hook];
59
- }
60
- return Promise.all(hook.map((hook) => {
61
- try {
62
- return hook(...args);
63
- }
64
- catch (err) {
65
- return catchFn(err);
66
- }
67
- })).catch(catchFn);
68
- }
69
- exports.runLauncherHook = runLauncherHook;
70
- /**
71
- * Run onCompleteHook in Launcher
72
- * @param {Array|Function} onCompleteHook - can be array of functions or single function
73
- * @param {*} config
74
- * @param {*} capabilities
75
- * @param {*} exitCode
76
- * @param {*} results
77
- */
78
- async function runOnCompleteHook(onCompleteHook, config, capabilities, exitCode, results) {
79
- if (typeof onCompleteHook === 'function') {
80
- onCompleteHook = [onCompleteHook];
81
- }
82
- return Promise.all(onCompleteHook.map(async (hook) => {
83
- try {
84
- await hook(exitCode, config, capabilities, results);
85
- return 0;
86
- }
87
- catch (err) {
88
- log.error(`Error in onCompleteHook: ${err.stack}`);
89
- return 1;
90
- }
91
- }));
92
- }
93
- exports.runOnCompleteHook = runOnCompleteHook;
94
- /**
95
- * get runner identification by caps
96
- */
97
- function getRunnerName(caps = {}) {
98
- let runner = caps.browserName ||
99
- caps.appPackage ||
100
- caps.appWaitActivity ||
101
- caps.app ||
102
- caps.platformName ||
103
- caps['appium:platformName'] ||
104
- caps['appium:appPackage'] ||
105
- caps['appium:appWaitActivity'] ||
106
- caps['appium:app'];
107
- // MultiRemote
108
- if (!runner) {
109
- runner = Object.values(caps).length === 0 || Object.values(caps).some(cap => !cap.capabilities) ? 'undefined' : 'MultiRemote';
110
- }
111
- return runner;
112
- }
113
- exports.getRunnerName = getRunnerName;
114
- function buildNewConfigArray(str, type, change) {
115
- var _a;
116
- const newStr = str
117
- .split(`${type}s: `)[1]
118
- .replace(/'/g, '');
119
- let newArray = ((_a = newStr.match(/(\w*)/gmi)) === null || _a === void 0 ? void 0 : _a.filter(e => !!e).concat([change])) || [];
120
- return str
121
- .replace('// ', '')
122
- .replace(new RegExp(`(${type}s: )((.*\\s*)*)`), `$1[${newArray.map(e => `'${e}'`)}]`);
123
- }
124
- function buildNewConfigString(str, type, change) {
125
- return str.replace(new RegExp(`(${type}: )('\\w*')`), `$1'${change}'`);
126
- }
127
- function findInConfig(config, type) {
128
- let regexStr = `[\\/\\/]*[\\s]*${type}s: [\\s]*\\[([\\s]*['|"]\\w*['|"],*)*[\\s]*\\]`;
129
- if (type === 'framework') {
130
- regexStr = `[\\/\\/]*[\\s]*${type}: ([\\s]*['|"]\\w*['|"])`;
131
- }
132
- const regex = new RegExp(regexStr, 'gmi');
133
- return config.match(regex);
134
- }
135
- exports.findInConfig = findInConfig;
136
- function replaceConfig(config, type, name) {
137
- if (type === 'framework') {
138
- return buildNewConfigString(config, type, name);
139
- }
140
- const match = findInConfig(config, type);
141
- if (!match || match.length === 0) {
142
- return;
143
- }
144
- const text = match.pop() || '';
145
- return config.replace(text, buildNewConfigArray(text, type, name));
146
- }
147
- exports.replaceConfig = replaceConfig;
148
- function addServiceDeps(names, packages, update = false) {
149
- /**
150
- * automatically install latest Chromedriver if `wdio-chromedriver-service`
151
- * was selected for install
152
- */
153
- if (names.some(({ short }) => short === 'chromedriver')) {
154
- packages.push('chromedriver');
155
- if (update) {
156
- // eslint-disable-next-line no-console
157
- console.log('\n=======', '\nPlease change path to / in your wdio.conf.js:', "\npath: '/'", '\n=======\n');
158
- }
159
- }
160
- /**
161
- * install Appium if it is not installed globally if `@wdio/appium-service`
162
- * was selected for install
163
- */
164
- if (names.some(({ short }) => short === 'appium')) {
165
- const result = (0, child_process_1.execSync)('appium --version || echo APPIUM_MISSING').toString().trim();
166
- if (result === 'APPIUM_MISSING') {
167
- packages.push('appium');
168
- }
169
- else if (update) {
170
- // eslint-disable-next-line no-console
171
- console.log('\n=======', '\nUsing globally installed appium', result, '\nPlease add the following to your wdio.conf.js:', "\nappium: { command: 'appium' }", '\n=======\n');
172
- }
173
- }
174
- }
175
- exports.addServiceDeps = addServiceDeps;
176
- /**
177
- * @todo add JSComments
178
- */
179
- function convertPackageHashToObject(pkg, hash = '$--$') {
180
- const splitHash = pkg.split(hash);
181
- return {
182
- package: splitHash[0],
183
- short: splitHash[1]
184
- };
185
- }
186
- exports.convertPackageHashToObject = convertPackageHashToObject;
187
- async function renderConfigurationFile(answers) {
188
- const tplPath = path_1.default.join(__dirname, 'templates/wdio.conf.tpl.ejs');
189
- const filename = `wdio.conf.${answers.isUsingTypeScript ? 'ts' : 'js'}`;
190
- const renderedTpl = await renderFile(tplPath, { answers });
191
- return fs_extra_1.default.promises.writeFile(path_1.default.join(process.cwd(), answers.isUsingTypeScript ? 'test' : '', filename), renderedTpl);
192
- }
193
- exports.renderConfigurationFile = renderConfigurationFile;
194
- const validateServiceAnswers = (answers) => {
195
- let result = true;
196
- Object.entries(constants_1.EXCLUSIVE_SERVICES).forEach(([name, { services, message }]) => {
197
- const exists = answers.some(answer => answer.includes(name));
198
- const hasExclusive = services.some(service => answers.some(answer => answer.includes(service)));
199
- if (exists && hasExclusive) {
200
- result = `${name} cannot work together with ${services.join(', ')}\n${message}\nPlease uncheck one of them.`;
201
- }
202
- });
203
- return result;
204
- };
205
- exports.validateServiceAnswers = validateServiceAnswers;
206
- function getCapabilities(arg) {
207
- const optionalCapabilites = {
208
- platformVersion: arg.platformVersion,
209
- udid: arg.udid,
210
- ...(arg.deviceName && { deviceName: arg.deviceName })
211
- };
212
- /**
213
- * Parsing of option property and constructing desiredCapabilities
214
- * for Appium session. Could be application(1) or browser(2-3) session.
215
- */
216
- if (/.*\.(apk|app|ipa)$/.test(arg.option)) {
217
- return {
218
- capabilities: {
219
- app: arg.option,
220
- ...(arg.option.endsWith('apk') ? constants_1.ANDROID_CONFIG : constants_1.IOS_CONFIG),
221
- ...optionalCapabilites,
222
- }
223
- };
224
- }
225
- else if (/android/.test(arg.option)) {
226
- return { capabilities: { browserName: 'Chrome', ...constants_1.ANDROID_CONFIG, ...optionalCapabilites } };
227
- }
228
- else if (/ios/.test(arg.option)) {
229
- return { capabilities: { browserName: 'Safari', ...constants_1.IOS_CONFIG, ...optionalCapabilites } };
230
- }
231
- return { capabilities: { browserName: arg.option } };
232
- }
233
- exports.getCapabilities = getCapabilities;
234
- /**
235
- * Check if file exists in current work directory
236
- * @param {string} filename to check existance for
237
- */
238
- function hasFile(filename) {
239
- return fs_extra_1.default.existsSync(path_1.default.join(process.cwd(), filename));
240
- }
241
- exports.hasFile = hasFile;
242
- /**
243
- * Check if package is installed
244
- * @param {string} package to check existance for
245
- */
246
- function hasPackage(pkg) {
247
- try {
248
- /**
249
- * this is only for testing purposes as we want to check whether
250
- * we add `@babel/register` to the packages to install when resolving fails
251
- */
252
- if (process.env.JEST_WORKER_ID && process.env.WDIO_TEST_THROW_RESOLVE) {
253
- throw new Error('resolve error');
254
- }
255
- require.resolve(pkg);
256
- return true;
257
- }
258
- catch (err) {
259
- return false;
260
- }
261
- }
262
- exports.hasPackage = hasPackage;
263
- /**
264
- * generate test files based on CLI answers
265
- */
266
- async function generateTestFiles(answers) {
267
- const testFiles = answers.framework === 'cucumber'
268
- ? [path_1.default.join(TEMPLATE_ROOT_DIR, 'cucumber')]
269
- : (answers.framework === 'mocha'
270
- ? [path_1.default.join(TEMPLATE_ROOT_DIR, 'mocha')]
271
- : [path_1.default.join(TEMPLATE_ROOT_DIR, 'jasmine')]);
272
- if (answers.usePageObjects) {
273
- testFiles.push(path_1.default.join(TEMPLATE_ROOT_DIR, 'pageobjects'));
274
- }
275
- const files = (await Promise.all(testFiles.map((dirPath) => (0, recursive_readdir_1.default)(dirPath, [(file, stats) => !stats.isDirectory() && !(file.endsWith('.ejs') || file.endsWith('.feature'))])))).reduce((cur, acc) => [...acc, ...(cur)], []);
276
- for (const file of files) {
277
- const renderedTpl = await renderFile(file, answers);
278
- let destPath = (file.endsWith('page.js.ejs')
279
- ? `${answers.destPageObjectRootPath}/${path_1.default.basename(file)}`
280
- : file.includes('step_definition')
281
- ? `${answers.stepDefinitions}`
282
- : `${answers.destSpecRootPath}/${path_1.default.basename(file)}`).replace(/\.ejs$/, '').replace(/\.js$/, answers.isUsingTypeScript ? '.ts' : '.js');
283
- fs_extra_1.default.ensureDirSync(path_1.default.dirname(destPath));
284
- await fs_extra_1.default.promises.writeFile(destPath, renderedTpl);
285
- }
286
- }
287
- exports.generateTestFiles = generateTestFiles;
288
- async function getAnswers(yes) {
289
- return yes
290
- ? constants_1.QUESTIONNAIRE.reduce((answers, question) => Object.assign(answers, question.when && !question.when(answers)
291
- /**
292
- * set nothing if question doesn't apply
293
- */
294
- ? {}
295
- : { [question.name]: typeof question.default !== 'undefined'
296
- /**
297
- * set default value if existing
298
- */
299
- ? typeof question.default === 'function'
300
- ? question.default(answers)
301
- : question.default
302
- : question.choices && question.choices.length
303
- /**
304
- * pick first choice, select value if it exists
305
- */
306
- ? question.choices[0].value
307
- ? question.choices[0].value
308
- : question.choices[0]
309
- : {}
310
- }), {})
311
- : await inquirer_1.default.prompt(constants_1.QUESTIONNAIRE);
312
- }
313
- exports.getAnswers = getAnswers;
314
- function getPathForFileGeneration(answers) {
315
- const destSpecRootPath = path_1.default.join(process.cwd(), path_1.default.dirname(answers.specs || '').replace(/\*\*$/, ''));
316
- const destStepRootPath = path_1.default.join(process.cwd(), path_1.default.dirname(answers.stepDefinitions || ''));
317
- const destPageObjectRootPath = answers.usePageObjects
318
- ? path_1.default.join(process.cwd(), path_1.default.dirname(answers.pages || '').replace(/\*\*$/, ''))
319
- : '';
320
- let relativePath = (answers.generateTestFiles && answers.usePageObjects)
321
- ? !(convertPackageHashToObject(answers.framework).short === 'cucumber')
322
- ? path_1.default.relative(destSpecRootPath, destPageObjectRootPath)
323
- : path_1.default.relative(destStepRootPath, destPageObjectRootPath)
324
- : '';
325
- /**
326
- * On Windows, path.relative can return backslashes that could be interpreted as espace sequences in strings
327
- */
328
- if (process.platform === 'win32') {
329
- relativePath = relativePath.replace(/\\/g, '/');
330
- }
331
- return {
332
- destSpecRootPath: destSpecRootPath,
333
- destStepRootPath: destStepRootPath,
334
- destPageObjectRootPath: destPageObjectRootPath,
335
- relativePath: relativePath
336
- };
337
- }
338
- exports.getPathForFileGeneration = getPathForFileGeneration;
339
- function getDefaultFiles(answers, filePath) {
340
- var _a;
341
- return ((_a = answers === null || answers === void 0 ? void 0 : answers.isUsingCompiler) === null || _a === void 0 ? void 0 : _a.toString().includes('TypeScript'))
342
- ? `${filePath}.ts`
343
- : `${filePath}.js`;
344
- }
345
- exports.getDefaultFiles = getDefaultFiles;
@@ -1,34 +0,0 @@
1
- import type { Workers } from '@wdio/types';
2
- import { RunCommandArguments, ValueKeyIteratee } from './types.js';
3
- declare type Spec = string | string[];
4
- export default class Watcher {
5
- private _configFile;
6
- private _args;
7
- private _launcher;
8
- private _specs;
9
- constructor(_configFile: string, _args: Omit<RunCommandArguments, 'configPath'>);
10
- watch(): Promise<void>;
11
- /**
12
- * return file listener callback that calls `run` method
13
- * @param {Boolean} [passOnFile=true] if true pass on file change as parameter
14
- * @return {Function} chokidar event callback
15
- */
16
- getFileListener(passOnFile?: boolean): (spec: string) => void[];
17
- /**
18
- * helper method to get workers from worker pool of wdio runner
19
- * @param predicate filter by property value (see lodash.pickBy)
20
- * @param includeBusyWorker don't filter out busy worker (default: false)
21
- * @return Object with workers, e.g. {'0-0': { ... }}
22
- */
23
- getWorkers(predicate?: ValueKeyIteratee<Workers.Worker> | null | undefined, includeBusyWorker?: boolean): Workers.WorkerPool;
24
- /**
25
- * run workers with params
26
- * @param params parameters to run the worker with
27
- */
28
- run(params?: Omit<Partial<RunCommandArguments>, 'spec'> & {
29
- spec?: Spec;
30
- }): void;
31
- cleanUp(): void;
32
- }
33
- export {};
34
- //# sourceMappingURL=watcher.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAgB,OAAO,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAIlE,aAAK,IAAI,GAAG,MAAM,GAAG,MAAM,EAAE,CAAA;AAC7B,MAAM,CAAC,OAAO,OAAO,OAAO;IAKpB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,KAAK;IALjB,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,MAAM,CAAQ;gBAGV,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,YAAY,CAAC;IAYpD,KAAK;IAwCX;;;;OAIG;IACH,eAAe,CAAE,UAAU,UAAO,UAChB,MAAM;IA6BxB;;;;;OAKG;IACH,UAAU,CAAE,SAAS,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU;IAiB7H;;;OAGG;IACH,GAAG,CAAE,MAAM,GAAE,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC,GAAG;QAAE,IAAI,CAAC,EAAE,IAAI,CAAA;KAAO;IAuC9E,OAAO;CAGV"}
package/build/watcher.js DELETED
@@ -1,150 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const chokidar_1 = __importDefault(require("chokidar"));
7
- const logger_1 = __importDefault(require("@wdio/logger"));
8
- const lodash_pickby_1 = __importDefault(require("lodash.pickby"));
9
- const lodash_flattendeep_1 = __importDefault(require("lodash.flattendeep"));
10
- const lodash_union_1 = __importDefault(require("lodash.union"));
11
- const launcher_1 = __importDefault(require("./launcher"));
12
- const log = (0, logger_1.default)('@wdio/cli:watch');
13
- class Watcher {
14
- constructor(_configFile, _args) {
15
- this._configFile = _configFile;
16
- this._args = _args;
17
- log.info('Starting launcher in watch mode');
18
- this._launcher = new launcher_1.default(this._configFile, this._args, true);
19
- const specs = this._launcher.configParser.getSpecs();
20
- const capSpecs = this._launcher.isMultiremote ? [] : (0, lodash_union_1.default)((0, lodash_flattendeep_1.default)(this._launcher.configParser.getCapabilities().map(cap => cap.specs || [])));
21
- this._specs = [...specs, ...capSpecs];
22
- }
23
- async watch() {
24
- /**
25
- * listen on spec changes and rerun specific spec file
26
- */
27
- let flattenedSpecs = (0, lodash_flattendeep_1.default)(this._specs);
28
- chokidar_1.default.watch(flattenedSpecs, { ignoreInitial: true })
29
- .on('add', this.getFileListener())
30
- .on('change', this.getFileListener());
31
- /**
32
- * listen on filesToWatch changes an rerun complete suite
33
- */
34
- const { filesToWatch } = this._launcher.configParser.getConfig();
35
- if (filesToWatch.length) {
36
- chokidar_1.default.watch(filesToWatch, { ignoreInitial: true })
37
- .on('add', this.getFileListener(false))
38
- .on('change', this.getFileListener(false));
39
- }
40
- /**
41
- * run initial test suite
42
- */
43
- await this._launcher.run();
44
- /**
45
- * clean interface once all worker finish
46
- */
47
- const workers = this.getWorkers();
48
- Object.values(workers).forEach((worker) => worker.on('exit', () => {
49
- /**
50
- * check if all workers have finished
51
- */
52
- if (Object.values(workers).find((w) => w.isBusy)) {
53
- return;
54
- }
55
- this._launcher.interface.finalise();
56
- }));
57
- }
58
- /**
59
- * return file listener callback that calls `run` method
60
- * @param {Boolean} [passOnFile=true] if true pass on file change as parameter
61
- * @return {Function} chokidar event callback
62
- */
63
- getFileListener(passOnFile = true) {
64
- return (spec) => {
65
- const runSpecs = [];
66
- let singleSpecFound = false;
67
- for (let index = 0, length = this._specs.length; index < length; index += 1) {
68
- const value = this._specs[index];
69
- if (Array.isArray(value) && value.indexOf(spec) > -1) {
70
- runSpecs.push(value);
71
- }
72
- else if (!singleSpecFound && spec === value) {
73
- // Only need to run a singleFile once - so avoid duplicates
74
- singleSpecFound = true;
75
- runSpecs.push(value);
76
- }
77
- }
78
- // If the runSpecs array is empty, then this must be a new file/array
79
- // so add the spec directly to the runSpecs
80
- if (runSpecs.length === 0) {
81
- runSpecs.push(spec);
82
- }
83
- // Do not pass the `spec` command line option to `this.run()`
84
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
85
- const { spec: _specArg, ...args } = this._args;
86
- return runSpecs.map((spec) => {
87
- return this.run({ ...args, ...(passOnFile ? { spec } : {}) });
88
- });
89
- };
90
- }
91
- /**
92
- * helper method to get workers from worker pool of wdio runner
93
- * @param predicate filter by property value (see lodash.pickBy)
94
- * @param includeBusyWorker don't filter out busy worker (default: false)
95
- * @return Object with workers, e.g. {'0-0': { ... }}
96
- */
97
- getWorkers(predicate, includeBusyWorker) {
98
- let workers = this._launcher.runner.workerPool;
99
- if (typeof predicate === 'function') {
100
- workers = (0, lodash_pickby_1.default)(workers, predicate);
101
- }
102
- /**
103
- * filter out busy workers, only skip if explicitly desired
104
- */
105
- if (!includeBusyWorker) {
106
- workers = (0, lodash_pickby_1.default)(workers, (worker) => !worker.isBusy);
107
- }
108
- return workers;
109
- }
110
- /**
111
- * run workers with params
112
- * @param params parameters to run the worker with
113
- */
114
- run(params = {}) {
115
- const workers = this.getWorkers((params.spec ? (worker) => {
116
- if (Array.isArray(params.spec)) {
117
- return params.spec === worker.specs;
118
- }
119
- return worker.specs.includes(params.spec);
120
- } : undefined));
121
- /**
122
- * don't do anything if no worker was found
123
- */
124
- if (Object.keys(workers).length === 0) {
125
- return;
126
- }
127
- /**
128
- * update total worker count interface
129
- * ToDo: this should have a cleaner solution
130
- */
131
- this._launcher.interface.totalWorkerCnt = Object.entries(workers).length;
132
- /**
133
- * clean up interface
134
- */
135
- this.cleanUp();
136
- /**
137
- * trigger new run for non busy worker
138
- */
139
- for (const [, worker] of Object.entries(workers)) {
140
- const { cid, caps, specs, sessionId } = worker;
141
- const args = Object.assign({ sessionId }, params);
142
- worker.postMessage('run', args);
143
- this._launcher.interface.emit('job:start', { cid, caps, specs });
144
- }
145
- }
146
- cleanUp() {
147
- this._launcher.interface.setup();
148
- }
149
- }
150
- exports.default = Watcher;