vest 5.0.0-dev-781e21 → 5.0.0-dev-ec989a

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.
Files changed (71) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +2 -57
  3. package/dist/cjs/classnames.development.js +37 -17
  4. package/dist/cjs/classnames.production.js +1 -1
  5. package/dist/cjs/enforce/compose.development.js +1 -54
  6. package/dist/cjs/enforce/compose.production.js +1 -1
  7. package/dist/cjs/enforce/compounds.development.js +18 -84
  8. package/dist/cjs/enforce/compounds.production.js +1 -1
  9. package/dist/cjs/enforce/schema.development.js +18 -84
  10. package/dist/cjs/enforce/schema.production.js +1 -1
  11. package/dist/cjs/parser.development.js +30 -11
  12. package/dist/cjs/parser.production.js +1 -1
  13. package/dist/cjs/promisify.development.js +21 -9
  14. package/dist/cjs/promisify.production.js +1 -1
  15. package/dist/cjs/vest.development.js +1324 -1294
  16. package/dist/cjs/vest.production.js +1 -1
  17. package/dist/es/classnames.development.js +39 -19
  18. package/dist/es/classnames.production.js +1 -1
  19. package/dist/es/enforce/compose.development.js +1 -58
  20. package/dist/es/enforce/compose.production.js +1 -1
  21. package/dist/es/enforce/compounds.development.js +2 -90
  22. package/dist/es/enforce/compounds.production.js +1 -1
  23. package/dist/es/enforce/schema.development.js +2 -88
  24. package/dist/es/enforce/schema.production.js +1 -1
  25. package/dist/es/parser.development.js +31 -10
  26. package/dist/es/parser.production.js +1 -1
  27. package/dist/es/promisify.development.js +22 -10
  28. package/dist/es/promisify.production.js +1 -1
  29. package/dist/es/vest.development.js +1321 -1286
  30. package/dist/es/vest.production.js +1 -1
  31. package/dist/umd/classnames.development.js +40 -20
  32. package/dist/umd/classnames.production.js +1 -1
  33. package/dist/umd/enforce/compose.development.js +6 -60
  34. package/dist/umd/enforce/compose.production.js +1 -1
  35. package/dist/umd/enforce/compounds.development.js +29 -94
  36. package/dist/umd/enforce/compounds.production.js +1 -1
  37. package/dist/umd/enforce/schema.development.js +29 -94
  38. package/dist/umd/enforce/schema.production.js +1 -1
  39. package/dist/umd/parser.development.js +33 -14
  40. package/dist/umd/parser.production.js +1 -1
  41. package/dist/umd/promisify.development.js +24 -12
  42. package/dist/umd/promisify.production.js +1 -1
  43. package/dist/umd/vest.development.js +1327 -1298
  44. package/dist/umd/vest.production.js +1 -1
  45. package/package.json +144 -147
  46. package/testUtils/TVestMock.ts +7 -0
  47. package/testUtils/__tests__/partition.test.ts +4 -4
  48. package/testUtils/asVestTest.ts +9 -0
  49. package/testUtils/mockThrowError.ts +4 -2
  50. package/testUtils/suiteDummy.ts +4 -1
  51. package/testUtils/testDummy.ts +12 -10
  52. package/testUtils/testPromise.ts +3 -0
  53. package/types/classnames.d.ts +141 -12
  54. package/types/classnames.d.ts.map +1 -0
  55. package/types/enforce/compose.d.ts +2 -126
  56. package/types/enforce/compose.d.ts.map +1 -0
  57. package/types/enforce/compounds.d.ts +2 -136
  58. package/types/enforce/compounds.d.ts.map +1 -0
  59. package/types/enforce/schema.d.ts +2 -144
  60. package/types/enforce/schema.d.ts.map +1 -0
  61. package/types/parser.d.ts +147 -18
  62. package/types/parser.d.ts.map +1 -0
  63. package/types/promisify.d.ts +139 -43
  64. package/types/promisify.d.ts.map +1 -0
  65. package/types/vest.d.ts +257 -242
  66. package/types/vest.d.ts.map +1 -0
  67. package/CHANGELOG.md +0 -87
  68. package/testUtils/expandStateRef.ts +0 -8
  69. package/testUtils/runCreateRef.ts +0 -10
  70. package/testUtils/testObjects.ts +0 -6
  71. package/tsconfig.json +0 -8
@@ -1,440 +1,131 @@
1
1
  export { enforce } from 'n4s';
2
- import { isUndefined, isStringValue, assign, cache as cache$2, nestedArray, asArray, seq, optionalFunctionValue, isFunction, isNullish, deferThrow, invariant, bindNot, either, isArray, isEmpty, isNotEmpty, isPositive, numberEquals, hasOwnProperty, callEach, bus, defaultTo, isPromise, isNull } from 'vest-utils';
2
+ import { VestRuntime, Isolate, Reconciler, Walker } from 'vest-runtime';
3
+ import { isArray, isStringValue, asArray, cache, tinyState, seq, bindNot, defaultTo, assign, isPositive, optionalFunctionValue, hasOwnProperty, isNullish, deferThrow, text, StateMachine, isUndefined, isPromise, invariant, either, isEmpty, callEach, isNull, isFunction, numberEquals } from 'vest-utils';
3
4
  import { createCascade } from 'context';
4
5
 
5
- function shouldUseErrorAsMessage(message, error) {
6
- // kind of cheating with this safe guard, but it does the job
7
- return isUndefined(message) && isStringValue(error);
8
- }
9
-
10
- var IsolateTypes;
11
- (function (IsolateTypes) {
12
- IsolateTypes[IsolateTypes["DEFAULT"] = 0] = "DEFAULT";
13
- IsolateTypes[IsolateTypes["SUITE"] = 1] = "SUITE";
14
- IsolateTypes[IsolateTypes["EACH"] = 2] = "EACH";
15
- IsolateTypes[IsolateTypes["SKIP_WHEN"] = 3] = "SKIP_WHEN";
16
- IsolateTypes[IsolateTypes["OMIT_WHEN"] = 4] = "OMIT_WHEN";
17
- IsolateTypes[IsolateTypes["GROUP"] = 5] = "GROUP";
18
- })(IsolateTypes || (IsolateTypes = {}));
19
-
20
- var Modes;
21
- (function (Modes) {
22
- Modes[Modes["ALL"] = 0] = "ALL";
23
- Modes[Modes["EAGER"] = 1] = "EAGER";
24
- })(Modes || (Modes = {}));
6
+ var OptionalFieldTypes;
7
+ (function (OptionalFieldTypes) {
8
+ OptionalFieldTypes[OptionalFieldTypes["CUSTOM_LOGIC"] = 0] = "CUSTOM_LOGIC";
9
+ OptionalFieldTypes[OptionalFieldTypes["AUTO"] = 1] = "AUTO";
10
+ })(OptionalFieldTypes || (OptionalFieldTypes = {}));
25
11
 
26
- function createIsolateCursor() {
27
- var cursor = {
28
- value: 0
29
- };
30
- return {
31
- current: current,
32
- next: next
33
- };
34
- /**
35
- * @returns {number} The current value of the cursor
36
- */
37
- function current() {
38
- return cursor.value;
12
+ // @vx-allow use-use
13
+ function optional(optionals) {
14
+ const suiteRoot = VestRuntime.useAvailableRoot();
15
+ // There are two types of optional field declarations:
16
+ // 1 AUTO: Vest will automatically determine whether the field should be omitted
17
+ // Based on the current run. Vest will ommit "auto" added fields without any
18
+ // configuration if their tests did not run at all in the suite.
19
+ //
20
+ // 2 Custom logic: Vest will determine whether they should fail based on the custom
21
+ // logic supplied by the user.
22
+ // AUTO case (field name)
23
+ if (isArray(optionals) || isStringValue(optionals)) {
24
+ asArray(optionals).forEach(optionalField => {
25
+ suiteRoot.setOptionalField(optionalField, () => ({
26
+ type: OptionalFieldTypes.AUTO,
27
+ applied: false,
28
+ rule: null,
29
+ }));
30
+ });
39
31
  }
40
- /**
41
- * Moves the isolate cursor forward by 1
42
- */
43
- function next() {
44
- cursor.value++;
32
+ else {
33
+ // CUSTOM_LOGIC case (function or boolean)
34
+ for (const field in optionals) {
35
+ const value = optionals[field];
36
+ suiteRoot.setOptionalField(field, () => ({
37
+ type: OptionalFieldTypes.CUSTOM_LOGIC,
38
+ rule: value,
39
+ applied: value === true,
40
+ }));
41
+ }
45
42
  }
46
43
  }
47
-
48
- function generateIsolate(type, path) {
49
- if (path === void 0) { path = []; }
50
- return {
51
- cursor: createIsolateCursor(),
52
- keys: {
53
- current: {},
54
- prev: {}
55
- },
56
- path: path,
57
- type: type
58
- };
59
- }
60
-
61
- var context = createCascade(function (ctxRef, parentContext) {
62
- return parentContext
63
- ? null
64
- : assign({
65
- exclusion: {
66
- tests: {},
67
- groups: {}
68
- },
69
- inclusion: {},
70
- isolate: generateIsolate(IsolateTypes.DEFAULT),
71
- mode: [Modes.ALL]
72
- }, ctxRef);
73
- });
74
-
75
- // STATE REF
76
- function useStateRef() {
77
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
78
- return context.useX().stateRef; // I should revisit this
79
- }
80
- // STATE KEYS
81
- function useSuiteId() {
82
- return useStateRef().suiteId()[0];
83
- }
84
- function useSuiteName() {
85
- return useStateRef().suiteName()[0];
86
- }
87
- function useTestCallbacks() {
88
- return useStateRef().testCallbacks();
89
- }
90
- // OPTIONAL FIELDS
91
- function useOptionalFields() {
92
- return useStateRef().optionalFields();
93
- }
94
- function useSetOptionalField(fieldName, setter) {
95
- var _a = useOptionalFields(), setOptionalFields = _a[1];
96
- setOptionalFields(function (prev) {
97
- var _a;
98
- return assign(prev, (_a = {},
99
- _a[fieldName] = assign({}, prev[fieldName], setter(prev[fieldName])),
100
- _a));
101
- });
102
- }
103
- function useOptionalField(fieldName) {
104
- var _a;
105
- var optionalFields = useOptionalFields()[0];
106
- return (_a = optionalFields[fieldName]) !== null && _a !== void 0 ? _a : {};
107
- }
108
- function useTestObjects() {
109
- return useStateRef().testObjects();
110
- }
111
- // STATE ACTIONS
112
- function useRefreshTestObjects() {
113
- useSetTests(function (tests) { return tests; });
114
- }
115
- function useSetTests(handler) {
116
- var _a = useTestObjects(), testObjects = _a[1];
117
- testObjects(function (_a) {
118
- var current = _a.current, prev = _a.prev;
119
- return ({
120
- prev: prev,
121
- current: asArray(handler(current))
122
- });
123
- });
124
- }
125
- // Derived state
126
- function useAllIncomplete() {
127
- return useTestsFlat().filter(function (test) { return test.isPending(); });
128
- }
129
- var flatCache = cache$2();
130
- function useTestsFlat() {
131
- var current = useTestObjects()[0].current;
132
- return flatCache([current], function () { return nestedArray.flatten(current); });
133
- }
134
- function useEachTestObject(handler) {
135
- var testObjects = useTestsFlat();
136
- testObjects.forEach(handler);
44
+ function useIsOptionalFiedApplied(fieldName) {
45
+ var _a, _b, _c;
46
+ if (!fieldName) {
47
+ return false;
48
+ }
49
+ return ((_c = (_b = (_a = VestRuntime.useAvailableRoot()) === null || _a === void 0 ? void 0 : _a.getOptionalField(fieldName)) === null || _b === void 0 ? void 0 : _b.applied) !== null && _c !== void 0 ? _c : false);
137
50
  }
138
51
 
139
- var TestSeverity;
140
- (function (TestSeverity) {
141
- TestSeverity["Error"] = "error";
142
- TestSeverity["Warning"] = "warning";
143
- })(TestSeverity || (TestSeverity = {}));
144
- var VestTest = /** @class */ (function () {
145
- function VestTest(fieldName, testFn, _a) {
146
- var _b = _a === void 0 ? {} : _a, message = _b.message, groupName = _b.groupName, key = _b.key;
147
- this.key = null;
148
- this.id = seq();
149
- this.severity = TestSeverity.Error;
150
- this.status = STATUS_UNTESTED;
151
- this.fieldName = fieldName;
152
- this.testFn = testFn;
153
- if (groupName) {
154
- this.groupName = groupName;
155
- }
156
- if (message) {
157
- this.message = message;
158
- }
159
- if (key) {
160
- this.key = key;
161
- }
162
- }
163
- VestTest.prototype.run = function () {
164
- var result;
165
- try {
166
- result = this.testFn();
167
- }
168
- catch (error) {
169
- if (shouldUseErrorAsMessage(this.message, error)) {
170
- this.message = error;
171
- }
172
- result = false;
173
- }
174
- if (result === false) {
175
- this.fail();
176
- }
177
- return result;
178
- };
179
- VestTest.prototype.setStatus = function (status) {
180
- if (this.isFinalStatus() && status !== STATUS_OMITTED) {
181
- return;
182
- }
183
- this.status = status;
184
- };
185
- VestTest.prototype.warns = function () {
186
- return this.severity === TestSeverity.Warning;
187
- };
188
- VestTest.prototype.setPending = function () {
189
- this.setStatus(STATUS_PENDING);
190
- };
191
- VestTest.prototype.fail = function () {
192
- this.setStatus(this.warns() ? STATUS_WARNING : STATUS_FAILED);
193
- };
194
- VestTest.prototype.done = function () {
195
- if (this.isFinalStatus()) {
196
- return;
197
- }
198
- this.setStatus(STATUS_PASSING);
199
- };
200
- VestTest.prototype.warn = function () {
201
- this.severity = TestSeverity.Warning;
202
- };
203
- VestTest.prototype.isFinalStatus = function () {
204
- return this.hasFailures() || this.isCanceled() || this.isPassing();
205
- };
206
- VestTest.prototype.skip = function (force) {
207
- if (this.isPending() && !force) {
208
- // Without this condition, the test will be marked as skipped even if it is pending.
209
- // This means that it will not be counted in "allIncomplete" and its done callbacks
210
- // will not be called, or will be called prematurely.
211
- // What this mostly say is that when we have a pending test for one field, and we then
212
- // start typing in a different field - the pending test will be canceled, which
213
- // is usually an unwanted behavior.
214
- // The only scenario in which we DO want to cancel the async test regardless
215
- // is when we specifically skip a test with `skipWhen`, which is handled by the
216
- // "force" boolean flag.
217
- // I am not a fan of this flag, but it gets the job done.
218
- return;
219
- }
220
- this.setStatus(STATUS_SKIPPED);
221
- };
222
- VestTest.prototype.cancel = function () {
223
- this.setStatus(STATUS_CANCELED);
224
- useRefreshTestObjects();
225
- };
226
- VestTest.prototype.reset = function () {
227
- this.status = STATUS_UNTESTED;
228
- useRefreshTestObjects();
229
- };
230
- VestTest.prototype.omit = function () {
231
- this.setStatus(STATUS_OMITTED);
232
- };
233
- VestTest.prototype.valueOf = function () {
234
- return !this.isFailing();
235
- };
236
- VestTest.prototype.isPending = function () {
237
- return this.statusEquals(STATUS_PENDING);
238
- };
239
- VestTest.prototype.isOmitted = function () {
240
- return this.statusEquals(STATUS_OMITTED);
241
- };
242
- VestTest.prototype.isUntested = function () {
243
- return this.statusEquals(STATUS_UNTESTED);
244
- };
245
- VestTest.prototype.isFailing = function () {
246
- return this.statusEquals(STATUS_FAILED);
247
- };
248
- VestTest.prototype.isCanceled = function () {
249
- return this.statusEquals(STATUS_CANCELED);
250
- };
251
- VestTest.prototype.isSkipped = function () {
252
- return this.statusEquals(STATUS_SKIPPED);
253
- };
254
- VestTest.prototype.isPassing = function () {
255
- return this.statusEquals(STATUS_PASSING);
256
- };
257
- VestTest.prototype.isWarning = function () {
258
- return this.statusEquals(STATUS_WARNING);
259
- };
260
- VestTest.prototype.hasFailures = function () {
261
- return this.isFailing() || this.isWarning();
262
- };
263
- VestTest.prototype.isNonActionable = function () {
264
- return this.isSkipped() || this.isOmitted() || this.isCanceled();
265
- };
266
- VestTest.prototype.isTested = function () {
267
- return this.hasFailures() || this.isPassing();
268
- };
269
- VestTest.prototype.awaitsResolution = function () {
270
- // Is the test in a state where it can still be run, or complete running
271
- // and its final status is indeterminate?
272
- return this.isSkipped() || this.isUntested() || this.isPending();
273
- };
274
- VestTest.prototype.statusEquals = function (status) {
275
- return this.status === status;
276
- };
277
- return VestTest;
278
- }());
279
- var STATUS_UNTESTED = 'UNTESTED';
280
- var STATUS_SKIPPED = 'SKIPPED';
281
- var STATUS_FAILED = 'FAILED';
282
- var STATUS_WARNING = 'WARNING';
283
- var STATUS_PASSING = 'PASSING';
284
- var STATUS_PENDING = 'PENDING';
285
- var STATUS_CANCELED = 'CANCELED';
286
- var STATUS_OMITTED = 'OMITTED';
52
+ var Events;
53
+ (function (Events) {
54
+ Events["TEST_RUN_STARTED"] = "test_run_started";
55
+ Events["TEST_COMPLETED"] = "test_completed";
56
+ Events["ALL_RUNNING_TESTS_FINISHED"] = "all_running_tests_finished";
57
+ Events["REMOVE_FIELD"] = "remove_field";
58
+ Events["RESET_FIELD"] = "reset_field";
59
+ Events["RESET_SUITE"] = "reset_suite";
60
+ Events["SUITE_RUN_STARTED"] = "suite_run_started";
61
+ Events["SUITE_CALLBACK_RUN_FINISHED"] = "SUITE_CALLBACK_RUN_FINISHED";
62
+ Events["DONE_TEST_OMISSION_PASS"] = "DONE_TEST_OMISSION_PASS";
63
+ })(Events || (Events = {}));
287
64
 
288
- // eslint-disable-next-line max-lines-per-function
289
- function createState(onStateChange) {
290
- var state = {
291
- references: []
292
- };
293
- var registrations = [];
294
- return {
295
- registerStateKey: registerStateKey,
296
- reset: reset
297
- };
298
- /**
299
- * Registers a new key in the state, takes the initial value (may be a function that returns the initial value), returns a function.
300
- *
301
- * @example
302
- *
303
- * const useColor = state.registerStateKey("blue");
304
- *
305
- * let [color, setColor] = useColor(); // -> ["blue", Function]
306
- *
307
- * setColor("green");
308
- *
309
- * useColor()[0]; -> "green"
310
- */
311
- function registerStateKey(initialState, onUpdate) {
312
- var key = registrations.length;
313
- registrations.push([initialState, onUpdate]);
314
- return initKey(key, initialState);
315
- }
316
- function reset() {
317
- var prev = current();
318
- state.references = [];
319
- registrations.forEach(function (_a, index) {
320
- var initialValue = _a[0];
321
- return initKey(index, initialValue, prev[index]);
65
+ class IsolateSuite extends Isolate {
66
+ constructor() {
67
+ super(...arguments);
68
+ this.optional = {};
69
+ }
70
+ setOptionalField(fieldName, setter) {
71
+ const current = this.optional;
72
+ const currentField = current[fieldName];
73
+ Object.assign(current, {
74
+ [fieldName]: Object.assign({}, currentField, setter(currentField)),
322
75
  });
323
76
  }
324
- function initKey(key, initialState, prevState) {
325
- current().push();
326
- set(key, optionalFunctionValue(initialState, prevState));
327
- return function useStateKey() {
328
- return [
329
- current()[key],
330
- function (nextState) {
331
- return set(key, optionalFunctionValue(nextState, current()[key]));
332
- },
333
- ];
334
- };
335
- }
336
- function current() {
337
- return state.references;
77
+ getOptionalField(fieldName) {
78
+ var _a;
79
+ return (_a = this.optional[fieldName]) !== null && _a !== void 0 ? _a : {};
338
80
  }
339
- function set(index, value) {
340
- var prevValue = state.references[index];
341
- state.references[index] = value;
342
- var _a = registrations[index], onUpdate = _a[1];
343
- if (isFunction(onUpdate)) {
344
- onUpdate(value, prevValue);
345
- }
346
- if (isFunction(onStateChange)) {
347
- onStateChange();
348
- }
81
+ getOptionalFields() {
82
+ return this.optional;
349
83
  }
350
84
  }
351
85
 
352
- function createStateRef(state, _a) {
353
- var suiteId = _a.suiteId, suiteName = _a.suiteName;
354
- return {
355
- optionalFields: state.registerStateKey(function () { return ({}); }),
356
- suiteId: state.registerStateKey(suiteId),
357
- suiteName: state.registerStateKey(suiteName),
358
- testCallbacks: state.registerStateKey(function () { return ({
359
- fieldCallbacks: {},
360
- doneCallbacks: []
361
- }); }),
362
- testObjects: state.registerStateKey(function (prev) {
363
- return {
364
- prev: prev ? prev.current : [],
365
- current: []
366
- };
367
- })
86
+ const suiteResultCache = cache();
87
+ function useCreateVestState({ suiteName, } = {}) {
88
+ const stateRef = {
89
+ doneCallbacks: tinyState.createTinyState(() => []),
90
+ fieldCallbacks: tinyState.createTinyState(() => ({})),
91
+ suiteId: seq(),
92
+ suiteName,
93
+ suiteResultCache,
368
94
  };
95
+ return VestRuntime.createRef(stateRef);
369
96
  }
370
-
371
- /**
372
- * @returns {Isolate} The current isolate layer
373
- */
374
- function useIsolate() {
375
- return context.useX().isolate;
97
+ function useX() {
98
+ return VestRuntime.useXAppData();
376
99
  }
377
- /**
378
- * @returns {number[]} The current cursor path of the isolate tree
379
- */
380
- function useCurrentPath() {
381
- var isolate = useIsolate();
382
- return isolate.path.concat(isolate.cursor.current());
100
+ function useDoneCallbacks() {
101
+ return useX().doneCallbacks();
383
102
  }
384
- /**
385
- * @returns {IsolateCursor} The cursor object for the current isolate
386
- */
387
- function useCursor() {
388
- return useIsolate().cursor;
103
+ function useFieldCallbacks() {
104
+ return useX().fieldCallbacks();
389
105
  }
390
-
391
- function usePrevKeys() {
392
- var prev = useTestObjects()[0].prev;
393
- return asArray(nestedArray.getCurrent(prev, useCurrentPath())).reduce(function (prevKeys, testObject) {
394
- if (!(testObject instanceof VestTest)) {
395
- return prevKeys;
396
- }
397
- if (isNullish(testObject.key)) {
398
- return prevKeys;
399
- }
400
- prevKeys[testObject.key] = testObject;
401
- return prevKeys;
402
- }, {});
106
+ function useSuiteName() {
107
+ return useX().suiteName;
403
108
  }
404
- function usePrevTestByKey(key) {
405
- var prev = useIsolate().keys.prev;
406
- return prev[key];
109
+ function useSuiteId() {
110
+ return useX().suiteId;
407
111
  }
408
- function useRetainTestKey(key, testObject) {
409
- var current = useIsolate().keys.current;
410
- if (isNullish(current[key])) {
411
- current[key] = testObject;
412
- }
413
- else {
414
- deferThrow("Encountered the same test key \"".concat(key, "\" twice. This may lead to tests overriding each other's results, or to tests being unexpectedly omitted."));
415
- }
112
+ function useSuiteResultCache(action) {
113
+ const suiteResultCache = useX().suiteResultCache;
114
+ return suiteResultCache([useSuiteId()], action);
416
115
  }
417
-
418
- function isolate(_a, callback) {
419
- var _b = _a.type, type = _b === void 0 ? IsolateTypes.DEFAULT : _b;
420
- invariant(isFunction(callback));
421
- // Generate a new Isolate layer, with its own cursor
422
- var isolate = generateIsolate(type, useCurrentPath());
423
- var output = context.run({ isolate: isolate }, function () {
424
- isolate.keys.prev = usePrevKeys();
425
- useSetTests(function (tests) { return nestedArray.setValueAtPath(tests, isolate.path, []); });
426
- var res = callback();
427
- return res;
428
- });
429
- // Move the parent cursor forward once we're done
430
- useCursor().next();
431
- return output;
116
+ function useExpireSuiteResultCache() {
117
+ const suiteResultCache = useX().suiteResultCache;
118
+ suiteResultCache.invalidate([useSuiteId()]);
432
119
  }
433
- /**
434
- * @returns {boolean} Whether or not the current isolate allows tests to be reordered
435
- */
436
- function shouldAllowReorder() {
437
- return useIsolate().type === IsolateTypes.EACH;
120
+ function useResetCallbacks() {
121
+ const [, , resetDoneCallbacks] = useDoneCallbacks();
122
+ const [, , resetFieldCallbacks] = useFieldCallbacks();
123
+ resetDoneCallbacks();
124
+ resetFieldCallbacks();
125
+ }
126
+ function useResetSuite() {
127
+ useResetCallbacks();
128
+ VestRuntime.reset();
438
129
  }
439
130
 
440
131
  var Severity;
@@ -452,195 +143,159 @@ function countKeyBySeverity(severity) {
452
143
  ? SeverityCount.ERROR_COUNT
453
144
  : SeverityCount.WARN_COUNT;
454
145
  }
146
+ var TestSeverity;
147
+ (function (TestSeverity) {
148
+ TestSeverity["Error"] = "error";
149
+ TestSeverity["Warning"] = "warning";
150
+ })(TestSeverity || (TestSeverity = {}));
455
151
 
456
- function nonMatchingFieldName(testObject, fieldName) {
457
- return !!fieldName && !matchingFieldName(testObject, fieldName);
152
+ var ErrorStrings;
153
+ (function (ErrorStrings) {
154
+ ErrorStrings["HOOK_CALLED_OUTSIDE"] = "hook called outside of a running suite.";
155
+ ErrorStrings["EXPECTED_VEST_TEST"] = "Expected value to be an instance of IsolateTest";
156
+ ErrorStrings["FIELD_NAME_REQUIRED"] = "Field name must be passed";
157
+ ErrorStrings["SUITE_MUST_BE_INITIALIZED_WITH_FUNCTION"] = "Suite must be initialized with a function";
158
+ ErrorStrings["PROMISIFY_REQUIRE_FUNCTION"] = "Vest.Promisify must be called with a function";
159
+ ErrorStrings["PARSER_EXPECT_RESULT_OBJECT"] = "Vest parser: expected argument at position 0 to be Vest's result object.";
160
+ ErrorStrings["WARN_MUST_BE_CALLED_FROM_TEST"] = "Warn must be called from within the body of a test function";
161
+ ErrorStrings["EACH_CALLBACK_MUST_BE_A_FUNCTION"] = "Each must be called with a function";
162
+ ErrorStrings["INVALID_PARAM_PASSED_TO_FUNCTION"] = "Incompatible params passed to {fn_name} function. \"{param}\" must be of type {expected}";
163
+ ErrorStrings["TESTS_CALLED_IN_DIFFERENT_ORDER"] = "Vest Critical Error: Tests called in different order than previous run.\n expected: {fieldName}\n received: {prevName}\n This can happen on one of two reasons:\n 1. You're using if/else statements to conditionally select tests. Instead, use \"skipWhen\".\n 2. You are iterating over a list of tests, and their order changed. Use \"each\" and a custom key prop so that Vest retains their state.";
164
+ ErrorStrings["UNEXPECTED_TEST_REGISTRATION_ERROR"] = "Unexpected error encountered during test registration.\n Please report this issue to Vest's Github repository.\n Test Object: {testObject}.\n Error: {error}.";
165
+ ErrorStrings["UNEXPECTED_TEST_RUN_ERROR"] = "Unexpected error encountered during test run. Please report this issue to Vest's Github repository.\n Test Object: {testObject}.";
166
+ })(ErrorStrings || (ErrorStrings = {}));
167
+
168
+ function nonMatchingFieldName(WithFieldName, fieldName) {
169
+ return !!fieldName && !matchingFieldName(WithFieldName, fieldName);
458
170
  }
459
- function matchingFieldName(testObject, fieldName) {
460
- return !!(fieldName && testObject.fieldName === fieldName);
171
+ function matchingFieldName(WithFieldName, fieldName) {
172
+ return !!(fieldName && WithFieldName.fieldName === fieldName);
461
173
  }
462
174
 
463
- var nonMatchingGroupName = bindNot(matchingGroupName);
464
- function matchingGroupName(testObject, groupName) {
465
- return testObject.groupName === groupName;
175
+ function isSameProfileTest(testObject1, testObject2) {
176
+ return (matchingFieldName(testObject1, testObject2.fieldName) &&
177
+ testObject1.groupName === testObject2.groupName &&
178
+ testObject1.key === testObject2.key);
466
179
  }
467
180
 
468
- /**
469
- * Checks that a given test object matches the currently specified severity level
470
- */
471
- function nonMatchingSeverityProfile(severity, testObject) {
472
- return either(severity === Severity.WARNINGS, testObject.warns());
181
+ function cancelOverriddenPendingTest(prevRunTestObject, currentRunTestObject) {
182
+ if (currentRunTestObject !== prevRunTestObject &&
183
+ isSameProfileTest(prevRunTestObject, currentRunTestObject) &&
184
+ prevRunTestObject.isPending()) {
185
+ prevRunTestObject.cancel();
186
+ }
473
187
  }
474
188
 
475
- /**
476
- * The difference between this file and hasFailures is that hasFailures uses the static
477
- * summary object, while this one uses the actual validation state
478
- */
479
- function hasErrorsByTestObjects(fieldName) {
480
- return hasFailuresByTestObjects(Severity.ERRORS, fieldName);
481
- }
482
- function hasFailuresByTestObjects(severityKey, fieldName) {
483
- var testObjects = useTestsFlat();
484
- return testObjects.some(function (testObject) {
485
- return hasFailuresByTestObject(testObject, severityKey, fieldName);
486
- });
487
- }
488
- function hasGroupFailuresByTestObjects(severityKey, groupName, fieldName) {
489
- var testObjects = useTestsFlat();
490
- return testObjects.some(function (testObject) {
491
- if (nonMatchingGroupName(testObject, groupName)) {
492
- return false;
493
- }
494
- return hasFailuresByTestObject(testObject, severityKey, fieldName);
495
- });
496
- }
497
- /**
498
- * Determines whether a certain test profile has failures.
499
- */
500
- function hasFailuresByTestObject(testObject, severityKey, fieldName) {
501
- if (!testObject.hasFailures()) {
502
- return false;
503
- }
504
- if (nonMatchingFieldName(testObject, fieldName)) {
505
- return false;
189
+ var _a, _b;
190
+ class SummaryBase {
191
+ constructor() {
192
+ this.errorCount = 0;
193
+ this.warnCount = 0;
194
+ this.testCount = 0;
506
195
  }
507
- if (nonMatchingSeverityProfile(severityKey, testObject)) {
508
- return false;
196
+ }
197
+ class SuiteSummary extends SummaryBase {
198
+ constructor() {
199
+ super(...arguments);
200
+ this[_a] = [];
201
+ this[_b] = [];
202
+ this.groups = {};
203
+ this.tests = {};
204
+ this.valid = false;
509
205
  }
510
- return true;
511
206
  }
207
+ _a = Severity.ERRORS, _b = Severity.WARNINGS;
512
208
 
513
- /**
514
- * Marks a field as optional, either just by name, or by a given condition.
515
- *
516
- * @example
517
- *
518
- * optional('field_name');
519
- *
520
- * optional({
521
- * username: () => allowUsernameEmpty,
522
- * });
523
- */
524
- function optional(optionals) {
525
- // There are two types of optional field declarations:
526
- // 1. Delayed: A string, which is the name of the field to be optional.
527
- // We will only determine whether to omit the test after the suite is done running
528
- //
529
- // 2. Immediate: Either a boolean or a function, which is used to determine
530
- // if the field should be optional.
531
- // Delayed case (field name)
532
- if (isArray(optionals) || isStringValue(optionals)) {
533
- asArray(optionals).forEach(function (optionalField) {
534
- useSetOptionalField(optionalField, function () { return ({
535
- type: OptionalFieldTypes.Delayed,
536
- applied: false,
537
- rule: null
538
- }); });
539
- });
209
+ class SummaryFailure {
210
+ constructor(fieldName, message, groupName) {
211
+ this.fieldName = fieldName;
212
+ this.message = message;
213
+ this.groupName = groupName;
540
214
  }
541
- else {
542
- var _loop_1 = function (field) {
543
- var value = optionals[field];
544
- useSetOptionalField(field, function () { return ({
545
- type: OptionalFieldTypes.Immediate,
546
- rule: value,
547
- applied: optionalFunctionValue(value)
548
- }); });
549
- };
550
- // Immediately case (function or boolean)
551
- for (var field in optionals) {
552
- _loop_1(field);
553
- }
215
+ static fromTestObject(testObject) {
216
+ return new SummaryFailure(testObject.fieldName, testObject.message, testObject.groupName);
554
217
  }
555
- }
556
- function optionalFiedIsApplied(fieldName) {
557
- if (!fieldName) {
558
- return false;
218
+ toString() {
219
+ return this.message || '';
559
220
  }
560
- return useOptionalField(fieldName).applied;
561
221
  }
562
- var OptionalFieldTypes;
563
- (function (OptionalFieldTypes) {
564
- OptionalFieldTypes[OptionalFieldTypes["Immediate"] = 0] = "Immediate";
565
- OptionalFieldTypes[OptionalFieldTypes["Delayed"] = 1] = "Delayed";
566
- })(OptionalFieldTypes || (OptionalFieldTypes = {}));
567
222
 
568
- // eslint-disable-next-line max-statements, complexity
569
- function shouldAddValidProperty(fieldName) {
223
+ const nonMatchingGroupName = bindNot(matchingGroupName);
224
+ function matchingGroupName(testObject, groupName) {
225
+ return testObject.groupName === groupName;
226
+ }
227
+
228
+ function useShouldAddValidProperty(fieldName) {
570
229
  // Is the field optional, and the optional condition is applied
571
- if (optionalFiedIsApplied(fieldName)) {
230
+ if (useIsOptionalFiedApplied(fieldName)) {
572
231
  return true;
573
232
  }
574
- var testObjects = useTestsFlat();
575
233
  // Are there no tests?
576
- if (isEmpty(testObjects)) {
234
+ if (TestWalker.hasNoTests()) {
577
235
  return false;
578
236
  }
579
- // Does the field have any tests with errors?
237
+ // // Does the field have any tests with errors?
580
238
  if (hasErrorsByTestObjects(fieldName)) {
581
239
  return false;
582
240
  }
583
241
  // Does the given field have any pending tests that are not optional?
584
- if (hasNonOptionalIncomplete(fieldName)) {
242
+ if (useHasNonOptionalIncomplete(fieldName)) {
585
243
  return false;
586
244
  }
587
245
  // Does the field have no missing tests?
588
- return noMissingTests(fieldName);
246
+ return useNoMissingTests(fieldName);
589
247
  }
590
- function shouldAddValidPropertyInGroup(groupName, fieldName) {
591
- if (optionalFiedIsApplied(fieldName)) {
248
+ function useShouldAddValidPropertyInGroup(groupName, fieldName) {
249
+ if (useIsOptionalFiedApplied(fieldName)) {
592
250
  return true;
593
251
  }
594
252
  if (hasGroupFailuresByTestObjects(Severity.ERRORS, groupName, fieldName)) {
595
253
  return false;
596
254
  }
597
255
  // Do the given group/field have any pending tests that are not optional?
598
- if (hasNonOptionalIncompleteByGroup(groupName, fieldName)) {
256
+ if (useHasNonOptionalIncompleteByGroup(groupName, fieldName)) {
599
257
  return false;
600
258
  }
601
- return noMissingTestsByGroup(groupName, fieldName);
259
+ return useNoMissingTestsByGroup(groupName, fieldName);
602
260
  }
603
261
  // Does the given field have any pending tests that are not optional?
604
- function hasNonOptionalIncomplete(fieldName) {
605
- return isNotEmpty(useAllIncomplete().filter(function (testObject) {
606
- return isTestObjectOptional(testObject, fieldName);
607
- }));
262
+ function useHasNonOptionalIncomplete(fieldName) {
263
+ return TestWalker.someIncompleteTests(testObject => {
264
+ if (nonMatchingFieldName(testObject, fieldName)) {
265
+ return false;
266
+ }
267
+ return !useIsOptionalFiedApplied(fieldName);
268
+ });
608
269
  }
609
270
  // Do the given group/field have any pending tests that are not optional?
610
- function hasNonOptionalIncompleteByGroup(groupName, fieldName) {
611
- return isNotEmpty(useAllIncomplete().filter(function (testObject) {
271
+ function useHasNonOptionalIncompleteByGroup(groupName, fieldName) {
272
+ return TestWalker.someIncompleteTests(testObject => {
612
273
  if (nonMatchingGroupName(testObject, groupName)) {
613
274
  return false;
614
275
  }
615
- return isTestObjectOptional(testObject, fieldName);
616
- }));
617
- }
618
- function isTestObjectOptional(testObject, fieldName) {
619
- if (nonMatchingFieldName(testObject, fieldName)) {
620
- return false;
621
- }
622
- return optionalFiedIsApplied(fieldName);
276
+ if (nonMatchingFieldName(testObject, fieldName)) {
277
+ return false;
278
+ }
279
+ return !useIsOptionalFiedApplied(fieldName);
280
+ });
623
281
  }
624
282
  // Did all of the tests for the provided field run/omit?
625
283
  // This makes sure that the fields are not skipped or pending.
626
- function noMissingTests(fieldName) {
627
- var testObjects = useTestsFlat();
628
- return testObjects.every(function (testObject) {
629
- return noMissingTestsLogic(testObject, fieldName);
284
+ function useNoMissingTests(fieldName) {
285
+ return TestWalker.everyTest(testObject => {
286
+ return useNoMissingTestsLogic(testObject, fieldName);
630
287
  });
631
288
  }
632
289
  // Does the group have no missing tests?
633
- function noMissingTestsByGroup(groupName, fieldName) {
634
- var testObjects = useTestsFlat();
635
- return testObjects.every(function (testObject) {
290
+ function useNoMissingTestsByGroup(groupName, fieldName) {
291
+ return TestWalker.everyTest(testObject => {
636
292
  if (nonMatchingGroupName(testObject, groupName)) {
637
293
  return true;
638
294
  }
639
- return noMissingTestsLogic(testObject, fieldName);
295
+ return useNoMissingTestsLogic(testObject, fieldName);
640
296
  });
641
297
  }
642
- // Does the object qualify as either tested or omitted (but not skipped!)
643
- function noMissingTestsLogic(testObject, fieldName) {
298
+ function useNoMissingTestsLogic(testObject, fieldName) {
644
299
  if (nonMatchingFieldName(testObject, fieldName)) {
645
300
  return true;
646
301
  }
@@ -653,102 +308,108 @@ function noMissingTestsLogic(testObject, fieldName) {
653
308
  * or if it is marked as optional, even if the optional check did not apply yet -
654
309
  * but the test did not reach its final state.
655
310
  */
656
- return (optionalTestAwaitsResolution(testObject) ||
311
+ return (testObject.isOmitted() ||
657
312
  testObject.isTested() ||
658
- testObject.isOmitted());
313
+ useOptionalTestAwaitsResolution(testObject));
659
314
  }
660
- function optionalTestAwaitsResolution(testObject) {
315
+ function useOptionalTestAwaitsResolution(testObject) {
661
316
  // Does the test belong to an optional field,
662
317
  // and the test itself is still in an indeterminate state?
663
- return (useOptionalField(testObject.fieldName).type ===
664
- OptionalFieldTypes.Delayed && testObject.awaitsResolution());
318
+ var _a;
319
+ return (((_a = VestRuntime.useAvailableRoot()) === null || _a === void 0 ? void 0 : _a.getOptionalField(testObject.fieldName).type) === OptionalFieldTypes.AUTO && testObject.awaitsResolution());
665
320
  }
666
321
 
667
- /**
668
- * Reads the testObjects list and gets full validation result from it.
669
- */
670
- function genTestsSummary() {
671
- var testObjects = useTestsFlat();
672
- var summary = assign(baseStats(), {
673
- groups: {},
674
- tests: {},
675
- valid: false
322
+ function useProduceSuiteSummary() {
323
+ const summary = new SuiteSummary();
324
+ TestWalker.walkTests(testObject => {
325
+ summary.tests = useAppendToTest(summary.tests, testObject);
326
+ summary.groups = useAppendToGroup(summary.groups, testObject);
327
+ summary.errors = appendFailures(Severity.ERRORS, summary.errors, testObject);
328
+ summary.warnings = appendFailures(Severity.WARNINGS, summary.warnings, testObject);
676
329
  });
677
- testObjects.reduce(function (summary, testObject) {
678
- appendToTest(summary.tests, testObject);
679
- appendToGroup(summary.groups, testObject);
680
- return summary;
681
- }, summary);
682
- summary.valid = shouldAddValidProperty();
330
+ summary.valid = useShouldAddValidProperty();
683
331
  return countFailures(summary);
684
332
  }
685
- function appendToTest(tests, testObject) {
686
- tests[testObject.fieldName] = appendTestObject(tests, testObject);
333
+ function appendFailures(key, failures, testObject) {
334
+ if (testObject.isOmitted()) {
335
+ return failures;
336
+ }
337
+ const shouldAppend = key === Severity.WARNINGS ? testObject.isWarning() : testObject.isFailing();
338
+ if (shouldAppend) {
339
+ return failures.concat(SummaryFailure.fromTestObject(testObject));
340
+ }
341
+ return failures;
342
+ }
343
+ function useAppendToTest(tests, testObject) {
344
+ const { fieldName } = testObject;
345
+ const newTests = Object.assign({}, tests);
346
+ newTests[fieldName] = appendTestObject(newTests[fieldName], testObject);
687
347
  // If `valid` is false to begin with, keep it that way. Otherwise, assess.
688
- tests[testObject.fieldName].valid =
689
- tests[testObject.fieldName].valid === false
348
+ newTests[fieldName].valid =
349
+ newTests[fieldName].valid === false
690
350
  ? false
691
- : shouldAddValidProperty(testObject.fieldName);
351
+ : useShouldAddValidProperty(fieldName);
352
+ return newTests;
692
353
  }
693
354
  /**
694
355
  * Appends to a group object if within a group
695
356
  */
696
- function appendToGroup(groups, testObject) {
697
- var groupName = testObject.groupName;
357
+ function useAppendToGroup(groups, testObject) {
358
+ const { groupName, fieldName } = testObject;
698
359
  if (!groupName) {
699
- return;
360
+ return groups;
700
361
  }
701
- groups[groupName] = groups[groupName] || {};
702
- groups[groupName][testObject.fieldName] = appendTestObject(groups[groupName], testObject);
703
- groups[groupName][testObject.fieldName].valid =
704
- groups[groupName][testObject.fieldName].valid === false
362
+ const newGroups = Object.assign({}, groups);
363
+ newGroups[groupName] = newGroups[groupName] || {};
364
+ newGroups[groupName][fieldName] = appendTestObject(newGroups[groupName][fieldName], testObject);
365
+ newGroups[groupName][fieldName].valid =
366
+ newGroups[groupName][fieldName].valid === false
705
367
  ? false
706
- : shouldAddValidPropertyInGroup(groupName, testObject.fieldName);
368
+ : useShouldAddValidPropertyInGroup(groupName, fieldName);
369
+ return newGroups;
707
370
  }
708
371
  /**
709
372
  * Counts the failed tests and adds global counters
710
373
  */
711
374
  function countFailures(summary) {
712
- for (var test in summary.tests) {
375
+ for (const test in summary.tests) {
713
376
  summary.errorCount += summary.tests[test].errorCount;
714
377
  summary.warnCount += summary.tests[test].warnCount;
715
378
  summary.testCount += summary.tests[test].testCount;
716
379
  }
717
380
  return summary;
718
381
  }
382
+ /**
383
+ * Appends the test to a results object.
384
+ * Overload is only needed to satisfy typescript. No use in breaking it down to multiple
385
+ * functions as it is really the same, with the difference of "valid" missing in groups
386
+ */
719
387
  function appendTestObject(summaryKey, testObject) {
720
- var fieldName = testObject.fieldName, message = testObject.message;
721
- summaryKey[fieldName] = summaryKey[fieldName] || baseTestStats();
722
- var testKey = summaryKey[fieldName];
388
+ const { message } = testObject;
389
+ const nextSummaryKey = defaultTo(summaryKey ? Object.assign({}, summaryKey) : null, baseTestStats);
723
390
  if (testObject.isNonActionable())
724
- return testKey;
725
- summaryKey[fieldName].testCount++;
391
+ return nextSummaryKey;
392
+ nextSummaryKey.testCount++;
726
393
  if (testObject.isFailing()) {
727
394
  incrementFailures(Severity.ERRORS);
728
395
  }
729
396
  else if (testObject.isWarning()) {
730
397
  incrementFailures(Severity.WARNINGS);
731
398
  }
732
- return testKey;
399
+ return nextSummaryKey;
733
400
  function incrementFailures(severity) {
734
- var countKey = countKeyBySeverity(severity);
735
- testKey[countKey]++;
401
+ const countKey = countKeyBySeverity(severity);
402
+ nextSummaryKey[countKey]++;
736
403
  if (message) {
737
- testKey[severity] = (testKey[severity] || []).concat(message);
404
+ nextSummaryKey[severity] = (nextSummaryKey[severity] || []).concat(message);
738
405
  }
739
406
  }
740
407
  }
741
- function baseStats() {
742
- return {
743
- errorCount: 0,
744
- warnCount: 0,
745
- testCount: 0
746
- };
747
- }
748
408
  function baseTestStats() {
749
- return assign(baseStats(), {
409
+ return assign(new SummaryBase(), {
750
410
  errors: [],
751
- warnings: []
411
+ warnings: [],
412
+ valid: true,
752
413
  });
753
414
  }
754
415
 
@@ -763,9 +424,9 @@ function getByFieldName(testGroup, severityKey, fieldName) {
763
424
  return ((_a = testGroup === null || testGroup === void 0 ? void 0 : testGroup[fieldName]) === null || _a === void 0 ? void 0 : _a[severityKey]) || [];
764
425
  }
765
426
  function collectAll(testGroup, severityKey) {
766
- var output = {};
767
- var countKey = countKeyBySeverity(severityKey);
768
- for (var field in testGroup) {
427
+ const output = {};
428
+ const countKey = countKeyBySeverity(severityKey);
429
+ for (const field in testGroup) {
769
430
  if (isPositive(testGroup[field][countKey])) {
770
431
  // We will probably never get to the fallback array
771
432
  // leaving it just in case the implementation changes
@@ -775,19 +436,37 @@ function collectAll(testGroup, severityKey) {
775
436
  return output;
776
437
  }
777
438
 
439
+ function bindSuiteSelectors(get) {
440
+ return {
441
+ getError: (...args) => get().getError(...args),
442
+ getErrors: (...args) => get().getErrors(...args),
443
+ getErrorsByGroup: (...args) => get().getErrorsByGroup(...args),
444
+ getWarning: (...args) => get().getWarning(...args),
445
+ getWarnings: (...args) => get().getWarnings(...args),
446
+ getWarningsByGroup: (...args) => get().getWarningsByGroup(...args),
447
+ hasErrors: (...args) => get().hasErrors(...args),
448
+ hasErrorsByGroup: (...args) => get().hasErrorsByGroup(...args),
449
+ hasWarnings: (...args) => get().hasWarnings(...args),
450
+ hasWarningsByGroup: (...args) => get().hasWarningsByGroup(...args),
451
+ isValid: (...args) => get().isValid(...args),
452
+ isValidByGroup: (...args) => get().isValidByGroup(...args),
453
+ };
454
+ }
778
455
  // eslint-disable-next-line max-lines-per-function, max-statements
779
456
  function suiteSelectors(summary) {
780
- var selectors = {
781
- getErrors: getErrors,
782
- getErrorsByGroup: getErrorsByGroup,
783
- getWarnings: getWarnings,
784
- getWarningsByGroup: getWarningsByGroup,
785
- hasErrors: hasErrors,
786
- hasErrorsByGroup: hasErrorsByGroup,
787
- hasWarnings: hasWarnings,
788
- hasWarningsByGroup: hasWarningsByGroup,
789
- isValid: isValid,
790
- isValidByGroup: isValidByGroup
457
+ const selectors = {
458
+ getError,
459
+ getErrors,
460
+ getErrorsByGroup,
461
+ getWarning,
462
+ getWarnings,
463
+ getWarningsByGroup,
464
+ hasErrors,
465
+ hasErrorsByGroup,
466
+ hasWarnings,
467
+ hasWarningsByGroup,
468
+ isValid,
469
+ isValidByGroup,
791
470
  };
792
471
  return selectors;
793
472
  // Booleans
@@ -796,15 +475,15 @@ function suiteSelectors(summary) {
796
475
  return fieldName ? Boolean((_a = summary.tests[fieldName]) === null || _a === void 0 ? void 0 : _a.valid) : summary.valid;
797
476
  }
798
477
  function isValidByGroup(groupName, fieldName) {
799
- var group = summary.groups[groupName];
478
+ const group = summary.groups[groupName];
800
479
  if (!group) {
801
480
  return false;
802
481
  }
803
482
  if (fieldName) {
804
483
  return isFieldValid(group, fieldName);
805
484
  }
806
- for (var fieldName_1 in group) {
807
- if (!isFieldValid(group, fieldName_1)) {
485
+ for (const fieldName in group) {
486
+ if (!isFieldValid(group, fieldName)) {
808
487
  return false;
809
488
  }
810
489
  }
@@ -825,9 +504,15 @@ function suiteSelectors(summary) {
825
504
  function getWarnings(fieldName) {
826
505
  return getFailures(summary, Severity.WARNINGS, fieldName);
827
506
  }
507
+ function getWarning(fieldName) {
508
+ return getFailure(Severity.WARNINGS, summary, fieldName);
509
+ }
828
510
  function getErrors(fieldName) {
829
511
  return getFailures(summary, Severity.ERRORS, fieldName);
830
512
  }
513
+ function getError(fieldName) {
514
+ return getFailure(Severity.ERRORS, summary, fieldName);
515
+ }
831
516
  function getErrorsByGroup(groupName, fieldName) {
832
517
  return getFailuresByGroup(summary, Severity.ERRORS, groupName, fieldName);
833
518
  }
@@ -835,8 +520,6 @@ function suiteSelectors(summary) {
835
520
  return getFailuresByGroup(summary, Severity.WARNINGS, groupName, fieldName);
836
521
  }
837
522
  }
838
- // Gathers all failures of a given severity
839
- // With a fieldName, it will only gather failures for that field
840
523
  function getFailures(summary, severityKey, fieldName) {
841
524
  return gatherFailures(summary.tests, severityKey, fieldName);
842
525
  }
@@ -854,14 +537,14 @@ function isFieldValid(testContainer, fieldName) {
854
537
  // If a fieldName is provided, it will only check for failures within that field
855
538
  function hasFailuresByGroup(summary, severityCount, groupName, fieldName) {
856
539
  var _a, _b;
857
- var group = summary.groups[groupName];
540
+ const group = summary.groups[groupName];
858
541
  if (!group) {
859
542
  return false;
860
543
  }
861
544
  if (fieldName) {
862
545
  return isPositive((_a = group[fieldName]) === null || _a === void 0 ? void 0 : _a[severityCount]);
863
546
  }
864
- for (var field in group) {
547
+ for (const field in group) {
865
548
  if (isPositive((_b = group[field]) === null || _b === void 0 ? void 0 : _b[severityCount])) {
866
549
  return true;
867
550
  }
@@ -872,309 +555,32 @@ function hasFailuresByGroup(summary, severityCount, groupName, fieldName) {
872
555
  // If a fieldName is provided, it will only check for failures within that field
873
556
  function hasFailures(summary, countKey, fieldName) {
874
557
  var _a;
875
- var failureCount = fieldName
558
+ const failureCount = fieldName
876
559
  ? (_a = summary.tests[fieldName]) === null || _a === void 0 ? void 0 : _a[countKey]
877
560
  : summary[countKey] || 0;
878
561
  return isPositive(failureCount);
879
562
  }
880
-
881
- var cache$1 = cache$2(1);
882
- function produceSuiteResult() {
883
- var testObjects = useTestsFlat();
884
- var ctxRef = { stateRef: useStateRef() };
885
- return cache$1([testObjects], context.bind(ctxRef, function () {
886
- var summary = genTestsSummary();
887
- var suiteName = useSuiteName();
888
- return assign(summary, suiteSelectors(summary), {
889
- suiteName: suiteName
890
- });
891
- }));
892
- }
893
-
894
- /**
895
- * Checks if a given field, or the suite as a whole still have remaining tests.
896
- */
897
- function hasRemainingTests(fieldName) {
898
- var allIncomplete = useAllIncomplete();
899
- if (isEmpty(allIncomplete)) {
900
- return false;
901
- }
902
- if (fieldName) {
903
- return allIncomplete.some(function (testObject) {
904
- return matchingFieldName(testObject, fieldName);
905
- });
906
- }
907
- return true;
908
- }
909
-
910
- var cache = cache$2(20);
911
- function produceFullResult() {
912
- var testObjects = useTestsFlat();
913
- var ctxRef = { stateRef: useStateRef() };
914
- return cache([testObjects], context.bind(ctxRef, function () {
915
- return assign({}, produceSuiteResult(), {
916
- done: context.bind(ctxRef, done)
917
- });
918
- }));
919
- }
920
- /**
921
- * DONE is here and not in its own module to prevent circular dependency issues.
922
- */
923
- function shouldSkipDoneRegistration(callback, fieldName, output) {
563
+ function getFailure(severity, summary, fieldName) {
924
564
  var _a;
925
- // If we do not have any test runs for the current field
926
- return !!(!isFunction(callback) ||
927
- (fieldName && numberEquals((_a = output.tests[fieldName]) === null || _a === void 0 ? void 0 : _a.testCount, 0)));
928
- }
929
- function shouldRunDoneCallback(fieldName) {
930
- // is suite finished || field name exists, and test is finished;
931
- return !!(!hasRemainingTests() ||
932
- (fieldName && !hasRemainingTests(fieldName)));
933
- }
934
- /**
935
- * Registers done callbacks.
936
- * @register {Object} Vest output object.
937
- */
938
- var done = function done() {
939
- var args = [];
940
- for (var _i = 0; _i < arguments.length; _i++) {
941
- args[_i] = arguments[_i];
942
- }
943
- var _a = args.reverse(), callback = _a[0], fieldName = _a[1];
944
- var output = produceFullResult();
945
- if (shouldSkipDoneRegistration(callback, fieldName, output)) {
946
- return output;
947
- }
948
- var doneCallback = function () { return callback(produceSuiteResult()); };
949
- if (shouldRunDoneCallback(fieldName)) {
950
- doneCallback();
951
- return output;
952
- }
953
- deferDoneCallback(doneCallback, fieldName);
954
- return output;
955
- };
956
- function deferDoneCallback(doneCallback, fieldName) {
957
- var _a = useTestCallbacks(), setTestCallbacks = _a[1];
958
- setTestCallbacks(function (current) {
959
- if (fieldName) {
960
- current.fieldCallbacks[fieldName] = (current.fieldCallbacks[fieldName] || []).concat(doneCallback);
961
- }
962
- else {
963
- current.doneCallbacks.push(doneCallback);
964
- }
965
- return current;
966
- });
967
- }
968
-
969
- /**
970
- * This module gets triggered once the suite is done running its sync tests.
971
- *
972
- * It goes over all the tests in the state, and checks if they need to be omitted.
973
- */
974
- function omitOptionalFields() {
975
- var optionalFields = useOptionalFields()[0];
976
- // If there are no optional fields, we don't need to do anything
977
- if (isEmpty(optionalFields)) {
978
- return;
979
- }
980
- // Create an object to store the fields that need to be omitted
981
- var shouldOmit = {};
982
- // iterate over each of the tests in the state
983
- useTestsFlat().forEach(function (testObject) {
984
- // If we already added the current field (not this test specifically)
985
- // no need for further checks, go and omit the test
986
- if (hasOwnProperty(shouldOmit, testObject.fieldName)) {
987
- verifyAndOmit(testObject);
988
- }
989
- else {
990
- // check if the field has an optional function
991
- // if so, run it and verify/omit the test
992
- runOptionalConfig(testObject);
993
- }
994
- });
995
- // refresh the tests in the state so that the omitted fields are applied
996
- useRefreshTestObjects();
997
- function verifyAndOmit(testObject) {
998
- if (shouldOmit[testObject.fieldName]) {
999
- testObject.omit();
1000
- useSetOptionalField(testObject.fieldName, function () { return ({
1001
- applied: true
1002
- }); });
1003
- }
1004
- }
1005
- function runOptionalConfig(testObject) {
1006
- // Ge the optional configuration for the given field
1007
- var optionalConfig = useOptionalField(testObject.fieldName);
1008
- // If the optional was set to a function or a boolean, run it and verify/omit the test
1009
- if (optionalConfig.type === OptionalFieldTypes.Immediate) {
1010
- shouldOmit[testObject.fieldName] = optionalFunctionValue(optionalConfig.rule);
1011
- verifyAndOmit(testObject);
1012
- }
1013
- }
1014
- }
1015
-
1016
- /**
1017
- * Removes test object from suite state
1018
- */
1019
- function removeTestFromState (testObject) {
1020
- useSetTests(function (tests) {
1021
- return nestedArray.transform(tests, function (test) { return (testObject !== test ? test : null); });
1022
- });
1023
- }
1024
-
1025
- /**
1026
- * Runs done callback per field when async tests are finished running.
1027
- */
1028
- function runFieldCallbacks(fieldName) {
1029
- var fieldCallbacks = useTestCallbacks()[0].fieldCallbacks;
1030
- if (fieldName &&
1031
- !hasRemainingTests(fieldName) &&
1032
- isArray(fieldCallbacks[fieldName])) {
1033
- callEach(fieldCallbacks[fieldName]);
565
+ const summaryKey = summary[severity];
566
+ if (!fieldName) {
567
+ return summaryKey[0];
1034
568
  }
569
+ return (_a = summaryKey.find((summaryFailure) => matchingFieldName(summaryFailure, fieldName))) === null || _a === void 0 ? void 0 : _a.message;
1035
570
  }
1036
- /**
1037
- * Runs unlabelled done callback when async tests are finished running.
1038
- */
1039
- function runDoneCallbacks() {
1040
- var doneCallbacks = useTestCallbacks()[0].doneCallbacks;
1041
- callEach(doneCallbacks);
1042
- }
1043
-
1044
- // eslint-disable-next-line max-lines-per-function
1045
- function initBus() {
1046
- var vestBus = bus.createBus();
1047
- // Report a the completion of a test. There may be other tests with the same
1048
- // name that are still running, or not yet started.
1049
- vestBus.on(Events.TEST_COMPLETED, function (testObject) {
1050
- if (testObject.isCanceled()) {
1051
- return;
1052
- }
1053
- testObject.done();
1054
- runFieldCallbacks(testObject.fieldName);
1055
- if (!hasRemainingTests()) {
1056
- // When no more tests are running, emit the done event
1057
- vestBus.emit(Events.ALL_RUNNING_TESTS_FINISHED);
1058
- }
1059
- });
1060
- // Report that the suite completed its synchronous test run.
1061
- // Async operations may still be running.
1062
- vestBus.on(Events.SUITE_CALLBACK_DONE_RUNNING, function () {
1063
- // Remove tests that are optional and need to be omitted
1064
- omitOptionalFields();
1065
- });
1066
- // Called when all the tests, including async, are done running
1067
- vestBus.on(Events.ALL_RUNNING_TESTS_FINISHED, function () {
1068
- runDoneCallbacks();
1069
- });
1070
- // Removes a certain field from the state.
1071
- vestBus.on(Events.REMOVE_FIELD, function (fieldName) {
1072
- useEachTestObject(function (testObject) {
1073
- if (matchingFieldName(testObject, fieldName)) {
1074
- testObject.cancel();
1075
- removeTestFromState(testObject);
1076
- }
1077
- });
1078
- });
1079
- // Resets a certain field in the state.
1080
- vestBus.on(Events.RESET_FIELD, function (fieldName) {
1081
- useEachTestObject(function (testObject) {
1082
- if (matchingFieldName(testObject, fieldName)) {
1083
- testObject.reset();
1084
- }
1085
- });
1086
- });
1087
- return vestBus;
1088
- }
1089
- function useBus() {
1090
- var context$1 = context.useX();
1091
- invariant(context$1.bus);
1092
- return context$1.bus;
1093
- }
1094
- var Events;
1095
- (function (Events) {
1096
- Events["TEST_COMPLETED"] = "test_completed";
1097
- Events["ALL_RUNNING_TESTS_FINISHED"] = "all_running_tests_finished";
1098
- Events["REMOVE_FIELD"] = "remove_field";
1099
- Events["RESET_FIELD"] = "reset_field";
1100
- Events["SUITE_CALLBACK_DONE_RUNNING"] = "suite_callback_done_running";
1101
- })(Events || (Events = {}));
1102
571
 
1103
- // eslint-disable-next-line max-lines-per-function
1104
- function create() {
1105
- var args = [];
1106
- for (var _i = 0; _i < arguments.length; _i++) {
1107
- args[_i] = arguments[_i];
1108
- }
1109
- var _a = args.reverse(), suiteCallback = _a[0], suiteName = _a[1];
1110
- invariant(isFunction(suiteCallback), 'vest.create: Expected callback to be a function.');
1111
- // Event bus initialization
1112
- var bus = initBus();
1113
- // State initialization
1114
- var state = createState();
1115
- // State reference - this holds the actual state values
1116
- var stateRef = createStateRef(state, { suiteId: seq(), suiteName: suiteName });
1117
- // Create base context reference. All hooks will derive their data from this
1118
- var ctxRef = { stateRef: stateRef, bus: bus };
1119
- var suite = assign(
1120
- // Bind the suite body to the context
1121
- context.bind(ctxRef, function () {
1122
- var args = [];
1123
- for (var _i = 0; _i < arguments.length; _i++) {
1124
- args[_i] = arguments[_i];
1125
- }
1126
- // Reset the state. Migrates current test objects to `prev` array.
1127
- state.reset();
1128
- // Create a top level isolate
1129
- isolate({ type: IsolateTypes.SUITE }, function () {
1130
- // Run the consumer's callback
1131
- suiteCallback.apply(void 0, args);
1132
- });
1133
- // Report the suite is done registering tests
1134
- // Async tests may still be running
1135
- bus.emit(Events.SUITE_CALLBACK_DONE_RUNNING);
1136
- // Return the result
1137
- return produceFullResult();
1138
- }), {
1139
- get: context.bind(ctxRef, produceSuiteResult),
1140
- remove: context.bind(ctxRef, function (fieldName) {
1141
- bus.emit(Events.REMOVE_FIELD, fieldName);
1142
- }),
1143
- reset: state.reset,
1144
- resetField: context.bind(ctxRef, function (fieldName) {
1145
- bus.emit(Events.RESET_FIELD, fieldName);
1146
- })
572
+ function useCreateSuiteResult() {
573
+ return useSuiteResultCache(() => {
574
+ // eslint-disable-next-line vest-internal/use-use
575
+ const summary = useProduceSuiteSummary();
576
+ // eslint-disable-next-line vest-internal/use-use
577
+ const suiteName = useSuiteName();
578
+ return Object.freeze(assign(summary, suiteSelectors(summary), {
579
+ suiteName,
580
+ }));
1147
581
  });
1148
- return suite;
1149
582
  }
1150
583
 
1151
- /**
1152
- * Iterates over an array of items, allowing to run tests individually per item.
1153
- *
1154
- * Requires setting a "key" property on each item tested.
1155
- *
1156
- * @example
1157
- *
1158
- * each(itemsArray, (item) => {
1159
- * test(item.name, 'Item value must not be empty', () => {
1160
- * enforce(item.value).isNotEmpty();
1161
- * }, item.id)
1162
- * })
1163
- */
1164
- function each(list, callback) {
1165
- invariant(isFunction(callback), 'each callback must be a function');
1166
- isolate({ type: IsolateTypes.EACH }, function () {
1167
- list.forEach(function (arg, index) {
1168
- callback(arg, index);
1169
- });
1170
- });
1171
- }
1172
-
1173
- /**
1174
- * Error message to display when a hook was called outside of context.
1175
- */
1176
- var ERROR_HOOK_CALLED_OUTSIDE = 'hook called outside of a running suite.';
1177
-
1178
584
  /**
1179
585
  * Conditionally skips running tests within the callback.
1180
586
  *
@@ -1184,20 +590,21 @@ var ERROR_HOOK_CALLED_OUTSIDE = 'hook called outside of a running suite.';
1184
590
  * test('username', 'User already taken', async () => await doesUserExist(username)
1185
591
  * });
1186
592
  */
1187
- function skipWhen(conditional, callback) {
1188
- isolate({ type: IsolateTypes.SKIP_WHEN }, function () {
1189
- context.run({
593
+ // @vx-allow use-use
594
+ function skipWhen(condition, callback) {
595
+ Isolate.create(() => {
596
+ SuiteContext.run({
1190
597
  skipped:
1191
598
  // Checking for nested conditional. If we're in a nested skipWhen,
1192
599
  // we should skip the test if the parent conditional is true.
1193
- isExcludedIndividually() ||
600
+ useIsExcludedIndividually() ||
1194
601
  // Otherwise, we should skip the test if the conditional is true.
1195
- optionalFunctionValue(conditional, optionalFunctionValue(produceSuiteResult))
1196
- }, function () { return callback(); });
602
+ optionalFunctionValue(condition, optionalFunctionValue(useCreateSuiteResult)),
603
+ }, callback);
1197
604
  });
1198
605
  }
1199
- function isExcludedIndividually() {
1200
- return !!context.useX().skipped;
606
+ function useIsExcludedIndividually() {
607
+ return useSkipped();
1201
608
  }
1202
609
 
1203
610
  /**
@@ -1207,11 +614,12 @@ function isExcludedIndividually() {
1207
614
  *
1208
615
  * only('username');
1209
616
  */
617
+ // @vx-allow use-use
1210
618
  function only(item) {
1211
- return addTo(0 /* ExclusionGroup.ONLY */, 'tests', item);
619
+ return useAddTo(0 /* ExclusionGroup.ONLY */, 'tests', item);
1212
620
  }
1213
- only.group = function (item) {
1214
- return addTo(0 /* ExclusionGroup.ONLY */, 'groups', item);
621
+ only.group = function group(item) {
622
+ return useAddTo(0 /* ExclusionGroup.ONLY */, 'groups', item);
1215
623
  };
1216
624
  /**
1217
625
  * Adds a field or a list of fields into the exclusion list
@@ -1220,31 +628,31 @@ only.group = function (item) {
1220
628
  *
1221
629
  * skip('username');
1222
630
  */
631
+ // @vx-allow use-use
1223
632
  function skip(item) {
1224
- return addTo(1 /* ExclusionGroup.SKIP */, 'tests', item);
633
+ return useAddTo(1 /* ExclusionGroup.SKIP */, 'tests', item);
1225
634
  }
1226
- skip.group = function (item) {
1227
- return addTo(1 /* ExclusionGroup.SKIP */, 'groups', item);
635
+ skip.group = function group(item) {
636
+ return useAddTo(1 /* ExclusionGroup.SKIP */, 'groups', item);
1228
637
  };
1229
638
  //Checks whether a certain test profile excluded by any of the exclusion groups.
1230
639
  // eslint-disable-next-line complexity, max-statements
1231
- function isExcluded(testObject) {
1232
- var fieldName = testObject.fieldName, groupName = testObject.groupName;
1233
- if (isExcludedIndividually())
640
+ function useIsExcluded(testObject) {
641
+ const { fieldName, groupName } = testObject;
642
+ if (useIsExcludedIndividually())
1234
643
  return true;
1235
- var context$1 = context.useX();
1236
- var exclusion = context$1.exclusion;
1237
- var inclusion = context$1.inclusion;
1238
- var keyTests = exclusion.tests;
1239
- var testValue = keyTests[fieldName];
644
+ const exclusion = useExclusion();
645
+ const inclusion = useInclusion();
646
+ const keyTests = exclusion.tests;
647
+ const testValue = keyTests[fieldName];
1240
648
  // if test is skipped
1241
649
  // no need to proceed
1242
650
  if (testValue === false)
1243
651
  return true;
1244
- var isTestIncluded = testValue === true;
652
+ const isTestIncluded = testValue === true;
1245
653
  // If inside a group
1246
654
  if (groupName) {
1247
- if (isGroupExcluded(groupName)) {
655
+ if (useIsGroupExcluded(groupName)) {
1248
656
  return true; // field excluded by group
1249
657
  // if group is `only`ed
1250
658
  }
@@ -1257,7 +665,7 @@ function isExcluded(testObject) {
1257
665
  return keyTests[fieldName] === false;
1258
666
  }
1259
667
  }
1260
- if (isTopLevelWhenThereIsAnIncludedGroup(groupName)) {
668
+ if (useIsTopLevelWhenThereIsAnIncludedGroup(groupName)) {
1261
669
  return true;
1262
670
  }
1263
671
  // if field is only'ed
@@ -1266,8 +674,6 @@ function isExcluded(testObject) {
1266
674
  // If there is _ANY_ `only`ed test (and we already know this one isn't) return true
1267
675
  if (hasIncludedTests(keyTests)) {
1268
676
  // Check if inclusion rules for this field (`include` hook)
1269
- // TODO: Check if this may need to be moved outside of the condition.
1270
- // What if there are no included tests? This shouldn't run then?
1271
677
  return !optionalFunctionValue(inclusion[fieldName]);
1272
678
  }
1273
679
  // We're done here. This field is not excluded
@@ -1276,98 +682,760 @@ function isExcluded(testObject) {
1276
682
  /**
1277
683
  * Checks whether a given group is excluded from running.
1278
684
  */
1279
- function isGroupExcluded(groupName) {
1280
- var context$1 = context.useX();
1281
- var exclusion = context$1.exclusion;
1282
- var keyGroups = exclusion.groups;
1283
- var groupPresent = hasOwnProperty(keyGroups, groupName);
685
+ function useIsGroupExcluded(groupName) {
686
+ const exclusion = useExclusion();
687
+ const keyGroups = exclusion.groups;
688
+ const groupPresent = hasOwnProperty(keyGroups, groupName);
1284
689
  // When group is either only'ed or skipped
1285
690
  if (groupPresent) {
1286
691
  // Return true if group is skipped and false if only'ed
1287
692
  return keyGroups[groupName] === false;
1288
693
  }
1289
- // Group is not present
1290
- // Return whether other groups are included
1291
- return hasIncludedGroups();
694
+ // Group is not present
695
+ // Return whether other groups are included
696
+ return useHasIncludedGroups();
697
+ }
698
+ /**
699
+ * Adds fields to a specified exclusion group.
700
+ */
701
+ function useAddTo(exclusionGroup, itemType, item) {
702
+ const exclusion = useExclusion(ErrorStrings.HOOK_CALLED_OUTSIDE);
703
+ if (!item) {
704
+ return;
705
+ }
706
+ asArray(item).forEach((itemName) => {
707
+ if (!isStringValue(itemName)) {
708
+ return;
709
+ }
710
+ exclusion[itemType][itemName] = exclusionGroup === 0 /* ExclusionGroup.ONLY */;
711
+ });
712
+ }
713
+ /**
714
+ * Checks if context has included tests
715
+ */
716
+ function hasIncludedTests(keyTests) {
717
+ for (const test in keyTests) {
718
+ if (keyTests[test] === true) {
719
+ return true; // excluded implicitly
720
+ }
721
+ }
722
+ return false;
723
+ }
724
+ // are we not in a group and there is an included group?
725
+ function useIsTopLevelWhenThereIsAnIncludedGroup(groupName) {
726
+ if (!useHasIncludedGroups()) {
727
+ return false;
728
+ }
729
+ // Return whether there's an included group, and we're not inside a group
730
+ return !groupName;
731
+ }
732
+ function useHasIncludedGroups() {
733
+ const exclusion = useExclusion();
734
+ for (const group in exclusion.groups) {
735
+ if (exclusion.groups[group]) {
736
+ return true;
737
+ }
738
+ }
739
+ return false;
740
+ }
741
+
742
+ /**
743
+ * Conditionally omits tests from the suite.
744
+ *
745
+ * @example
746
+ *
747
+ * omitWhen(res => res.hasErrors('username'), () => {
748
+ * test('username', 'User already taken', async () => await doesUserExist(username)
749
+ * });
750
+ */
751
+ // @vx-allow use-use
752
+ function omitWhen(conditional, callback) {
753
+ Isolate.create(() => {
754
+ SuiteContext.run({
755
+ omitted: useWithinActiveOmitWhen() ||
756
+ optionalFunctionValue(conditional, optionalFunctionValue(useCreateSuiteResult)),
757
+ }, callback);
758
+ });
759
+ }
760
+ // Checks that we're currently in an active omitWhen block
761
+ function useWithinActiveOmitWhen() {
762
+ return useOmitted();
763
+ }
764
+
765
+ function useVerifyTestRun(testObject, collisionResult = testObject) {
766
+ if (useShouldSkipBasedOnMode(testObject)) {
767
+ return skipTestAndReturn(testObject);
768
+ }
769
+ if (useShouldOmit(testObject)) {
770
+ return omitTestAndReturn(testObject);
771
+ }
772
+ if (useIsExcluded(testObject)) {
773
+ return useForceSkipIfInSkipWhen(collisionResult);
774
+ }
775
+ return testObject;
776
+ }
777
+ function useShouldOmit(testObject) {
778
+ return (useWithinActiveOmitWhen() || useIsOptionalFiedApplied(testObject.fieldName));
779
+ }
780
+ function skipTestAndReturn(testNode) {
781
+ testNode.skip();
782
+ return testNode;
783
+ }
784
+ function omitTestAndReturn(testNode) {
785
+ testNode.omit();
786
+ return testNode;
787
+ }
788
+ function useForceSkipIfInSkipWhen(testNode) {
789
+ // We're forcing skipping the pending test
790
+ // if we're directly within a skipWhen block
791
+ // This mostly means that we're probably giving
792
+ // up on this async test intentionally.
793
+ testNode.skip(useIsExcludedIndividually());
794
+ return testNode;
795
+ }
796
+
797
+ // @vx-allow use-use
798
+ function IsolateTestReconciler(currentNode, historyNode) {
799
+ // Start by verifying params
800
+ if (!IsolateTest.is(currentNode)) {
801
+ // This is unreachable, since this function should only be called with IsolateTest nodes
802
+ return currentNode;
803
+ }
804
+ if (isNullish(historyNode)) {
805
+ return handleNoHistoryNode(currentNode);
806
+ }
807
+ if (!IsolateTest.is(historyNode)) {
808
+ return currentNode;
809
+ }
810
+ const reconcilerOutput = usePickNode(historyNode, currentNode);
811
+ cancelOverriddenPendingTestOnTestReRun(reconcilerOutput, currentNode, historyNode);
812
+ return reconcilerOutput;
813
+ }
814
+ // eslint-disable-next-line max-statements
815
+ function nodeReorderDetected(newNode, prevNode) {
816
+ return !!IsolateTest.is(prevNode) && !isSameProfileTest(prevNode, newNode);
817
+ }
818
+ function handleCollision(newNode, prevNode) {
819
+ if (newNode.usesKey()) {
820
+ return IsolateTest.cast(Reconciler.handleIsolateNodeWithKey(newNode));
821
+ }
822
+ if (nodeReorderDetected(newNode, prevNode)) {
823
+ return onNodeReorder(newNode, prevNode);
824
+ }
825
+ if (!IsolateTest.is(prevNode)) {
826
+ // I believe we cannot actually reach this point.
827
+ // Because it should already be handled by nodeReorderDetected.
828
+ return newNode;
829
+ }
830
+ // FIXME: May-13-2023
831
+ // This may not be the most ideal solution.
832
+ // In short: if the node was omitted in the previous run,
833
+ // we want to re-evaluate it. The reason is that we may incorrectly
834
+ // identify it is "optional" because it was omitted in the previous run.
835
+ // There may be a better way to handle this. Need to revisit this.
836
+ if (prevNode.isOmitted()) {
837
+ return newNode;
838
+ }
839
+ return prevNode;
840
+ }
841
+ function onNodeReorder(newNode, prevNode) {
842
+ throwTestOrderError(newNode, prevNode);
843
+ Reconciler.removeAllNextNodesInIsolate();
844
+ return newNode;
845
+ }
846
+ function usePickNode(historyNode, currentNode) {
847
+ const collisionResult = handleCollision(currentNode, historyNode);
848
+ return useVerifyTestRun(currentNode, collisionResult);
849
+ }
850
+ function handleNoHistoryNode(testNode) {
851
+ if (testNode.usesKey()) {
852
+ return IsolateTest.cast(Reconciler.handleIsolateNodeWithKey(testNode));
853
+ }
854
+ return testNode;
855
+ }
856
+ function cancelOverriddenPendingTestOnTestReRun(nextNode, currentNode, prevTestObject) {
857
+ if (nextNode === currentNode && IsolateTest.is(currentNode)) {
858
+ cancelOverriddenPendingTest(prevTestObject, currentNode);
859
+ }
860
+ }
861
+ function throwTestOrderError(newNode, prevNode) {
862
+ if (newNode.shouldAllowReorder()) {
863
+ return;
864
+ }
865
+ deferThrow(text(ErrorStrings.TESTS_CALLED_IN_DIFFERENT_ORDER, {
866
+ fieldName: newNode.fieldName,
867
+ prevName: IsolateTest.is(prevNode) ? prevNode.fieldName : undefined,
868
+ }));
869
+ }
870
+
871
+ var TestStatus;
872
+ (function (TestStatus) {
873
+ TestStatus["UNTESTED"] = "UNTESTED";
874
+ TestStatus["SKIPPED"] = "SKIPPED";
875
+ TestStatus["FAILED"] = "FAILED";
876
+ TestStatus["WARNING"] = "WARNING";
877
+ TestStatus["PASSING"] = "PASSING";
878
+ TestStatus["PENDING"] = "PENDING";
879
+ TestStatus["CANCELED"] = "CANCELED";
880
+ TestStatus["OMITTED"] = "OMITTED";
881
+ })(TestStatus || (TestStatus = {}));
882
+ var TestAction;
883
+ (function (TestAction) {
884
+ TestAction["RESET"] = "RESET";
885
+ })(TestAction || (TestAction = {}));
886
+ function createTestStateMachine() {
887
+ return StateMachine(machine);
888
+ }
889
+ /* eslint-disable sort-keys */
890
+ const machine = {
891
+ initial: TestStatus.UNTESTED,
892
+ states: {
893
+ '*': {
894
+ [TestStatus.OMITTED]: TestStatus.OMITTED,
895
+ [TestAction.RESET]: TestStatus.UNTESTED,
896
+ },
897
+ [TestStatus.UNTESTED]: {
898
+ [TestStatus.CANCELED]: TestStatus.CANCELED,
899
+ [TestStatus.FAILED]: TestStatus.FAILED,
900
+ [TestStatus.PASSING]: TestStatus.PASSING,
901
+ [TestStatus.PENDING]: TestStatus.PENDING,
902
+ [TestStatus.SKIPPED]: TestStatus.SKIPPED,
903
+ [TestStatus.WARNING]: TestStatus.WARNING,
904
+ },
905
+ [TestStatus.PENDING]: {
906
+ [TestStatus.CANCELED]: TestStatus.CANCELED,
907
+ [TestStatus.FAILED]: TestStatus.FAILED,
908
+ [TestStatus.PASSING]: TestStatus.PASSING,
909
+ [TestStatus.SKIPPED]: [
910
+ TestStatus.SKIPPED,
911
+ (force) => force === true,
912
+ ],
913
+ [TestStatus.WARNING]: TestStatus.WARNING,
914
+ },
915
+ [TestStatus.SKIPPED]: {},
916
+ [TestStatus.FAILED]: {},
917
+ [TestStatus.WARNING]: {},
918
+ [TestStatus.PASSING]: {},
919
+ [TestStatus.CANCELED]: {},
920
+ [TestStatus.OMITTED]: {},
921
+ },
922
+ };
923
+ /* eslint-enable sort-keys */
924
+
925
+ function shouldUseErrorAsMessage(message, error) {
926
+ // kind of cheating with this safe guard, but it does the job
927
+ return isUndefined(message) && isStringValue(error);
928
+ }
929
+
930
+ class IsolateTest extends Isolate {
931
+ constructor({ fieldName, testFn, message, groupName, key = null, }) {
932
+ super();
933
+ this.children = null;
934
+ this.id = seq();
935
+ this.severity = TestSeverity.Error;
936
+ this.stateMachine = createTestStateMachine();
937
+ this.fieldName = fieldName;
938
+ this.testFn = testFn;
939
+ if (groupName) {
940
+ this.groupName = groupName;
941
+ }
942
+ if (message) {
943
+ this.message = message;
944
+ }
945
+ this.setKey(key);
946
+ }
947
+ static create(callback, data) {
948
+ return IsolateTest.cast(super.create(callback, data));
949
+ }
950
+ static cast(isolate) {
951
+ IsolateTest.isX(isolate);
952
+ return isolate;
953
+ }
954
+ get status() {
955
+ return this.stateMachine.getState();
956
+ }
957
+ setStatus(status, payload) {
958
+ this.stateMachine.transition(status, payload);
959
+ }
960
+ run() {
961
+ let result;
962
+ try {
963
+ result = this.testFn();
964
+ }
965
+ catch (error) {
966
+ if (shouldUseErrorAsMessage(this.message, error)) {
967
+ this.message = error;
968
+ }
969
+ result = false;
970
+ }
971
+ if (result === false) {
972
+ this.fail();
973
+ }
974
+ return result;
975
+ }
976
+ // Selectors
977
+ warns() {
978
+ return this.severity === TestSeverity.Warning;
979
+ }
980
+ isPending() {
981
+ return this.statusEquals(TestStatus.PENDING);
982
+ }
983
+ isOmitted() {
984
+ return this.statusEquals(TestStatus.OMITTED);
985
+ }
986
+ isUntested() {
987
+ return this.statusEquals(TestStatus.UNTESTED);
988
+ }
989
+ isFailing() {
990
+ return this.statusEquals(TestStatus.FAILED);
991
+ }
992
+ isCanceled() {
993
+ return this.statusEquals(TestStatus.CANCELED);
994
+ }
995
+ isSkipped() {
996
+ return this.statusEquals(TestStatus.SKIPPED);
997
+ }
998
+ isPassing() {
999
+ return this.statusEquals(TestStatus.PASSING);
1000
+ }
1001
+ isWarning() {
1002
+ return this.statusEquals(TestStatus.WARNING);
1003
+ }
1004
+ hasFailures() {
1005
+ return this.isFailing() || this.isWarning();
1006
+ }
1007
+ isNonActionable() {
1008
+ return this.isSkipped() || this.isOmitted() || this.isCanceled();
1009
+ }
1010
+ isTested() {
1011
+ return this.hasFailures() || this.isPassing();
1012
+ }
1013
+ awaitsResolution() {
1014
+ // Is the test in a state where it can still be run, or complete running
1015
+ // and its final status is indeterminate?
1016
+ return this.isSkipped() || this.isUntested() || this.isPending();
1017
+ }
1018
+ statusEquals(status) {
1019
+ return this.status === status;
1020
+ }
1021
+ // State modifiers
1022
+ setPending() {
1023
+ this.setStatus(TestStatus.PENDING);
1024
+ }
1025
+ fail() {
1026
+ this.setStatus(this.warns() ? TestStatus.WARNING : TestStatus.FAILED);
1027
+ }
1028
+ pass() {
1029
+ this.setStatus(TestStatus.PASSING);
1030
+ }
1031
+ warn() {
1032
+ this.severity = TestSeverity.Warning;
1033
+ }
1034
+ skip(force) {
1035
+ // Without this force flag, the test will be marked as skipped even if it is pending.
1036
+ // This means that it will not be counted in "allIncomplete" and its done callbacks
1037
+ // will not be called, or will be called prematurely.
1038
+ // What this mostly say is that when we have a pending test for one field, and we then
1039
+ // start typing in a different field - the pending test will be canceled, which
1040
+ // is usually an unwanted behavior.
1041
+ // The only scenario in which we DO want to cancel the async test regardless
1042
+ // is when we specifically skip a test with `skipWhen`, which is handled by the
1043
+ // "force" boolean flag.
1044
+ // I am not a fan of this flag, but it gets the job done.
1045
+ this.setStatus(TestStatus.SKIPPED, force);
1046
+ }
1047
+ cancel() {
1048
+ this.setStatus(TestStatus.CANCELED);
1049
+ }
1050
+ reset() {
1051
+ this.stateMachine.transition(TestAction.RESET);
1052
+ }
1053
+ omit() {
1054
+ this.setStatus(TestStatus.OMITTED);
1055
+ }
1056
+ valueOf() {
1057
+ return !this.isFailing();
1058
+ }
1059
+ isAsyncTest() {
1060
+ return isPromise(this.asyncTest);
1061
+ }
1062
+ static is(value) {
1063
+ return value instanceof IsolateTest;
1064
+ }
1065
+ static isX(value) {
1066
+ invariant(this.is(value));
1067
+ }
1068
+ }
1069
+ IsolateTest.reconciler = IsolateTestReconciler;
1070
+
1071
+ class TestWalker {
1072
+ static defaultRoot() {
1073
+ return VestRuntime.useAvailableRoot();
1074
+ }
1075
+ static hasNoTests(root = TestWalker.defaultRoot()) {
1076
+ if (!root)
1077
+ return true;
1078
+ return !Walker.has(root, IsolateTest.is);
1079
+ }
1080
+ static someIncompleteTests(predicate, root = TestWalker.defaultRoot()) {
1081
+ if (!root)
1082
+ return false;
1083
+ return Walker.some(root, isolate => {
1084
+ IsolateTest.isX(isolate);
1085
+ return isolate.isPending() && predicate(isolate);
1086
+ }, IsolateTest.is);
1087
+ }
1088
+ static someTests(predicate, root = TestWalker.defaultRoot()) {
1089
+ if (!root)
1090
+ return false;
1091
+ return Walker.some(root, isolate => {
1092
+ IsolateTest.isX(isolate);
1093
+ return predicate(isolate);
1094
+ }, IsolateTest.is);
1095
+ }
1096
+ static everyTest(predicate, root = TestWalker.defaultRoot()) {
1097
+ if (!root)
1098
+ return false;
1099
+ return Walker.every(root, isolate => {
1100
+ IsolateTest.isX(isolate);
1101
+ return predicate(isolate);
1102
+ }, IsolateTest.is);
1103
+ }
1104
+ static walkTests(callback, root = TestWalker.defaultRoot()) {
1105
+ if (!root)
1106
+ return;
1107
+ Walker.walk(root, (isolate, breakout) => {
1108
+ callback(IsolateTest.cast(isolate), breakout);
1109
+ }, IsolateTest.is);
1110
+ }
1111
+ static hasRemainingTests(fieldName) {
1112
+ return TestWalker.someIncompleteTests(testObject => {
1113
+ if (fieldName) {
1114
+ return matchingFieldName(testObject, fieldName);
1115
+ }
1116
+ return true;
1117
+ });
1118
+ }
1119
+ static pluckTests(predicate, root = TestWalker.defaultRoot()) {
1120
+ if (!root)
1121
+ return;
1122
+ Walker.pluck(root, isolate => {
1123
+ IsolateTest.isX(isolate);
1124
+ return predicate(isolate);
1125
+ }, IsolateTest.is);
1126
+ }
1127
+ static resetField(fieldName) {
1128
+ TestWalker.walkTests(testObject => {
1129
+ if (matchingFieldName(testObject, fieldName)) {
1130
+ testObject.reset();
1131
+ }
1132
+ }, TestWalker.defaultRoot());
1133
+ }
1134
+ static removeTestByFieldName(fieldName, root = TestWalker.defaultRoot()) {
1135
+ TestWalker.pluckTests(testObject => {
1136
+ return matchingFieldName(testObject, fieldName);
1137
+ }, root);
1138
+ }
1139
+ }
1140
+
1141
+ /**
1142
+ * Checks that a given test object matches the currently specified severity level
1143
+ */
1144
+ function nonMatchingSeverityProfile(severity, testObject) {
1145
+ return either(severity === Severity.WARNINGS, testObject.warns());
1146
+ }
1147
+
1148
+ /**
1149
+ * The difference between this file and hasFailures is that hasFailures uses the static
1150
+ * summary object, while this one uses the actual validation state
1151
+ */
1152
+ function hasErrorsByTestObjects(fieldName) {
1153
+ return hasFailuresByTestObjects(Severity.ERRORS, fieldName);
1154
+ }
1155
+ function hasFailuresByTestObjects(severityKey, fieldName) {
1156
+ return TestWalker.someTests(testObject => {
1157
+ return hasFailuresByTestObject(testObject, severityKey, fieldName);
1158
+ });
1159
+ }
1160
+ function hasGroupFailuresByTestObjects(severityKey, groupName, fieldName) {
1161
+ return TestWalker.someTests(testObject => {
1162
+ if (nonMatchingGroupName(testObject, groupName)) {
1163
+ return false;
1164
+ }
1165
+ return hasFailuresByTestObject(testObject, severityKey, fieldName);
1166
+ });
1167
+ }
1168
+ /**
1169
+ * Determines whether a certain test profile has failures.
1170
+ */
1171
+ function hasFailuresByTestObject(testObject, severityKey, fieldName) {
1172
+ if (!testObject.hasFailures()) {
1173
+ return false;
1174
+ }
1175
+ if (nonMatchingFieldName(testObject, fieldName)) {
1176
+ return false;
1177
+ }
1178
+ if (nonMatchingSeverityProfile(severityKey, testObject)) {
1179
+ return false;
1180
+ }
1181
+ return true;
1182
+ }
1183
+
1184
+ var Modes;
1185
+ (function (Modes) {
1186
+ Modes["EAGER"] = "EAGER";
1187
+ Modes["ALL"] = "ALL";
1188
+ Modes["ONE"] = "ONE";
1189
+ })(Modes || (Modes = {}));
1190
+ /**
1191
+ * Sets the current execution mode for the current suite.
1192
+ *
1193
+ * Supported modes:
1194
+ * - `EAGER` - (default) Runs all tests, but stops on first failure for each given field.
1195
+ * - `ALL` - Runs all tests, regardless of failures.
1196
+ * - `ONE` - Stops suite execution on first failure of any field.
1197
+ *
1198
+ * @example
1199
+ * ```js
1200
+ * import {Modes, create} from 'vest';
1201
+ *
1202
+ * const suite = create('suite_name', () => {
1203
+ * vest.mode(Modes.ALL);
1204
+ *
1205
+ * // ...
1206
+ * });
1207
+ * ```
1208
+ * @param 'ALL' | 'EAGER' | 'ONE' mode - The mode to set.
1209
+ */
1210
+ // @vx-allow use-use
1211
+ function mode(mode) {
1212
+ const [, setMode] = useMode();
1213
+ setMode(mode);
1214
+ }
1215
+ function useIsMode(mode) {
1216
+ const [currentMode] = useMode();
1217
+ return currentMode === mode;
1218
+ }
1219
+ function useIsEager() {
1220
+ return useIsMode(Modes.EAGER);
1221
+ }
1222
+ function useIsOne() {
1223
+ return useIsMode(Modes.ONE);
1224
+ }
1225
+ function useShouldSkipBasedOnMode(testObject) {
1226
+ if (useIsOne()) {
1227
+ return hasErrorsByTestObjects();
1228
+ }
1229
+ if (useIsEager()) {
1230
+ return hasErrorsByTestObjects(testObject.fieldName);
1231
+ }
1232
+ return false;
1233
+ }
1234
+
1235
+ const SuiteContext = createCascade((ctxRef, parentContext) => {
1236
+ if (parentContext) {
1237
+ return null;
1238
+ }
1239
+ return assign({
1240
+ exclusion: {
1241
+ tests: {},
1242
+ groups: {},
1243
+ },
1244
+ inclusion: {},
1245
+ mode: tinyState.createTinyState(Modes.EAGER),
1246
+ testMemoCache,
1247
+ }, ctxRef);
1248
+ });
1249
+ function useCurrentTest(msg) {
1250
+ return SuiteContext.useX(msg).currentTest;
1251
+ }
1252
+ function useGroupName() {
1253
+ return SuiteContext.useX().groupName;
1254
+ }
1255
+ function useExclusion(hookError) {
1256
+ return SuiteContext.useX(hookError).exclusion;
1257
+ }
1258
+ function useInclusion() {
1259
+ return SuiteContext.useX().inclusion;
1260
+ }
1261
+ function useMode() {
1262
+ return SuiteContext.useX().mode();
1263
+ }
1264
+ function useSkipped() {
1265
+ var _a;
1266
+ return (_a = SuiteContext.useX().skipped) !== null && _a !== void 0 ? _a : false;
1267
+ }
1268
+ function useOmitted() {
1269
+ var _a;
1270
+ return (_a = SuiteContext.useX().omitted) !== null && _a !== void 0 ? _a : false;
1271
+ }
1272
+ const testMemoCache = cache(10);
1273
+ function useTestMemoCache() {
1274
+ return SuiteContext.useX().testMemoCache;
1292
1275
  }
1276
+
1293
1277
  /**
1294
- * Adds fields to a specified exclusion group.
1278
+ * This module gets triggered once the suite is done running its sync tests.
1279
+ *
1280
+ * It goes over all the tests in the state, and checks if they need to be omitted.
1295
1281
  */
1296
- function addTo(exclusionGroup, itemType, item) {
1297
- var context$1 = context.useX(ERROR_HOOK_CALLED_OUTSIDE);
1298
- if (!item) {
1282
+ function useOmitOptionalFields() {
1283
+ const root = VestRuntime.useAvailableRoot();
1284
+ const emit = VestRuntime.useEmit();
1285
+ const optionalFields = root === null || root === void 0 ? void 0 : root.getOptionalFields();
1286
+ // If there are no optional fields, we don't need to do anything
1287
+ if (isEmpty(optionalFields)) {
1299
1288
  return;
1300
1289
  }
1301
- asArray(item).forEach(function (itemName) {
1302
- if (!isStringValue(itemName)) {
1290
+ // Create an object to store the fields that need to be omitted
1291
+ const shouldOmit = new Set();
1292
+ // iterate over each of the tests in the state
1293
+ TestWalker.walkTests(testObject => {
1294
+ if (testObject.isPending()) {
1303
1295
  return;
1304
1296
  }
1305
- context$1.exclusion[itemType][itemName] =
1306
- exclusionGroup === 0 /* ExclusionGroup.ONLY */;
1297
+ // If we already added the current field (not this test specifically)
1298
+ // no need for further checks, go and omit the test
1299
+ if (hasOwnProperty(shouldOmit, testObject.fieldName)) {
1300
+ verifyAndOmit(testObject);
1301
+ }
1302
+ else {
1303
+ // check if the field has an optional function
1304
+ // if so, run it and verify/omit the test
1305
+ runOptionalConfig(testObject);
1306
+ }
1307
1307
  });
1308
+ emit(Events.DONE_TEST_OMISSION_PASS);
1309
+ function verifyAndOmit(testObject) {
1310
+ if (shouldOmit.has(testObject.fieldName)) {
1311
+ testObject.omit();
1312
+ root === null || root === void 0 ? void 0 : root.setOptionalField(testObject.fieldName, current => (Object.assign(Object.assign({}, current), { applied: true })));
1313
+ }
1314
+ }
1315
+ function runOptionalConfig(testObject) {
1316
+ // Ge the optional configuration for the given field
1317
+ const optionalConfig = root === null || root === void 0 ? void 0 : root.getOptionalField(testObject.fieldName);
1318
+ if (isNullish(optionalConfig)) {
1319
+ return;
1320
+ }
1321
+ // If the optional was set to a function or a boolean, run it and verify/omit the test
1322
+ if (optionalFunctionValue(optionalConfig.rule) === true) {
1323
+ shouldOmit.add(testObject.fieldName);
1324
+ }
1325
+ verifyAndOmit(testObject);
1326
+ }
1308
1327
  }
1328
+
1309
1329
  /**
1310
- * Checks if context has included tests
1330
+ * Runs done callback per field when async tests are finished running.
1311
1331
  */
1312
- function hasIncludedTests(keyTests) {
1313
- for (var test in keyTests) {
1314
- if (keyTests[test] === true) {
1315
- return true; // excluded implicitly
1316
- }
1332
+ function useRunFieldCallbacks(fieldName) {
1333
+ const [fieldCallbacks] = useFieldCallbacks();
1334
+ if (fieldName &&
1335
+ !TestWalker.hasRemainingTests(fieldName) &&
1336
+ isArray(fieldCallbacks[fieldName])) {
1337
+ callEach(fieldCallbacks[fieldName]);
1317
1338
  }
1318
- return false;
1319
1339
  }
1320
- // are we not in a group and there is an included group?
1321
- function isTopLevelWhenThereIsAnIncludedGroup(groupName) {
1322
- if (!hasIncludedGroups()) {
1323
- return false;
1324
- }
1325
- // Return whether there's an included group, and we're not inside a group
1326
- return !groupName;
1340
+ /**
1341
+ * Runs unlabelled done callback when async tests are finished running.
1342
+ */
1343
+ function useRunDoneCallbacks() {
1344
+ const [doneCallbacks] = useDoneCallbacks();
1345
+ callEach(doneCallbacks);
1327
1346
  }
1328
- function hasIncludedGroups() {
1329
- var context$1 = context.useX();
1330
- var exclusion = context$1.exclusion;
1331
- for (var group in exclusion.groups) {
1332
- if (exclusion.groups[group]) {
1333
- return true;
1347
+
1348
+ // eslint-disable-next-line max-statements
1349
+ function useInitVestBus() {
1350
+ const VestBus = VestRuntime.useBus();
1351
+ // Report a the completion of a test. There may be other tests with the same
1352
+ // name that are still running, or not yet started.
1353
+ on(Events.TEST_COMPLETED, (testObject) => {
1354
+ if (testObject.isCanceled()) {
1355
+ return;
1334
1356
  }
1357
+ useRunFieldCallbacks(testObject.fieldName);
1358
+ if (!TestWalker.hasRemainingTests()) {
1359
+ // When no more tests are running, emit the done event
1360
+ VestBus.emit(Events.ALL_RUNNING_TESTS_FINISHED);
1361
+ }
1362
+ });
1363
+ on(Events.TEST_RUN_STARTED, () => {
1364
+ /* Let's just invalidate the suite cache for now */
1365
+ });
1366
+ on(Events.DONE_TEST_OMISSION_PASS, () => {
1367
+ /* We NEED to refresh the cache here. Don't ask */
1368
+ });
1369
+ // Called when all the tests, including async, are done running
1370
+ on(Events.ALL_RUNNING_TESTS_FINISHED, () => {
1371
+ // Small optimization. We don't need to run this if there are no async tests
1372
+ // The reason is that we run this function immediately after the suite callback
1373
+ // is run, so if the suite is only comprised of sync tests, we don't need to
1374
+ // run this function twice since we know for a fact the state is up to date
1375
+ if (TestWalker.someTests(test => test.isAsyncTest())) {
1376
+ useOmitOptionalFields();
1377
+ }
1378
+ useRunDoneCallbacks();
1379
+ });
1380
+ on(Events.RESET_FIELD, (fieldName) => {
1381
+ TestWalker.resetField(fieldName);
1382
+ });
1383
+ on(Events.SUITE_RUN_STARTED, () => {
1384
+ useResetCallbacks();
1385
+ });
1386
+ on(Events.SUITE_CALLBACK_RUN_FINISHED, () => {
1387
+ useOmitOptionalFields();
1388
+ });
1389
+ on(Events.REMOVE_FIELD, (fieldName) => {
1390
+ TestWalker.removeTestByFieldName(fieldName);
1391
+ });
1392
+ on(Events.RESET_SUITE, () => {
1393
+ useResetSuite();
1394
+ });
1395
+ return VestBus;
1396
+ function on(event, cb) {
1397
+ VestBus.on(event, (...args) => {
1398
+ // This is more concise, but it might be an overkill
1399
+ // if we're adding events that don't need to invalidate the cache
1400
+ useExpireSuiteResultCache();
1401
+ cb(...args);
1402
+ });
1335
1403
  }
1336
- return false;
1337
1404
  }
1338
1405
 
1339
1406
  /**
1340
- * Runs tests within a group so that they can be controlled or queried separately.
1407
+ * Conditionally includes a field for testing, based on specified criteria.
1408
+ *
1409
+ * @param {string} fieldName - The name of the field to include for testing.
1341
1410
  *
1342
1411
  * @example
1412
+ * include('confirm').when('password');
1413
+ * // Includes the "confirm" field for testing when the "password" field is included
1343
1414
  *
1344
- * group('group_name', () => {
1345
- * // Tests go here
1346
- * });
1415
+ * include('confirm').when(someValue);
1416
+ * // Includes the "confirm" field for testing when the value of `someValue` is true
1417
+ *
1418
+ * include('confirm').when(() => someValue);
1419
+ * // Includes the "confirm" field for testing when the callback function returns true
1420
+ *
1421
+ * include('username').when(result => result.hasErrors('username'));
1422
+ * // Includes the "username" field for testing when there are errors associated with it in the current suite result
1347
1423
  */
1348
- function group(groupName, tests) {
1349
- invariant(isStringValue(groupName), groupErrorMsg('name must be a string'));
1350
- invariant(isFunction(tests), groupErrorMsg('callback must be a function'));
1351
- // Running with the context applied
1352
- isolate({ type: IsolateTypes.GROUP }, function () {
1353
- context.run({ groupName: groupName }, tests);
1354
- });
1355
- }
1356
- function groupErrorMsg(error) {
1357
- return "Wrong arguments passed to group. Group ".concat(error, ".");
1358
- }
1359
-
1424
+ // @vx-allow use-use
1360
1425
  function include(fieldName) {
1361
- var context$1 = context.useX();
1362
- var inclusion = context$1.inclusion, exclusion = context$1.exclusion;
1426
+ const inclusion = useInclusion();
1427
+ const exclusion = useExclusion();
1363
1428
  invariant(isStringValue(fieldName));
1364
1429
  inclusion[fieldName] = defaultTo(exclusion.tests[fieldName], true);
1365
- return { when: when };
1430
+ return { when };
1431
+ /**
1432
+ * Specifies the inclusion criteria for the field in `include` function.
1433
+ */
1366
1434
  function when(condition) {
1367
- var context$1 = context.useX();
1368
- var inclusion = context$1.inclusion, exclusion = context$1.exclusion;
1435
+ const inclusion = useInclusion();
1436
+ const exclusion = useExclusion();
1369
1437
  // This callback will run as part of the "isExcluded" series of checks
1370
- inclusion[fieldName] = function () {
1438
+ inclusion[fieldName] = () => {
1371
1439
  if (hasOwnProperty(exclusion.tests, fieldName)) {
1372
1440
  // I suspect this code is technically unreachable because
1373
1441
  // if there are any skip/only rules applied to the current
@@ -1377,123 +1445,66 @@ function include(fieldName) {
1377
1445
  if (isStringValue(condition)) {
1378
1446
  return Boolean(exclusion.tests[condition]);
1379
1447
  }
1380
- return optionalFunctionValue(condition, optionalFunctionValue(produceSuiteResult));
1448
+ return optionalFunctionValue(condition, optionalFunctionValue(useCreateSuiteResult));
1381
1449
  };
1382
1450
  }
1383
1451
  }
1384
1452
 
1385
- /**
1386
- * Sets the suite to "eager" (fail fast) mode.
1387
- * Eager mode will skip running subsequent tests of a failing fields.
1388
- *
1389
- * @example
1390
- * // in the following example, the second test of username will not run
1391
- * // if the first test of username failed.
1392
- * const suite = create((data) => {
1393
- * eager();
1394
- *
1395
- * test('username', 'username is required', () => {
1396
- * enforce(data.username).isNotBlank();
1397
- * });
1398
- *
1399
- * test('username', 'username is too short', () => {
1400
- * enforce(data.username).longerThan(2);
1401
- * });
1402
- * });
1403
- */
1404
- function eager() {
1405
- setMode(Modes.EAGER);
1406
- }
1407
- function shouldSkipBasedOnMode(testObject) {
1408
- return isEager() && hasErrorsByTestObjects(testObject.fieldName);
1409
- }
1410
- function isEager() {
1411
- return isMode(Modes.EAGER);
1412
- }
1413
- function isMode(mode) {
1414
- var currentMode = context.useX().mode;
1415
- return currentMode[0] === mode;
1453
+ // eslint-disable-next-line max-statements
1454
+ function useAttemptRunTest(testObject) {
1455
+ useVerifyTestRun(testObject);
1456
+ if (testObject.isUntested()) {
1457
+ return useRunTest(testObject);
1458
+ }
1459
+ if (!testObject.isNonActionable()) {
1460
+ deferThrow(text(ErrorStrings.UNEXPECTED_TEST_REGISTRATION_ERROR, {
1461
+ testObject: JSON.stringify(testObject),
1462
+ }));
1463
+ }
1416
1464
  }
1417
- function setMode(nextMode) {
1418
- var mode = context.useX().mode;
1419
- mode[0] = nextMode;
1465
+ function runSyncTest(testObject) {
1466
+ return SuiteContext.run({ currentTest: testObject }, () => testObject.run());
1420
1467
  }
1421
-
1422
1468
  /**
1423
- * Conditionally omits tests from the suite.
1424
- *
1425
- * @example
1426
- *
1427
- * omitWhen(res => res.hasErrors('username'), () => {
1428
- * test('username', 'User already taken', async () => await doesUserExist(username)
1429
- * });
1469
+ * runs test, if async - adds to pending array
1430
1470
  */
1431
- function omitWhen(conditional, callback) {
1432
- isolate({ type: IsolateTypes.OMIT_WHEN }, function () {
1433
- context.run({
1434
- omitted: inActiveOmitWhen() ||
1435
- optionalFunctionValue(conditional, optionalFunctionValue(produceSuiteResult))
1436
- }, function () { return callback(); });
1437
- });
1438
- }
1439
- // Checks that we're currently in an active omitWhen block
1440
- function inActiveOmitWhen() {
1441
- return !!context.useX().omitted;
1442
- }
1443
-
1444
- /******************************************************************************
1445
- Copyright (c) Microsoft Corporation.
1446
-
1447
- Permission to use, copy, modify, and/or distribute this software for any
1448
- purpose with or without fee is hereby granted.
1449
-
1450
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
1451
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
1452
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
1453
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
1454
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
1455
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1456
- PERFORMANCE OF THIS SOFTWARE.
1457
- ***************************************************************************** */
1458
-
1459
- function __spreadArray(to, from, pack) {
1460
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
1461
- if (ar || !(i in from)) {
1462
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
1463
- ar[i] = from[i];
1464
- }
1465
- }
1466
- return to.concat(ar || Array.prototype.slice.call(from));
1467
- }
1468
-
1469
- function isSameProfileTest(testObject1, testObject2) {
1470
- return (testObject1.fieldName === testObject2.fieldName &&
1471
- testObject1.groupName === testObject2.groupName);
1472
- }
1473
-
1474
- function cancelOverriddenPendingTest(prevRunTestObject, currentRunTestObject) {
1475
- if (currentRunTestObject !== prevRunTestObject &&
1476
- isSameProfileTest(prevRunTestObject, currentRunTestObject) &&
1477
- prevRunTestObject.isPending()) {
1478
- prevRunTestObject.cancel();
1471
+ function useRunTest(testObject) {
1472
+ const VestBus = VestRuntime.useBus();
1473
+ // Run test callback.
1474
+ // If a promise is returned, set as async and
1475
+ // Move to pending list.
1476
+ const result = runSyncTest(testObject);
1477
+ try {
1478
+ // try catch for safe property access
1479
+ // in case object is an enforce chain
1480
+ if (isPromise(result)) {
1481
+ testObject.asyncTest = result;
1482
+ useRunAsyncTest(testObject);
1483
+ }
1484
+ else {
1485
+ onTestCompleted(VestBus, testObject);
1486
+ }
1487
+ }
1488
+ catch (e) {
1489
+ throw new Error(text(ErrorStrings.UNEXPECTED_TEST_REGISTRATION_ERROR, {
1490
+ testObject: JSON.stringify(testObject),
1491
+ error: e,
1492
+ }));
1479
1493
  }
1480
1494
  }
1481
-
1482
1495
  /**
1483
1496
  * Runs async test.
1484
1497
  */
1485
- function runAsyncTest(testObject) {
1486
- var asyncTest = testObject.asyncTest, message = testObject.message;
1498
+ function useRunAsyncTest(testObject) {
1499
+ const { asyncTest, message } = testObject;
1487
1500
  if (!isPromise(asyncTest))
1488
1501
  return;
1489
- var emit = useBus().emit;
1490
- var stateRef = useStateRef();
1491
- var done = context.bind({ stateRef: stateRef }, function () {
1492
- // invalidating the "produce" cache
1493
- useRefreshTestObjects();
1494
- emit(Events.TEST_COMPLETED, testObject);
1502
+ testObject.setPending();
1503
+ const VestBus = VestRuntime.useBus();
1504
+ const done = VestRuntime.persist(() => {
1505
+ onTestCompleted(VestBus, testObject);
1495
1506
  });
1496
- var fail = context.bind({ stateRef: stateRef }, function (rejectionMessage) {
1507
+ const fail = VestRuntime.persist((rejectionMessage) => {
1497
1508
  if (testObject.isCanceled()) {
1498
1509
  return;
1499
1510
  }
@@ -1503,232 +1514,256 @@ function runAsyncTest(testObject) {
1503
1514
  testObject.fail();
1504
1515
  done();
1505
1516
  });
1506
- try {
1507
- asyncTest.then(done, fail);
1517
+ asyncTest.then(done, fail);
1518
+ }
1519
+ function onTestCompleted(VestBus, testObject) {
1520
+ // Attempts passing if the test is not already failed.
1521
+ // or is not canceled/omitted.
1522
+ testObject.pass();
1523
+ VestBus.emit(Events.TEST_COMPLETED, testObject);
1524
+ }
1525
+
1526
+ // @vx-allow use-use
1527
+ function wrapTestMemo(test) {
1528
+ function memo(fieldName, ...args) {
1529
+ const [deps, testFn, msg] = args.reverse();
1530
+ // Implicit dependency for better specificity
1531
+ const dependencies = [
1532
+ useSuiteId(),
1533
+ fieldName,
1534
+ VestRuntime.useCurrentCursor(),
1535
+ ].concat(deps);
1536
+ return useGetTestFromCache(dependencies, cacheAction);
1537
+ function cacheAction() {
1538
+ return test(fieldName, msg, testFn);
1539
+ }
1508
1540
  }
1509
- catch (e) {
1510
- // We will probably never get here, unless the consumer uses a buggy custom Promise
1511
- // implementation that behaves differently than the native one, or if they for some
1512
- fail();
1541
+ return memo;
1542
+ }
1543
+ function useGetTestFromCache(dependencies, cacheAction) {
1544
+ const cache = useTestMemoCache();
1545
+ const cached = cache.get(dependencies);
1546
+ if (isNull(cached)) {
1547
+ // cache miss
1548
+ return cache(dependencies, cacheAction);
1549
+ }
1550
+ const [, cachedValue] = cached;
1551
+ if (cachedValue.isCanceled()) {
1552
+ // cache hit, but test is canceled
1553
+ cache.invalidate(dependencies);
1554
+ return cache(dependencies, cacheAction);
1555
+ }
1556
+ IsolateTest.setNode(cachedValue);
1557
+ return cachedValue;
1558
+ }
1559
+
1560
+ // @vx-allow use-use
1561
+ function vestTest(fieldName, ...args) {
1562
+ const [message, testFn, key] = (isFunction(args[1]) ? args : [undefined, ...args]);
1563
+ validateTestParams(fieldName, testFn);
1564
+ const groupName = useGroupName();
1565
+ const emit = VestRuntime.useEmit();
1566
+ const testObjectInput = { fieldName, groupName, key, message, testFn };
1567
+ // This invalidates the suite cache.
1568
+ emit(Events.TEST_RUN_STARTED);
1569
+ return IsolateTest.create(useAttemptRunTest, testObjectInput);
1570
+ }
1571
+ const test = assign(vestTest, {
1572
+ memo: wrapTestMemo(vestTest),
1573
+ });
1574
+ function validateTestParams(fieldName, testFn) {
1575
+ const fnName = 'test';
1576
+ invariant(isStringValue(fieldName), text(ErrorStrings.INVALID_PARAM_PASSED_TO_FUNCTION, {
1577
+ fn_name: fnName,
1578
+ param: 'fieldName',
1579
+ expected: 'string',
1580
+ }));
1581
+ invariant(isFunction(testFn), text(ErrorStrings.INVALID_PARAM_PASSED_TO_FUNCTION, {
1582
+ fn_name: fnName,
1583
+ param: 'callback',
1584
+ expected: 'function',
1585
+ }));
1586
+ }
1587
+
1588
+ function getTypedMethods() {
1589
+ return {
1590
+ group,
1591
+ include,
1592
+ omitWhen,
1593
+ only,
1594
+ optional,
1595
+ skip,
1596
+ skipWhen,
1597
+ test,
1598
+ };
1599
+ }
1600
+
1601
+ function useDeferDoneCallback(doneCallback, fieldName) {
1602
+ const [, setFieldCallbacks] = useFieldCallbacks();
1603
+ const [, setDoneCallbacks] = useDoneCallbacks();
1604
+ if (fieldName) {
1605
+ setFieldCallbacks(fieldCallbacks => assign(fieldCallbacks, {
1606
+ [fieldName]: (fieldCallbacks[fieldName] || []).concat(doneCallback),
1607
+ }));
1608
+ return;
1513
1609
  }
1610
+ setDoneCallbacks(doneCallbacks => doneCallbacks.concat(doneCallback));
1514
1611
  }
1515
1612
 
1516
1613
  /**
1517
- * Runs sync tests - or extracts promise.
1614
+ * DONE is here and not in its own module to prevent circular dependency issues.
1518
1615
  */
1519
- function runSyncTest(testObject) {
1520
- return context.run({ currentTest: testObject }, function () { return testObject.run(); });
1616
+ function shouldSkipDoneRegistration(callback, fieldName, output) {
1617
+ var _a;
1618
+ // If we do not have any test runs for the current field
1619
+ return !!(!isFunction(callback) ||
1620
+ (fieldName && numberEquals((_a = output.tests[fieldName]) === null || _a === void 0 ? void 0 : _a.testCount, 0)));
1521
1621
  }
1522
1622
 
1623
+ function useSuiteRunResult() {
1624
+ return Object.freeze(assign({
1625
+ done: VestRuntime.persist(done),
1626
+ }, useCreateSuiteResult()));
1627
+ }
1523
1628
  /**
1524
- * Registers test, if async - adds to pending array
1629
+ * Registers done callbacks.
1630
+ * @register {Object} Vest output object.
1525
1631
  */
1526
- function registerTest(testObject) {
1527
- var bus = useBus();
1528
- // Run test callback.
1529
- // If a promise is returned, set as async and
1530
- // Move to pending list.
1531
- var result = runSyncTest(testObject);
1532
- try {
1533
- // try catch for safe property access
1534
- // in case object is an enforce chain
1535
- if (isPromise(result)) {
1536
- testObject.asyncTest = result;
1537
- testObject.setPending();
1538
- runAsyncTest(testObject);
1539
- }
1540
- else {
1541
- bus.emit(Events.TEST_COMPLETED, testObject);
1542
- }
1632
+ // @vx-allow use-use
1633
+ function done(...args) {
1634
+ const [callback, fieldName] = args.reverse();
1635
+ const output = useSuiteRunResult();
1636
+ if (shouldSkipDoneRegistration(callback, fieldName, output)) {
1637
+ return output;
1543
1638
  }
1544
- catch (e) {
1545
- throw new Error("Unexpected error encountered during test registration.\n Test Object: ".concat(JSON.stringify(testObject), ".\n Error: ").concat(e, "."));
1639
+ const useDoneCallback = () => callback(useCreateSuiteResult());
1640
+ if (!TestWalker.hasRemainingTests(fieldName)) {
1641
+ useDoneCallback();
1642
+ return output;
1546
1643
  }
1644
+ useDeferDoneCallback(useDoneCallback, fieldName);
1645
+ return output;
1547
1646
  }
1548
1647
 
1549
- /**
1550
- * This module serves as the "collision detection" mechanism for Vest.
1551
- * It is used to ensure that tests are not called in a different order than
1552
- * they were called in the previous run.
1553
- * If they are, it will throw a deferred error unless explicitly allowed.
1554
- *
1555
- * For now it seems pretty safe, and it covers most common use cases, but it can
1556
- * be improved in the future both in terms of performance and scenarios it covers.
1557
- */
1558
- // eslint-disable-next-line max-statements, max-lines-per-function
1559
- function useTestAtCursor(newTestObject) {
1560
- var testObjects = useTestObjects()[0];
1561
- var prevTests = testObjects.prev;
1562
- if (isEmpty(prevTests)) {
1563
- useSetTestAtCursor(newTestObject);
1564
- return newTestObject;
1565
- }
1566
- var prevTest = useGetTestAtCursor(prevTests);
1567
- if (!isNullish(newTestObject.key)) {
1568
- var nextTest_1 = handleKeyTest(newTestObject.key, newTestObject);
1569
- useSetTestAtCursor(nextTest_1);
1570
- return nextTest_1;
1571
- }
1572
- if (testReorderDetected(prevTest, newTestObject)) {
1573
- throwTestOrderError(prevTest, newTestObject);
1574
- removeAllNextTestsInIsolate();
1575
- // Need to see if this has any effect at all.
1576
- prevTest = null;
1577
- }
1578
- var nextTest = defaultTo(prevTest, newTestObject);
1579
- useSetTestAtCursor(nextTest);
1580
- return nextTest;
1581
- }
1582
- function removeAllNextTestsInIsolate() {
1583
- var cursorAt = useCursor().current();
1584
- // We actually don't mind mutating the state directly (as can be seen above). There is no harm in it
1585
- // since we're only touching the "prev" state. The reason we still use the setter function is
1586
- // to prevent future headaches if we ever do need to rely on prev-state immutability.
1587
- useSetTests(function (current) {
1588
- current.splice(cursorAt);
1589
- return current;
1590
- });
1648
+ function validateSuiteCallback(suiteCallback) {
1649
+ invariant(isFunction(suiteCallback), ErrorStrings.SUITE_MUST_BE_INITIALIZED_WITH_FUNCTION);
1591
1650
  }
1592
- function useSetTestAtCursor(testObject) {
1593
- var cursorPath = useCurrentPath();
1594
- useSetTests(function (tests) {
1595
- return nestedArray.setValueAtPath(tests, cursorPath, testObject);
1651
+
1652
+ // @vx-allow use-use
1653
+ function createSuite(...args) {
1654
+ const [suiteCallback, suiteName] = args.reverse();
1655
+ validateSuiteCallback(suiteCallback);
1656
+ // Create a stateRef for the suite
1657
+ // It holds the suite's persisted values that may remain between runs.
1658
+ const stateRef = useCreateVestState({ suiteName });
1659
+ function suite(...args) {
1660
+ return SuiteContext.run({}, () => {
1661
+ // eslint-disable-next-line vest-internal/use-use
1662
+ const emit = VestRuntime.useEmit();
1663
+ emit(Events.SUITE_RUN_STARTED);
1664
+ return IsolateSuite.create(useRunSuiteCallback(suiteCallback, ...args));
1665
+ }).output;
1666
+ }
1667
+ // Assign methods to the suite
1668
+ // We do this within the VestRuntime so that the suite methods
1669
+ // will be bound to the suite's stateRef and be able to access it.
1670
+ return VestRuntime.Run(stateRef, () => {
1671
+ useInitVestBus();
1672
+ return assign(
1673
+ // We're also binding the suite to the stateRef, so that the suite
1674
+ // can access the stateRef when it's called.
1675
+ VestRuntime.persist(suite), Object.assign(Object.assign({ get: VestRuntime.persist(useCreateSuiteResult), remove: VestRuntime.usePrepareEmitter(Events.REMOVE_FIELD), reset: VestRuntime.usePrepareEmitter(Events.RESET_SUITE), resetField: VestRuntime.usePrepareEmitter(Events.RESET_FIELD) }, bindSuiteSelectors(VestRuntime.persist(useCreateSuiteResult))), getTypedMethods()));
1596
1676
  });
1597
1677
  }
1598
- function useGetTestAtCursor(tests) {
1599
- var cursorPath = useCurrentPath();
1600
- return nestedArray.valueAtPath(tests, cursorPath);
1601
- }
1602
- function testReorderDetected(prevTest, newTest) {
1603
- return isNotEmpty(prevTest) && !isSameProfileTest(prevTest, newTest);
1604
- }
1605
- function throwTestOrderError(prevTest, newTestObject) {
1606
- if (shouldAllowReorder()) {
1607
- return;
1608
- }
1609
- deferThrow("Vest Critical Error: Tests called in different order than previous run.\n expected: ".concat(prevTest.fieldName, "\n received: ").concat(newTestObject.fieldName, "\n This can happen on one of two reasons:\n 1. You're using if/else statements to conditionally select tests. Instead, use \"skipWhen\".\n 2. You are iterating over a list of tests, and their order changed. Use \"each\" and a custom key prop so that Vest retains their state."));
1610
- }
1611
- function handleKeyTest(key, newTestObject) {
1612
- var prevTestByKey = usePrevTestByKey(key);
1613
- var nextTest = newTestObject;
1614
- if (prevTestByKey) {
1615
- nextTest = prevTestByKey;
1616
- }
1617
- useRetainTestKey(key, nextTest);
1618
- return nextTest;
1678
+ function useRunSuiteCallback(suiteCallback, ...args) {
1679
+ const emit = VestRuntime.useEmit();
1680
+ return () => {
1681
+ suiteCallback(...args);
1682
+ emit(Events.SUITE_CALLBACK_RUN_FINISHED);
1683
+ return useSuiteRunResult();
1684
+ };
1619
1685
  }
1620
1686
 
1621
- // eslint-disable-next-line max-statements
1622
- function registerPrevRunTest(testObject) {
1623
- var cursor = useCursor();
1624
- if (shouldSkipBasedOnMode(testObject)) {
1625
- testObject.skip();
1626
- useTestAtCursor(testObject);
1627
- cursor.next();
1628
- return testObject;
1629
- }
1630
- var prevRunTest = useTestAtCursor(testObject);
1631
- if (inActiveOmitWhen() || optionalFiedIsApplied(testObject.fieldName)) {
1632
- prevRunTest.omit();
1633
- cursor.next();
1634
- return prevRunTest;
1635
- }
1636
- if (isExcluded(testObject)) {
1637
- // We're forcing skipping the pending test
1638
- // if we're directly within a skipWhen block
1639
- // This mostly means that we're probably giving
1640
- // up on this async test intentionally.
1641
- prevRunTest.skip(isExcludedIndividually());
1642
- cursor.next();
1643
- return prevRunTest;
1644
- }
1645
- cancelOverriddenPendingTest(prevRunTest, testObject);
1646
- useSetTestAtCursor(testObject);
1647
- registerTestObjectByTier(testObject);
1648
- cursor.next();
1649
- return testObject;
1650
- }
1651
- function registerTestObjectByTier(testObject) {
1652
- if (testObject.isUntested()) {
1653
- registerTest(testObject);
1654
- }
1655
- else if (isPromise(testObject.asyncTest)) {
1656
- testObject.setPending();
1657
- runAsyncTest(testObject);
1687
+ class IsolateEach extends Isolate {
1688
+ constructor() {
1689
+ super(...arguments);
1690
+ this.allowReorder = true;
1658
1691
  }
1659
1692
  }
1660
1693
 
1661
- /* eslint-disable jest/valid-title */
1662
- function bindTestMemo(test) {
1663
- var cache = cache$2(10); // arbitrary cache size
1664
- // eslint-disable-next-line max-statements
1665
- function memo(fieldName) {
1666
- var args = [];
1667
- for (var _i = 1; _i < arguments.length; _i++) {
1668
- args[_i - 1] = arguments[_i];
1669
- }
1670
- var cursorAt = useCursor().current();
1671
- var _a = args.reverse(), deps = _a[0], testFn = _a[1], msg = _a[2];
1672
- // Implicit dependency for more specificity
1673
- var dependencies = [useSuiteId(), fieldName, cursorAt].concat(deps);
1674
- var cached = cache.get(dependencies);
1675
- if (isNull(cached)) {
1676
- // cache miss
1677
- return cache(dependencies, function () { return test(fieldName, msg, testFn); });
1678
- }
1679
- if (cached[1].isCanceled()) {
1680
- // cache hit, but test is canceled
1681
- cache.invalidate(dependencies);
1682
- return cache(dependencies, function () { return test(fieldName, msg, testFn); });
1683
- }
1684
- return registerPrevRunTest(cached[1]);
1685
- }
1686
- return memo;
1694
+ /**
1695
+ * Iterates over an array of items, allowing to run tests individually per item.
1696
+ *
1697
+ * Requires setting a "key" property on each item tested.
1698
+ *
1699
+ * @example
1700
+ *
1701
+ * each(itemsArray, (item) => {
1702
+ * test(item.name, 'Item value must not be empty', () => {
1703
+ * enforce(item.value).isNotEmpty();
1704
+ * }, item.id)
1705
+ * })
1706
+ */
1707
+ function each(list, callback) {
1708
+ invariant(isFunction(callback), ErrorStrings.EACH_CALLBACK_MUST_BE_A_FUNCTION);
1709
+ IsolateEach.create(() => {
1710
+ list.forEach((arg, index) => {
1711
+ callback(arg, index);
1712
+ });
1713
+ });
1687
1714
  }
1688
1715
 
1689
- function testBase(fieldName) {
1690
- var args = [];
1691
- for (var _i = 1; _i < arguments.length; _i++) {
1692
- args[_i - 1] = arguments[_i];
1693
- }
1694
- var _a = (isFunction(args[1]) ? args : __spreadArray([undefined], args, true)), message = _a[0], testFn = _a[1], key = _a[2];
1695
- invariant(isStringValue(fieldName), incompatibleParamsError('fieldName', 'string'));
1696
- invariant(isFunction(testFn), incompatibleParamsError('Test callback', 'function'));
1697
- var context$1 = context.useX();
1698
- var testObject = new VestTest(fieldName, testFn, {
1699
- message: message,
1700
- groupName: context$1.groupName,
1701
- key: key
1716
+ function group(groupName, callback) {
1717
+ return Isolate.create(() => {
1718
+ SuiteContext.run({ groupName }, callback);
1702
1719
  });
1703
- return registerPrevRunTest(testObject);
1704
1720
  }
1721
+
1705
1722
  /**
1706
- * Represents a single case in a validation suite.
1723
+ * Creates a static suite for server-side validation.
1724
+ *
1725
+ * @param {Function} validationFn - The validation function that defines the suite's tests.
1726
+ * @returns {Function} - A function that runs the validations defined in the suite.
1707
1727
  *
1708
1728
  * @example
1729
+ * import { staticSuite, test, enforce } from 'vest';
1709
1730
  *
1710
- * test("username", "Username is required", () => {
1711
- * enforce(data.username).isNotBlank();
1731
+ * const suite = staticSuite(data => {
1732
+ * test('username', 'username is required', () => {
1733
+ * enforce(data.username).isNotEmpty();
1734
+ * });
1712
1735
  * });
1736
+ *
1737
+ * suite(data);
1713
1738
  */
1714
- var test = assign(testBase, {
1715
- memo: bindTestMemo(testBase)
1716
- });
1717
- function incompatibleParamsError(name, expected) {
1718
- return "Incompatible params passed to test function. ".concat(name, " must be a ").concat(expected);
1739
+ function staticSuite(suiteCallback) {
1740
+ return assign((...args) => createSuite(suiteCallback)(...args), Object.assign({}, getTypedMethods()));
1719
1741
  }
1720
1742
 
1721
- var ERROR_OUTSIDE_OF_TEST = "warn hook called outside of a test callback. It won't have an effect."
1722
- ;
1743
+ const ERROR_OUTSIDE_OF_TEST = ErrorStrings.WARN_MUST_BE_CALLED_FROM_TEST;
1723
1744
  /**
1724
- * Sets a running test to warn only mode.
1745
+ * Sets the severity level of a test to `warn`, allowing it to fail without marking the suite as invalid.
1746
+ * Use this function within the body of a test to create warn-only tests.
1747
+ *
1748
+ * @returns {void}
1749
+ *
1750
+ * @example
1751
+ * test('password', 'Your password strength is: WEAK', () => {
1752
+ * warn();
1753
+ *
1754
+ * enforce(data.password).matches(/0-9/);
1755
+ * });
1756
+ *
1757
+ * @limitations
1758
+ * - The `warn` function should only be used within the body of a `test` function.
1759
+ * - When using `warn()` in an async test, it should be called in the synchronous portion of the test, not after an `await` call or in the Promise body.
1760
+ * - It is recommended to call `warn()` at the top of the test function.
1725
1761
  */
1762
+ // @vx-allow use-use
1726
1763
  function warn() {
1727
- var ctx = context.useX('warn ' + ERROR_HOOK_CALLED_OUTSIDE);
1728
- invariant(ctx.currentTest, ERROR_OUTSIDE_OF_TEST);
1729
- ctx.currentTest.warn();
1764
+ const currentTest = useCurrentTest(ErrorStrings.HOOK_CALLED_OUTSIDE);
1765
+ invariant(currentTest, ERROR_OUTSIDE_OF_TEST);
1766
+ currentTest.warn();
1730
1767
  }
1731
1768
 
1732
- var VERSION = "5.0.0-dev-781e21";
1733
-
1734
- export { VERSION, context, create, each, eager, group, include, omitWhen, only, optional, skip, skipWhen, suiteSelectors, test, warn };
1769
+ export { Modes, createSuite as create, each, group, include, mode, omitWhen, only, optional, skip, skipWhen, staticSuite, suiteSelectors, test, warn };