vest 4.1.3 → 4.2.1

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,199 +12,76 @@ var genId = (function (n) { return function () {
14
12
  return "".concat(n++);
15
13
  }; })(0);
16
14
 
17
- function isFunction(value) {
18
- return typeof value === 'function';
19
- }
20
-
21
- function optionalFunctionValue(value) {
22
- var args = [];
23
- for (var _i = 1; _i < arguments.length; _i++) {
24
- args[_i - 1] = arguments[_i];
25
- }
26
- return isFunction(value) ? value.apply(void 0, args) : value;
27
- }
28
-
29
- function defaultTo(callback, defaultValue) {
30
- var _a;
31
- return (_a = optionalFunctionValue(callback)) !== null && _a !== void 0 ? _a : defaultValue;
32
- }
33
-
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);
15
+ function isStringValue(v) {
16
+ return String(v) === v;
44
17
  }
45
18
 
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);
103
- }
104
- if (isFunction(onStateChange)) {
105
- onStateChange();
19
+ function bindNot(fn) {
20
+ return function () {
21
+ var args = [];
22
+ for (var _i = 0; _i < arguments.length; _i++) {
23
+ args[_i] = arguments[_i];
106
24
  }
107
- }
25
+ return !fn.apply(void 0, args);
26
+ };
108
27
  }
109
28
 
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 = {}));
29
+ function isUndefined(value) {
30
+ return value === undefined;
31
+ }
119
32
 
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
- };
33
+ function shouldUseErrorAsMessage(message, error) {
34
+ // kind of cheating with this safe guard, but it does the job
35
+ return isUndefined(message) && isStringValue(error);
137
36
  }
138
37
 
139
38
  function asArray(possibleArg) {
140
39
  return [].concat(possibleArg);
141
40
  }
142
41
 
143
- function last(values) {
144
- var valuesArray = asArray(values);
145
- return valuesArray[valuesArray.length - 1];
42
+ function lengthEquals(value, arg1) {
43
+ return value.length === Number(arg1);
146
44
  }
147
45
 
148
- function createCursor() {
149
- var storage = {
150
- cursor: []
151
- };
152
- function addLevel() {
153
- storage.cursor.push(0);
154
- }
155
- function removeLevel() {
156
- storage.cursor.pop();
157
- }
158
- function cursorAt() {
159
- return last(storage.cursor);
160
- }
161
- function getCursor() {
162
- return asArray(storage.cursor);
163
- }
164
- function next() {
165
- storage.cursor[storage.cursor.length - 1]++;
166
- return last(storage.cursor);
167
- }
168
- function reset() {
169
- storage.cursor = [0];
170
- }
171
- reset();
172
- return {
173
- addLevel: addLevel,
174
- cursorAt: cursorAt,
175
- getCursor: getCursor,
176
- next: next,
177
- removeLevel: removeLevel,
178
- reset: reset
179
- };
46
+ function longerThan(value, arg1) {
47
+ return value.length > Number(arg1);
180
48
  }
181
49
 
182
- var context = context$1.createContext(function (ctxRef, parentContext) {
183
- return parentContext
184
- ? null
185
- : assign({}, {
186
- exclusion: {
187
- tests: {},
188
- groups: {}
189
- },
190
- inclusion: {},
191
- isolate: {
192
- type: IsolateTypes.DEFAULT,
193
- keys: {
194
- current: {},
195
- prev: {}
196
- }
197
- },
198
- testCursor: createCursor()
199
- }, ctxRef);
200
- });
201
-
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);
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;
209
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
+ }
210
85
  }
211
86
 
212
87
  // The module is named "isArrayValue" since it
@@ -221,9 +96,31 @@ function isNull(value) {
221
96
  }
222
97
  var isNotNull = bindNot(isNull);
223
98
 
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
99
+ function isFunction(value) {
100
+ return typeof value === 'function';
101
+ }
102
+
103
+ function optionalFunctionValue(value) {
104
+ var args = [];
105
+ for (var _i = 1; _i < arguments.length; _i++) {
106
+ args[_i - 1] = arguments[_i];
107
+ }
108
+ return isFunction(value) ? value.apply(void 0, args) : value;
109
+ }
110
+
111
+ function defaultTo(callback, defaultValue) {
112
+ var _a;
113
+ return (_a = optionalFunctionValue(callback)) !== null && _a !== void 0 ? _a : defaultValue;
114
+ }
115
+
116
+ function last(values) {
117
+ var valuesArray = asArray(values);
118
+ return valuesArray[valuesArray.length - 1];
119
+ }
120
+
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
227
124
  function transform(array, cb) {
228
125
  var res = [];
229
126
  for (var _i = 0, array_1 = array; _i < array_1.length; _i++) {
@@ -266,68 +163,78 @@ function getCurrent(array, path) {
266
163
  return current;
267
164
  }
268
165
 
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
- }
166
+ var assign = Object.assign;
280
167
 
281
- function shouldUseErrorAsMessage(message, error) {
282
- // kind of cheating with this safe guard, but it does the job
283
- return isUndefined(message) && isStringValue(error);
168
+ function createCursor() {
169
+ var storage = {
170
+ cursor: []
171
+ };
172
+ function addLevel() {
173
+ storage.cursor.push(0);
174
+ }
175
+ function removeLevel() {
176
+ storage.cursor.pop();
177
+ }
178
+ function cursorAt() {
179
+ return last(storage.cursor);
180
+ }
181
+ function getCursor() {
182
+ return asArray(storage.cursor);
183
+ }
184
+ function next() {
185
+ storage.cursor[storage.cursor.length - 1]++;
186
+ return last(storage.cursor);
187
+ }
188
+ function reset() {
189
+ storage.cursor = [0];
190
+ }
191
+ reset();
192
+ return {
193
+ addLevel: addLevel,
194
+ cursorAt: cursorAt,
195
+ getCursor: getCursor,
196
+ next: next,
197
+ removeLevel: removeLevel,
198
+ reset: reset
199
+ };
284
200
  }
285
201
 
286
- function lengthEquals(value, arg1) {
287
- return value.length === Number(arg1);
288
- }
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 = {}));
289
211
 
290
- function longerThan(value, arg1) {
291
- return value.length > Number(arg1);
292
- }
212
+ var Modes;
213
+ (function (Modes) {
214
+ Modes[Modes["ALL"] = 0] = "ALL";
215
+ Modes[Modes["EAGER"] = 1] = "EAGER";
216
+ })(Modes || (Modes = {}));
293
217
 
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
- }
218
+ var context = context$1.createContext(function (ctxRef, parentContext) {
219
+ return parentContext
220
+ ? null
221
+ : assign({}, {
222
+ exclusion: {
223
+ tests: {},
224
+ groups: {}
225
+ },
226
+ inclusion: {},
227
+ isolate: {
228
+ type: IsolateTypes.DEFAULT,
229
+ keys: {
230
+ current: {},
231
+ prev: {}
232
+ }
233
+ },
234
+ mode: [Modes.ALL],
235
+ testCursor: createCursor()
236
+ }, ctxRef);
237
+ });
331
238
 
332
239
  // STATE REF
333
240
  function useStateRef() {
@@ -544,6 +451,105 @@ var STATUS_PENDING = 'PENDING';
544
451
  var STATUS_CANCELED = 'CANCELED';
545
452
  var STATUS_OMITTED = 'OMITTED';
546
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
+ }
552
+
547
553
  function usePath() {
548
554
  var context$1 = context.useX();
549
555
  return context$1.testCursor.getCursor();
@@ -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 produceSuiteResult() {
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(produceSuiteResult(), 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({}, produceSuiteResult(), {
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(produceSuiteResult()); };
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, produceSuiteResult),
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) {
@@ -1275,7 +1288,7 @@ function skipWhen(conditional, callback) {
1275
1288
  // we should skip the test if the parent conditional is true.
1276
1289
  isExcludedIndividually() ||
1277
1290
  // Otherwise, we should skip the test if the conditional is true.
1278
- optionalFunctionValue(conditional, optionalFunctionValue(produceDraft))
1291
+ optionalFunctionValue(conditional, optionalFunctionValue(produceSuiteResult))
1279
1292
  }, function () { return callback(); });
1280
1293
  });
1281
1294
  }
@@ -1475,11 +1488,50 @@ function include(fieldName) {
1475
1488
  if (isStringValue(condition)) {
1476
1489
  return Boolean(exclusion.tests[condition]);
1477
1490
  }
1478
- return optionalFunctionValue(condition, optionalFunctionValue(produceDraft));
1491
+ return optionalFunctionValue(condition, optionalFunctionValue(produceSuiteResult));
1479
1492
  };
1480
1493
  }
1481
1494
  }
1482
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
+
1483
1535
  /**
1484
1536
  * Conditionally omits tests from the suite.
1485
1537
  *
@@ -1493,7 +1545,7 @@ function omitWhen(conditional, callback) {
1493
1545
  isolate({ type: IsolateTypes.OMIT_WHEN }, function () {
1494
1546
  context.run({
1495
1547
  omitted: isOmitted() ||
1496
- optionalFunctionValue(conditional, optionalFunctionValue(produceDraft))
1548
+ optionalFunctionValue(conditional, optionalFunctionValue(produceSuiteResult))
1497
1549
  }, function () { return callback(); });
1498
1550
  });
1499
1551
  }
@@ -1626,7 +1678,7 @@ function registerTest(testObject) {
1626
1678
  }
1627
1679
  }
1628
1680
  catch (e) {
1629
- 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, "."));
1630
1682
  }
1631
1683
  }
1632
1684
 
@@ -1712,6 +1764,11 @@ function handleKeyTest(key, newTestObject) {
1712
1764
  // eslint-disable-next-line max-statements
1713
1765
  function registerPrevRunTest(testObject) {
1714
1766
  var prevRunTest = useTestAtCursor(testObject);
1767
+ if (shouldSkipBasedOnMode(testObject)) {
1768
+ moveForward();
1769
+ testObject.skip();
1770
+ return testObject;
1771
+ }
1715
1772
  if (isOmitted()) {
1716
1773
  prevRunTest.omit();
1717
1774
  moveForward();
@@ -1820,7 +1877,7 @@ function warn() {
1820
1877
  ctx.currentTest.warn();
1821
1878
  }
1822
1879
 
1823
- var VERSION = "4.1.3";
1880
+ var VERSION = "4.2.1";
1824
1881
 
1825
1882
  Object.defineProperty(exports, 'enforce', {
1826
1883
  enumerable: true,
@@ -1832,6 +1889,7 @@ exports.VERSION = VERSION;
1832
1889
  exports.context = context;
1833
1890
  exports.create = create;
1834
1891
  exports.each = each;
1892
+ exports.eager = eager;
1835
1893
  exports.group = group;
1836
1894
  exports.include = include;
1837
1895
  exports.omitWhen = omitWhen;