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

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