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

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