vest 5.0.0-dev-040354 → 5.0.0-dev-9c596e

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