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.
@@ -1,8 +1,6 @@
1
1
  export { enforce } from 'n4s';
2
2
  import { createContext } from 'context';
3
3
 
4
- var assign = Object.assign;
5
-
6
4
  /**
7
5
  * @returns a unique numeric id.
8
6
  */
@@ -10,199 +8,76 @@ var genId = (function (n) { return function () {
10
8
  return "".concat(n++);
11
9
  }; })(0);
12
10
 
13
- function isFunction(value) {
14
- return typeof value === 'function';
15
- }
16
-
17
- function optionalFunctionValue(value) {
18
- var args = [];
19
- for (var _i = 1; _i < arguments.length; _i++) {
20
- args[_i - 1] = arguments[_i];
21
- }
22
- return isFunction(value) ? value.apply(void 0, args) : value;
23
- }
24
-
25
- function defaultTo(callback, defaultValue) {
26
- var _a;
27
- return (_a = optionalFunctionValue(callback)) !== null && _a !== void 0 ? _a : defaultValue;
28
- }
29
-
30
- /**
31
- * Throws a timed out error.
32
- */
33
- function throwError(devMessage, productionMessage) {
34
- throw new Error(devMessage );
35
- }
36
- function throwErrorDeferred(devMessage, productionMessage) {
37
- setTimeout(function () {
38
- throwError(devMessage);
39
- }, 0);
11
+ function isStringValue(v) {
12
+ return String(v) === v;
40
13
  }
41
14
 
42
- // eslint-disable-next-line max-lines-per-function
43
- function createState(onStateChange) {
44
- var state = {
45
- references: []
46
- };
47
- var registrations = [];
48
- return {
49
- registerStateKey: registerStateKey,
50
- reset: reset
51
- };
52
- /**
53
- * Registers a new key in the state, takes the initial value (may be a function that returns the initial value), returns a function.
54
- *
55
- * @example
56
- *
57
- * const useColor = state.registerStateKey("blue");
58
- *
59
- * let [color, setColor] = useColor(); // -> ["blue", Function]
60
- *
61
- * setColor("green");
62
- *
63
- * useColor()[0]; -> "green"
64
- */
65
- function registerStateKey(initialState, onUpdate) {
66
- var key = registrations.length;
67
- registrations.push([initialState, onUpdate]);
68
- return initKey(key, initialState);
69
- }
70
- function reset() {
71
- var prev = current();
72
- state.references = [];
73
- registrations.forEach(function (_a, index) {
74
- var initialValue = _a[0];
75
- return initKey(index, initialValue, prev[index]);
76
- });
77
- }
78
- function initKey(key, initialState, prevState) {
79
- current().push();
80
- set(key, optionalFunctionValue(initialState, prevState));
81
- return function useStateKey() {
82
- return [
83
- current()[key],
84
- function (nextState) {
85
- return set(key, optionalFunctionValue(nextState, current()[key]));
86
- },
87
- ];
88
- };
89
- }
90
- function current() {
91
- return state.references;
92
- }
93
- function set(index, value) {
94
- var prevValue = state.references[index];
95
- state.references[index] = value;
96
- var _a = registrations[index], onUpdate = _a[1];
97
- if (isFunction(onUpdate)) {
98
- onUpdate(value, prevValue);
99
- }
100
- if (isFunction(onStateChange)) {
101
- onStateChange();
15
+ function bindNot(fn) {
16
+ return function () {
17
+ var args = [];
18
+ for (var _i = 0; _i < arguments.length; _i++) {
19
+ args[_i] = arguments[_i];
102
20
  }
103
- }
21
+ return !fn.apply(void 0, args);
22
+ };
104
23
  }
105
24
 
106
- var IsolateTypes;
107
- (function (IsolateTypes) {
108
- IsolateTypes[IsolateTypes["DEFAULT"] = 0] = "DEFAULT";
109
- IsolateTypes[IsolateTypes["SUITE"] = 1] = "SUITE";
110
- IsolateTypes[IsolateTypes["EACH"] = 2] = "EACH";
111
- IsolateTypes[IsolateTypes["SKIP_WHEN"] = 3] = "SKIP_WHEN";
112
- IsolateTypes[IsolateTypes["OMIT_WHEN"] = 4] = "OMIT_WHEN";
113
- IsolateTypes[IsolateTypes["GROUP"] = 5] = "GROUP";
114
- })(IsolateTypes || (IsolateTypes = {}));
25
+ function isUndefined(value) {
26
+ return value === undefined;
27
+ }
115
28
 
116
- function createStateRef(state, _a) {
117
- var suiteId = _a.suiteId, suiteName = _a.suiteName;
118
- return {
119
- optionalFields: state.registerStateKey(function () { return ({}); }),
120
- suiteId: state.registerStateKey(suiteId),
121
- suiteName: state.registerStateKey(suiteName),
122
- testCallbacks: state.registerStateKey(function () { return ({
123
- fieldCallbacks: {},
124
- doneCallbacks: []
125
- }); }),
126
- testObjects: state.registerStateKey(function (prev) {
127
- return {
128
- prev: prev ? prev.current : [],
129
- current: []
130
- };
131
- })
132
- };
29
+ function shouldUseErrorAsMessage(message, error) {
30
+ // kind of cheating with this safe guard, but it does the job
31
+ return isUndefined(message) && isStringValue(error);
133
32
  }
134
33
 
135
34
  function asArray(possibleArg) {
136
35
  return [].concat(possibleArg);
137
36
  }
138
37
 
139
- function last(values) {
140
- var valuesArray = asArray(values);
141
- return valuesArray[valuesArray.length - 1];
38
+ function lengthEquals(value, arg1) {
39
+ return value.length === Number(arg1);
142
40
  }
143
41
 
144
- function createCursor() {
145
- var storage = {
146
- cursor: []
147
- };
148
- function addLevel() {
149
- storage.cursor.push(0);
150
- }
151
- function removeLevel() {
152
- storage.cursor.pop();
153
- }
154
- function cursorAt() {
155
- return last(storage.cursor);
156
- }
157
- function getCursor() {
158
- return asArray(storage.cursor);
159
- }
160
- function next() {
161
- storage.cursor[storage.cursor.length - 1]++;
162
- return last(storage.cursor);
163
- }
164
- function reset() {
165
- storage.cursor = [0];
166
- }
167
- reset();
168
- return {
169
- addLevel: addLevel,
170
- cursorAt: cursorAt,
171
- getCursor: getCursor,
172
- next: next,
173
- removeLevel: removeLevel,
174
- reset: reset
175
- };
42
+ function longerThan(value, arg1) {
43
+ return value.length > Number(arg1);
176
44
  }
177
45
 
178
- var context = createContext(function (ctxRef, parentContext) {
179
- return parentContext
180
- ? null
181
- : assign({}, {
182
- exclusion: {
183
- tests: {},
184
- groups: {}
185
- },
186
- inclusion: {},
187
- isolate: {
188
- type: IsolateTypes.DEFAULT,
189
- keys: {
190
- current: {},
191
- prev: {}
192
- }
193
- },
194
- testCursor: createCursor()
195
- }, ctxRef);
196
- });
197
-
198
- function bindNot(fn) {
199
- return function () {
200
- var args = [];
201
- for (var _i = 0; _i < arguments.length; _i++) {
202
- args[_i] = arguments[_i];
203
- }
204
- return !fn.apply(void 0, args);
46
+ /**
47
+ * Creates a cache function
48
+ */
49
+ function createCache(maxSize) {
50
+ if (maxSize === void 0) { maxSize = 1; }
51
+ var cacheStorage = [];
52
+ var cache = function (deps, cacheAction) {
53
+ var cacheHit = cache.get(deps);
54
+ // cache hit is not null
55
+ if (cacheHit)
56
+ return cacheHit[1];
57
+ var result = cacheAction();
58
+ cacheStorage.unshift([deps.concat(), result]);
59
+ if (longerThan(cacheStorage, maxSize))
60
+ cacheStorage.length = maxSize;
61
+ return result;
62
+ };
63
+ // invalidate an item in the cache by its dependencies
64
+ cache.invalidate = function (deps) {
65
+ var index = findIndex(deps);
66
+ if (index > -1)
67
+ cacheStorage.splice(index, 1);
68
+ };
69
+ // Retrieves an item from the cache.
70
+ cache.get = function (deps) {
71
+ return cacheStorage[findIndex(deps)] || null;
205
72
  };
73
+ return cache;
74
+ function findIndex(deps) {
75
+ return cacheStorage.findIndex(function (_a) {
76
+ var cachedDeps = _a[0];
77
+ return lengthEquals(deps, cachedDeps.length) &&
78
+ deps.every(function (dep, i) { return dep === cachedDeps[i]; });
79
+ });
80
+ }
206
81
  }
207
82
 
208
83
  // The module is named "isArrayValue" since it
@@ -217,9 +92,31 @@ function isNull(value) {
217
92
  }
218
93
  var isNotNull = bindNot(isNull);
219
94
 
220
- // This is sort of a map/filter in one function.
221
- // Normally, behaves like a nested-array map
222
- // Returning `null` will drop the element from the array
95
+ function isFunction(value) {
96
+ return typeof value === 'function';
97
+ }
98
+
99
+ function optionalFunctionValue(value) {
100
+ var args = [];
101
+ for (var _i = 1; _i < arguments.length; _i++) {
102
+ args[_i - 1] = arguments[_i];
103
+ }
104
+ return isFunction(value) ? value.apply(void 0, args) : value;
105
+ }
106
+
107
+ function defaultTo(callback, defaultValue) {
108
+ var _a;
109
+ return (_a = optionalFunctionValue(callback)) !== null && _a !== void 0 ? _a : defaultValue;
110
+ }
111
+
112
+ function last(values) {
113
+ var valuesArray = asArray(values);
114
+ return valuesArray[valuesArray.length - 1];
115
+ }
116
+
117
+ // This is kind of a map/filter in one function.
118
+ // Normally, behaves like a nested-array map,
119
+ // but returning `null` will drop the element from the array
223
120
  function transform(array, cb) {
224
121
  var res = [];
225
122
  for (var _i = 0, array_1 = array; _i < array_1.length; _i++) {
@@ -262,68 +159,78 @@ function getCurrent(array, path) {
262
159
  return current;
263
160
  }
264
161
 
265
- function isUndefined(value) {
266
- return value === undefined;
267
- }
268
-
269
- function isNullish(value) {
270
- return isNull(value) || isUndefined(value);
271
- }
272
-
273
- function isStringValue(v) {
274
- return String(v) === v;
275
- }
162
+ var assign = Object.assign;
276
163
 
277
- function shouldUseErrorAsMessage(message, error) {
278
- // kind of cheating with this safe guard, but it does the job
279
- return isUndefined(message) && isStringValue(error);
164
+ function createCursor() {
165
+ var storage = {
166
+ cursor: []
167
+ };
168
+ function addLevel() {
169
+ storage.cursor.push(0);
170
+ }
171
+ function removeLevel() {
172
+ storage.cursor.pop();
173
+ }
174
+ function cursorAt() {
175
+ return last(storage.cursor);
176
+ }
177
+ function getCursor() {
178
+ return asArray(storage.cursor);
179
+ }
180
+ function next() {
181
+ storage.cursor[storage.cursor.length - 1]++;
182
+ return last(storage.cursor);
183
+ }
184
+ function reset() {
185
+ storage.cursor = [0];
186
+ }
187
+ reset();
188
+ return {
189
+ addLevel: addLevel,
190
+ cursorAt: cursorAt,
191
+ getCursor: getCursor,
192
+ next: next,
193
+ removeLevel: removeLevel,
194
+ reset: reset
195
+ };
280
196
  }
281
197
 
282
- function lengthEquals(value, arg1) {
283
- return value.length === Number(arg1);
284
- }
198
+ var IsolateTypes;
199
+ (function (IsolateTypes) {
200
+ IsolateTypes[IsolateTypes["DEFAULT"] = 0] = "DEFAULT";
201
+ IsolateTypes[IsolateTypes["SUITE"] = 1] = "SUITE";
202
+ IsolateTypes[IsolateTypes["EACH"] = 2] = "EACH";
203
+ IsolateTypes[IsolateTypes["SKIP_WHEN"] = 3] = "SKIP_WHEN";
204
+ IsolateTypes[IsolateTypes["OMIT_WHEN"] = 4] = "OMIT_WHEN";
205
+ IsolateTypes[IsolateTypes["GROUP"] = 5] = "GROUP";
206
+ })(IsolateTypes || (IsolateTypes = {}));
285
207
 
286
- function longerThan(value, arg1) {
287
- return value.length > Number(arg1);
288
- }
208
+ var Modes;
209
+ (function (Modes) {
210
+ Modes[Modes["ALL"] = 0] = "ALL";
211
+ Modes[Modes["EAGER"] = 1] = "EAGER";
212
+ })(Modes || (Modes = {}));
289
213
 
290
- /**
291
- * Creates a cache function
292
- */
293
- function createCache(maxSize) {
294
- if (maxSize === void 0) { maxSize = 1; }
295
- var cacheStorage = [];
296
- var cache = function (deps, cacheAction) {
297
- var cacheHit = cache.get(deps);
298
- // cache hit is not null
299
- if (cacheHit)
300
- return cacheHit[1];
301
- var result = cacheAction();
302
- cacheStorage.unshift([deps.concat(), result]);
303
- if (longerThan(cacheStorage, maxSize))
304
- cacheStorage.length = maxSize;
305
- return result;
306
- };
307
- // invalidate an item in the cache by its dependencies
308
- cache.invalidate = function (deps) {
309
- var index = cacheStorage.findIndex(function (_a) {
310
- var cachedDeps = _a[0];
311
- return lengthEquals(deps, cachedDeps.length) &&
312
- deps.every(function (dep, i) { return dep === cachedDeps[i]; });
313
- });
314
- if (index > -1)
315
- cacheStorage.splice(index, 1);
316
- };
317
- // Retrieves an item from the cache.
318
- cache.get = function (deps) {
319
- return cacheStorage[cacheStorage.findIndex(function (_a) {
320
- var cachedDeps = _a[0];
321
- return lengthEquals(deps, cachedDeps.length) &&
322
- deps.every(function (dep, i) { return dep === cachedDeps[i]; });
323
- })] || null;
324
- };
325
- return cache;
326
- }
214
+ var context = createContext(function (ctxRef, parentContext) {
215
+ return parentContext
216
+ ? null
217
+ : assign({}, {
218
+ exclusion: {
219
+ tests: {},
220
+ groups: {}
221
+ },
222
+ inclusion: {},
223
+ isolate: {
224
+ type: IsolateTypes.DEFAULT,
225
+ keys: {
226
+ current: {},
227
+ prev: {}
228
+ }
229
+ },
230
+ mode: [Modes.ALL],
231
+ testCursor: createCursor()
232
+ }, ctxRef);
233
+ });
327
234
 
328
235
  // STATE REF
329
236
  function useStateRef() {
@@ -540,6 +447,105 @@ var STATUS_PENDING = 'PENDING';
540
447
  var STATUS_CANCELED = 'CANCELED';
541
448
  var STATUS_OMITTED = 'OMITTED';
542
449
 
450
+ /**
451
+ * Throws a timed out error.
452
+ */
453
+ function throwError(devMessage, productionMessage) {
454
+ throw new Error(devMessage );
455
+ }
456
+ function throwErrorDeferred(devMessage, productionMessage) {
457
+ setTimeout(function () {
458
+ throwError(devMessage);
459
+ }, 0);
460
+ }
461
+
462
+ // eslint-disable-next-line max-lines-per-function
463
+ function createState(onStateChange) {
464
+ var state = {
465
+ references: []
466
+ };
467
+ var registrations = [];
468
+ return {
469
+ registerStateKey: registerStateKey,
470
+ reset: reset
471
+ };
472
+ /**
473
+ * Registers a new key in the state, takes the initial value (may be a function that returns the initial value), returns a function.
474
+ *
475
+ * @example
476
+ *
477
+ * const useColor = state.registerStateKey("blue");
478
+ *
479
+ * let [color, setColor] = useColor(); // -> ["blue", Function]
480
+ *
481
+ * setColor("green");
482
+ *
483
+ * useColor()[0]; -> "green"
484
+ */
485
+ function registerStateKey(initialState, onUpdate) {
486
+ var key = registrations.length;
487
+ registrations.push([initialState, onUpdate]);
488
+ return initKey(key, initialState);
489
+ }
490
+ function reset() {
491
+ var prev = current();
492
+ state.references = [];
493
+ registrations.forEach(function (_a, index) {
494
+ var initialValue = _a[0];
495
+ return initKey(index, initialValue, prev[index]);
496
+ });
497
+ }
498
+ function initKey(key, initialState, prevState) {
499
+ current().push();
500
+ set(key, optionalFunctionValue(initialState, prevState));
501
+ return function useStateKey() {
502
+ return [
503
+ current()[key],
504
+ function (nextState) {
505
+ return set(key, optionalFunctionValue(nextState, current()[key]));
506
+ },
507
+ ];
508
+ };
509
+ }
510
+ function current() {
511
+ return state.references;
512
+ }
513
+ function set(index, value) {
514
+ var prevValue = state.references[index];
515
+ state.references[index] = value;
516
+ var _a = registrations[index], onUpdate = _a[1];
517
+ if (isFunction(onUpdate)) {
518
+ onUpdate(value, prevValue);
519
+ }
520
+ if (isFunction(onStateChange)) {
521
+ onStateChange();
522
+ }
523
+ }
524
+ }
525
+
526
+ function createStateRef(state, _a) {
527
+ var suiteId = _a.suiteId, suiteName = _a.suiteName;
528
+ return {
529
+ optionalFields: state.registerStateKey(function () { return ({}); }),
530
+ suiteId: state.registerStateKey(suiteId),
531
+ suiteName: state.registerStateKey(suiteName),
532
+ testCallbacks: state.registerStateKey(function () { return ({
533
+ fieldCallbacks: {},
534
+ doneCallbacks: []
535
+ }); }),
536
+ testObjects: state.registerStateKey(function (prev) {
537
+ return {
538
+ prev: prev ? prev.current : [],
539
+ current: []
540
+ };
541
+ })
542
+ };
543
+ }
544
+
545
+ function isNullish(value) {
546
+ return isNull(value) || isUndefined(value);
547
+ }
548
+
543
549
  function usePath() {
544
550
  var context$1 = context.useX();
545
551
  return context$1.testCursor.getCursor();
@@ -613,69 +619,21 @@ function shouldAllowReorder() {
613
619
  return context.useX().isolate.type === IsolateTypes.EACH;
614
620
  }
615
621
 
616
- /**
617
- * A safe hasOwnProperty access
618
- */
619
- function hasOwnProperty(obj, key) {
620
- return Object.prototype.hasOwnProperty.call(obj, key);
621
- }
622
-
623
- function isNumber(value) {
624
- return Boolean(typeof value === 'number');
625
- }
626
-
627
- function isEmpty(value) {
628
- if (!value) {
629
- return true;
630
- }
631
- else if (isNumber(value)) {
632
- return value === 0;
633
- }
634
- else if (hasOwnProperty(value, 'length')) {
635
- return lengthEquals(value, 0);
636
- }
637
- else if (typeof value === 'object') {
638
- return lengthEquals(Object.keys(value), 0);
639
- }
640
- return true;
641
- }
642
- var isNotEmpty = bindNot(isEmpty);
643
-
644
- function nonMatchingFieldName(testObject, fieldName) {
645
- return !!fieldName && !matchingFieldName(testObject, fieldName);
646
- }
647
- function matchingFieldName(testObject, fieldName) {
648
- return !!(fieldName && testObject.fieldName === fieldName);
649
- }
650
-
651
- /**
652
- * Checks if a given field, or the suite as a whole still have remaining tests.
653
- */
654
- function hasRemainingTests(fieldName) {
655
- var allIncomplete = useAllIncomplete();
656
- if (isEmpty(allIncomplete)) {
657
- return false;
658
- }
659
- if (fieldName) {
660
- return allIncomplete.some(function (testObject) {
661
- return matchingFieldName(testObject, fieldName);
662
- });
663
- }
664
- return isNotEmpty(allIncomplete);
665
- }
622
+ var Severity;
623
+ (function (Severity) {
624
+ Severity["WARNINGS"] = "warnings";
625
+ Severity["ERRORS"] = "errors";
626
+ })(Severity || (Severity = {}));
666
627
 
667
628
  /**
668
629
  * Reads the testObjects list and gets full validation result from it.
669
630
  */
670
631
  function genTestsSummary() {
671
632
  var testObjects = useTestsFlat();
672
- var summary = {
673
- errorCount: 0,
633
+ var summary = assign(baseStats(), {
674
634
  groups: {},
675
- testCount: 0,
676
- tests: {},
677
- warnCount: 0
678
- };
635
+ tests: {}
636
+ });
679
637
  appendSummary(testObjects);
680
638
  return countFailures(summary);
681
639
  function appendSummary(testObjects) {
@@ -703,30 +661,34 @@ function countFailures(summary) {
703
661
  // eslint-disable-next-line max-statements
704
662
  function genTestObject(summaryKey, testObject) {
705
663
  var fieldName = testObject.fieldName, message = testObject.message;
706
- summaryKey[fieldName] = summaryKey[fieldName] || {
707
- errorCount: 0,
708
- warnCount: 0,
709
- testCount: 0
710
- };
664
+ summaryKey[fieldName] = summaryKey[fieldName] || baseStats();
711
665
  var testKey = summaryKey[fieldName];
712
666
  if (testObject.isNonActionable())
713
667
  return testKey;
714
668
  summaryKey[fieldName].testCount++;
715
669
  // Adds to severity group
716
- function addTo(countKey, group) {
670
+ function addTo(severity) {
671
+ var countKey = severity === Severity.ERRORS ? 'errorCount' : 'warnCount';
717
672
  testKey[countKey]++;
718
673
  if (message) {
719
- testKey[group] = (testKey[group] || []).concat(message);
674
+ testKey[severity] = (testKey[severity] || []).concat(message);
720
675
  }
721
676
  }
722
677
  if (testObject.isFailing()) {
723
- addTo('errorCount', 'errors');
678
+ addTo(Severity.ERRORS);
724
679
  }
725
680
  else if (testObject.isWarning()) {
726
- addTo('warnCount', 'warnings');
681
+ addTo(Severity.WARNINGS);
727
682
  }
728
683
  return testKey;
729
684
  }
685
+ function baseStats() {
686
+ return {
687
+ errorCount: 0,
688
+ warnCount: 0,
689
+ testCount: 0
690
+ };
691
+ }
730
692
 
731
693
  /*! *****************************************************************************
732
694
  Copyright (c) Microsoft Corporation.
@@ -764,6 +726,13 @@ function __spreadArray(to, from, pack) {
764
726
  return to.concat(ar || Array.prototype.slice.call(from));
765
727
  }
766
728
 
729
+ function nonMatchingFieldName(testObject, fieldName) {
730
+ return !!fieldName && !matchingFieldName(testObject, fieldName);
731
+ }
732
+ function matchingFieldName(testObject, fieldName) {
733
+ return !!(fieldName && testObject.fieldName === fieldName);
734
+ }
735
+
767
736
  function either(a, b) {
768
737
  return !!a !== !!b;
769
738
  }
@@ -772,7 +741,7 @@ function either(a, b) {
772
741
  * Checks that a given test object matches the currently specified severity level
773
742
  */
774
743
  function nonMatchingSeverityProfile(severity, testObject) {
775
- return either(severity === 'warnings', testObject.warns());
744
+ return either(severity === Severity.WARNINGS, testObject.warns());
776
745
  }
777
746
 
778
747
  function collectFailureMessages(severity, testObjects, options) {
@@ -814,10 +783,10 @@ function getFailuresArrayOrObject(group, fieldName) {
814
783
  }
815
784
 
816
785
  function getErrors(fieldName) {
817
- return getFailures('errors', fieldName);
786
+ return getFailures(Severity.ERRORS, fieldName);
818
787
  }
819
788
  function getWarnings(fieldName) {
820
- return getFailures('warnings', fieldName);
789
+ return getFailures(Severity.WARNINGS, fieldName);
821
790
  }
822
791
  /**
823
792
  * @returns suite or field's errors or warnings.
@@ -831,11 +800,11 @@ function getFailures(severityKey, fieldName) {
831
800
  }
832
801
 
833
802
  function getErrorsByGroup(groupName, fieldName) {
834
- var errors = getByGroup('errors', groupName, fieldName);
803
+ var errors = getByGroup(Severity.ERRORS, groupName, fieldName);
835
804
  return getFailuresArrayOrObject(errors, fieldName);
836
805
  }
837
806
  function getWarningsByGroup(groupName, fieldName) {
838
- var warnings = getByGroup('warnings', groupName, fieldName);
807
+ var warnings = getByGroup(Severity.WARNINGS, groupName, fieldName);
839
808
  return getFailuresArrayOrObject(warnings, fieldName);
840
809
  }
841
810
  /**
@@ -869,10 +838,10 @@ function hasFailuresLogic(testObject, severityKey, fieldName) {
869
838
  }
870
839
 
871
840
  function hasErrors(fieldName) {
872
- return has('errors', fieldName);
841
+ return has(Severity.ERRORS, fieldName);
873
842
  }
874
843
  function hasWarnings(fieldName) {
875
- return has('warnings', fieldName);
844
+ return has(Severity.WARNINGS, fieldName);
876
845
  }
877
846
  function has(severityKey, fieldName) {
878
847
  var testObjects = useTestsFlat();
@@ -882,10 +851,10 @@ function has(severityKey, fieldName) {
882
851
  }
883
852
 
884
853
  function hasErrorsByGroup(groupName, fieldName) {
885
- return hasByGroup('errors', groupName, fieldName);
854
+ return hasByGroup(Severity.ERRORS, groupName, fieldName);
886
855
  }
887
856
  function hasWarningsByGroup(groupName, fieldName) {
888
- return hasByGroup('warnings', groupName, fieldName);
857
+ return hasByGroup(Severity.WARNINGS, groupName, fieldName);
889
858
  }
890
859
  /**
891
860
  * Checks whether there are failures in a given group.
@@ -899,6 +868,34 @@ function hasByGroup(severityKey, group, fieldName) {
899
868
  });
900
869
  }
901
870
 
871
+ /**
872
+ * A safe hasOwnProperty access
873
+ */
874
+ function hasOwnProperty(obj, key) {
875
+ return Object.prototype.hasOwnProperty.call(obj, key);
876
+ }
877
+
878
+ function isNumber(value) {
879
+ return Boolean(typeof value === 'number');
880
+ }
881
+
882
+ function isEmpty(value) {
883
+ if (!value) {
884
+ return true;
885
+ }
886
+ else if (isNumber(value)) {
887
+ return value === 0;
888
+ }
889
+ else if (hasOwnProperty(value, 'length')) {
890
+ return lengthEquals(value, 0);
891
+ }
892
+ else if (typeof value === 'object') {
893
+ return lengthEquals(Object.keys(value), 0);
894
+ }
895
+ return true;
896
+ }
897
+ var isNotEmpty = bindNot(isEmpty);
898
+
902
899
  // eslint-disable-next-line max-statements, complexity
903
900
  function isValid(result, fieldName) {
904
901
  if (fieldIsOmitted(fieldName)) {
@@ -952,7 +949,7 @@ function noMissingTests(fieldName) {
952
949
  }
953
950
 
954
951
  var cache$1 = createCache(20);
955
- function produceDraft() {
952
+ function produceSuiteResult() {
956
953
  var testObjects = useTestsFlat();
957
954
  var ctxRef = { stateRef: useStateRef() };
958
955
  return cache$1([testObjects], context.bind(ctxRef, function () {
@@ -967,19 +964,35 @@ function produceDraft() {
967
964
  hasWarnings: context.bind(ctxRef, hasWarnings),
968
965
  hasWarningsByGroup: context.bind(ctxRef, hasWarningsByGroup),
969
966
  isValid: context.bind(ctxRef, function (fieldName) {
970
- return isValid(produceDraft(), fieldName);
967
+ return isValid(produceSuiteResult(), fieldName);
971
968
  }),
972
969
  suiteName: suiteName
973
970
  });
974
971
  }));
975
972
  }
976
973
 
974
+ /**
975
+ * Checks if a given field, or the suite as a whole still have remaining tests.
976
+ */
977
+ function hasRemainingTests(fieldName) {
978
+ var allIncomplete = useAllIncomplete();
979
+ if (isEmpty(allIncomplete)) {
980
+ return false;
981
+ }
982
+ if (fieldName) {
983
+ return allIncomplete.some(function (testObject) {
984
+ return matchingFieldName(testObject, fieldName);
985
+ });
986
+ }
987
+ return isNotEmpty(allIncomplete);
988
+ }
989
+
977
990
  var cache = createCache(20);
978
991
  function produceFullResult() {
979
992
  var testObjects = useTestsFlat();
980
993
  var ctxRef = { stateRef: useStateRef() };
981
994
  return cache([testObjects], context.bind(ctxRef, function () {
982
- return assign({}, produceDraft(), {
995
+ return assign({}, produceSuiteResult(), {
983
996
  done: context.bind(ctxRef, done)
984
997
  });
985
998
  }));
@@ -1012,7 +1025,7 @@ var done = function done() {
1012
1025
  if (shouldSkipDoneRegistration(callback, fieldName, output)) {
1013
1026
  return output;
1014
1027
  }
1015
- var doneCallback = function () { return callback(produceDraft()); };
1028
+ var doneCallback = function () { return callback(produceSuiteResult()); };
1016
1029
  if (shouldRunDoneCallback(fieldName)) {
1017
1030
  doneCallback();
1018
1031
  return output;
@@ -1038,18 +1051,12 @@ function createBus() {
1038
1051
  var listeners = {};
1039
1052
  return {
1040
1053
  emit: function (event, data) {
1041
- if (!listeners[event]) {
1042
- return;
1043
- }
1044
- listeners[event].forEach(function (listener) {
1045
- listener(data);
1054
+ (listeners[event] || []).forEach(function (handler) {
1055
+ handler(data);
1046
1056
  });
1047
1057
  },
1048
1058
  on: function (event, handler) {
1049
- if (!listeners[event]) {
1050
- listeners[event] = [];
1051
- }
1052
- listeners[event].push(handler);
1059
+ listeners[event] = (listeners[event] || []).concat(handler);
1053
1060
  return {
1054
1061
  off: function () {
1055
1062
  listeners[event] = listeners[event].filter(function (h) { return h !== handler; });
@@ -1117,9 +1124,7 @@ function runFieldCallbacks(fieldName) {
1117
1124
  */
1118
1125
  function runDoneCallbacks() {
1119
1126
  var doneCallbacks = useTestCallbacks()[0].doneCallbacks;
1120
- if (!hasRemainingTests()) {
1121
- callEach(doneCallbacks);
1122
- }
1127
+ callEach(doneCallbacks);
1123
1128
  }
1124
1129
 
1125
1130
  // eslint-disable-next-line max-lines-per-function
@@ -1133,14 +1138,21 @@ function initBus() {
1133
1138
  }
1134
1139
  testObject.done();
1135
1140
  runFieldCallbacks(testObject.fieldName);
1136
- runDoneCallbacks();
1141
+ if (!hasRemainingTests()) {
1142
+ // When no more tests are running, emit the done event
1143
+ bus.emit(Events.ALL_RUNNING_TESTS_FINISHED);
1144
+ }
1137
1145
  });
1138
1146
  // Report that the suite completed its synchronous test run.
1139
1147
  // Async operations may still be running.
1140
- bus.on(Events.SUITE_COMPLETED, function () {
1148
+ bus.on(Events.SUITE_CALLBACK_DONE_RUNNING, function () {
1141
1149
  // Remove tests that are optional and need to be omitted
1142
1150
  omitOptionalTests();
1143
1151
  });
1152
+ // Called when all the tests, including async, are done running
1153
+ bus.on(Events.ALL_RUNNING_TESTS_FINISHED, function () {
1154
+ runDoneCallbacks();
1155
+ });
1144
1156
  // Removes a certain field from the state.
1145
1157
  bus.on(Events.REMOVE_FIELD, function (fieldName) {
1146
1158
  useEachTestObject(function (testObject) {
@@ -1170,9 +1182,10 @@ function useBus() {
1170
1182
  var Events;
1171
1183
  (function (Events) {
1172
1184
  Events["TEST_COMPLETED"] = "test_completed";
1185
+ Events["ALL_RUNNING_TESTS_FINISHED"] = "all_running_tests_finished";
1173
1186
  Events["REMOVE_FIELD"] = "remove_field";
1174
1187
  Events["RESET_FIELD"] = "reset_field";
1175
- Events["SUITE_COMPLETED"] = "suite_completed";
1188
+ Events["SUITE_CALLBACK_DONE_RUNNING"] = "suite_callback_done_running";
1176
1189
  })(Events || (Events = {}));
1177
1190
 
1178
1191
  // eslint-disable-next-line max-lines-per-function
@@ -1209,11 +1222,11 @@ function create() {
1209
1222
  });
1210
1223
  // Report the suite is done registering tests
1211
1224
  // Async tests may still be running
1212
- bus.emit(Events.SUITE_COMPLETED);
1225
+ bus.emit(Events.SUITE_CALLBACK_DONE_RUNNING);
1213
1226
  // Return the result
1214
1227
  return produceFullResult();
1215
1228
  }), {
1216
- get: context.bind(ctxRef, produceDraft),
1229
+ get: context.bind(ctxRef, produceSuiteResult),
1217
1230
  remove: context.bind(ctxRef, function (fieldName) {
1218
1231
  bus.emit(Events.REMOVE_FIELD, fieldName);
1219
1232
  }),
@@ -1240,7 +1253,7 @@ function create() {
1240
1253
  */
1241
1254
  function each(list, callback) {
1242
1255
  if (!isFunction(callback)) {
1243
- throwError('callback must be a function');
1256
+ throwError('each callback must be a function');
1244
1257
  }
1245
1258
  isolate({ type: IsolateTypes.EACH }, function () {
1246
1259
  list.forEach(function (arg, index) {
@@ -1271,7 +1284,7 @@ function skipWhen(conditional, callback) {
1271
1284
  // we should skip the test if the parent conditional is true.
1272
1285
  isExcludedIndividually() ||
1273
1286
  // Otherwise, we should skip the test if the conditional is true.
1274
- optionalFunctionValue(conditional, optionalFunctionValue(produceDraft))
1287
+ optionalFunctionValue(conditional, optionalFunctionValue(produceSuiteResult))
1275
1288
  }, function () { return callback(); });
1276
1289
  });
1277
1290
  }
@@ -1471,11 +1484,50 @@ function include(fieldName) {
1471
1484
  if (isStringValue(condition)) {
1472
1485
  return Boolean(exclusion.tests[condition]);
1473
1486
  }
1474
- return optionalFunctionValue(condition, optionalFunctionValue(produceDraft));
1487
+ return optionalFunctionValue(condition, optionalFunctionValue(produceSuiteResult));
1475
1488
  };
1476
1489
  }
1477
1490
  }
1478
1491
 
1492
+ /**
1493
+ * Sets the suite to "eager" (fail fast) mode.
1494
+ * Eager mode will skip running subsequent tests of a failing fields.
1495
+ *
1496
+ * @example
1497
+ * // in the following example, the second test of username will not run
1498
+ * // if the first test of username failed.
1499
+ * const suite = create((data) => {
1500
+ * eager();
1501
+ *
1502
+ * test('username', 'username is required', () => {
1503
+ * enforce(data.username).isNotBlank();
1504
+ * });
1505
+ *
1506
+ * test('username', 'username is too short', () => {
1507
+ * enforce(data.username).longerThan(2);
1508
+ * });
1509
+ * });
1510
+ */
1511
+ function eager() {
1512
+ setMode(Modes.EAGER);
1513
+ }
1514
+ function shouldSkipBasedOnMode(testObject) {
1515
+ if (isEager() && hasErrors(testObject.fieldName))
1516
+ return true;
1517
+ return false;
1518
+ }
1519
+ function isEager() {
1520
+ return isMode(Modes.EAGER);
1521
+ }
1522
+ function isMode(mode) {
1523
+ var currentMode = context.useX().mode;
1524
+ return currentMode[0] === mode;
1525
+ }
1526
+ function setMode(nextMode) {
1527
+ var mode = context.useX().mode;
1528
+ mode[0] = nextMode;
1529
+ }
1530
+
1479
1531
  /**
1480
1532
  * Conditionally omits tests from the suite.
1481
1533
  *
@@ -1489,7 +1541,7 @@ function omitWhen(conditional, callback) {
1489
1541
  isolate({ type: IsolateTypes.OMIT_WHEN }, function () {
1490
1542
  context.run({
1491
1543
  omitted: isOmitted() ||
1492
- optionalFunctionValue(conditional, optionalFunctionValue(produceDraft))
1544
+ optionalFunctionValue(conditional, optionalFunctionValue(produceSuiteResult))
1493
1545
  }, function () { return callback(); });
1494
1546
  });
1495
1547
  }
@@ -1622,7 +1674,7 @@ function registerTest(testObject) {
1622
1674
  }
1623
1675
  }
1624
1676
  catch (e) {
1625
- throwError("Unexpected error encountered during test registration.\n Test Object: ".concat(testObject, ".\n Error: ").concat(e, "."));
1677
+ throwError("Unexpected error encountered during test registration.\n Test Object: ".concat(JSON.stringify(testObject), ".\n Error: ").concat(e, "."));
1626
1678
  }
1627
1679
  }
1628
1680
 
@@ -1708,6 +1760,11 @@ function handleKeyTest(key, newTestObject) {
1708
1760
  // eslint-disable-next-line max-statements
1709
1761
  function registerPrevRunTest(testObject) {
1710
1762
  var prevRunTest = useTestAtCursor(testObject);
1763
+ if (shouldSkipBasedOnMode(testObject)) {
1764
+ moveForward();
1765
+ testObject.skip();
1766
+ return testObject;
1767
+ }
1711
1768
  if (isOmitted()) {
1712
1769
  prevRunTest.omit();
1713
1770
  moveForward();
@@ -1816,6 +1873,6 @@ function warn() {
1816
1873
  ctx.currentTest.warn();
1817
1874
  }
1818
1875
 
1819
- var VERSION = "4.1.3";
1876
+ var VERSION = "4.2.1";
1820
1877
 
1821
- export { VERSION, context, create, each, group, include, omitWhen, only, optional, skip, skipWhen, test, warn };
1878
+ export { VERSION, context, create, each, eager, group, include, omitWhen, only, optional, skip, skipWhen, test, warn };