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