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