-
-
Notifications
You must be signed in to change notification settings - Fork 61
/
Copy pathtests-options-validator.js
372 lines (322 loc) · 10.5 KB
/
tests-options-validator.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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
'use strict';
const fs = require('fs-extra');
const SilentError = require('silent-error');
const semver = require('semver');
/**
* Validates the specified partitions
*
* @private
* @param {Array<Number>} partitions
* @param {Number} split
*/
function validatePartitions(partitions, split) {
validatePartitionSplit(partitions, split);
validateElementsUnique(partitions, 'partition');
}
/**
* Returns thr number of browsers defined within the test execution file.
*
* @param {*} fileName
*/
function getNumberOfBrowser(fileName) {
const executionJson = fs.readJsonSync(fileName);
return executionJson.numberOfBrowsers;
}
/**
* Validates the specified replay-browser
*
* @param {String} replayExecution
* @param {Array<Number>} replayBrowser
*/
function validateReplayBrowser(replayExecution, replayBrowser) {
if (!replayExecution) {
throw new SilentError(
'EmberExam: You must specify replay-execution when using the replay-browser option.'
);
}
const numberOfBrowsers = getNumberOfBrowser(replayExecution);
for (const i in replayBrowser) {
const browserId = replayBrowser[i];
if (browserId < 1) {
throw new SilentError(
'EmberExam: You must specify replay-browser values greater than or equal to 1.'
);
}
if (browserId > numberOfBrowsers) {
throw new SilentError(
'EmberExam: You must specify replayBrowser value smaller than a number of browsers in the specified json file.'
);
}
}
validateElementsUnique(replayBrowser, 'replayBrowser');
}
/**
* Determines if the specified partitions value makes sense for a given split.
*
* @private
* @param {Array<Number>} partitions
* @param {Number} split
*/
function validatePartitionSplit(partitions, split) {
if (!split) {
throw new SilentError(
'EmberExam: You must specify a `split` value in order to use `partition`.'
);
}
for (let i = 0; i < partitions.length; i++) {
const partition = partitions[i];
if (partition < 1) {
throw new SilentError(
'EmberExam: Split tests are one-indexed, so you must specify partition values greater than or equal to 1.'
);
}
if (partition > split) {
throw new SilentError(
'EmberExam: You must specify `partition` values that are less than or equal to your `split` value.'
);
}
}
}
/**
* Ensures that there is no value duplicated in a given array.
*
* @private
* @param {Array} arr
* @param {String} typeOfValue
*/
function validateElementsUnique(arr, typeOfValue) {
const sorted = arr.slice().sort();
for (let i = 0; i < sorted.length - 1; i++) {
if (sorted[i] === sorted[i + 1]) {
const errorMsg = `EmberExam: You cannot specify the same ${typeOfValue} value twice. ${
sorted[i]
} is repeated.`;
throw new SilentError(errorMsg);
}
}
}
/**
* Performs logic related to validating command options for testing and
* determining which functions to run on the tests.
*
* @class TestsOptionsValidator
*/
module.exports = class TestsOptionsValidator {
constructor(options, framework, emberCliVersion) {
this.options = options;
this.framework = framework;
this.emberCliVersion = emberCliVersion;
}
/**
* Validates the command and returns a map of the options and whether they are enabled or not.
*
* @public
* @return {Object} Map of the options and whether they are enabled or not.
*/
validateCommands() {
const validatedOptions = new Map();
if (this.options.writeModuleMetadataFile) {
validatedOptions.set(
'writeModuleMetadataFile',
true
);
}
if (this.options.split || this.options.partition) {
validatedOptions.set('split', this.validateSplit());
}
// The parallel option accepts a number, which can be 0
if (typeof this.options.parallel !== 'undefined') {
validatedOptions.set('parallel', this.validateParallel());
}
// As random option can be an empty string it should check a type of random option rather than the option is not empty.
if (typeof this.options.random !== 'undefined') {
validatedOptions.set('random', this.validateRandom());
}
if (typeof this.options.writeExecutionFile !== 'undefined') {
validatedOptions.set(
'writeExecutionFile',
this.validateWriteExecutionFile()
);
}
if (this.options.loadBalance) {
validatedOptions.set('loadBalance', this.validateLoadBalance());
}
if (this.options.replayExecution || this.options.replayBrowser) {
validatedOptions.set('replayExecution', this.validateReplayExecution());
}
return validatedOptions;
}
/**
* Determines if we should split the tests file and validates associated options
* (`split`, `partition`).
*
* @return {boolean}
*/
validateSplit() {
const options = this.options;
let split = options.split;
if (typeof split !== 'undefined' && split < 2) {
// eslint-disable-next-line no-console
console.warn(
'You should specify a number of files greater than 1 to split your tests across. Defaulting to 1 split which is the same as not using `split`.'
);
split = 1;
}
if (
typeof split !== 'undefined' &&
typeof this.options.replayBrowser !== 'undefined'
) {
throw new SilentError(
'EmberExam: You must not use the `replay-browser` option with the `split` option.'
);
}
if (typeof split !== 'undefined' && this.options.replayExecution) {
throw new SilentError(
'EmberExam: You must not use the `replay-execution` option with the `split` option.'
);
}
const partitions = options.partition;
if (typeof partitions !== 'undefined') {
validatePartitions(partitions, split);
}
return !!split;
}
/**
* Determines if we should randomize the tests and validates associated options
* (`random`).
*
* @return {boolean}
*/
validateRandom() {
const shouldRandomize = typeof this.options.random === 'string';
if (shouldRandomize && this.framework === 'mocha') {
// eslint-disable-next-line no-console
console.warn(
'Mocha does not currently support randomizing test order, so tests will run in normal order. Please see https://github.com/mochajs/mocha/issues/902 for more info.'
);
}
return shouldRandomize;
}
/**
* Determines if a test execution json file should be written after running a test suite and validates associated
*
* @return {boolean}
*/
validateWriteExecutionFile() {
if (!this.options.writeExecutionFile) {
return false;
}
if (!this.options.loadBalance) {
throw new SilentError(
'EmberExam: You must run test suite with the `load-balance` option in order to use the `write-execution-file` option.'
);
} else if (this.options.launch === 'false') {
//When `--no-launch` option is passed, a value for launch in testem config is set to be string false.
throw new SilentError(
'EmberExam: You must not use no-launch with write-execution-file option.'
);
}
return true;
}
/**
* Determines if we should run split tests in parallel and validates associated
* options (`parallel`).
*
* @return {boolean}
*/
validateParallel() {
const parallelValue = parseInt(this.options.parallel, 10);
if (isNaN(parallelValue)) {
throw new SilentError(
`EmberExam: You must specify a Numeric value to 'parallel'. Value passed: ${this.options.parallel}`
);
}
this.options.parallel = parallelValue;
if (typeof this.options.replayBrowser !== 'undefined') {
throw new SilentError(
'EmberExam: You must not use the `replay-browser` option with the `parallel` option.'
);
}
if (this.options.replayExecution) {
throw new SilentError(
'EmberExam: You must not use the `replay-execution` option with the `parallel` option.'
);
}
if (!this.options.loadBalance) {
if (!this.options.split) {
throw new SilentError(
'EmberExam: You must specify the `split` option in order to run your tests in parallel.'
);
} else if (this.options.parallel !== 1) {
throw new SilentError(
'EmberExam: When used with `split` or `partition`, `parallel` does not accept a value other than 1.'
);
}
}
if (this.options.parallel < 1) {
throw new SilentError(
'EmberExam: You must specify a value greater than 1 to `parallel`.'
);
}
return true;
}
/**
* Determines if we should run tests in load balance mode.
* options (`load-balance`).
*
* @return {boolean}
*/
validateLoadBalance() {
// It's required to use ember-cli version 3.2.0 or greater to support the `load-balance` feature.
const emberCliVersionRange = semver.validRange(this.emberCliVersion);
if (semver.gtr('3.2.0', emberCliVersionRange)) {
throw new SilentError(
'EmberExam: You must be using ember-cli version ^3.2.0 for this feature to work properly.'
);
}
if (typeof this.options.replayBrowser !== 'undefined') {
throw new SilentError(
'EmberExam: You must not use the `replay-browser` option with the `load-balance` option.'
);
}
if (this.options.replayExecution) {
throw new SilentError(
'EmberExam: You must not use the `replay-execution` option with the `load-balance` option.'
);
}
//When `--no-launch` option is passed, a value for launch in testem config is set to be string false.
if (this.options.launch === 'false') {
throw new SilentError(
'EmberExam: You must not use `no-launch` option with the `load-balance` option.'
);
}
if (!this.options.parallel) {
throw new SilentError(
'EmberExam: You should specify the number of browsers to load-balance against using `parallel` when using `load-balance`.'
);
}
return true;
}
/**
* Determines if we should replay execution for reproduction.
* options (`replay-execution`).
*
* @return {boolean}
*/
validateReplayExecution() {
const replayBrowser = this.options.replayBrowser;
const replayExecution = this.options.replayExecution;
if (!replayExecution) {
return false;
}
if (this.options.launch === 'false') {
throw new SilentError(
'EmberExam: You must not use `no-launch` option with the `replay-execution` option.'
);
}
if (replayBrowser) {
validateReplayBrowser(replayExecution, replayBrowser, this.options);
}
return true;
}
};