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