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.
@@ -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,6 +8,90 @@ var genId = (function (n) { return function () {
10
8
  return "".concat(n++);
11
9
  }; })(0);
12
10
 
11
+ function isStringValue(v) {
12
+ return String(v) === v;
13
+ }
14
+
15
+ function bindNot(fn) {
16
+ return function () {
17
+ var args = [];
18
+ for (var _i = 0; _i < arguments.length; _i++) {
19
+ args[_i] = arguments[_i];
20
+ }
21
+ return !fn.apply(void 0, args);
22
+ };
23
+ }
24
+
25
+ function isUndefined(value) {
26
+ return value === undefined;
27
+ }
28
+
29
+ function shouldUseErrorAsMessage(message, error) {
30
+ // kind of cheating with this safe guard, but it does the job
31
+ return isUndefined(message) && isStringValue(error);
32
+ }
33
+
34
+ function asArray(possibleArg) {
35
+ return [].concat(possibleArg);
36
+ }
37
+
38
+ function lengthEquals(value, arg1) {
39
+ return value.length === Number(arg1);
40
+ }
41
+
42
+ function longerThan(value, arg1) {
43
+ return value.length > Number(arg1);
44
+ }
45
+
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;
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
+ }
81
+ }
82
+
83
+ // The module is named "isArrayValue" since it
84
+ // is conflicting with a nested npm dependency.
85
+ // We may need to revisit this in the future.
86
+ function isArray(value) {
87
+ return Boolean(Array.isArray(value));
88
+ }
89
+
90
+ function isNull(value) {
91
+ return value === null;
92
+ }
93
+ var isNotNull = bindNot(isNull);
94
+
13
95
  function isFunction(value) {
14
96
  return typeof value === 'function';
15
97
  }
@@ -27,120 +109,58 @@ function defaultTo(callback, defaultValue) {
27
109
  return (_a = optionalFunctionValue(callback)) !== null && _a !== void 0 ? _a : defaultValue;
28
110
  }
29
111
 
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);
112
+ function last(values) {
113
+ var valuesArray = asArray(values);
114
+ return valuesArray[valuesArray.length - 1];
40
115
  }
41
116
 
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);
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
120
+ function transform(array, cb) {
121
+ var res = [];
122
+ for (var _i = 0, array_1 = array; _i < array_1.length; _i++) {
123
+ var v = array_1[_i];
124
+ if (isArray(v)) {
125
+ res.push(transform(v, cb));
99
126
  }
100
- if (isFunction(onStateChange)) {
101
- onStateChange();
127
+ else {
128
+ var output = cb(v);
129
+ if (isNotNull(output)) {
130
+ res.push(output);
131
+ }
102
132
  }
103
133
  }
134
+ return res;
104
135
  }
105
-
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 = {}));
115
-
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
- };
136
+ function valueAtPath(array, path) {
137
+ return getCurrent(array, path)[last(path)];
133
138
  }
134
-
135
- function asArray(possibleArg) {
136
- return [].concat(possibleArg);
139
+ function setValueAtPath(array, path, value) {
140
+ var current = getCurrent(array, path);
141
+ current[last(path)] = value;
142
+ return array;
137
143
  }
138
-
139
- function last(values) {
140
- var valuesArray = asArray(values);
141
- return valuesArray[valuesArray.length - 1];
144
+ function flatten(values) {
145
+ return asArray(values).reduce(function (acc, value) {
146
+ if (isArray(value)) {
147
+ return acc.concat(flatten(value));
148
+ }
149
+ return asArray(acc).concat(value);
150
+ }, []);
151
+ }
152
+ function getCurrent(array, path) {
153
+ var current = array;
154
+ for (var _i = 0, _a = path.slice(0, -1); _i < _a.length; _i++) {
155
+ var p = _a[_i];
156
+ current[p] = defaultTo(current[p], []);
157
+ current = current[p];
158
+ }
159
+ return current;
142
160
  }
143
161
 
162
+ var assign = Object.assign;
163
+
144
164
  function createCursor() {
145
165
  var storage = {
146
166
  cursor: []
@@ -175,6 +195,22 @@ function createCursor() {
175
195
  };
176
196
  }
177
197
 
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 = {}));
207
+
208
+ var Modes;
209
+ (function (Modes) {
210
+ Modes[Modes["ALL"] = 0] = "ALL";
211
+ Modes[Modes["EAGER"] = 1] = "EAGER";
212
+ })(Modes || (Modes = {}));
213
+
178
214
  var context = createContext(function (ctxRef, parentContext) {
179
215
  return parentContext
180
216
  ? null
@@ -191,140 +227,11 @@ var context = createContext(function (ctxRef, parentContext) {
191
227
  prev: {}
192
228
  }
193
229
  },
230
+ mode: [Modes.ALL],
194
231
  testCursor: createCursor()
195
232
  }, ctxRef);
196
233
  });
197
234
 
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);
205
- };
206
- }
207
-
208
- // The module is named "isArrayValue" since it
209
- // is conflicting with a nested npm dependency.
210
- // We may need to revisit this in the future.
211
- function isArray(value) {
212
- return Boolean(Array.isArray(value));
213
- }
214
-
215
- function isNull(value) {
216
- return value === null;
217
- }
218
- var isNotNull = bindNot(isNull);
219
-
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
223
- function transform(array, cb) {
224
- var res = [];
225
- for (var _i = 0, array_1 = array; _i < array_1.length; _i++) {
226
- var v = array_1[_i];
227
- if (isArray(v)) {
228
- res.push(transform(v, cb));
229
- }
230
- else {
231
- var output = cb(v);
232
- if (isNotNull(output)) {
233
- res.push(output);
234
- }
235
- }
236
- }
237
- return res;
238
- }
239
- function valueAtPath(array, path) {
240
- return getCurrent(array, path)[last(path)];
241
- }
242
- function setValueAtPath(array, path, value) {
243
- var current = getCurrent(array, path);
244
- current[last(path)] = value;
245
- return array;
246
- }
247
- function flatten(values) {
248
- return asArray(values).reduce(function (acc, value) {
249
- if (isArray(value)) {
250
- return acc.concat(flatten(value));
251
- }
252
- return asArray(acc).concat(value);
253
- }, []);
254
- }
255
- function getCurrent(array, path) {
256
- var current = array;
257
- for (var _i = 0, _a = path.slice(0, -1); _i < _a.length; _i++) {
258
- var p = _a[_i];
259
- current[p] = defaultTo(current[p], []);
260
- current = current[p];
261
- }
262
- return current;
263
- }
264
-
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
- }
276
-
277
- function shouldUseErrorAsMessage(message, error) {
278
- // kind of cheating with this safe guard, but it does the job
279
- return isUndefined(message) && isStringValue(error);
280
- }
281
-
282
- function lengthEquals(value, arg1) {
283
- return value.length === Number(arg1);
284
- }
285
-
286
- function longerThan(value, arg1) {
287
- return value.length > Number(arg1);
288
- }
289
-
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
- }
327
-
328
235
  // STATE REF
329
236
  function useStateRef() {
330
237
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -529,16 +436,115 @@ var VestTest = /** @class */ (function () {
529
436
  VestTest.prototype.statusEquals = function (status) {
530
437
  return this.status === status;
531
438
  };
532
- return VestTest;
533
- }());
534
- var STATUS_UNTESTED = 'UNTESTED';
535
- var STATUS_SKIPPED = 'SKIPPED';
536
- var STATUS_FAILED = 'FAILED';
537
- var STATUS_WARNING = 'WARNING';
538
- var STATUS_PASSING = 'PASSING';
539
- var STATUS_PENDING = 'PENDING';
540
- var STATUS_CANCELED = 'CANCELED';
541
- var STATUS_OMITTED = 'OMITTED';
439
+ return VestTest;
440
+ }());
441
+ var STATUS_UNTESTED = 'UNTESTED';
442
+ var STATUS_SKIPPED = 'SKIPPED';
443
+ var STATUS_FAILED = 'FAILED';
444
+ var STATUS_WARNING = 'WARNING';
445
+ var STATUS_PASSING = 'PASSING';
446
+ var STATUS_PENDING = 'PENDING';
447
+ var STATUS_CANCELED = 'CANCELED';
448
+ var STATUS_OMITTED = 'OMITTED';
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
+ }
542
548
 
543
549
  function usePath() {
544
550
  var context$1 = context.useX();
@@ -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 produceBase() {
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(produceBase(), 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({}, produceBase(), {
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(produceBase()); };
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, produceBase),
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) {
@@ -1254,6 +1267,31 @@ function each(list, callback) {
1254
1267
  */
1255
1268
  var ERROR_HOOK_CALLED_OUTSIDE = 'hook called outside of a running suite.';
1256
1269
 
1270
+ /**
1271
+ * Conditionally skips running tests within the callback.
1272
+ *
1273
+ * @example
1274
+ *
1275
+ * skipWhen(res => res.hasErrors('username'), () => {
1276
+ * test('username', 'User already taken', async () => await doesUserExist(username)
1277
+ * });
1278
+ */
1279
+ function skipWhen(conditional, callback) {
1280
+ isolate({ type: IsolateTypes.SKIP_WHEN }, function () {
1281
+ context.run({
1282
+ skipped:
1283
+ // Checking for nested conditional. If we're in a nested skipWhen,
1284
+ // we should skip the test if the parent conditional is true.
1285
+ isExcludedIndividually() ||
1286
+ // Otherwise, we should skip the test if the conditional is true.
1287
+ optionalFunctionValue(conditional, optionalFunctionValue(produceBase))
1288
+ }, function () { return callback(); });
1289
+ });
1290
+ }
1291
+ function isExcludedIndividually() {
1292
+ return !!context.useX().skipped;
1293
+ }
1294
+
1257
1295
  /**
1258
1296
  * Adds a field or a list of fields into the inclusion list
1259
1297
  *
@@ -1280,9 +1318,6 @@ function skip(item) {
1280
1318
  skip.group = function (item) {
1281
1319
  return addTo(1 /* SKIP */, 'groups', item);
1282
1320
  };
1283
- function isExcludedIndividually() {
1284
- return !!context.useX().skipped;
1285
- }
1286
1321
  //Checks whether a certain test profile excluded by any of the exclusion groups.
1287
1322
  // eslint-disable-next-line complexity, max-statements, max-lines-per-function
1288
1323
  function isExcluded(testObject) {
@@ -1449,11 +1484,50 @@ function include(fieldName) {
1449
1484
  if (isStringValue(condition)) {
1450
1485
  return Boolean(exclusion.tests[condition]);
1451
1486
  }
1452
- return optionalFunctionValue(condition, optionalFunctionValue(produceDraft));
1487
+ return optionalFunctionValue(condition, optionalFunctionValue(produceBase));
1453
1488
  };
1454
1489
  }
1455
1490
  }
1456
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
+
1457
1531
  /**
1458
1532
  * Conditionally omits tests from the suite.
1459
1533
  *
@@ -1466,7 +1540,8 @@ function include(fieldName) {
1466
1540
  function omitWhen(conditional, callback) {
1467
1541
  isolate({ type: IsolateTypes.OMIT_WHEN }, function () {
1468
1542
  context.run({
1469
- omitted: optionalFunctionValue(conditional, optionalFunctionValue(produceDraft))
1543
+ omitted: isOmitted() ||
1544
+ optionalFunctionValue(conditional, optionalFunctionValue(produceBase))
1470
1545
  }, function () { return callback(); });
1471
1546
  });
1472
1547
  }
@@ -1504,28 +1579,6 @@ function optional(optionals) {
1504
1579
  });
1505
1580
  }
1506
1581
 
1507
- /**
1508
- * Conditionally skips running tests within the callback.
1509
- *
1510
- * @example
1511
- *
1512
- * skipWhen(res => res.hasErrors('username'), () => {
1513
- * test('username', 'User already taken', async () => await doesUserExist(username)
1514
- * });
1515
- */
1516
- function skipWhen(conditional, callback) {
1517
- isolate({ type: IsolateTypes.SKIP_WHEN }, function () {
1518
- context.run({
1519
- skipped:
1520
- // Checking for nested conditional. If we're in a nested skipWhen,
1521
- // we should skip the test if the parent conditional is true.
1522
- isExcludedIndividually() ||
1523
- // Otherwise, we should skip the test if the conditional is true.
1524
- optionalFunctionValue(conditional, optionalFunctionValue(produceDraft))
1525
- }, function () { return callback(); });
1526
- });
1527
- }
1528
-
1529
1582
  var isNotString = bindNot(isStringValue);
1530
1583
 
1531
1584
  function isPromise(value) {
@@ -1621,7 +1674,7 @@ function registerTest(testObject) {
1621
1674
  }
1622
1675
  }
1623
1676
  catch (e) {
1624
- 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, "."));
1625
1678
  }
1626
1679
  }
1627
1680
 
@@ -1707,6 +1760,11 @@ function handleKeyTest(key, newTestObject) {
1707
1760
  // eslint-disable-next-line max-statements
1708
1761
  function registerPrevRunTest(testObject) {
1709
1762
  var prevRunTest = useTestAtCursor(testObject);
1763
+ if (shouldSkipBasedOnMode(testObject)) {
1764
+ moveForward();
1765
+ testObject.skip();
1766
+ return testObject;
1767
+ }
1710
1768
  if (isOmitted()) {
1711
1769
  prevRunTest.omit();
1712
1770
  moveForward();
@@ -1815,6 +1873,6 @@ function warn() {
1815
1873
  ctx.currentTest.warn();
1816
1874
  }
1817
1875
 
1818
- var VERSION = "4.1.1";
1876
+ var VERSION = "4.2.1-dev-ee64be";
1819
1877
 
1820
- 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 };