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