vest 4.1.2 → 4.2.1-dev-ee64be

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.
@@ -5,8 +5,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var n4s = require('n4s');
6
6
  var context$1 = require('context');
7
7
 
8
- var assign = Object.assign;
9
-
10
8
  /**
11
9
  * @returns a unique numeric id.
12
10
  */
@@ -14,6 +12,90 @@ var genId = (function (n) { return function () {
14
12
  return "".concat(n++);
15
13
  }; })(0);
16
14
 
15
+ function isStringValue(v) {
16
+ return String(v) === v;
17
+ }
18
+
19
+ function bindNot(fn) {
20
+ return function () {
21
+ var args = [];
22
+ for (var _i = 0; _i < arguments.length; _i++) {
23
+ args[_i] = arguments[_i];
24
+ }
25
+ return !fn.apply(void 0, args);
26
+ };
27
+ }
28
+
29
+ function isUndefined(value) {
30
+ return value === undefined;
31
+ }
32
+
33
+ function shouldUseErrorAsMessage(message, error) {
34
+ // kind of cheating with this safe guard, but it does the job
35
+ return isUndefined(message) && isStringValue(error);
36
+ }
37
+
38
+ function asArray(possibleArg) {
39
+ return [].concat(possibleArg);
40
+ }
41
+
42
+ function lengthEquals(value, arg1) {
43
+ return value.length === Number(arg1);
44
+ }
45
+
46
+ function longerThan(value, arg1) {
47
+ return value.length > Number(arg1);
48
+ }
49
+
50
+ /**
51
+ * Creates a cache function
52
+ */
53
+ function createCache(maxSize) {
54
+ if (maxSize === void 0) { maxSize = 1; }
55
+ var cacheStorage = [];
56
+ var cache = function (deps, cacheAction) {
57
+ var cacheHit = cache.get(deps);
58
+ // cache hit is not null
59
+ if (cacheHit)
60
+ return cacheHit[1];
61
+ var result = cacheAction();
62
+ cacheStorage.unshift([deps.concat(), result]);
63
+ if (longerThan(cacheStorage, maxSize))
64
+ cacheStorage.length = maxSize;
65
+ return result;
66
+ };
67
+ // invalidate an item in the cache by its dependencies
68
+ cache.invalidate = function (deps) {
69
+ var index = findIndex(deps);
70
+ if (index > -1)
71
+ cacheStorage.splice(index, 1);
72
+ };
73
+ // Retrieves an item from the cache.
74
+ cache.get = function (deps) {
75
+ return cacheStorage[findIndex(deps)] || null;
76
+ };
77
+ return cache;
78
+ function findIndex(deps) {
79
+ return cacheStorage.findIndex(function (_a) {
80
+ var cachedDeps = _a[0];
81
+ return lengthEquals(deps, cachedDeps.length) &&
82
+ deps.every(function (dep, i) { return dep === cachedDeps[i]; });
83
+ });
84
+ }
85
+ }
86
+
87
+ // The module is named "isArrayValue" since it
88
+ // is conflicting with a nested npm dependency.
89
+ // We may need to revisit this in the future.
90
+ function isArray(value) {
91
+ return Boolean(Array.isArray(value));
92
+ }
93
+
94
+ function isNull(value) {
95
+ return value === null;
96
+ }
97
+ var isNotNull = bindNot(isNull);
98
+
17
99
  function isFunction(value) {
18
100
  return typeof value === 'function';
19
101
  }
@@ -31,120 +113,58 @@ function defaultTo(callback, defaultValue) {
31
113
  return (_a = optionalFunctionValue(callback)) !== null && _a !== void 0 ? _a : defaultValue;
32
114
  }
33
115
 
34
- /**
35
- * Throws a timed out error.
36
- */
37
- function throwError(devMessage, productionMessage) {
38
- throw new Error(devMessage );
39
- }
40
- function throwErrorDeferred(devMessage, productionMessage) {
41
- setTimeout(function () {
42
- throwError(devMessage);
43
- }, 0);
116
+ function last(values) {
117
+ var valuesArray = asArray(values);
118
+ return valuesArray[valuesArray.length - 1];
44
119
  }
45
120
 
46
- // eslint-disable-next-line max-lines-per-function
47
- function createState(onStateChange) {
48
- var state = {
49
- references: []
50
- };
51
- var registrations = [];
52
- return {
53
- registerStateKey: registerStateKey,
54
- reset: reset
55
- };
56
- /**
57
- * Registers a new key in the state, takes the initial value (may be a function that returns the initial value), returns a function.
58
- *
59
- * @example
60
- *
61
- * const useColor = state.registerStateKey("blue");
62
- *
63
- * let [color, setColor] = useColor(); // -> ["blue", Function]
64
- *
65
- * setColor("green");
66
- *
67
- * useColor()[0]; -> "green"
68
- */
69
- function registerStateKey(initialState, onUpdate) {
70
- var key = registrations.length;
71
- registrations.push([initialState, onUpdate]);
72
- return initKey(key, initialState);
73
- }
74
- function reset() {
75
- var prev = current();
76
- state.references = [];
77
- registrations.forEach(function (_a, index) {
78
- var initialValue = _a[0];
79
- return initKey(index, initialValue, prev[index]);
80
- });
81
- }
82
- function initKey(key, initialState, prevState) {
83
- current().push();
84
- set(key, optionalFunctionValue(initialState, prevState));
85
- return function useStateKey() {
86
- return [
87
- current()[key],
88
- function (nextState) {
89
- return set(key, optionalFunctionValue(nextState, current()[key]));
90
- },
91
- ];
92
- };
93
- }
94
- function current() {
95
- return state.references;
96
- }
97
- function set(index, value) {
98
- var prevValue = state.references[index];
99
- state.references[index] = value;
100
- var _a = registrations[index], onUpdate = _a[1];
101
- if (isFunction(onUpdate)) {
102
- onUpdate(value, prevValue);
121
+ // This is kind of a map/filter in one function.
122
+ // Normally, behaves like a nested-array map,
123
+ // but returning `null` will drop the element from the array
124
+ function transform(array, cb) {
125
+ var res = [];
126
+ for (var _i = 0, array_1 = array; _i < array_1.length; _i++) {
127
+ var v = array_1[_i];
128
+ if (isArray(v)) {
129
+ res.push(transform(v, cb));
103
130
  }
104
- if (isFunction(onStateChange)) {
105
- onStateChange();
131
+ else {
132
+ var output = cb(v);
133
+ if (isNotNull(output)) {
134
+ res.push(output);
135
+ }
106
136
  }
107
137
  }
138
+ return res;
108
139
  }
109
-
110
- var IsolateTypes;
111
- (function (IsolateTypes) {
112
- IsolateTypes[IsolateTypes["DEFAULT"] = 0] = "DEFAULT";
113
- IsolateTypes[IsolateTypes["SUITE"] = 1] = "SUITE";
114
- IsolateTypes[IsolateTypes["EACH"] = 2] = "EACH";
115
- IsolateTypes[IsolateTypes["SKIP_WHEN"] = 3] = "SKIP_WHEN";
116
- IsolateTypes[IsolateTypes["OMIT_WHEN"] = 4] = "OMIT_WHEN";
117
- IsolateTypes[IsolateTypes["GROUP"] = 5] = "GROUP";
118
- })(IsolateTypes || (IsolateTypes = {}));
119
-
120
- function createStateRef(state, _a) {
121
- var suiteId = _a.suiteId, suiteName = _a.suiteName;
122
- return {
123
- optionalFields: state.registerStateKey(function () { return ({}); }),
124
- suiteId: state.registerStateKey(suiteId),
125
- suiteName: state.registerStateKey(suiteName),
126
- testCallbacks: state.registerStateKey(function () { return ({
127
- fieldCallbacks: {},
128
- doneCallbacks: []
129
- }); }),
130
- testObjects: state.registerStateKey(function (prev) {
131
- return {
132
- prev: prev ? prev.current : [],
133
- current: []
134
- };
135
- })
136
- };
140
+ function valueAtPath(array, path) {
141
+ return getCurrent(array, path)[last(path)];
137
142
  }
138
-
139
- function asArray(possibleArg) {
140
- return [].concat(possibleArg);
143
+ function setValueAtPath(array, path, value) {
144
+ var current = getCurrent(array, path);
145
+ current[last(path)] = value;
146
+ return array;
141
147
  }
142
-
143
- function last(values) {
144
- var valuesArray = asArray(values);
145
- return valuesArray[valuesArray.length - 1];
148
+ function flatten(values) {
149
+ return asArray(values).reduce(function (acc, value) {
150
+ if (isArray(value)) {
151
+ return acc.concat(flatten(value));
152
+ }
153
+ return asArray(acc).concat(value);
154
+ }, []);
155
+ }
156
+ function getCurrent(array, path) {
157
+ var current = array;
158
+ for (var _i = 0, _a = path.slice(0, -1); _i < _a.length; _i++) {
159
+ var p = _a[_i];
160
+ current[p] = defaultTo(current[p], []);
161
+ current = current[p];
162
+ }
163
+ return current;
146
164
  }
147
165
 
166
+ var assign = Object.assign;
167
+
148
168
  function createCursor() {
149
169
  var storage = {
150
170
  cursor: []
@@ -179,6 +199,22 @@ function createCursor() {
179
199
  };
180
200
  }
181
201
 
202
+ var IsolateTypes;
203
+ (function (IsolateTypes) {
204
+ IsolateTypes[IsolateTypes["DEFAULT"] = 0] = "DEFAULT";
205
+ IsolateTypes[IsolateTypes["SUITE"] = 1] = "SUITE";
206
+ IsolateTypes[IsolateTypes["EACH"] = 2] = "EACH";
207
+ IsolateTypes[IsolateTypes["SKIP_WHEN"] = 3] = "SKIP_WHEN";
208
+ IsolateTypes[IsolateTypes["OMIT_WHEN"] = 4] = "OMIT_WHEN";
209
+ IsolateTypes[IsolateTypes["GROUP"] = 5] = "GROUP";
210
+ })(IsolateTypes || (IsolateTypes = {}));
211
+
212
+ var Modes;
213
+ (function (Modes) {
214
+ Modes[Modes["ALL"] = 0] = "ALL";
215
+ Modes[Modes["EAGER"] = 1] = "EAGER";
216
+ })(Modes || (Modes = {}));
217
+
182
218
  var context = context$1.createContext(function (ctxRef, parentContext) {
183
219
  return parentContext
184
220
  ? null
@@ -195,140 +231,11 @@ var context = context$1.createContext(function (ctxRef, parentContext) {
195
231
  prev: {}
196
232
  }
197
233
  },
234
+ mode: [Modes.ALL],
198
235
  testCursor: createCursor()
199
236
  }, ctxRef);
200
237
  });
201
238
 
202
- function bindNot(fn) {
203
- return function () {
204
- var args = [];
205
- for (var _i = 0; _i < arguments.length; _i++) {
206
- args[_i] = arguments[_i];
207
- }
208
- return !fn.apply(void 0, args);
209
- };
210
- }
211
-
212
- // The module is named "isArrayValue" since it
213
- // is conflicting with a nested npm dependency.
214
- // We may need to revisit this in the future.
215
- function isArray(value) {
216
- return Boolean(Array.isArray(value));
217
- }
218
-
219
- function isNull(value) {
220
- return value === null;
221
- }
222
- var isNotNull = bindNot(isNull);
223
-
224
- // This is sort of a map/filter in one function.
225
- // Normally, behaves like a nested-array map
226
- // Returning `null` will drop the element from the array
227
- function transform(array, cb) {
228
- var res = [];
229
- for (var _i = 0, array_1 = array; _i < array_1.length; _i++) {
230
- var v = array_1[_i];
231
- if (isArray(v)) {
232
- res.push(transform(v, cb));
233
- }
234
- else {
235
- var output = cb(v);
236
- if (isNotNull(output)) {
237
- res.push(output);
238
- }
239
- }
240
- }
241
- return res;
242
- }
243
- function valueAtPath(array, path) {
244
- return getCurrent(array, path)[last(path)];
245
- }
246
- function setValueAtPath(array, path, value) {
247
- var current = getCurrent(array, path);
248
- current[last(path)] = value;
249
- return array;
250
- }
251
- function flatten(values) {
252
- return asArray(values).reduce(function (acc, value) {
253
- if (isArray(value)) {
254
- return acc.concat(flatten(value));
255
- }
256
- return asArray(acc).concat(value);
257
- }, []);
258
- }
259
- function getCurrent(array, path) {
260
- var current = array;
261
- for (var _i = 0, _a = path.slice(0, -1); _i < _a.length; _i++) {
262
- var p = _a[_i];
263
- current[p] = defaultTo(current[p], []);
264
- current = current[p];
265
- }
266
- return current;
267
- }
268
-
269
- function isUndefined(value) {
270
- return value === undefined;
271
- }
272
-
273
- function isNullish(value) {
274
- return isNull(value) || isUndefined(value);
275
- }
276
-
277
- function isStringValue(v) {
278
- return String(v) === v;
279
- }
280
-
281
- function shouldUseErrorAsMessage(message, error) {
282
- // kind of cheating with this safe guard, but it does the job
283
- return isUndefined(message) && isStringValue(error);
284
- }
285
-
286
- function lengthEquals(value, arg1) {
287
- return value.length === Number(arg1);
288
- }
289
-
290
- function longerThan(value, arg1) {
291
- return value.length > Number(arg1);
292
- }
293
-
294
- /**
295
- * Creates a cache function
296
- */
297
- function createCache(maxSize) {
298
- if (maxSize === void 0) { maxSize = 1; }
299
- var cacheStorage = [];
300
- var cache = function (deps, cacheAction) {
301
- var cacheHit = cache.get(deps);
302
- // cache hit is not null
303
- if (cacheHit)
304
- return cacheHit[1];
305
- var result = cacheAction();
306
- cacheStorage.unshift([deps.concat(), result]);
307
- if (longerThan(cacheStorage, maxSize))
308
- cacheStorage.length = maxSize;
309
- return result;
310
- };
311
- // invalidate an item in the cache by its dependencies
312
- cache.invalidate = function (deps) {
313
- var index = cacheStorage.findIndex(function (_a) {
314
- var cachedDeps = _a[0];
315
- return lengthEquals(deps, cachedDeps.length) &&
316
- deps.every(function (dep, i) { return dep === cachedDeps[i]; });
317
- });
318
- if (index > -1)
319
- cacheStorage.splice(index, 1);
320
- };
321
- // Retrieves an item from the cache.
322
- cache.get = function (deps) {
323
- return cacheStorage[cacheStorage.findIndex(function (_a) {
324
- var cachedDeps = _a[0];
325
- return lengthEquals(deps, cachedDeps.length) &&
326
- deps.every(function (dep, i) { return dep === cachedDeps[i]; });
327
- })] || null;
328
- };
329
- return cache;
330
- }
331
-
332
239
  // STATE REF
333
240
  function useStateRef() {
334
241
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -533,16 +440,115 @@ var VestTest = /** @class */ (function () {
533
440
  VestTest.prototype.statusEquals = function (status) {
534
441
  return this.status === status;
535
442
  };
536
- return VestTest;
537
- }());
538
- var STATUS_UNTESTED = 'UNTESTED';
539
- var STATUS_SKIPPED = 'SKIPPED';
540
- var STATUS_FAILED = 'FAILED';
541
- var STATUS_WARNING = 'WARNING';
542
- var STATUS_PASSING = 'PASSING';
543
- var STATUS_PENDING = 'PENDING';
544
- var STATUS_CANCELED = 'CANCELED';
545
- var STATUS_OMITTED = 'OMITTED';
443
+ return VestTest;
444
+ }());
445
+ var STATUS_UNTESTED = 'UNTESTED';
446
+ var STATUS_SKIPPED = 'SKIPPED';
447
+ var STATUS_FAILED = 'FAILED';
448
+ var STATUS_WARNING = 'WARNING';
449
+ var STATUS_PASSING = 'PASSING';
450
+ var STATUS_PENDING = 'PENDING';
451
+ var STATUS_CANCELED = 'CANCELED';
452
+ var STATUS_OMITTED = 'OMITTED';
453
+
454
+ /**
455
+ * Throws a timed out error.
456
+ */
457
+ function throwError(devMessage, productionMessage) {
458
+ throw new Error(devMessage );
459
+ }
460
+ function throwErrorDeferred(devMessage, productionMessage) {
461
+ setTimeout(function () {
462
+ throwError(devMessage);
463
+ }, 0);
464
+ }
465
+
466
+ // eslint-disable-next-line max-lines-per-function
467
+ function createState(onStateChange) {
468
+ var state = {
469
+ references: []
470
+ };
471
+ var registrations = [];
472
+ return {
473
+ registerStateKey: registerStateKey,
474
+ reset: reset
475
+ };
476
+ /**
477
+ * Registers a new key in the state, takes the initial value (may be a function that returns the initial value), returns a function.
478
+ *
479
+ * @example
480
+ *
481
+ * const useColor = state.registerStateKey("blue");
482
+ *
483
+ * let [color, setColor] = useColor(); // -> ["blue", Function]
484
+ *
485
+ * setColor("green");
486
+ *
487
+ * useColor()[0]; -> "green"
488
+ */
489
+ function registerStateKey(initialState, onUpdate) {
490
+ var key = registrations.length;
491
+ registrations.push([initialState, onUpdate]);
492
+ return initKey(key, initialState);
493
+ }
494
+ function reset() {
495
+ var prev = current();
496
+ state.references = [];
497
+ registrations.forEach(function (_a, index) {
498
+ var initialValue = _a[0];
499
+ return initKey(index, initialValue, prev[index]);
500
+ });
501
+ }
502
+ function initKey(key, initialState, prevState) {
503
+ current().push();
504
+ set(key, optionalFunctionValue(initialState, prevState));
505
+ return function useStateKey() {
506
+ return [
507
+ current()[key],
508
+ function (nextState) {
509
+ return set(key, optionalFunctionValue(nextState, current()[key]));
510
+ },
511
+ ];
512
+ };
513
+ }
514
+ function current() {
515
+ return state.references;
516
+ }
517
+ function set(index, value) {
518
+ var prevValue = state.references[index];
519
+ state.references[index] = value;
520
+ var _a = registrations[index], onUpdate = _a[1];
521
+ if (isFunction(onUpdate)) {
522
+ onUpdate(value, prevValue);
523
+ }
524
+ if (isFunction(onStateChange)) {
525
+ onStateChange();
526
+ }
527
+ }
528
+ }
529
+
530
+ function createStateRef(state, _a) {
531
+ var suiteId = _a.suiteId, suiteName = _a.suiteName;
532
+ return {
533
+ optionalFields: state.registerStateKey(function () { return ({}); }),
534
+ suiteId: state.registerStateKey(suiteId),
535
+ suiteName: state.registerStateKey(suiteName),
536
+ testCallbacks: state.registerStateKey(function () { return ({
537
+ fieldCallbacks: {},
538
+ doneCallbacks: []
539
+ }); }),
540
+ testObjects: state.registerStateKey(function (prev) {
541
+ return {
542
+ prev: prev ? prev.current : [],
543
+ current: []
544
+ };
545
+ })
546
+ };
547
+ }
548
+
549
+ function isNullish(value) {
550
+ return isNull(value) || isUndefined(value);
551
+ }
546
552
 
547
553
  function usePath() {
548
554
  var context$1 = context.useX();
@@ -617,69 +623,21 @@ function shouldAllowReorder() {
617
623
  return context.useX().isolate.type === IsolateTypes.EACH;
618
624
  }
619
625
 
620
- /**
621
- * A safe hasOwnProperty access
622
- */
623
- function hasOwnProperty(obj, key) {
624
- return Object.prototype.hasOwnProperty.call(obj, key);
625
- }
626
-
627
- function isNumber(value) {
628
- return Boolean(typeof value === 'number');
629
- }
630
-
631
- function isEmpty(value) {
632
- if (!value) {
633
- return true;
634
- }
635
- else if (isNumber(value)) {
636
- return value === 0;
637
- }
638
- else if (hasOwnProperty(value, 'length')) {
639
- return lengthEquals(value, 0);
640
- }
641
- else if (typeof value === 'object') {
642
- return lengthEquals(Object.keys(value), 0);
643
- }
644
- return true;
645
- }
646
- var isNotEmpty = bindNot(isEmpty);
647
-
648
- function nonMatchingFieldName(testObject, fieldName) {
649
- return !!fieldName && !matchingFieldName(testObject, fieldName);
650
- }
651
- function matchingFieldName(testObject, fieldName) {
652
- return !!(fieldName && testObject.fieldName === fieldName);
653
- }
654
-
655
- /**
656
- * Checks if a given field, or the suite as a whole still have remaining tests.
657
- */
658
- function hasRemainingTests(fieldName) {
659
- var allIncomplete = useAllIncomplete();
660
- if (isEmpty(allIncomplete)) {
661
- return false;
662
- }
663
- if (fieldName) {
664
- return allIncomplete.some(function (testObject) {
665
- return matchingFieldName(testObject, fieldName);
666
- });
667
- }
668
- return isNotEmpty(allIncomplete);
669
- }
626
+ var Severity;
627
+ (function (Severity) {
628
+ Severity["WARNINGS"] = "warnings";
629
+ Severity["ERRORS"] = "errors";
630
+ })(Severity || (Severity = {}));
670
631
 
671
632
  /**
672
633
  * Reads the testObjects list and gets full validation result from it.
673
634
  */
674
635
  function genTestsSummary() {
675
636
  var testObjects = useTestsFlat();
676
- var summary = {
677
- errorCount: 0,
637
+ var summary = assign(baseStats(), {
678
638
  groups: {},
679
- testCount: 0,
680
- tests: {},
681
- warnCount: 0
682
- };
639
+ tests: {}
640
+ });
683
641
  appendSummary(testObjects);
684
642
  return countFailures(summary);
685
643
  function appendSummary(testObjects) {
@@ -707,30 +665,34 @@ function countFailures(summary) {
707
665
  // eslint-disable-next-line max-statements
708
666
  function genTestObject(summaryKey, testObject) {
709
667
  var fieldName = testObject.fieldName, message = testObject.message;
710
- summaryKey[fieldName] = summaryKey[fieldName] || {
711
- errorCount: 0,
712
- warnCount: 0,
713
- testCount: 0
714
- };
668
+ summaryKey[fieldName] = summaryKey[fieldName] || baseStats();
715
669
  var testKey = summaryKey[fieldName];
716
670
  if (testObject.isNonActionable())
717
671
  return testKey;
718
672
  summaryKey[fieldName].testCount++;
719
673
  // Adds to severity group
720
- function addTo(countKey, group) {
674
+ function addTo(severity) {
675
+ var countKey = severity === Severity.ERRORS ? 'errorCount' : 'warnCount';
721
676
  testKey[countKey]++;
722
677
  if (message) {
723
- testKey[group] = (testKey[group] || []).concat(message);
678
+ testKey[severity] = (testKey[severity] || []).concat(message);
724
679
  }
725
680
  }
726
681
  if (testObject.isFailing()) {
727
- addTo('errorCount', 'errors');
682
+ addTo(Severity.ERRORS);
728
683
  }
729
684
  else if (testObject.isWarning()) {
730
- addTo('warnCount', 'warnings');
685
+ addTo(Severity.WARNINGS);
731
686
  }
732
687
  return testKey;
733
688
  }
689
+ function baseStats() {
690
+ return {
691
+ errorCount: 0,
692
+ warnCount: 0,
693
+ testCount: 0
694
+ };
695
+ }
734
696
 
735
697
  /*! *****************************************************************************
736
698
  Copyright (c) Microsoft Corporation.
@@ -768,6 +730,13 @@ function __spreadArray(to, from, pack) {
768
730
  return to.concat(ar || Array.prototype.slice.call(from));
769
731
  }
770
732
 
733
+ function nonMatchingFieldName(testObject, fieldName) {
734
+ return !!fieldName && !matchingFieldName(testObject, fieldName);
735
+ }
736
+ function matchingFieldName(testObject, fieldName) {
737
+ return !!(fieldName && testObject.fieldName === fieldName);
738
+ }
739
+
771
740
  function either(a, b) {
772
741
  return !!a !== !!b;
773
742
  }
@@ -776,7 +745,7 @@ function either(a, b) {
776
745
  * Checks that a given test object matches the currently specified severity level
777
746
  */
778
747
  function nonMatchingSeverityProfile(severity, testObject) {
779
- return either(severity === 'warnings', testObject.warns());
748
+ return either(severity === Severity.WARNINGS, testObject.warns());
780
749
  }
781
750
 
782
751
  function collectFailureMessages(severity, testObjects, options) {
@@ -818,10 +787,10 @@ function getFailuresArrayOrObject(group, fieldName) {
818
787
  }
819
788
 
820
789
  function getErrors(fieldName) {
821
- return getFailures('errors', fieldName);
790
+ return getFailures(Severity.ERRORS, fieldName);
822
791
  }
823
792
  function getWarnings(fieldName) {
824
- return getFailures('warnings', fieldName);
793
+ return getFailures(Severity.WARNINGS, fieldName);
825
794
  }
826
795
  /**
827
796
  * @returns suite or field's errors or warnings.
@@ -835,11 +804,11 @@ function getFailures(severityKey, fieldName) {
835
804
  }
836
805
 
837
806
  function getErrorsByGroup(groupName, fieldName) {
838
- var errors = getByGroup('errors', groupName, fieldName);
807
+ var errors = getByGroup(Severity.ERRORS, groupName, fieldName);
839
808
  return getFailuresArrayOrObject(errors, fieldName);
840
809
  }
841
810
  function getWarningsByGroup(groupName, fieldName) {
842
- var warnings = getByGroup('warnings', groupName, fieldName);
811
+ var warnings = getByGroup(Severity.WARNINGS, groupName, fieldName);
843
812
  return getFailuresArrayOrObject(warnings, fieldName);
844
813
  }
845
814
  /**
@@ -873,10 +842,10 @@ function hasFailuresLogic(testObject, severityKey, fieldName) {
873
842
  }
874
843
 
875
844
  function hasErrors(fieldName) {
876
- return has('errors', fieldName);
845
+ return has(Severity.ERRORS, fieldName);
877
846
  }
878
847
  function hasWarnings(fieldName) {
879
- return has('warnings', fieldName);
848
+ return has(Severity.WARNINGS, fieldName);
880
849
  }
881
850
  function has(severityKey, fieldName) {
882
851
  var testObjects = useTestsFlat();
@@ -886,10 +855,10 @@ function has(severityKey, fieldName) {
886
855
  }
887
856
 
888
857
  function hasErrorsByGroup(groupName, fieldName) {
889
- return hasByGroup('errors', groupName, fieldName);
858
+ return hasByGroup(Severity.ERRORS, groupName, fieldName);
890
859
  }
891
860
  function hasWarningsByGroup(groupName, fieldName) {
892
- return hasByGroup('warnings', groupName, fieldName);
861
+ return hasByGroup(Severity.WARNINGS, groupName, fieldName);
893
862
  }
894
863
  /**
895
864
  * Checks whether there are failures in a given group.
@@ -903,6 +872,34 @@ function hasByGroup(severityKey, group, fieldName) {
903
872
  });
904
873
  }
905
874
 
875
+ /**
876
+ * A safe hasOwnProperty access
877
+ */
878
+ function hasOwnProperty(obj, key) {
879
+ return Object.prototype.hasOwnProperty.call(obj, key);
880
+ }
881
+
882
+ function isNumber(value) {
883
+ return Boolean(typeof value === 'number');
884
+ }
885
+
886
+ function isEmpty(value) {
887
+ if (!value) {
888
+ return true;
889
+ }
890
+ else if (isNumber(value)) {
891
+ return value === 0;
892
+ }
893
+ else if (hasOwnProperty(value, 'length')) {
894
+ return lengthEquals(value, 0);
895
+ }
896
+ else if (typeof value === 'object') {
897
+ return lengthEquals(Object.keys(value), 0);
898
+ }
899
+ return true;
900
+ }
901
+ var isNotEmpty = bindNot(isEmpty);
902
+
906
903
  // eslint-disable-next-line max-statements, complexity
907
904
  function isValid(result, fieldName) {
908
905
  if (fieldIsOmitted(fieldName)) {
@@ -956,7 +953,7 @@ function noMissingTests(fieldName) {
956
953
  }
957
954
 
958
955
  var cache$1 = createCache(20);
959
- function produceDraft() {
956
+ function produceBase() {
960
957
  var testObjects = useTestsFlat();
961
958
  var ctxRef = { stateRef: useStateRef() };
962
959
  return cache$1([testObjects], context.bind(ctxRef, function () {
@@ -971,19 +968,35 @@ function produceDraft() {
971
968
  hasWarnings: context.bind(ctxRef, hasWarnings),
972
969
  hasWarningsByGroup: context.bind(ctxRef, hasWarningsByGroup),
973
970
  isValid: context.bind(ctxRef, function (fieldName) {
974
- return isValid(produceDraft(), fieldName);
971
+ return isValid(produceBase(), fieldName);
975
972
  }),
976
973
  suiteName: suiteName
977
974
  });
978
975
  }));
979
976
  }
980
977
 
978
+ /**
979
+ * Checks if a given field, or the suite as a whole still have remaining tests.
980
+ */
981
+ function hasRemainingTests(fieldName) {
982
+ var allIncomplete = useAllIncomplete();
983
+ if (isEmpty(allIncomplete)) {
984
+ return false;
985
+ }
986
+ if (fieldName) {
987
+ return allIncomplete.some(function (testObject) {
988
+ return matchingFieldName(testObject, fieldName);
989
+ });
990
+ }
991
+ return isNotEmpty(allIncomplete);
992
+ }
993
+
981
994
  var cache = createCache(20);
982
995
  function produceFullResult() {
983
996
  var testObjects = useTestsFlat();
984
997
  var ctxRef = { stateRef: useStateRef() };
985
998
  return cache([testObjects], context.bind(ctxRef, function () {
986
- return assign({}, produceDraft(), {
999
+ return assign({}, produceBase(), {
987
1000
  done: context.bind(ctxRef, done)
988
1001
  });
989
1002
  }));
@@ -1016,7 +1029,7 @@ var done = function done() {
1016
1029
  if (shouldSkipDoneRegistration(callback, fieldName, output)) {
1017
1030
  return output;
1018
1031
  }
1019
- var doneCallback = function () { return callback(produceDraft()); };
1032
+ var doneCallback = function () { return callback(produceBase()); };
1020
1033
  if (shouldRunDoneCallback(fieldName)) {
1021
1034
  doneCallback();
1022
1035
  return output;
@@ -1042,18 +1055,12 @@ function createBus() {
1042
1055
  var listeners = {};
1043
1056
  return {
1044
1057
  emit: function (event, data) {
1045
- if (!listeners[event]) {
1046
- return;
1047
- }
1048
- listeners[event].forEach(function (listener) {
1049
- listener(data);
1058
+ (listeners[event] || []).forEach(function (handler) {
1059
+ handler(data);
1050
1060
  });
1051
1061
  },
1052
1062
  on: function (event, handler) {
1053
- if (!listeners[event]) {
1054
- listeners[event] = [];
1055
- }
1056
- listeners[event].push(handler);
1063
+ listeners[event] = (listeners[event] || []).concat(handler);
1057
1064
  return {
1058
1065
  off: function () {
1059
1066
  listeners[event] = listeners[event].filter(function (h) { return h !== handler; });
@@ -1121,9 +1128,7 @@ function runFieldCallbacks(fieldName) {
1121
1128
  */
1122
1129
  function runDoneCallbacks() {
1123
1130
  var doneCallbacks = useTestCallbacks()[0].doneCallbacks;
1124
- if (!hasRemainingTests()) {
1125
- callEach(doneCallbacks);
1126
- }
1131
+ callEach(doneCallbacks);
1127
1132
  }
1128
1133
 
1129
1134
  // eslint-disable-next-line max-lines-per-function
@@ -1137,14 +1142,21 @@ function initBus() {
1137
1142
  }
1138
1143
  testObject.done();
1139
1144
  runFieldCallbacks(testObject.fieldName);
1140
- runDoneCallbacks();
1145
+ if (!hasRemainingTests()) {
1146
+ // When no more tests are running, emit the done event
1147
+ bus.emit(Events.ALL_RUNNING_TESTS_FINISHED);
1148
+ }
1141
1149
  });
1142
1150
  // Report that the suite completed its synchronous test run.
1143
1151
  // Async operations may still be running.
1144
- bus.on(Events.SUITE_COMPLETED, function () {
1152
+ bus.on(Events.SUITE_CALLBACK_DONE_RUNNING, function () {
1145
1153
  // Remove tests that are optional and need to be omitted
1146
1154
  omitOptionalTests();
1147
1155
  });
1156
+ // Called when all the tests, including async, are done running
1157
+ bus.on(Events.ALL_RUNNING_TESTS_FINISHED, function () {
1158
+ runDoneCallbacks();
1159
+ });
1148
1160
  // Removes a certain field from the state.
1149
1161
  bus.on(Events.REMOVE_FIELD, function (fieldName) {
1150
1162
  useEachTestObject(function (testObject) {
@@ -1174,9 +1186,10 @@ function useBus() {
1174
1186
  var Events;
1175
1187
  (function (Events) {
1176
1188
  Events["TEST_COMPLETED"] = "test_completed";
1189
+ Events["ALL_RUNNING_TESTS_FINISHED"] = "all_running_tests_finished";
1177
1190
  Events["REMOVE_FIELD"] = "remove_field";
1178
1191
  Events["RESET_FIELD"] = "reset_field";
1179
- Events["SUITE_COMPLETED"] = "suite_completed";
1192
+ Events["SUITE_CALLBACK_DONE_RUNNING"] = "suite_callback_done_running";
1180
1193
  })(Events || (Events = {}));
1181
1194
 
1182
1195
  // eslint-disable-next-line max-lines-per-function
@@ -1213,11 +1226,11 @@ function create() {
1213
1226
  });
1214
1227
  // Report the suite is done registering tests
1215
1228
  // Async tests may still be running
1216
- bus.emit(Events.SUITE_COMPLETED);
1229
+ bus.emit(Events.SUITE_CALLBACK_DONE_RUNNING);
1217
1230
  // Return the result
1218
1231
  return produceFullResult();
1219
1232
  }), {
1220
- get: context.bind(ctxRef, produceDraft),
1233
+ get: context.bind(ctxRef, produceBase),
1221
1234
  remove: context.bind(ctxRef, function (fieldName) {
1222
1235
  bus.emit(Events.REMOVE_FIELD, fieldName);
1223
1236
  }),
@@ -1244,7 +1257,7 @@ function create() {
1244
1257
  */
1245
1258
  function each(list, callback) {
1246
1259
  if (!isFunction(callback)) {
1247
- throwError('callback must be a function');
1260
+ throwError('each callback must be a function');
1248
1261
  }
1249
1262
  isolate({ type: IsolateTypes.EACH }, function () {
1250
1263
  list.forEach(function (arg, index) {
@@ -1258,6 +1271,31 @@ function each(list, callback) {
1258
1271
  */
1259
1272
  var ERROR_HOOK_CALLED_OUTSIDE = 'hook called outside of a running suite.';
1260
1273
 
1274
+ /**
1275
+ * Conditionally skips running tests within the callback.
1276
+ *
1277
+ * @example
1278
+ *
1279
+ * skipWhen(res => res.hasErrors('username'), () => {
1280
+ * test('username', 'User already taken', async () => await doesUserExist(username)
1281
+ * });
1282
+ */
1283
+ function skipWhen(conditional, callback) {
1284
+ isolate({ type: IsolateTypes.SKIP_WHEN }, function () {
1285
+ context.run({
1286
+ skipped:
1287
+ // Checking for nested conditional. If we're in a nested skipWhen,
1288
+ // we should skip the test if the parent conditional is true.
1289
+ isExcludedIndividually() ||
1290
+ // Otherwise, we should skip the test if the conditional is true.
1291
+ optionalFunctionValue(conditional, optionalFunctionValue(produceBase))
1292
+ }, function () { return callback(); });
1293
+ });
1294
+ }
1295
+ function isExcludedIndividually() {
1296
+ return !!context.useX().skipped;
1297
+ }
1298
+
1261
1299
  /**
1262
1300
  * Adds a field or a list of fields into the inclusion list
1263
1301
  *
@@ -1284,9 +1322,6 @@ function skip(item) {
1284
1322
  skip.group = function (item) {
1285
1323
  return addTo(1 /* SKIP */, 'groups', item);
1286
1324
  };
1287
- function isExcludedIndividually() {
1288
- return !!context.useX().skipped;
1289
- }
1290
1325
  //Checks whether a certain test profile excluded by any of the exclusion groups.
1291
1326
  // eslint-disable-next-line complexity, max-statements, max-lines-per-function
1292
1327
  function isExcluded(testObject) {
@@ -1453,11 +1488,50 @@ function include(fieldName) {
1453
1488
  if (isStringValue(condition)) {
1454
1489
  return Boolean(exclusion.tests[condition]);
1455
1490
  }
1456
- return optionalFunctionValue(condition, optionalFunctionValue(produceDraft));
1491
+ return optionalFunctionValue(condition, optionalFunctionValue(produceBase));
1457
1492
  };
1458
1493
  }
1459
1494
  }
1460
1495
 
1496
+ /**
1497
+ * Sets the suite to "eager" (fail fast) mode.
1498
+ * Eager mode will skip running subsequent tests of a failing fields.
1499
+ *
1500
+ * @example
1501
+ * // in the following example, the second test of username will not run
1502
+ * // if the first test of username failed.
1503
+ * const suite = create((data) => {
1504
+ * eager();
1505
+ *
1506
+ * test('username', 'username is required', () => {
1507
+ * enforce(data.username).isNotBlank();
1508
+ * });
1509
+ *
1510
+ * test('username', 'username is too short', () => {
1511
+ * enforce(data.username).longerThan(2);
1512
+ * });
1513
+ * });
1514
+ */
1515
+ function eager() {
1516
+ setMode(Modes.EAGER);
1517
+ }
1518
+ function shouldSkipBasedOnMode(testObject) {
1519
+ if (isEager() && hasErrors(testObject.fieldName))
1520
+ return true;
1521
+ return false;
1522
+ }
1523
+ function isEager() {
1524
+ return isMode(Modes.EAGER);
1525
+ }
1526
+ function isMode(mode) {
1527
+ var currentMode = context.useX().mode;
1528
+ return currentMode[0] === mode;
1529
+ }
1530
+ function setMode(nextMode) {
1531
+ var mode = context.useX().mode;
1532
+ mode[0] = nextMode;
1533
+ }
1534
+
1461
1535
  /**
1462
1536
  * Conditionally omits tests from the suite.
1463
1537
  *
@@ -1470,7 +1544,8 @@ function include(fieldName) {
1470
1544
  function omitWhen(conditional, callback) {
1471
1545
  isolate({ type: IsolateTypes.OMIT_WHEN }, function () {
1472
1546
  context.run({
1473
- omitted: optionalFunctionValue(conditional, optionalFunctionValue(produceDraft))
1547
+ omitted: isOmitted() ||
1548
+ optionalFunctionValue(conditional, optionalFunctionValue(produceBase))
1474
1549
  }, function () { return callback(); });
1475
1550
  });
1476
1551
  }
@@ -1508,28 +1583,6 @@ function optional(optionals) {
1508
1583
  });
1509
1584
  }
1510
1585
 
1511
- /**
1512
- * Conditionally skips running tests within the callback.
1513
- *
1514
- * @example
1515
- *
1516
- * skipWhen(res => res.hasErrors('username'), () => {
1517
- * test('username', 'User already taken', async () => await doesUserExist(username)
1518
- * });
1519
- */
1520
- function skipWhen(conditional, callback) {
1521
- isolate({ type: IsolateTypes.SKIP_WHEN }, function () {
1522
- context.run({
1523
- skipped:
1524
- // Checking for nested conditional. If we're in a nested skipWhen,
1525
- // we should skip the test if the parent conditional is true.
1526
- isExcludedIndividually() ||
1527
- // Otherwise, we should skip the test if the conditional is true.
1528
- optionalFunctionValue(conditional, optionalFunctionValue(produceDraft))
1529
- }, function () { return callback(); });
1530
- });
1531
- }
1532
-
1533
1586
  var isNotString = bindNot(isStringValue);
1534
1587
 
1535
1588
  function isPromise(value) {
@@ -1625,7 +1678,7 @@ function registerTest(testObject) {
1625
1678
  }
1626
1679
  }
1627
1680
  catch (e) {
1628
- throwError("Unexpected error encountered during test registration.\n Test Object: ".concat(testObject, ".\n Error: ").concat(e, "."));
1681
+ throwError("Unexpected error encountered during test registration.\n Test Object: ".concat(JSON.stringify(testObject), ".\n Error: ").concat(e, "."));
1629
1682
  }
1630
1683
  }
1631
1684
 
@@ -1711,6 +1764,11 @@ function handleKeyTest(key, newTestObject) {
1711
1764
  // eslint-disable-next-line max-statements
1712
1765
  function registerPrevRunTest(testObject) {
1713
1766
  var prevRunTest = useTestAtCursor(testObject);
1767
+ if (shouldSkipBasedOnMode(testObject)) {
1768
+ moveForward();
1769
+ testObject.skip();
1770
+ return testObject;
1771
+ }
1714
1772
  if (isOmitted()) {
1715
1773
  prevRunTest.omit();
1716
1774
  moveForward();
@@ -1819,7 +1877,7 @@ function warn() {
1819
1877
  ctx.currentTest.warn();
1820
1878
  }
1821
1879
 
1822
- var VERSION = "4.1.1";
1880
+ var VERSION = "4.2.1-dev-ee64be";
1823
1881
 
1824
1882
  Object.defineProperty(exports, 'enforce', {
1825
1883
  enumerable: true,
@@ -1831,6 +1889,7 @@ exports.VERSION = VERSION;
1831
1889
  exports.context = context;
1832
1890
  exports.create = create;
1833
1891
  exports.each = each;
1892
+ exports.eager = eager;
1834
1893
  exports.group = group;
1835
1894
  exports.include = include;
1836
1895
  exports.omitWhen = omitWhen;