forked from orca-scan/puppeteer-cucumber-js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
executable file
·351 lines (278 loc) · 10.9 KB
/
index.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
#!/usr/bin/env node
var fs = require('fs-plus');
var path = require('path');
var program = require('commander');
var pjson = require('./package.json');
var cucumber = require('cucumber');
var chalk = require('chalk');
var helpers = require('./runtime/helpers.js');
var merge = require('merge');
var requireDir = require('require-dir');
var textFilesLoader = require('text-files-loader');
var networkSpeeds = require('./runtime/network-speed.js');
var config = {
featureFiles: './features',
steps: './features/step-definitions',
pageObjects: './features/page-objects',
sharedObjects: './features/shared-objects',
reports: './features/reports',
browser: 'chrome',
browserTeardownStrategy: 'always',
timeout: 15000,
headless: false,
devTools: false,
slowMo: 10,
noSandbox: false,
disableSetuidSandbox: false,
disableDevShmUsage: false
};
folderCheck();
// global defaults (before cli commands)
global.browserName = 'chrome';
global.browserPath = '';
global.browserTeardownStrategy = config.browserTeardownStrategy;
global.headless = config.headless;
global.devTools = config.devTools;
global.userAgent = '';
global.disableLaunchReport = false;
global.noScreenshot = false;
global.slowMo = config.slowMo;
global.noSandbox = config.noSandbox;
global.disableSetuidSandbox = config.disableSetuidSandbox;
global.disableDevShmUsage = config.disableDevShmUsage;
program
.version(pjson.version)
.description(pjson.description)
.option('--tags <@tagname>', 'cucumber @tag name to run', collectPaths, [])
.option('--featureFiles <paths>', 'comma-separated list of feature files or path to directory. defaults to ' + config.featureFiles, config.featureFiles)
.option('--browser <name>', 'name of browser to use (chrome, firefox, edge, brave). default ' + config.browser, config.browser)
.option('--browserPath <path>', 'optional path to a browser executable')
.option('--browser-teardown <optional>', 'browser teardown after each scenario (always, clear, none). defaults ' + config.browserTeardownStrategy, config.browserTeardownStrategy)
.option('--headless', 'whether to run browser in headless mode. defaults to true unless the devtools option is true', config.headless)
.option('--devTools', 'auto-open a DevTools. if true headless mode is disabled.', config.devTools)
.option('--noScreenshot', 'disable auto capturing of screenshots when an error is encountered')
.option('--disableLaunchReport', 'Disables the auto opening the browser with test report')
.option('--timeOut <number>', 'steps definition timeout in milliseconds. defaults to ' + config.timeout, coerceInt, config.timeout)
.option('--worldParameters <JSON>', 'JSON object to pass to cucumber-js world constructor. defaults to empty', config.worldParameters)
.option('--userAgent <string>', 'user agent string')
.option('--failFast', 'abort the run on first failure')
.option('--slowMo <number>', 'specified amount of milliseconds to slow down Puppeteer operations by. Defaults to ' + config.slowMo)
.option('--networkSpeed <name>', 'simulate connection speeds, options are: gprs, 2g, 3g, 4g, dsl, wifi. Defaults is unset (full speed)')
.option('--updateBaselineImage', 'automatically update the baseline image after a failed comparison')
.option('--noSandbox', 'disable sandboxing')
.option('--disableSetuidSandbox', 'disable sandboxing')
.option('--disableDevShmUsage', 'prevent puppeteer fron using /dev/shm')
.parse(process.argv);
program.on('--help', function () {
console.log(' For more details please visit https://github.com/orca-scan/puppeteer-cucumber-js#readme\n');
});
// name of the browser to use for the test
global.browserName = program.browser || global.browserName;
// if user specified a browser path, check it exists first
if (program.browserPath) {
if (fs.existsSync(program.browserPath)) {
global.browserPath = program.browserPath;
}
else {
throw new Error('browserPath not found');
}
}
// if user specified brave, attempt to find the path
if (program.browser === 'brave' && global.browserPath === '') {
var bravePath = findBrave();
if (bravePath) {
global.browserPath = bravePath;
global.browserName = '';
}
else {
throw new Error('Brave Browser not found, check your installation or provide the path using --browserPath');
}
}
// how should the browser clean up
global.browserTeardownStrategy = program.browserTeardown || global.browserTeardownStrategy;
// should the browser be headless?
global.headless = program.headless;
// pass dev tools option
global.devTools = program.devTools;
// pass user agent if set (remove wrapped quotes)
global.userAgent = String(program.userAgent || '').replace(/(^"|"$)/g, '');
global.noSandbox = program.noSandbox;
global.disableSetuidSandbox = program.disableSetuidSandbox;
global.disableDevShmUsage = program.disableDevShmUsage;
// used within world.js to import page objects
var pageObjectPath = path.resolve(config.pageObjects);
// load page objects (after global vars have been created)
if (fs.existsSync(pageObjectPath)) {
// require all page objects using camel case as object names
global.pageObjects = requireDir(pageObjectPath, { camelcase: true, recurse: true });
}
// used within world.js to output reports
global.reportsPath = path.resolve(config.reports);
if (!fs.existsSync(config.reports)) {
fs.makeTreeSync(config.reports);
}
// used within world.js to decide if reports should be generated
global.disableLaunchReport = (program.disableLaunchReport);
// used with world.js to determine if a screenshot should be captured on error
global.noScreenshot = (program.noScreenshot);
// set the default timeout if not passed via the command line
global.DEFAULT_TIMEOUT = program.timeOut || config.timeout;
// set the default slowMo if not passed via the command line
global.DEFAULT_SLOW_MO = program.slowMo || config.slowMo;
// set network speed if present
if (program.networkSpeed) {
// if network speed is defined, use it
if (networkSpeeds[program.networkSpeed]) {
global.networkSpeed = networkSpeeds[program.networkSpeed];
}
// otherwise throw an error
else {
throw new Error('Invalid --networkSpeed, options are ' + Object.keys(networkSpeeds));
}
}
// used within world.js to import shared objects into the shared namespace
var sharedObjectsPath = path.resolve(config.sharedObjects);
// import shared objects
if (fs.existsSync(sharedObjectsPath)) {
var allDirs = {};
var dir = requireDir(sharedObjectsPath, { camelcase: true, recurse: true });
merge(allDirs, dir);
// if we managed to import some directories, expose them
if (Object.keys(allDirs).length > 0) {
// expose globally
global.sharedObjects = allDirs;
}
}
// add helpers
global.helpers = helpers;
// rewrite command line switches for cucumber
process.argv.splice(2, 100);
// allow specific feature files to be executed
if (program.featureFiles) {
var splitFeatureFiles = program.featureFiles.split(',');
splitFeatureFiles.forEach(function (feature) {
process.argv.push(feature);
});
}
// add switch to tell cucumber to produce json report files
process.argv.push('-f');
process.argv.push('pretty');
process.argv.push('-f');
process.argv.push('json:' + path.resolve(__dirname, global.reportsPath, 'cucumber-report.json'));
// add cucumber world as first required script (this sets up the globals)
process.argv.push('-r');
process.argv.push(path.resolve(__dirname, 'runtime/world.js'));
// add path to import step definitions
process.argv.push('-r');
process.argv.push(path.resolve(config.steps));
if (program.failFast) {
process.argv.push('--fail-fast');
}
// add tag(s)
if (program.tags) {
// get all tags from feature files
var tagsFound = getFeaturesFileTags();
// go through each tag user passed in
program.tags.forEach(function (tag) {
// if tag does not start with `@` exit with error
if (tag[0] !== '@') {
logErrorToConsole('tags must start with a @');
process.exit();
}
// if tag does not exist in feature files, error
if (tagsFound.indexOf(tag) === -1) {
logErrorToConsole(`tag ${tag} not found`);
process.exit();
}
});
program.tags.forEach(function (tag) {
process.argv.push('-t');
process.argv.push(tag);
});
}
if (program.worldParameters) {
process.argv.push('--world-parameters');
process.argv.push(program.worldParameters);
}
// add strict option (fail if there are any undefined or pending steps)
process.argv.push('-S');
// // abort the run on first failure
// process.argv.push('--fail-fast');
//
// execute cucumber
//
var cucumberCli = new cucumber.Cli(process.argv);
global.cucumber = cucumber;
cucumberCli.run(function (succeeded) {
var code = succeeded ? 0 : 1;
function exitNow() {
process.exit(code);
}
if (process.stdout.write('')) {
exitNow();
}
else {
// write() returned false, kernel buffer is not empty yet...
process.stdout.on('drain', exitNow);
}
});
/**
* Check if required folders exist
* @return {void}
*/
function folderCheck() {
if (!fs.existsSync(config.featureFiles)) {
logErrorToConsole(config.featureFiles + ' folder not found');
process.exit();
}
if (!fs.existsSync(config.steps)) {
logErrorToConsole(config.steps + ' folder not found');
process.exit();
}
if (!fs.existsSync(config.pageObjects)) {
logErrorToConsole(config.pageObjects + ' folder not found');
process.exit();
}
}
/**
* Writes an error message to the console in red
* @param {string} message - error message
* @returns {void}
*/
function logErrorToConsole(message) {
console.log(chalk.red('Error: ' + message || ''));
}
function findBrave() {
var locations = [
'/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
'C:\\Program Files (x86)\\BraveSoftware\\Brave-Browser\\Application\\brave.exe'
];
return locations.find(function(item) {
return fs.existsSync(item);
});
}
function collectPaths(value, paths) {
paths.push(value);
return paths;
}
function coerceInt(value, defaultValue) {
var int = parseInt(value, 10);
if (typeof int === 'number') return int;
return defaultValue;
}
/**
* Get tags from feature files
* @returns {Array<string>} list of all tags found
*/
function getFeaturesFileTags() {
// load all feature files into memory
textFilesLoader.setup({ matchRegExp: /\.feature/ });
var featureFiles = textFilesLoader.loadSync(path.resolve(config.featureFiles));
var result = [];
// go through each one and extract @tags
Object.keys(featureFiles).forEach(function(key) {
var content = String(featureFiles[key] || '');
result = result.concat(content.match(new RegExp('@[a-z0-9]+', 'g')));
});
return result;
}