vest 5.0.0-dev-9c596e → 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/dist/cjs/classnames.development.js +1 -2
- package/dist/cjs/classnames.production.js +1 -1
- package/dist/cjs/enforce/compose.development.js +1 -5
- package/dist/cjs/enforce/compose.production.js +1 -1
- package/dist/cjs/enforce/compounds.development.js +3 -6
- package/dist/cjs/enforce/compounds.production.js +1 -1
- package/dist/cjs/enforce/schema.development.js +3 -6
- package/dist/cjs/enforce/schema.production.js +1 -1
- package/dist/cjs/parser.development.js +1 -4
- package/dist/cjs/parser.production.js +1 -1
- package/dist/cjs/promisify.development.js +1 -2
- package/dist/cjs/promisify.production.js +1 -1
- package/dist/cjs/vest.development.js +596 -764
- package/dist/cjs/vest.production.js +1 -1
- package/dist/es/classnames.development.js +1 -2
- package/dist/es/classnames.production.js +1 -1
- package/dist/es/parser.development.js +1 -2
- package/dist/es/parser.production.js +1 -1
- package/dist/es/promisify.development.js +1 -2
- package/dist/es/promisify.production.js +1 -1
- package/dist/es/vest.development.js +591 -757
- package/dist/es/vest.production.js +1 -1
- package/dist/umd/classnames.development.js +1 -2
- package/dist/umd/classnames.production.js +1 -1
- package/dist/umd/enforce/compose.development.js +1 -7
- package/dist/umd/enforce/compose.production.js +1 -1
- package/dist/umd/enforce/compounds.development.js +3 -6
- package/dist/umd/enforce/compounds.production.js +1 -1
- package/dist/umd/enforce/schema.development.js +3 -6
- package/dist/umd/enforce/schema.production.js +1 -1
- package/dist/umd/parser.development.js +1 -4
- package/dist/umd/parser.production.js +1 -1
- package/dist/umd/promisify.development.js +1 -2
- package/dist/umd/promisify.production.js +1 -1
- package/dist/umd/vest.development.js +599 -768
- package/dist/umd/vest.production.js +1 -1
- package/package.json +137 -136
- package/testUtils/TVestMock.ts +1 -1
- package/testUtils/asVestTest.ts +9 -0
- package/types/classnames.d.ts +82 -4
- package/types/classnames.d.ts.map +1 -1
- package/types/parser.d.ts +82 -4
- package/types/parser.d.ts.map +1 -1
- package/types/promisify.d.ts +82 -4
- package/types/promisify.d.ts.map +1 -1
- package/types/vest.d.ts +150 -138
- package/types/vest.d.ts.map +1 -1
|
@@ -1,29 +1,53 @@
|
|
|
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
|
var OptionalFieldTypes;
|
|
6
7
|
(function (OptionalFieldTypes) {
|
|
7
|
-
OptionalFieldTypes[OptionalFieldTypes["
|
|
8
|
-
OptionalFieldTypes[OptionalFieldTypes["
|
|
8
|
+
OptionalFieldTypes[OptionalFieldTypes["CUSTOM_LOGIC"] = 0] = "CUSTOM_LOGIC";
|
|
9
|
+
OptionalFieldTypes[OptionalFieldTypes["AUTO"] = 1] = "AUTO";
|
|
9
10
|
})(OptionalFieldTypes || (OptionalFieldTypes = {}));
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
+
});
|
|
31
|
+
}
|
|
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
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
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);
|
|
50
|
+
}
|
|
27
51
|
|
|
28
52
|
var Events;
|
|
29
53
|
(function (Events) {
|
|
@@ -34,250 +58,74 @@ var Events;
|
|
|
34
58
|
Events["RESET_FIELD"] = "reset_field";
|
|
35
59
|
Events["RESET_SUITE"] = "reset_suite";
|
|
36
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";
|
|
37
63
|
})(Events || (Events = {}));
|
|
38
64
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
let broke = false;
|
|
45
|
-
for (const isolate of startNode.children) {
|
|
46
|
-
if (broke) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (isNullish(visitOnly) || optionalFunctionValue(visitOnly, isolate)) {
|
|
50
|
-
callback(isolate, breakout);
|
|
51
|
-
}
|
|
52
|
-
if (broke) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
walk(isolate, (child, innerBreakout) => {
|
|
56
|
-
callback(child, () => {
|
|
57
|
-
innerBreakout();
|
|
58
|
-
breakout();
|
|
59
|
-
});
|
|
60
|
-
}, visitOnly);
|
|
61
|
-
}
|
|
62
|
-
function breakout() {
|
|
63
|
-
broke = true;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function some(startNode, predicate, visitOnly) {
|
|
67
|
-
let hasMatch = false;
|
|
68
|
-
walk(startNode, (node, breakout) => {
|
|
69
|
-
if (predicate(node)) {
|
|
70
|
-
breakout();
|
|
71
|
-
hasMatch = true;
|
|
72
|
-
}
|
|
73
|
-
}, visitOnly);
|
|
74
|
-
return hasMatch;
|
|
75
|
-
}
|
|
76
|
-
function has(startNode, match) {
|
|
77
|
-
return some(startNode, () => true, match);
|
|
78
|
-
}
|
|
79
|
-
function every(startNode, predicate, visitOnly) {
|
|
80
|
-
let hasMatch = true;
|
|
81
|
-
walk(startNode, (node, breakout) => {
|
|
82
|
-
if (!predicate(node)) {
|
|
83
|
-
breakout();
|
|
84
|
-
hasMatch = false;
|
|
85
|
-
}
|
|
86
|
-
}, visitOnly);
|
|
87
|
-
return hasMatch;
|
|
88
|
-
}
|
|
89
|
-
function pluck(startNode, predicate, visitOnly) {
|
|
90
|
-
walk(startNode, node => {
|
|
91
|
-
if (predicate(node) && node.parent) {
|
|
92
|
-
node.parent.removeChild(node);
|
|
93
|
-
}
|
|
94
|
-
}, visitOnly);
|
|
95
|
-
}
|
|
96
|
-
function closest(startNode, predicate) {
|
|
97
|
-
let current = startNode;
|
|
98
|
-
while (current.parent) {
|
|
99
|
-
if (predicate(current)) {
|
|
100
|
-
return current;
|
|
101
|
-
}
|
|
102
|
-
current = current.parent;
|
|
65
|
+
class IsolateSuite extends Isolate {
|
|
66
|
+
constructor() {
|
|
67
|
+
super(...arguments);
|
|
68
|
+
this.optional = {};
|
|
103
69
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
class Reconciler {
|
|
111
|
-
static reconciler(currentNode, historicNode) {
|
|
112
|
-
if (isNullish(historicNode)) {
|
|
113
|
-
return currentNode;
|
|
114
|
-
}
|
|
115
|
-
return currentNode;
|
|
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)),
|
|
75
|
+
});
|
|
116
76
|
}
|
|
117
|
-
|
|
77
|
+
getOptionalField(fieldName) {
|
|
118
78
|
var _a;
|
|
119
|
-
|
|
120
|
-
const historyNode = useHistoryNode();
|
|
121
|
-
let localHistoryNode = historyNode;
|
|
122
|
-
if (parent) {
|
|
123
|
-
// If we have a parent, we need to get the history node from the parent's children
|
|
124
|
-
// We take the history node from the cursor of the active node's children
|
|
125
|
-
localHistoryNode = (_a = historyNode === null || historyNode === void 0 ? void 0 : historyNode.at(useCurrentCursor())) !== null && _a !== void 0 ? _a : null;
|
|
126
|
-
}
|
|
127
|
-
const nextNode = this.reconciler(node, localHistoryNode);
|
|
128
|
-
invariant(nextNode);
|
|
129
|
-
if (Object.is(nextNode, node)) {
|
|
130
|
-
return [node, useRunAsNew(localHistoryNode, node, callback)];
|
|
131
|
-
}
|
|
132
|
-
return [nextNode, nextNode.output];
|
|
79
|
+
return (_a = this.optional[fieldName]) !== null && _a !== void 0 ? _a : {};
|
|
133
80
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const historyNode = useHistoryNode();
|
|
137
|
-
if (!historyNode || !testIsolate) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
historyNode.slice(useCurrentCursor());
|
|
81
|
+
getOptionalFields() {
|
|
82
|
+
return this.optional;
|
|
141
83
|
}
|
|
142
|
-
static handleCollision(newNode, prevNode) {
|
|
143
|
-
// we should base our calculation on the key property
|
|
144
|
-
if (newNode.usesKey()) {
|
|
145
|
-
return this.handleIsolateNodeWithKey(newNode);
|
|
146
|
-
}
|
|
147
|
-
if (this.nodeReorderDetected(newNode, prevNode)) {
|
|
148
|
-
this.onNodeReorder(newNode, prevNode);
|
|
149
|
-
}
|
|
150
|
-
return prevNode ? prevNode : newNode;
|
|
151
|
-
}
|
|
152
|
-
static nodeReorderDetected(newNode, prevNode) {
|
|
153
|
-
// This is a dummy return just to satisfy the linter. Overrides will supply the real implementation.
|
|
154
|
-
return !(newNode !== null && newNode !== void 0 ? newNode : prevNode);
|
|
155
|
-
}
|
|
156
|
-
static onNodeReorder(newNode, prevNode) {
|
|
157
|
-
this.removeAllNextNodesInIsolate();
|
|
158
|
-
// This is a dummy return just to satisfy the linter. Overrides will supply the real implementation.
|
|
159
|
-
return newNode !== null && newNode !== void 0 ? newNode : prevNode;
|
|
160
|
-
}
|
|
161
|
-
static handleIsolateNodeWithKey(node) {
|
|
162
|
-
invariant(node.usesKey());
|
|
163
|
-
const prevNodeByKey = useHistoryKey(node.key);
|
|
164
|
-
let nextNode = node;
|
|
165
|
-
if (!isNullish(prevNodeByKey)) {
|
|
166
|
-
nextNode = prevNodeByKey;
|
|
167
|
-
}
|
|
168
|
-
useSetIsolateKey(node.key, node);
|
|
169
|
-
return nextNode;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
function useRunAsNew(localHistoryNode, current, callback) {
|
|
173
|
-
const runtimeRoot = useRuntimeRoot();
|
|
174
|
-
// We're creating a new child isolate context where the local history node
|
|
175
|
-
// is the current history node, thus advancing the history cursor.
|
|
176
|
-
const output = PersistedContext.run(Object.assign({ historyNode: localHistoryNode, runtimeNode: current }, (!runtimeRoot && { runtimeRoot: current })), () => callback(current));
|
|
177
|
-
current.output = output;
|
|
178
|
-
return output;
|
|
179
84
|
}
|
|
180
85
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
this.parent = parent;
|
|
192
|
-
return this;
|
|
193
|
-
}
|
|
194
|
-
saveOutput(output) {
|
|
195
|
-
this.output = output;
|
|
196
|
-
return this;
|
|
197
|
-
}
|
|
198
|
-
setKey(key) {
|
|
199
|
-
this.key = key;
|
|
200
|
-
return this;
|
|
201
|
-
}
|
|
202
|
-
usesKey() {
|
|
203
|
-
return isNotNullish(this.key);
|
|
204
|
-
}
|
|
205
|
-
addChild(child) {
|
|
206
|
-
invariant(this.children);
|
|
207
|
-
this.children.push(child);
|
|
208
|
-
}
|
|
209
|
-
removeChild(node) {
|
|
210
|
-
var _a, _b;
|
|
211
|
-
this.children = (_b = (_a = this.children) === null || _a === void 0 ? void 0 : _a.filter(child => child !== node)) !== null && _b !== void 0 ? _b : null;
|
|
212
|
-
}
|
|
213
|
-
slice(at) {
|
|
214
|
-
if (isNullish(this.children)) {
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
this.children.length = at;
|
|
218
|
-
}
|
|
219
|
-
at(at) {
|
|
220
|
-
var _a, _b;
|
|
221
|
-
return (_b = (_a = this.children) === null || _a === void 0 ? void 0 : _a[at]) !== null && _b !== void 0 ? _b : null;
|
|
222
|
-
}
|
|
223
|
-
cursor() {
|
|
224
|
-
var _a, _b;
|
|
225
|
-
return (_b = (_a = this.children) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
|
|
226
|
-
}
|
|
227
|
-
shouldAllowReorder() {
|
|
228
|
-
var _a;
|
|
229
|
-
return (_a = closestExists(this, node => node.allowReorder)) !== null && _a !== void 0 ? _a : false;
|
|
230
|
-
}
|
|
231
|
-
get rootNode() {
|
|
232
|
-
var _a;
|
|
233
|
-
return (_a = closest(this, node => isNullish(node.parent))) !== null && _a !== void 0 ? _a : this;
|
|
234
|
-
}
|
|
235
|
-
static create(callback, data) {
|
|
236
|
-
return this.createImplementation(callback, data);
|
|
237
|
-
}
|
|
238
|
-
static createImplementation(callback, data) {
|
|
239
|
-
const parent = useIsolate();
|
|
240
|
-
const newCreatedNode = new this(data).setParent(parent);
|
|
241
|
-
const [nextIsolateChild, output] = this.reconciler.reconcile(newCreatedNode, callback);
|
|
242
|
-
nextIsolateChild.saveOutput(output);
|
|
243
|
-
this.setNode(nextIsolateChild);
|
|
244
|
-
return nextIsolateChild;
|
|
245
|
-
}
|
|
246
|
-
static setNode(node) {
|
|
247
|
-
const parent = useIsolate();
|
|
248
|
-
if (parent) {
|
|
249
|
-
useSetNextIsolateChild(node);
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
useSetHistory(node);
|
|
253
|
-
}
|
|
254
|
-
node.setParent(parent);
|
|
255
|
-
}
|
|
256
|
-
static is(node) {
|
|
257
|
-
return node instanceof Isolate;
|
|
258
|
-
}
|
|
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,
|
|
94
|
+
};
|
|
95
|
+
return VestRuntime.createRef(stateRef);
|
|
259
96
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
function nonMatchingFieldName(WithFieldName, fieldName) {
|
|
263
|
-
return !!fieldName && !matchingFieldName(WithFieldName, fieldName);
|
|
97
|
+
function useX() {
|
|
98
|
+
return VestRuntime.useXAppData();
|
|
264
99
|
}
|
|
265
|
-
function
|
|
266
|
-
return
|
|
100
|
+
function useDoneCallbacks() {
|
|
101
|
+
return useX().doneCallbacks();
|
|
267
102
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return (matchingFieldName(testObject1, testObject2.fieldName) &&
|
|
271
|
-
testObject1.groupName === testObject2.groupName &&
|
|
272
|
-
testObject1.key === testObject2.key);
|
|
103
|
+
function useFieldCallbacks() {
|
|
104
|
+
return useX().fieldCallbacks();
|
|
273
105
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
106
|
+
function useSuiteName() {
|
|
107
|
+
return useX().suiteName;
|
|
108
|
+
}
|
|
109
|
+
function useSuiteId() {
|
|
110
|
+
return useX().suiteId;
|
|
111
|
+
}
|
|
112
|
+
function useSuiteResultCache(action) {
|
|
113
|
+
const suiteResultCache = useX().suiteResultCache;
|
|
114
|
+
return suiteResultCache([useSuiteId()], action);
|
|
115
|
+
}
|
|
116
|
+
function useExpireSuiteResultCache() {
|
|
117
|
+
const suiteResultCache = useX().suiteResultCache;
|
|
118
|
+
suiteResultCache.invalidate([useSuiteId()]);
|
|
119
|
+
}
|
|
120
|
+
function useResetCallbacks() {
|
|
121
|
+
const [, , resetDoneCallbacks] = useDoneCallbacks();
|
|
122
|
+
const [, , resetFieldCallbacks] = useFieldCallbacks();
|
|
123
|
+
resetDoneCallbacks();
|
|
124
|
+
resetFieldCallbacks();
|
|
125
|
+
}
|
|
126
|
+
function useResetSuite() {
|
|
127
|
+
useResetCallbacks();
|
|
128
|
+
VestRuntime.reset();
|
|
281
129
|
}
|
|
282
130
|
|
|
283
131
|
var Severity;
|
|
@@ -295,135 +143,47 @@ function countKeyBySeverity(severity) {
|
|
|
295
143
|
? SeverityCount.ERROR_COUNT
|
|
296
144
|
: SeverityCount.WARN_COUNT;
|
|
297
145
|
}
|
|
146
|
+
var TestSeverity;
|
|
147
|
+
(function (TestSeverity) {
|
|
148
|
+
TestSeverity["Error"] = "error";
|
|
149
|
+
TestSeverity["Warning"] = "warning";
|
|
150
|
+
})(TestSeverity || (TestSeverity = {}));
|
|
298
151
|
|
|
299
|
-
|
|
300
|
-
function
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
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 = {}));
|
|
310
167
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
* summary object, while this one uses the actual validation state
|
|
314
|
-
*/
|
|
315
|
-
function hasErrorsByTestObjects(fieldName) {
|
|
316
|
-
return hasFailuresByTestObjects(Severity.ERRORS, fieldName);
|
|
317
|
-
}
|
|
318
|
-
function hasFailuresByTestObjects(severityKey, fieldName) {
|
|
319
|
-
return TestWalker.someTests(testObject => {
|
|
320
|
-
return hasFailuresByTestObject(testObject, severityKey, fieldName);
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
function hasGroupFailuresByTestObjects(severityKey, groupName, fieldName) {
|
|
324
|
-
return TestWalker.someTests(testObject => {
|
|
325
|
-
if (nonMatchingGroupName(testObject, groupName)) {
|
|
326
|
-
return false;
|
|
327
|
-
}
|
|
328
|
-
return hasFailuresByTestObject(testObject, severityKey, fieldName);
|
|
329
|
-
});
|
|
168
|
+
function nonMatchingFieldName(WithFieldName, fieldName) {
|
|
169
|
+
return !!fieldName && !matchingFieldName(WithFieldName, fieldName);
|
|
330
170
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
*/
|
|
334
|
-
function hasFailuresByTestObject(testObject, severityKey, fieldName) {
|
|
335
|
-
if (!testObject.hasFailures()) {
|
|
336
|
-
return false;
|
|
337
|
-
}
|
|
338
|
-
if (nonMatchingFieldName(testObject, fieldName)) {
|
|
339
|
-
return false;
|
|
340
|
-
}
|
|
341
|
-
if (nonMatchingSeverityProfile(severityKey, testObject)) {
|
|
342
|
-
return false;
|
|
343
|
-
}
|
|
344
|
-
return true;
|
|
171
|
+
function matchingFieldName(WithFieldName, fieldName) {
|
|
172
|
+
return !!(fieldName && WithFieldName.fieldName === fieldName);
|
|
345
173
|
}
|
|
346
174
|
|
|
347
|
-
|
|
348
|
-
(
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
})(Modes || (Modes = {}));
|
|
352
|
-
/**
|
|
353
|
-
* Sets the suite to "eager" (fail fast) mode.
|
|
354
|
-
* Eager mode will skip running subsequent tests of a failing fields.
|
|
355
|
-
*
|
|
356
|
-
* @example
|
|
357
|
-
* // in the following example, the second test of username will not run
|
|
358
|
-
* // if the first test of username failed.
|
|
359
|
-
* const suite = create((data) => {
|
|
360
|
-
* eager();
|
|
361
|
-
*
|
|
362
|
-
* test('username', 'username is required', () => {
|
|
363
|
-
* enforce(data.username).isNotBlank();
|
|
364
|
-
* });
|
|
365
|
-
*
|
|
366
|
-
* test('username', 'username is too short', () => {
|
|
367
|
-
* enforce(data.username).longerThan(2);
|
|
368
|
-
* });
|
|
369
|
-
* });
|
|
370
|
-
*/
|
|
371
|
-
// @vx-allow use-use
|
|
372
|
-
function mode(mode) {
|
|
373
|
-
const [, setMode] = useMode();
|
|
374
|
-
setMode(mode);
|
|
375
|
-
}
|
|
376
|
-
function useIsMode(mode) {
|
|
377
|
-
const [currentMode] = useMode();
|
|
378
|
-
return currentMode === mode;
|
|
379
|
-
}
|
|
380
|
-
function useIsEager() {
|
|
381
|
-
return useIsMode(Modes.EAGER);
|
|
382
|
-
}
|
|
383
|
-
function useShouldSkipBasedOnMode(testObject) {
|
|
384
|
-
return useIsEager() && hasErrorsByTestObjects(testObject.fieldName);
|
|
175
|
+
function isSameProfileTest(testObject1, testObject2) {
|
|
176
|
+
return (matchingFieldName(testObject1, testObject2.fieldName) &&
|
|
177
|
+
testObject1.groupName === testObject2.groupName &&
|
|
178
|
+
testObject1.key === testObject2.key);
|
|
385
179
|
}
|
|
386
180
|
|
|
387
|
-
|
|
388
|
-
if (
|
|
389
|
-
|
|
181
|
+
function cancelOverriddenPendingTest(prevRunTestObject, currentRunTestObject) {
|
|
182
|
+
if (currentRunTestObject !== prevRunTestObject &&
|
|
183
|
+
isSameProfileTest(prevRunTestObject, currentRunTestObject) &&
|
|
184
|
+
prevRunTestObject.isPending()) {
|
|
185
|
+
prevRunTestObject.cancel();
|
|
390
186
|
}
|
|
391
|
-
return assign({
|
|
392
|
-
exclusion: {
|
|
393
|
-
tests: {},
|
|
394
|
-
groups: {},
|
|
395
|
-
},
|
|
396
|
-
inclusion: {},
|
|
397
|
-
mode: tinyState.createTinyState(Modes.EAGER),
|
|
398
|
-
testMemoCache,
|
|
399
|
-
}, ctxRef);
|
|
400
|
-
});
|
|
401
|
-
function useCurrentTest(msg) {
|
|
402
|
-
return SuiteContext.useX(msg).currentTest;
|
|
403
|
-
}
|
|
404
|
-
function useGroupName() {
|
|
405
|
-
return SuiteContext.useX().groupName;
|
|
406
|
-
}
|
|
407
|
-
function useExclusion(hookError) {
|
|
408
|
-
return SuiteContext.useX(hookError).exclusion;
|
|
409
|
-
}
|
|
410
|
-
function useInclusion() {
|
|
411
|
-
return SuiteContext.useX().inclusion;
|
|
412
|
-
}
|
|
413
|
-
function useMode() {
|
|
414
|
-
return SuiteContext.useX().mode();
|
|
415
|
-
}
|
|
416
|
-
function useSkipped() {
|
|
417
|
-
var _a;
|
|
418
|
-
return (_a = SuiteContext.useX().skipped) !== null && _a !== void 0 ? _a : false;
|
|
419
|
-
}
|
|
420
|
-
function useOmitted() {
|
|
421
|
-
var _a;
|
|
422
|
-
return (_a = SuiteContext.useX().omitted) !== null && _a !== void 0 ? _a : false;
|
|
423
|
-
}
|
|
424
|
-
const testMemoCache = cache(10);
|
|
425
|
-
function useTestMemoCache() {
|
|
426
|
-
return SuiteContext.useX().testMemoCache;
|
|
427
187
|
}
|
|
428
188
|
|
|
429
189
|
var _a, _b;
|
|
@@ -446,6 +206,25 @@ class SuiteSummary extends SummaryBase {
|
|
|
446
206
|
}
|
|
447
207
|
_a = Severity.ERRORS, _b = Severity.WARNINGS;
|
|
448
208
|
|
|
209
|
+
class SummaryFailure {
|
|
210
|
+
constructor(fieldName, message, groupName) {
|
|
211
|
+
this.fieldName = fieldName;
|
|
212
|
+
this.message = message;
|
|
213
|
+
this.groupName = groupName;
|
|
214
|
+
}
|
|
215
|
+
static fromTestObject(testObject) {
|
|
216
|
+
return new SummaryFailure(testObject.fieldName, testObject.message, testObject.groupName);
|
|
217
|
+
}
|
|
218
|
+
toString() {
|
|
219
|
+
return this.message || '';
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const nonMatchingGroupName = bindNot(matchingGroupName);
|
|
224
|
+
function matchingGroupName(testObject, groupName) {
|
|
225
|
+
return testObject.groupName === groupName;
|
|
226
|
+
}
|
|
227
|
+
|
|
449
228
|
function useShouldAddValidProperty(fieldName) {
|
|
450
229
|
// Is the field optional, and the optional condition is applied
|
|
451
230
|
if (useIsOptionalFiedApplied(fieldName)) {
|
|
@@ -482,7 +261,10 @@ function useShouldAddValidPropertyInGroup(groupName, fieldName) {
|
|
|
482
261
|
// Does the given field have any pending tests that are not optional?
|
|
483
262
|
function useHasNonOptionalIncomplete(fieldName) {
|
|
484
263
|
return TestWalker.someIncompleteTests(testObject => {
|
|
485
|
-
|
|
264
|
+
if (nonMatchingFieldName(testObject, fieldName)) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
return !useIsOptionalFiedApplied(fieldName);
|
|
486
268
|
});
|
|
487
269
|
}
|
|
488
270
|
// Do the given group/field have any pending tests that are not optional?
|
|
@@ -491,15 +273,12 @@ function useHasNonOptionalIncompleteByGroup(groupName, fieldName) {
|
|
|
491
273
|
if (nonMatchingGroupName(testObject, groupName)) {
|
|
492
274
|
return false;
|
|
493
275
|
}
|
|
494
|
-
|
|
276
|
+
if (nonMatchingFieldName(testObject, fieldName)) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
return !useIsOptionalFiedApplied(fieldName);
|
|
495
280
|
});
|
|
496
281
|
}
|
|
497
|
-
function useIsTestObjectOptional(testObject, fieldName) {
|
|
498
|
-
if (nonMatchingFieldName(testObject, fieldName)) {
|
|
499
|
-
return false;
|
|
500
|
-
}
|
|
501
|
-
return useIsOptionalFiedApplied(fieldName);
|
|
502
|
-
}
|
|
503
282
|
// Did all of the tests for the provided field run/omit?
|
|
504
283
|
// This makes sure that the fields are not skipped or pending.
|
|
505
284
|
function useNoMissingTests(fieldName) {
|
|
@@ -529,16 +308,15 @@ function useNoMissingTestsLogic(testObject, fieldName) {
|
|
|
529
308
|
* or if it is marked as optional, even if the optional check did not apply yet -
|
|
530
309
|
* but the test did not reach its final state.
|
|
531
310
|
*/
|
|
532
|
-
return (
|
|
311
|
+
return (testObject.isOmitted() ||
|
|
533
312
|
testObject.isTested() ||
|
|
534
|
-
testObject
|
|
313
|
+
useOptionalTestAwaitsResolution(testObject));
|
|
535
314
|
}
|
|
536
315
|
function useOptionalTestAwaitsResolution(testObject) {
|
|
537
316
|
// Does the test belong to an optional field,
|
|
538
317
|
// and the test itself is still in an indeterminate state?
|
|
539
318
|
var _a;
|
|
540
|
-
return (((_a =
|
|
541
|
-
OptionalFieldTypes.Delayed && testObject.awaitsResolution());
|
|
319
|
+
return (((_a = VestRuntime.useAvailableRoot()) === null || _a === void 0 ? void 0 : _a.getOptionalField(testObject.fieldName).type) === OptionalFieldTypes.AUTO && testObject.awaitsResolution());
|
|
542
320
|
}
|
|
543
321
|
|
|
544
322
|
function useProduceSuiteSummary() {
|
|
@@ -553,13 +331,12 @@ function useProduceSuiteSummary() {
|
|
|
553
331
|
return countFailures(summary);
|
|
554
332
|
}
|
|
555
333
|
function appendFailures(key, failures, testObject) {
|
|
334
|
+
if (testObject.isOmitted()) {
|
|
335
|
+
return failures;
|
|
336
|
+
}
|
|
556
337
|
const shouldAppend = key === Severity.WARNINGS ? testObject.isWarning() : testObject.isFailing();
|
|
557
338
|
if (shouldAppend) {
|
|
558
|
-
return failures.concat(
|
|
559
|
-
fieldName: testObject.fieldName,
|
|
560
|
-
groupName: testObject.groupName,
|
|
561
|
-
message: testObject.message,
|
|
562
|
-
});
|
|
339
|
+
return failures.concat(SummaryFailure.fromTestObject(testObject));
|
|
563
340
|
}
|
|
564
341
|
return failures;
|
|
565
342
|
}
|
|
@@ -659,6 +436,22 @@ function collectAll(testGroup, severityKey) {
|
|
|
659
436
|
return output;
|
|
660
437
|
}
|
|
661
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
|
+
}
|
|
662
455
|
// eslint-disable-next-line max-lines-per-function, max-statements
|
|
663
456
|
function suiteSelectors(summary) {
|
|
664
457
|
const selectors = {
|
|
@@ -777,11 +570,15 @@ function getFailure(severity, summary, fieldName) {
|
|
|
777
570
|
}
|
|
778
571
|
|
|
779
572
|
function useCreateSuiteResult() {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
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
|
+
}));
|
|
581
|
+
});
|
|
785
582
|
}
|
|
786
583
|
|
|
787
584
|
/**
|
|
@@ -877,8 +674,6 @@ function useIsExcluded(testObject) {
|
|
|
877
674
|
// If there is _ANY_ `only`ed test (and we already know this one isn't) return true
|
|
878
675
|
if (hasIncludedTests(keyTests)) {
|
|
879
676
|
// Check if inclusion rules for this field (`include` hook)
|
|
880
|
-
// TODO: Check if this may need to be moved outside of the condition.
|
|
881
|
-
// What if there are no included tests? This shouldn't run then?
|
|
882
677
|
return !optionalFunctionValue(inclusion[fieldName]);
|
|
883
678
|
}
|
|
884
679
|
// We're done here. This field is not excluded
|
|
@@ -999,49 +794,64 @@ function useForceSkipIfInSkipWhen(testNode) {
|
|
|
999
794
|
return testNode;
|
|
1000
795
|
}
|
|
1001
796
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
if (isNullish(historyNode)) {
|
|
1009
|
-
return this.handleNoHistoryNode(currentNode);
|
|
1010
|
-
}
|
|
1011
|
-
if (!IsolateTest.is(historyNode)) {
|
|
1012
|
-
return currentNode;
|
|
1013
|
-
}
|
|
1014
|
-
const reconcilerOutput = this.pickNode(historyNode, currentNode);
|
|
1015
|
-
cancelOverriddenPendingTestOnTestReRun(reconcilerOutput, currentNode, historyNode);
|
|
1016
|
-
return reconcilerOutput;
|
|
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;
|
|
1017
803
|
}
|
|
1018
|
-
|
|
1019
|
-
return
|
|
804
|
+
if (isNullish(historyNode)) {
|
|
805
|
+
return handleNoHistoryNode(currentNode);
|
|
1020
806
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
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));
|
|
1029
821
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
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.
|
|
1033
828
|
return newNode;
|
|
1034
829
|
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
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;
|
|
1038
838
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
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));
|
|
1044
853
|
}
|
|
854
|
+
return testNode;
|
|
1045
855
|
}
|
|
1046
856
|
function cancelOverriddenPendingTestOnTestReRun(nextNode, currentNode, prevTestObject) {
|
|
1047
857
|
if (nextNode === currentNode && IsolateTest.is(currentNode)) {
|
|
@@ -1058,30 +868,6 @@ function throwTestOrderError(newNode, prevNode) {
|
|
|
1058
868
|
}));
|
|
1059
869
|
}
|
|
1060
870
|
|
|
1061
|
-
function StateMachine(machine) {
|
|
1062
|
-
let state = machine.initial;
|
|
1063
|
-
const api = { getState, transition };
|
|
1064
|
-
return api;
|
|
1065
|
-
function getState() {
|
|
1066
|
-
return state;
|
|
1067
|
-
}
|
|
1068
|
-
function transition(action, payload) {
|
|
1069
|
-
const transitionTo = machine.states[state][action];
|
|
1070
|
-
let target = transitionTo;
|
|
1071
|
-
if (Array.isArray(target)) {
|
|
1072
|
-
const [, conditional] = target;
|
|
1073
|
-
if (!conditional(payload)) {
|
|
1074
|
-
return;
|
|
1075
|
-
}
|
|
1076
|
-
target = target[0];
|
|
1077
|
-
}
|
|
1078
|
-
if (!target || target === state) {
|
|
1079
|
-
return;
|
|
1080
|
-
}
|
|
1081
|
-
state = target;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
871
|
var TestStatus;
|
|
1086
872
|
(function (TestStatus) {
|
|
1087
873
|
TestStatus["UNTESTED"] = "UNTESTED";
|
|
@@ -1104,32 +890,21 @@ function createTestStateMachine() {
|
|
|
1104
890
|
const machine = {
|
|
1105
891
|
initial: TestStatus.UNTESTED,
|
|
1106
892
|
states: {
|
|
893
|
+
'*': {
|
|
894
|
+
[TestStatus.OMITTED]: TestStatus.OMITTED,
|
|
895
|
+
[TestAction.RESET]: TestStatus.UNTESTED,
|
|
896
|
+
},
|
|
1107
897
|
[TestStatus.UNTESTED]: {
|
|
1108
898
|
[TestStatus.CANCELED]: TestStatus.CANCELED,
|
|
1109
899
|
[TestStatus.FAILED]: TestStatus.FAILED,
|
|
1110
|
-
[TestStatus.OMITTED]: TestStatus.OMITTED,
|
|
1111
900
|
[TestStatus.PASSING]: TestStatus.PASSING,
|
|
1112
901
|
[TestStatus.PENDING]: TestStatus.PENDING,
|
|
1113
902
|
[TestStatus.SKIPPED]: TestStatus.SKIPPED,
|
|
1114
903
|
[TestStatus.WARNING]: TestStatus.WARNING,
|
|
1115
904
|
},
|
|
1116
|
-
[TestStatus.SKIPPED]: {
|
|
1117
|
-
[TestAction.RESET]: TestStatus.UNTESTED,
|
|
1118
|
-
},
|
|
1119
|
-
[TestStatus.FAILED]: {
|
|
1120
|
-
[TestAction.RESET]: TestStatus.UNTESTED,
|
|
1121
|
-
},
|
|
1122
|
-
[TestStatus.WARNING]: {
|
|
1123
|
-
[TestAction.RESET]: TestStatus.UNTESTED,
|
|
1124
|
-
},
|
|
1125
|
-
[TestStatus.PASSING]: {
|
|
1126
|
-
[TestAction.RESET]: TestStatus.UNTESTED,
|
|
1127
|
-
},
|
|
1128
905
|
[TestStatus.PENDING]: {
|
|
1129
|
-
[TestAction.RESET]: TestStatus.UNTESTED,
|
|
1130
906
|
[TestStatus.CANCELED]: TestStatus.CANCELED,
|
|
1131
907
|
[TestStatus.FAILED]: TestStatus.FAILED,
|
|
1132
|
-
[TestStatus.OMITTED]: TestStatus.OMITTED,
|
|
1133
908
|
[TestStatus.PASSING]: TestStatus.PASSING,
|
|
1134
909
|
[TestStatus.SKIPPED]: [
|
|
1135
910
|
TestStatus.SKIPPED,
|
|
@@ -1137,12 +912,12 @@ const machine = {
|
|
|
1137
912
|
],
|
|
1138
913
|
[TestStatus.WARNING]: TestStatus.WARNING,
|
|
1139
914
|
},
|
|
1140
|
-
[TestStatus.
|
|
1141
|
-
|
|
1142
|
-
},
|
|
1143
|
-
[TestStatus.
|
|
1144
|
-
|
|
1145
|
-
},
|
|
915
|
+
[TestStatus.SKIPPED]: {},
|
|
916
|
+
[TestStatus.FAILED]: {},
|
|
917
|
+
[TestStatus.WARNING]: {},
|
|
918
|
+
[TestStatus.PASSING]: {},
|
|
919
|
+
[TestStatus.CANCELED]: {},
|
|
920
|
+
[TestStatus.OMITTED]: {},
|
|
1146
921
|
},
|
|
1147
922
|
};
|
|
1148
923
|
/* eslint-enable sort-keys */
|
|
@@ -1292,79 +1067,262 @@ class IsolateTest extends Isolate {
|
|
|
1292
1067
|
}
|
|
1293
1068
|
}
|
|
1294
1069
|
IsolateTest.reconciler = IsolateTestReconciler;
|
|
1295
|
-
var TestSeverity;
|
|
1296
|
-
(function (TestSeverity) {
|
|
1297
|
-
TestSeverity["Error"] = "error";
|
|
1298
|
-
TestSeverity["Warning"] = "warning";
|
|
1299
|
-
})(TestSeverity || (TestSeverity = {}));
|
|
1300
1070
|
|
|
1301
1071
|
class TestWalker {
|
|
1302
1072
|
static defaultRoot() {
|
|
1303
|
-
return
|
|
1073
|
+
return VestRuntime.useAvailableRoot();
|
|
1304
1074
|
}
|
|
1305
1075
|
static hasNoTests(root = TestWalker.defaultRoot()) {
|
|
1306
1076
|
if (!root)
|
|
1307
1077
|
return true;
|
|
1308
|
-
return !has(root, IsolateTest.is);
|
|
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();
|
|
1309
1228
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
return false;
|
|
1313
|
-
return some(root, isolate => {
|
|
1314
|
-
IsolateTest.isX(isolate);
|
|
1315
|
-
return isolate.isPending() && predicate(isolate);
|
|
1316
|
-
}, IsolateTest.is);
|
|
1229
|
+
if (useIsEager()) {
|
|
1230
|
+
return hasErrorsByTestObjects(testObject.fieldName);
|
|
1317
1231
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
}, IsolateTest.is);
|
|
1232
|
+
return false;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
const SuiteContext = createCascade((ctxRef, parentContext) => {
|
|
1236
|
+
if (parentContext) {
|
|
1237
|
+
return null;
|
|
1325
1238
|
}
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
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;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
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.
|
|
1281
|
+
*/
|
|
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)) {
|
|
1288
|
+
return;
|
|
1333
1289
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
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()) {
|
|
1336
1295
|
return;
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
}
|
|
1296
|
+
}
|
|
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
|
+
});
|
|
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
|
+
}
|
|
1348
1314
|
}
|
|
1349
|
-
|
|
1350
|
-
|
|
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)) {
|
|
1351
1319
|
return;
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
TestWalker.walkTests(testObject => {
|
|
1359
|
-
if (matchingFieldName(testObject, fieldName)) {
|
|
1360
|
-
testObject.reset();
|
|
1361
|
-
}
|
|
1362
|
-
}, TestWalker.defaultRoot());
|
|
1363
|
-
}
|
|
1364
|
-
static removeTestByFieldName(fieldName, root = TestWalker.defaultRoot()) {
|
|
1365
|
-
TestWalker.pluckTests(testObject => {
|
|
1366
|
-
return matchingFieldName(testObject, fieldName);
|
|
1367
|
-
}, root);
|
|
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);
|
|
1368
1326
|
}
|
|
1369
1327
|
}
|
|
1370
1328
|
|
|
@@ -1387,8 +1345,9 @@ function useRunDoneCallbacks() {
|
|
|
1387
1345
|
callEach(doneCallbacks);
|
|
1388
1346
|
}
|
|
1389
1347
|
|
|
1348
|
+
// eslint-disable-next-line max-statements
|
|
1390
1349
|
function useInitVestBus() {
|
|
1391
|
-
const VestBus =
|
|
1350
|
+
const VestBus = VestRuntime.useBus();
|
|
1392
1351
|
// Report a the completion of a test. There may be other tests with the same
|
|
1393
1352
|
// name that are still running, or not yet started.
|
|
1394
1353
|
on(Events.TEST_COMPLETED, (testObject) => {
|
|
@@ -1404,8 +1363,18 @@ function useInitVestBus() {
|
|
|
1404
1363
|
on(Events.TEST_RUN_STARTED, () => {
|
|
1405
1364
|
/* Let's just invalidate the suite cache for now */
|
|
1406
1365
|
});
|
|
1366
|
+
on(Events.DONE_TEST_OMISSION_PASS, () => {
|
|
1367
|
+
/* We NEED to refresh the cache here. Don't ask */
|
|
1368
|
+
});
|
|
1407
1369
|
// Called when all the tests, including async, are done running
|
|
1408
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
|
+
}
|
|
1409
1378
|
useRunDoneCallbacks();
|
|
1410
1379
|
});
|
|
1411
1380
|
on(Events.RESET_FIELD, (fieldName) => {
|
|
@@ -1414,6 +1383,9 @@ function useInitVestBus() {
|
|
|
1414
1383
|
on(Events.SUITE_RUN_STARTED, () => {
|
|
1415
1384
|
useResetCallbacks();
|
|
1416
1385
|
});
|
|
1386
|
+
on(Events.SUITE_CALLBACK_RUN_FINISHED, () => {
|
|
1387
|
+
useOmitOptionalFields();
|
|
1388
|
+
});
|
|
1417
1389
|
on(Events.REMOVE_FIELD, (fieldName) => {
|
|
1418
1390
|
TestWalker.removeTestByFieldName(fieldName);
|
|
1419
1391
|
});
|
|
@@ -1431,202 +1403,24 @@ function useInitVestBus() {
|
|
|
1431
1403
|
}
|
|
1432
1404
|
}
|
|
1433
1405
|
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
doneCallbacks: tinyState.createTinyState(() => []),
|
|
1453
|
-
fieldCallbacks: tinyState.createTinyState(() => ({})),
|
|
1454
|
-
historyRoot: tinyState.createTinyState(null),
|
|
1455
|
-
suiteId: seq(),
|
|
1456
|
-
suiteName,
|
|
1457
|
-
suiteResultCache,
|
|
1458
|
-
};
|
|
1459
|
-
return stateRef;
|
|
1460
|
-
}
|
|
1461
|
-
function persist(cb) {
|
|
1462
|
-
const prev = PersistedContext.useX();
|
|
1463
|
-
return function persisted(...args) {
|
|
1464
|
-
var _a;
|
|
1465
|
-
const ctxToUse = (_a = PersistedContext.use()) !== null && _a !== void 0 ? _a : prev;
|
|
1466
|
-
return PersistedContext.run(ctxToUse, () => cb(...args));
|
|
1467
|
-
};
|
|
1468
|
-
}
|
|
1469
|
-
function useSuiteResultCache(action) {
|
|
1470
|
-
const suiteResultCache = useX().suiteResultCache;
|
|
1471
|
-
return suiteResultCache([useSuiteId()], action);
|
|
1472
|
-
}
|
|
1473
|
-
function useExpireSuiteResultCache() {
|
|
1474
|
-
const suiteResultCache = useX().suiteResultCache;
|
|
1475
|
-
suiteResultCache.invalidate([useSuiteId()]);
|
|
1476
|
-
}
|
|
1477
|
-
function useResetCallbacks() {
|
|
1478
|
-
const [, , resetDoneCallbacks] = useDoneCallbacks();
|
|
1479
|
-
const [, , resetFieldCallbacks] = useFieldCallbacks();
|
|
1480
|
-
resetDoneCallbacks();
|
|
1481
|
-
resetFieldCallbacks();
|
|
1482
|
-
}
|
|
1483
|
-
function useResetSuite() {
|
|
1484
|
-
useResetCallbacks();
|
|
1485
|
-
const [, , resetHistoryRoot] = useHistoryRoot();
|
|
1486
|
-
resetHistoryRoot();
|
|
1487
|
-
}
|
|
1488
|
-
function useX() {
|
|
1489
|
-
return PersistedContext.useX();
|
|
1490
|
-
}
|
|
1491
|
-
function useVestBus() {
|
|
1492
|
-
return useX().VestBus;
|
|
1493
|
-
}
|
|
1494
|
-
/*
|
|
1495
|
-
Returns an emitter, but it also has a shortcut for emitting an event immediately
|
|
1496
|
-
by passing an event name.
|
|
1497
|
-
*/
|
|
1498
|
-
function useEmit() {
|
|
1499
|
-
return persist(useVestBus().emit);
|
|
1500
|
-
}
|
|
1501
|
-
function usePrepareEmitter(event) {
|
|
1502
|
-
const emit = useEmit();
|
|
1503
|
-
return (arg) => emit(event, arg);
|
|
1504
|
-
}
|
|
1505
|
-
function useDoneCallbacks() {
|
|
1506
|
-
return useX().doneCallbacks();
|
|
1507
|
-
}
|
|
1508
|
-
function useFieldCallbacks() {
|
|
1509
|
-
return useX().fieldCallbacks();
|
|
1510
|
-
}
|
|
1511
|
-
function useHistoryRoot() {
|
|
1512
|
-
return useX().historyRoot();
|
|
1513
|
-
}
|
|
1514
|
-
function useHistoryNode() {
|
|
1515
|
-
return useX().historyNode;
|
|
1516
|
-
}
|
|
1517
|
-
function useSuiteName() {
|
|
1518
|
-
return useX().suiteName;
|
|
1519
|
-
}
|
|
1520
|
-
function useSuiteId() {
|
|
1521
|
-
return useX().suiteId;
|
|
1522
|
-
}
|
|
1523
|
-
function useSetHistory(history) {
|
|
1524
|
-
const context = PersistedContext.useX();
|
|
1525
|
-
const [, setHistoryRoot] = context.historyRoot();
|
|
1526
|
-
setHistoryRoot(history);
|
|
1527
|
-
}
|
|
1528
|
-
function useHistoryKey(key) {
|
|
1529
|
-
var _a;
|
|
1530
|
-
if (isNullish(key)) {
|
|
1531
|
-
return null;
|
|
1532
|
-
}
|
|
1533
|
-
const historyNode = useX().historyNode;
|
|
1534
|
-
return (_a = historyNode === null || historyNode === void 0 ? void 0 : historyNode.keys[key]) !== null && _a !== void 0 ? _a : null;
|
|
1535
|
-
}
|
|
1536
|
-
function useIsolate() {
|
|
1537
|
-
var _a;
|
|
1538
|
-
return (_a = useX().runtimeNode) !== null && _a !== void 0 ? _a : null;
|
|
1539
|
-
}
|
|
1540
|
-
function useCurrentCursor() {
|
|
1541
|
-
var _a, _b;
|
|
1542
|
-
return (_b = (_a = useIsolate()) === null || _a === void 0 ? void 0 : _a.cursor()) !== null && _b !== void 0 ? _b : 0;
|
|
1543
|
-
}
|
|
1544
|
-
function useRuntimeRoot() {
|
|
1545
|
-
return useX().runtimeRoot;
|
|
1546
|
-
}
|
|
1547
|
-
function useSetNextIsolateChild(child) {
|
|
1548
|
-
const currentIsolate = useIsolate();
|
|
1549
|
-
invariant(currentIsolate, ErrorStrings.NO_ACTIVE_ISOLATE);
|
|
1550
|
-
currentIsolate.addChild(child);
|
|
1551
|
-
}
|
|
1552
|
-
function useSetIsolateKey(key, value) {
|
|
1553
|
-
if (!key) {
|
|
1554
|
-
return;
|
|
1555
|
-
}
|
|
1556
|
-
const currentIsolate = useIsolate();
|
|
1557
|
-
invariant(currentIsolate, ErrorStrings.NO_ACTIVE_ISOLATE);
|
|
1558
|
-
if (isNullish(currentIsolate.keys[key])) {
|
|
1559
|
-
currentIsolate.keys[key] = value;
|
|
1560
|
-
return;
|
|
1561
|
-
}
|
|
1562
|
-
deferThrow(text(ErrorStrings.ENCOUNTERED_THE_SAME_KEY_TWICE, { key }));
|
|
1563
|
-
}
|
|
1564
|
-
function useAvailableSuiteRoot() {
|
|
1565
|
-
const root = useRuntimeRoot();
|
|
1566
|
-
if (root) {
|
|
1567
|
-
return root;
|
|
1568
|
-
}
|
|
1569
|
-
const [historyRoot] = useHistoryRoot();
|
|
1570
|
-
return historyRoot;
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
// @vx-allow use-use
|
|
1574
|
-
function optional(optionals) {
|
|
1575
|
-
const suiteRoot = useRuntimeRoot();
|
|
1576
|
-
// There are two types of optional field declarations:
|
|
1577
|
-
// 1. Delayed: A string, which is the name of the field to be optional.
|
|
1578
|
-
// We will only determine whether to omit the test after the suite is done running
|
|
1579
|
-
//
|
|
1580
|
-
// 2. Immediate: Either a boolean or a function, which is used to determine
|
|
1581
|
-
// if the field should be optional.
|
|
1582
|
-
// Delayed case (field name)
|
|
1583
|
-
if (isArray(optionals) || isStringValue(optionals)) {
|
|
1584
|
-
asArray(optionals).forEach(optionalField => {
|
|
1585
|
-
suiteRoot.setOptionalField(optionalField, () => ({
|
|
1586
|
-
type: OptionalFieldTypes.Delayed,
|
|
1587
|
-
applied: false,
|
|
1588
|
-
rule: null,
|
|
1589
|
-
}));
|
|
1590
|
-
});
|
|
1591
|
-
}
|
|
1592
|
-
else {
|
|
1593
|
-
// Immediately case (function or boolean)
|
|
1594
|
-
for (const field in optionals) {
|
|
1595
|
-
const value = optionals[field];
|
|
1596
|
-
suiteRoot.setOptionalField(field, () => ({
|
|
1597
|
-
type: OptionalFieldTypes.Immediate,
|
|
1598
|
-
rule: value,
|
|
1599
|
-
applied: optionalFunctionValue(value),
|
|
1600
|
-
}));
|
|
1601
|
-
}
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
function useIsOptionalFiedApplied(fieldName) {
|
|
1605
|
-
var _a, _b, _c;
|
|
1606
|
-
if (!fieldName) {
|
|
1607
|
-
return false;
|
|
1608
|
-
}
|
|
1609
|
-
return (_c = (_b = (_a = useAvailableSuiteRoot()) === null || _a === void 0 ? void 0 : _a.getOptionalField(fieldName)) === null || _b === void 0 ? void 0 : _b.applied) !== null && _c !== void 0 ? _c : false;
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
class IsolateSuite extends Isolate {
|
|
1613
|
-
constructor() {
|
|
1614
|
-
super(...arguments);
|
|
1615
|
-
this.optional = {};
|
|
1616
|
-
}
|
|
1617
|
-
setOptionalField(fieldName, setter) {
|
|
1618
|
-
const current = this.optional;
|
|
1619
|
-
const currentField = current[fieldName];
|
|
1620
|
-
Object.assign(current, {
|
|
1621
|
-
[fieldName]: Object.assign({}, currentField, setter(currentField)),
|
|
1622
|
-
});
|
|
1623
|
-
}
|
|
1624
|
-
getOptionalField(fieldName) {
|
|
1625
|
-
var _a;
|
|
1626
|
-
return (_a = this.optional[fieldName]) !== null && _a !== void 0 ? _a : {};
|
|
1627
|
-
}
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1406
|
+
/**
|
|
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.
|
|
1410
|
+
*
|
|
1411
|
+
* @example
|
|
1412
|
+
* include('confirm').when('password');
|
|
1413
|
+
* // Includes the "confirm" field for testing when the "password" field is included
|
|
1414
|
+
*
|
|
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
|
|
1423
|
+
*/
|
|
1630
1424
|
// @vx-allow use-use
|
|
1631
1425
|
function include(fieldName) {
|
|
1632
1426
|
const inclusion = useInclusion();
|
|
@@ -1634,6 +1428,9 @@ function include(fieldName) {
|
|
|
1634
1428
|
invariant(isStringValue(fieldName));
|
|
1635
1429
|
inclusion[fieldName] = defaultTo(exclusion.tests[fieldName], true);
|
|
1636
1430
|
return { when };
|
|
1431
|
+
/**
|
|
1432
|
+
* Specifies the inclusion criteria for the field in `include` function.
|
|
1433
|
+
*/
|
|
1637
1434
|
function when(condition) {
|
|
1638
1435
|
const inclusion = useInclusion();
|
|
1639
1436
|
const exclusion = useExclusion();
|
|
@@ -1653,18 +1450,16 @@ function include(fieldName) {
|
|
|
1653
1450
|
}
|
|
1654
1451
|
}
|
|
1655
1452
|
|
|
1656
|
-
|
|
1453
|
+
// eslint-disable-next-line max-statements
|
|
1454
|
+
function useAttemptRunTest(testObject) {
|
|
1657
1455
|
useVerifyTestRun(testObject);
|
|
1658
|
-
if (testObject.isNonActionable()) {
|
|
1659
|
-
// TODO: Need to test that this works as expected
|
|
1660
|
-
return;
|
|
1661
|
-
}
|
|
1662
1456
|
if (testObject.isUntested()) {
|
|
1663
|
-
useRunTest(testObject);
|
|
1457
|
+
return useRunTest(testObject);
|
|
1664
1458
|
}
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1459
|
+
if (!testObject.isNonActionable()) {
|
|
1460
|
+
deferThrow(text(ErrorStrings.UNEXPECTED_TEST_REGISTRATION_ERROR, {
|
|
1461
|
+
testObject: JSON.stringify(testObject),
|
|
1462
|
+
}));
|
|
1668
1463
|
}
|
|
1669
1464
|
}
|
|
1670
1465
|
function runSyncTest(testObject) {
|
|
@@ -1674,7 +1469,7 @@ function runSyncTest(testObject) {
|
|
|
1674
1469
|
* runs test, if async - adds to pending array
|
|
1675
1470
|
*/
|
|
1676
1471
|
function useRunTest(testObject) {
|
|
1677
|
-
const VestBus =
|
|
1472
|
+
const VestBus = VestRuntime.useBus();
|
|
1678
1473
|
// Run test callback.
|
|
1679
1474
|
// If a promise is returned, set as async and
|
|
1680
1475
|
// Move to pending list.
|
|
@@ -1684,7 +1479,6 @@ function useRunTest(testObject) {
|
|
|
1684
1479
|
// in case object is an enforce chain
|
|
1685
1480
|
if (isPromise(result)) {
|
|
1686
1481
|
testObject.asyncTest = result;
|
|
1687
|
-
testObject.setPending();
|
|
1688
1482
|
useRunAsyncTest(testObject);
|
|
1689
1483
|
}
|
|
1690
1484
|
else {
|
|
@@ -1705,11 +1499,12 @@ function useRunAsyncTest(testObject) {
|
|
|
1705
1499
|
const { asyncTest, message } = testObject;
|
|
1706
1500
|
if (!isPromise(asyncTest))
|
|
1707
1501
|
return;
|
|
1708
|
-
|
|
1709
|
-
const
|
|
1502
|
+
testObject.setPending();
|
|
1503
|
+
const VestBus = VestRuntime.useBus();
|
|
1504
|
+
const done = VestRuntime.persist(() => {
|
|
1710
1505
|
onTestCompleted(VestBus, testObject);
|
|
1711
1506
|
});
|
|
1712
|
-
const fail = persist((rejectionMessage) => {
|
|
1507
|
+
const fail = VestRuntime.persist((rejectionMessage) => {
|
|
1713
1508
|
if (testObject.isCanceled()) {
|
|
1714
1509
|
return;
|
|
1715
1510
|
}
|
|
@@ -1733,7 +1528,11 @@ function wrapTestMemo(test) {
|
|
|
1733
1528
|
function memo(fieldName, ...args) {
|
|
1734
1529
|
const [deps, testFn, msg] = args.reverse();
|
|
1735
1530
|
// Implicit dependency for better specificity
|
|
1736
|
-
const dependencies = [
|
|
1531
|
+
const dependencies = [
|
|
1532
|
+
useSuiteId(),
|
|
1533
|
+
fieldName,
|
|
1534
|
+
VestRuntime.useCurrentCursor(),
|
|
1535
|
+
].concat(deps);
|
|
1737
1536
|
return useGetTestFromCache(dependencies, cacheAction);
|
|
1738
1537
|
function cacheAction() {
|
|
1739
1538
|
return test(fieldName, msg, testFn);
|
|
@@ -1763,11 +1562,11 @@ function vestTest(fieldName, ...args) {
|
|
|
1763
1562
|
const [message, testFn, key] = (isFunction(args[1]) ? args : [undefined, ...args]);
|
|
1764
1563
|
validateTestParams(fieldName, testFn);
|
|
1765
1564
|
const groupName = useGroupName();
|
|
1766
|
-
const emit = useEmit();
|
|
1565
|
+
const emit = VestRuntime.useEmit();
|
|
1767
1566
|
const testObjectInput = { fieldName, groupName, key, message, testFn };
|
|
1768
1567
|
// This invalidates the suite cache.
|
|
1769
1568
|
emit(Events.TEST_RUN_STARTED);
|
|
1770
|
-
return IsolateTest.create(
|
|
1569
|
+
return IsolateTest.create(useAttemptRunTest, testObjectInput);
|
|
1771
1570
|
}
|
|
1772
1571
|
const test = assign(vestTest, {
|
|
1773
1572
|
memo: wrapTestMemo(vestTest),
|
|
@@ -1822,9 +1621,9 @@ function shouldSkipDoneRegistration(callback, fieldName, output) {
|
|
|
1822
1621
|
}
|
|
1823
1622
|
|
|
1824
1623
|
function useSuiteRunResult() {
|
|
1825
|
-
return assign({
|
|
1826
|
-
done: persist(done),
|
|
1827
|
-
});
|
|
1624
|
+
return Object.freeze(assign({
|
|
1625
|
+
done: VestRuntime.persist(done),
|
|
1626
|
+
}, useCreateSuiteResult()));
|
|
1828
1627
|
}
|
|
1829
1628
|
/**
|
|
1830
1629
|
* Registers done callbacks.
|
|
@@ -1860,24 +1659,27 @@ function createSuite(...args) {
|
|
|
1860
1659
|
function suite(...args) {
|
|
1861
1660
|
return SuiteContext.run({}, () => {
|
|
1862
1661
|
// eslint-disable-next-line vest-internal/use-use
|
|
1863
|
-
const emit = useEmit();
|
|
1662
|
+
const emit = VestRuntime.useEmit();
|
|
1864
1663
|
emit(Events.SUITE_RUN_STARTED);
|
|
1865
1664
|
return IsolateSuite.create(useRunSuiteCallback(suiteCallback, ...args));
|
|
1866
1665
|
}).output;
|
|
1867
1666
|
}
|
|
1868
1667
|
// Assign methods to the suite
|
|
1869
|
-
// We do this within the
|
|
1668
|
+
// We do this within the VestRuntime so that the suite methods
|
|
1870
1669
|
// will be bound to the suite's stateRef and be able to access it.
|
|
1871
|
-
return
|
|
1670
|
+
return VestRuntime.Run(stateRef, () => {
|
|
1671
|
+
useInitVestBus();
|
|
1872
1672
|
return assign(
|
|
1873
1673
|
// We're also binding the suite to the stateRef, so that the suite
|
|
1874
1674
|
// can access the stateRef when it's called.
|
|
1875
|
-
|
|
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()));
|
|
1876
1676
|
});
|
|
1877
1677
|
}
|
|
1878
1678
|
function useRunSuiteCallback(suiteCallback, ...args) {
|
|
1679
|
+
const emit = VestRuntime.useEmit();
|
|
1879
1680
|
return () => {
|
|
1880
1681
|
suiteCallback(...args);
|
|
1682
|
+
emit(Events.SUITE_CALLBACK_RUN_FINISHED);
|
|
1881
1683
|
return useSuiteRunResult();
|
|
1882
1684
|
};
|
|
1883
1685
|
}
|
|
@@ -1917,13 +1719,45 @@ function group(groupName, callback) {
|
|
|
1917
1719
|
});
|
|
1918
1720
|
}
|
|
1919
1721
|
|
|
1722
|
+
/**
|
|
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.
|
|
1727
|
+
*
|
|
1728
|
+
* @example
|
|
1729
|
+
* import { staticSuite, test, enforce } from 'vest';
|
|
1730
|
+
*
|
|
1731
|
+
* const suite = staticSuite(data => {
|
|
1732
|
+
* test('username', 'username is required', () => {
|
|
1733
|
+
* enforce(data.username).isNotEmpty();
|
|
1734
|
+
* });
|
|
1735
|
+
* });
|
|
1736
|
+
*
|
|
1737
|
+
* suite(data);
|
|
1738
|
+
*/
|
|
1920
1739
|
function staticSuite(suiteCallback) {
|
|
1921
1740
|
return assign((...args) => createSuite(suiteCallback)(...args), Object.assign({}, getTypedMethods()));
|
|
1922
1741
|
}
|
|
1923
1742
|
|
|
1924
1743
|
const ERROR_OUTSIDE_OF_TEST = ErrorStrings.WARN_MUST_BE_CALLED_FROM_TEST;
|
|
1925
1744
|
/**
|
|
1926
|
-
* 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.
|
|
1927
1761
|
*/
|
|
1928
1762
|
// @vx-allow use-use
|
|
1929
1763
|
function warn() {
|