vest 5.0.0-dev-781e21 → 5.0.2-dev-d315d9

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