vest 3.2.8-dev-6d7c74 → 3.2.8
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/README.md +115 -0
- package/any.d.ts +3 -0
- package/any.js +1 -0
- package/classNames.d.ts +14 -0
- package/classNames.js +1 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +115 -0
- package/docs/_assets/favicon.ico +0 -0
- package/docs/_assets/vest-logo.png +0 -0
- package/docs/_sidebar.md +19 -0
- package/docs/_sidebar.md.bak +14 -0
- package/docs/cross_field_validations.md +79 -0
- package/docs/enforce.md +18 -0
- package/docs/enforce.md.bak +13 -0
- package/docs/exclusion.md +128 -0
- package/docs/getting_started.md +79 -0
- package/docs/group.md +142 -0
- package/docs/index.html +41 -0
- package/docs/migration.md +107 -0
- package/docs/n4s/compound.md +187 -0
- package/docs/n4s/custom.md +52 -0
- package/docs/n4s/external.md +54 -0
- package/docs/n4s/rules.md +1282 -0
- package/docs/n4s/template.md +53 -0
- package/docs/node.md +43 -0
- package/docs/optional.md +51 -0
- package/docs/result.md +238 -0
- package/docs/state.md +102 -0
- package/docs/test.md +172 -0
- package/docs/utilities.md +105 -0
- package/docs/warn.md +82 -0
- package/enforce.d.ts +230 -0
- package/esm/package.json +1 -0
- package/esm/vest.es.development.js +2493 -0
- package/esm/vest.es.production.js +2490 -0
- package/esm/vest.es.production.min.js +1 -0
- package/package.json +65 -12
- package/promisify.d.ts +7 -0
- package/{dist/umd/promisify.production.js → promisify.js} +1 -1
- package/schema.d.ts +26 -0
- package/schema.js +1 -0
- package/vest.cjs.development.js +2494 -0
- package/vest.cjs.production.js +2491 -0
- package/vest.cjs.production.min.js +1 -0
- package/vest.d.ts +254 -0
- package/vest.js +7 -0
- package/vest.umd.development.js +2711 -0
- package/vest.umd.production.js +2708 -0
- package/vest.umd.production.min.js +1 -0
- package/vestResult.d.ts +105 -0
- package/CHANGELOG.md +0 -94
- package/LICENSE +0 -21
- package/classnames/index.js +0 -7
- package/classnames/package.json +0 -1
- package/dist/cjs/classnames.development.js +0 -67
- package/dist/cjs/classnames.production.js +0 -1
- package/dist/cjs/promisify.development.js +0 -20
- package/dist/cjs/promisify.production.js +0 -1
- package/dist/cjs/vest.development.js +0 -1616
- package/dist/cjs/vest.production.js +0 -1
- package/dist/es/classnames.development.js +0 -65
- package/dist/es/classnames.production.js +0 -1
- package/dist/es/promisify.development.js +0 -18
- package/dist/es/promisify.production.js +0 -1
- package/dist/es/vest.development.js +0 -1604
- package/dist/es/vest.production.js +0 -1
- package/dist/umd/classnames.development.js +0 -73
- package/dist/umd/classnames.production.js +0 -1
- package/dist/umd/promisify.development.js +0 -26
- package/dist/umd/vest.development.js +0 -1622
- package/dist/umd/vest.production.js +0 -1
- package/index.js +0 -7
- package/promisify/index.js +0 -7
- package/promisify/package.json +0 -1
- package/types/classnames.d.ts +0 -71
- package/types/classnames.d.ts.map +0 -1
- package/types/promisify.d.ts +0 -61
- package/types/promisify.d.ts.map +0 -1
- package/types/vest.d.ts +0 -271
- package/types/vest.d.ts.map +0 -1
|
@@ -0,0 +1,2708 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.vest = factory());
|
|
5
|
+
}(this, (function () { 'use strict';
|
|
6
|
+
|
|
7
|
+
function createState(onStateChange) {
|
|
8
|
+
var state = {
|
|
9
|
+
references: []
|
|
10
|
+
};
|
|
11
|
+
var registrations = [];
|
|
12
|
+
return {
|
|
13
|
+
registerStateKey: registerStateKey,
|
|
14
|
+
reset: reset
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Registers a new key in the state, takes the initial value (may be a function that returns the initial value), returns a function.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
*
|
|
21
|
+
* const useColor = state.registerStateKey("blue");
|
|
22
|
+
*
|
|
23
|
+
* let [color, setColor] = useColor(); // -> ["blue", Function]
|
|
24
|
+
*
|
|
25
|
+
* setColor("green");
|
|
26
|
+
*
|
|
27
|
+
* useColor()[0]; -> "green"
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
function registerStateKey(initialState, onUpdate) {
|
|
31
|
+
var key = registrations.length;
|
|
32
|
+
registrations.push([initialState, onUpdate]);
|
|
33
|
+
return initKey(key, initialState);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function reset() {
|
|
37
|
+
state.references = [];
|
|
38
|
+
registrations.forEach(function (_ref, index) {
|
|
39
|
+
var initialValue = _ref[0];
|
|
40
|
+
return initKey(index, initialValue);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function initKey(key, initialState) {
|
|
45
|
+
current().push();
|
|
46
|
+
set(key, optionalFunctionValue$1(initialState));
|
|
47
|
+
return function useStateKey() {
|
|
48
|
+
return [current()[key], function (nextState) {
|
|
49
|
+
return set(key, optionalFunctionValue$1(nextState, [current()[key]]));
|
|
50
|
+
}];
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function current() {
|
|
55
|
+
return state.references;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function set(key, value) {
|
|
59
|
+
var prevValue = state.references[key];
|
|
60
|
+
state.references[key] = value;
|
|
61
|
+
var _registrations$key = registrations[key],
|
|
62
|
+
onUpdate = _registrations$key[1];
|
|
63
|
+
|
|
64
|
+
if (isFunction$1(onUpdate)) {
|
|
65
|
+
onUpdate(value, prevValue);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (isFunction$1(onStateChange)) {
|
|
69
|
+
onStateChange();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isFunction$1(f) {
|
|
75
|
+
return typeof f === 'function';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function optionalFunctionValue$1(value, args) {
|
|
79
|
+
return isFunction$1(value) ? value.apply(null, args) : value;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function asArray(possibleArg) {
|
|
83
|
+
return [].concat(possibleArg);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function createStateRef(state, _ref) {
|
|
87
|
+
var suiteId = _ref.suiteId,
|
|
88
|
+
name = _ref.name;
|
|
89
|
+
return {
|
|
90
|
+
carryOverTests: state.registerStateKey(function () {
|
|
91
|
+
return [];
|
|
92
|
+
}),
|
|
93
|
+
optionalFields: state.registerStateKey(function () {
|
|
94
|
+
return {};
|
|
95
|
+
}),
|
|
96
|
+
pending: state.registerStateKey(function () {
|
|
97
|
+
return {
|
|
98
|
+
pending: [],
|
|
99
|
+
lagging: []
|
|
100
|
+
};
|
|
101
|
+
}),
|
|
102
|
+
skippedTests: state.registerStateKey(function () {
|
|
103
|
+
return [];
|
|
104
|
+
}),
|
|
105
|
+
suiteId: state.registerStateKey(function () {
|
|
106
|
+
return {
|
|
107
|
+
id: suiteId,
|
|
108
|
+
name: name
|
|
109
|
+
};
|
|
110
|
+
}),
|
|
111
|
+
testCallbacks: state.registerStateKey(function () {
|
|
112
|
+
return {
|
|
113
|
+
fieldCallbacks: {},
|
|
114
|
+
doneCallbacks: []
|
|
115
|
+
};
|
|
116
|
+
}),
|
|
117
|
+
testObjects: state.registerStateKey(function () {
|
|
118
|
+
return [];
|
|
119
|
+
})
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function _extends() {
|
|
124
|
+
_extends = Object.assign || function (target) {
|
|
125
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
126
|
+
var source = arguments[i];
|
|
127
|
+
|
|
128
|
+
for (var key in source) {
|
|
129
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
130
|
+
target[key] = source[key];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return target;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return _extends.apply(this, arguments);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function createContext(init) {
|
|
142
|
+
var storage = {
|
|
143
|
+
ancestry: []
|
|
144
|
+
};
|
|
145
|
+
return {
|
|
146
|
+
run: run,
|
|
147
|
+
bind: bind,
|
|
148
|
+
use: use
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
function run(ctxRef, fn) {
|
|
152
|
+
var _init;
|
|
153
|
+
|
|
154
|
+
var parentContext = use();
|
|
155
|
+
|
|
156
|
+
var out = _extends({}, parentContext ? parentContext : {}, (_init = init === null || init === void 0 ? void 0 : init(ctxRef, parentContext)) !== null && _init !== void 0 ? _init : ctxRef);
|
|
157
|
+
|
|
158
|
+
var ctx = set(Object.freeze(out));
|
|
159
|
+
storage.ancestry.unshift(ctx);
|
|
160
|
+
var res = fn(ctx);
|
|
161
|
+
clear();
|
|
162
|
+
return res;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function bind(ctxRef, fn) {
|
|
166
|
+
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
|
|
167
|
+
args[_key - 2] = arguments[_key];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return function () {
|
|
171
|
+
for (var _len2 = arguments.length, runTimeArgs = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
172
|
+
runTimeArgs[_key2] = arguments[_key2];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return run(ctxRef, function () {
|
|
176
|
+
return fn.apply(void 0, args.concat(runTimeArgs));
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function use() {
|
|
182
|
+
return storage.ctx;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function set(value) {
|
|
186
|
+
return storage.ctx = value;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function clear() {
|
|
190
|
+
var _storage$ancestry$;
|
|
191
|
+
|
|
192
|
+
storage.ancestry.shift();
|
|
193
|
+
set((_storage$ancestry$ = storage.ancestry[0]) !== null && _storage$ancestry$ !== void 0 ? _storage$ancestry$ : null);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
var assign = Object.assign;
|
|
198
|
+
|
|
199
|
+
var EXCLUSION_ITEM_TYPE_TESTS = 'tests';
|
|
200
|
+
var EXCLUSION_ITEM_TYPE_GROUPS = 'groups';
|
|
201
|
+
|
|
202
|
+
var context = createContext(function (ctxRef, parentContext) {
|
|
203
|
+
var _exclusion;
|
|
204
|
+
|
|
205
|
+
return parentContext ? null : assign({}, {
|
|
206
|
+
exclusion: (_exclusion = {}, _exclusion[EXCLUSION_ITEM_TYPE_TESTS] = {}, _exclusion[EXCLUSION_ITEM_TYPE_GROUPS] = {}, _exclusion)
|
|
207
|
+
}, ctxRef);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @returns a unique numeric id.
|
|
212
|
+
*/
|
|
213
|
+
var id = function (n) {
|
|
214
|
+
return function () {
|
|
215
|
+
return "" + n++;
|
|
216
|
+
};
|
|
217
|
+
}(0);
|
|
218
|
+
|
|
219
|
+
function isFunction (v) {
|
|
220
|
+
return typeof v === 'function';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function bindNot(fn) {
|
|
224
|
+
return function () {
|
|
225
|
+
return !fn.apply(this, arguments);
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function isNull(value) {
|
|
230
|
+
return value === null;
|
|
231
|
+
}
|
|
232
|
+
var isNotNull = bindNot(isNull);
|
|
233
|
+
|
|
234
|
+
function lengthEquals(value, arg1) {
|
|
235
|
+
return value.length === Number(arg1);
|
|
236
|
+
}
|
|
237
|
+
var lengthNotEquals = bindNot(lengthEquals);
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Creates a cache function
|
|
241
|
+
* @param {number} [maxSize] Max cache size
|
|
242
|
+
* @return {Function} cache function
|
|
243
|
+
*/
|
|
244
|
+
|
|
245
|
+
var createCache = function createCache(maxSize) {
|
|
246
|
+
if (maxSize === void 0) {
|
|
247
|
+
maxSize = 10;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
var cacheStorage = [];
|
|
251
|
+
/**
|
|
252
|
+
* @param {Any[]} deps dependency array.
|
|
253
|
+
* @param {Function} cache action function.
|
|
254
|
+
*/
|
|
255
|
+
|
|
256
|
+
var cache = function cache(deps, cacheAction) {
|
|
257
|
+
var cacheHit = cache.get(deps);
|
|
258
|
+
|
|
259
|
+
if (isNotNull(cacheHit)) {
|
|
260
|
+
return cacheHit[1];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
var result = cacheAction();
|
|
264
|
+
cacheStorage.unshift([deps.concat(), result]);
|
|
265
|
+
|
|
266
|
+
if (cacheStorage.length > maxSize) {
|
|
267
|
+
cacheStorage.length = maxSize;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return result;
|
|
271
|
+
};
|
|
272
|
+
/**
|
|
273
|
+
* Retrieves an item from the cache.
|
|
274
|
+
* @param {deps} deps Dependency array
|
|
275
|
+
*/
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
cache.get = function (deps) {
|
|
279
|
+
return cacheStorage[cacheStorage.findIndex(function (_ref) {
|
|
280
|
+
var cachedDeps = _ref[0];
|
|
281
|
+
return lengthEquals(deps, cachedDeps.length) && deps.every(function (dep, i) {
|
|
282
|
+
return dep === cachedDeps[i];
|
|
283
|
+
});
|
|
284
|
+
})] || null;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
return cache;
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
var SEVERITY_GROUP_WARN = 'warnings';
|
|
291
|
+
var SEVERITY_COUNT_WARN = 'warnCount';
|
|
292
|
+
var SEVERITY_GROUP_ERROR = 'errors';
|
|
293
|
+
var SEVERITY_COUNT_ERROR = 'errorCount';
|
|
294
|
+
var TEST_COUNT = 'testCount';
|
|
295
|
+
|
|
296
|
+
var getStateRef = function getStateRef() {
|
|
297
|
+
return context.use().stateRef;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
function useCarryOverTests() {
|
|
301
|
+
return getStateRef().carryOverTests();
|
|
302
|
+
}
|
|
303
|
+
function usePending() {
|
|
304
|
+
return getStateRef().pending();
|
|
305
|
+
}
|
|
306
|
+
function useSuiteId() {
|
|
307
|
+
return getStateRef().suiteId();
|
|
308
|
+
}
|
|
309
|
+
function useTestCallbacks() {
|
|
310
|
+
return getStateRef().testCallbacks();
|
|
311
|
+
}
|
|
312
|
+
function useTestObjects() {
|
|
313
|
+
return getStateRef().testObjects();
|
|
314
|
+
}
|
|
315
|
+
function useSkippedTests() {
|
|
316
|
+
return getStateRef().skippedTests();
|
|
317
|
+
}
|
|
318
|
+
function useOptionalFields() {
|
|
319
|
+
return getStateRef().optionalFields();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Reads the testObjects list and gets full validation result from it.
|
|
324
|
+
*/
|
|
325
|
+
|
|
326
|
+
var genTestsSummary = function genTestsSummary() {
|
|
327
|
+
var _summary;
|
|
328
|
+
|
|
329
|
+
var _useTestObjects = useTestObjects(),
|
|
330
|
+
testObjects = _useTestObjects[0];
|
|
331
|
+
|
|
332
|
+
var _useSuiteId = useSuiteId(),
|
|
333
|
+
suiteIdState = _useSuiteId[0];
|
|
334
|
+
|
|
335
|
+
var _useSkippedTests = useSkippedTests(),
|
|
336
|
+
skippedTests = _useSkippedTests[0];
|
|
337
|
+
|
|
338
|
+
var summary = (_summary = {}, _summary[SEVERITY_COUNT_ERROR] = 0, _summary[SEVERITY_COUNT_WARN] = 0, _summary[TEST_COUNT] = 0, _summary.groups = {}, _summary.name = suiteIdState.name, _summary.tests = {}, _summary);
|
|
339
|
+
appendSummary(testObjects);
|
|
340
|
+
appendSummary(skippedTests, true);
|
|
341
|
+
return countFailures(summary);
|
|
342
|
+
|
|
343
|
+
function appendSummary(testObject, skipped) {
|
|
344
|
+
testObject.forEach(function (testObject) {
|
|
345
|
+
var fieldName = testObject.fieldName,
|
|
346
|
+
groupName = testObject.groupName;
|
|
347
|
+
summary.tests[fieldName] = genTestObject(summary.tests, testObject, skipped);
|
|
348
|
+
|
|
349
|
+
if (groupName) {
|
|
350
|
+
summary.groups[groupName] = summary.groups[groupName] || {};
|
|
351
|
+
summary.groups[groupName][fieldName] = genTestObject(summary.groups[groupName], testObject, skipped);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
357
|
+
* Counts the failed tests and adds global counters
|
|
358
|
+
* @param {Object} summary (generated by genTestsSummary)
|
|
359
|
+
*/
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
var countFailures = function countFailures(summary) {
|
|
363
|
+
for (var test in summary.tests) {
|
|
364
|
+
summary[SEVERITY_COUNT_ERROR] += summary.tests[test][SEVERITY_COUNT_ERROR];
|
|
365
|
+
summary[SEVERITY_COUNT_WARN] += summary.tests[test][SEVERITY_COUNT_WARN];
|
|
366
|
+
summary[TEST_COUNT] += summary.tests[test][TEST_COUNT];
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return summary;
|
|
370
|
+
};
|
|
371
|
+
/**
|
|
372
|
+
*
|
|
373
|
+
* @param {Object} summaryKey The container for the test result data
|
|
374
|
+
* @param {VestTest} testObject
|
|
375
|
+
* @returns {Object} Test result summary
|
|
376
|
+
*/
|
|
377
|
+
|
|
378
|
+
var genTestObject = function genTestObject(summaryKey, testObject, skipped) {
|
|
379
|
+
var _ref;
|
|
380
|
+
|
|
381
|
+
var fieldName = testObject.fieldName,
|
|
382
|
+
isWarning = testObject.isWarning,
|
|
383
|
+
failed = testObject.failed,
|
|
384
|
+
statement = testObject.statement;
|
|
385
|
+
summaryKey[fieldName] = summaryKey[fieldName] || (_ref = {}, _ref[SEVERITY_COUNT_ERROR] = 0, _ref[SEVERITY_COUNT_WARN] = 0, _ref[TEST_COUNT] = 0, _ref);
|
|
386
|
+
var testKey = summaryKey[fieldName];
|
|
387
|
+
|
|
388
|
+
if (skipped) {
|
|
389
|
+
return testKey;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
summaryKey[fieldName][TEST_COUNT]++; // Adds to severity group
|
|
393
|
+
|
|
394
|
+
var addTo = function addTo(count, group) {
|
|
395
|
+
testKey[count]++;
|
|
396
|
+
|
|
397
|
+
if (statement) {
|
|
398
|
+
testKey[group] = (testKey[group] || []).concat(statement);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
if (failed) {
|
|
403
|
+
if (isWarning) {
|
|
404
|
+
addTo(SEVERITY_COUNT_WARN, SEVERITY_GROUP_WARN);
|
|
405
|
+
} else {
|
|
406
|
+
addTo(SEVERITY_COUNT_ERROR, SEVERITY_GROUP_ERROR);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return testKey;
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Checks that a given test object matches the currently specified severity level
|
|
415
|
+
* @param {string} severity Represents severity level
|
|
416
|
+
* @param {VestTest} testObject VestTest instance
|
|
417
|
+
* @returns {boolean}
|
|
418
|
+
*/
|
|
419
|
+
|
|
420
|
+
function isMatchingSeverityProfile(severity, testObject) {
|
|
421
|
+
return severity !== SEVERITY_GROUP_WARN && testObject.isWarning || severity === SEVERITY_GROUP_WARN && !testObject.isWarning;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* @param {'warn'|'error'} severity Filter by severity.
|
|
426
|
+
* @param {Object} options
|
|
427
|
+
* @param {String} [options.group] Group name for error lookup.
|
|
428
|
+
* @param {String} [options.fieldName] Field name for error lookup.
|
|
429
|
+
* @returns all messages for given criteria.
|
|
430
|
+
*/
|
|
431
|
+
|
|
432
|
+
var collectFailureMessages = function collectFailureMessages(severity, options) {
|
|
433
|
+
var _useTestObjects = useTestObjects(),
|
|
434
|
+
testObjects = _useTestObjects[0];
|
|
435
|
+
|
|
436
|
+
var _ref = options || {},
|
|
437
|
+
group = _ref.group,
|
|
438
|
+
fieldName = _ref.fieldName;
|
|
439
|
+
|
|
440
|
+
var res = testObjects.reduce(function (collector, testObject) {
|
|
441
|
+
if (group && testObject.groupName !== group) {
|
|
442
|
+
return collector;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (fieldName && testObject.fieldName !== fieldName) {
|
|
446
|
+
return collector;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (!testObject.failed) {
|
|
450
|
+
return collector;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (isMatchingSeverityProfile(severity, testObject)) {
|
|
454
|
+
return collector;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
collector[testObject.fieldName] = (collector[testObject.fieldName] || []).concat(testObject.statement);
|
|
458
|
+
return collector;
|
|
459
|
+
}, {});
|
|
460
|
+
|
|
461
|
+
if (fieldName) {
|
|
462
|
+
return res[fieldName] || [];
|
|
463
|
+
} else {
|
|
464
|
+
return res;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* @param {'errors'|'warnings'} severityKey lookup severity
|
|
470
|
+
* @param {string} [fieldName]
|
|
471
|
+
* @returns suite or field's errors or warnings.
|
|
472
|
+
*/
|
|
473
|
+
|
|
474
|
+
function getFailures(severityKey, fieldName) {
|
|
475
|
+
return collectFailureMessages(severityKey, {
|
|
476
|
+
fieldName: fieldName
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Throws a timed out error.
|
|
482
|
+
* @param {String} message Error message to display.
|
|
483
|
+
* @param {Error} [type] Alternative Error type.
|
|
484
|
+
*/
|
|
485
|
+
var throwError = function throwError(message, type) {
|
|
486
|
+
if (type === void 0) {
|
|
487
|
+
type = Error;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
throw new type("[" + "vest" + "]: " + message);
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Gets failure messages by group.
|
|
495
|
+
* @param {'errors'|'warnings'} severityKey lookup severity
|
|
496
|
+
* @param {string} group Group name.
|
|
497
|
+
* @param {string} [fieldName] Field name.
|
|
498
|
+
*/
|
|
499
|
+
|
|
500
|
+
var getByGroup = function getByGroup(severityKey, group, fieldName) {
|
|
501
|
+
if (!group) {
|
|
502
|
+
throwError("get" + severityKey[0].toUpperCase() + severityKey.slice(1) + "ByGroup requires a group name. Received `" + group + "` instead.");
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return collectFailureMessages(severityKey, {
|
|
506
|
+
group: group,
|
|
507
|
+
fieldName: fieldName
|
|
508
|
+
});
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Determines whether a certain test profile has failures.
|
|
513
|
+
* @param {VestTest} testObject
|
|
514
|
+
* @param {'warnings'|'errors'} severityKey lookup severity
|
|
515
|
+
* @param {string} [fieldName]
|
|
516
|
+
* @returns {Boolean}
|
|
517
|
+
*/
|
|
518
|
+
|
|
519
|
+
var hasLogic = function hasLogic(testObject, severityKey, fieldName) {
|
|
520
|
+
if (!testObject.failed) {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (fieldName && fieldName !== testObject.fieldName) {
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (isMatchingSeverityProfile(severityKey, testObject)) {
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return true;
|
|
533
|
+
};
|
|
534
|
+
/**
|
|
535
|
+
* @param {'warnings'|'errors'} severityKey lookup severity
|
|
536
|
+
* @param {string} [fieldName]
|
|
537
|
+
* @returns {Boolean} whether a suite or field have errors or warnings.
|
|
538
|
+
*/
|
|
539
|
+
|
|
540
|
+
var has = function has(severityKey, fieldName) {
|
|
541
|
+
var _useTestObjects = useTestObjects(),
|
|
542
|
+
testObjects = _useTestObjects[0];
|
|
543
|
+
|
|
544
|
+
return testObjects.some(function (testObject) {
|
|
545
|
+
return hasLogic(testObject, severityKey, fieldName);
|
|
546
|
+
});
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Checks whether there are failures in a given group.
|
|
551
|
+
* @param {'errors'|'warnings'} severityKey lookup severity
|
|
552
|
+
* @param {string} group Group name.
|
|
553
|
+
* @param {string} [fieldName] Field name.
|
|
554
|
+
* @return {boolean}
|
|
555
|
+
*/
|
|
556
|
+
|
|
557
|
+
var hasByGroup = function hasByGroup(severityKey, group, fieldName) {
|
|
558
|
+
var _useTestObjects = useTestObjects(),
|
|
559
|
+
testObjects = _useTestObjects[0];
|
|
560
|
+
|
|
561
|
+
return testObjects.some(function (testObject) {
|
|
562
|
+
if (group !== testObject.groupName) {
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return hasLogic(testObject, severityKey, fieldName);
|
|
567
|
+
});
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* A safe hasOwnProperty access
|
|
572
|
+
*/
|
|
573
|
+
function hasOwnProperty(obj, key) {
|
|
574
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function isNumeric(value) {
|
|
578
|
+
var result = !isNaN(parseFloat(value)) && !isNaN(Number(value)) && isFinite(value);
|
|
579
|
+
return Boolean(result);
|
|
580
|
+
}
|
|
581
|
+
var isNotNumeric = bindNot(isNumeric);
|
|
582
|
+
|
|
583
|
+
function isEmpty(value) {
|
|
584
|
+
if (!value) {
|
|
585
|
+
return true;
|
|
586
|
+
} else if (isNumeric(value)) {
|
|
587
|
+
return value === 0;
|
|
588
|
+
} else if (hasOwnProperty(value, 'length')) {
|
|
589
|
+
return lengthEquals(value, 0);
|
|
590
|
+
} else if (typeof value === 'object') {
|
|
591
|
+
return lengthEquals(Object.keys(value), 0);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return true;
|
|
595
|
+
}
|
|
596
|
+
var isNotEmpty = bindNot(isEmpty);
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Checks if a given tests, or the suite as a whole still have remaining tests.
|
|
600
|
+
* @param {string} [fieldName]
|
|
601
|
+
* @returns {Boolean}
|
|
602
|
+
*/
|
|
603
|
+
|
|
604
|
+
var hasRemainingTests = function hasRemainingTests(fieldName) {
|
|
605
|
+
var _usePending = usePending(),
|
|
606
|
+
_usePending$ = _usePending[0],
|
|
607
|
+
pending = _usePending$.pending,
|
|
608
|
+
lagging = _usePending$.lagging;
|
|
609
|
+
|
|
610
|
+
var allIncomplete = pending.concat(lagging);
|
|
611
|
+
|
|
612
|
+
if (isEmpty(allIncomplete)) {
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (fieldName) {
|
|
617
|
+
return allIncomplete.some(function (testObject) {
|
|
618
|
+
return testObject.fieldName === fieldName;
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return isNotEmpty(allIncomplete);
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
var HAS_WARNINGS = 'hasWarnings';
|
|
626
|
+
var HAS_ERRORS = 'hasErrors';
|
|
627
|
+
|
|
628
|
+
function setFnName(fn, value) {
|
|
629
|
+
var _Object$getOwnPropert;
|
|
630
|
+
|
|
631
|
+
// Pre ES2015 non standard implementation, "Function.name" is non configurable field
|
|
632
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
|
|
633
|
+
return (_Object$getOwnPropert = Object.getOwnPropertyDescriptor(fn, 'name')) !== null && _Object$getOwnPropert !== void 0 && _Object$getOwnPropert.configurable ? Object.defineProperty(fn, 'name', {
|
|
634
|
+
value: value
|
|
635
|
+
}) : fn;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* ES5 Transpilation increases the size of spread arguments by a lot.
|
|
640
|
+
* Wraps a function and passes its spread params as an array.
|
|
641
|
+
*
|
|
642
|
+
* @param {Function} cb
|
|
643
|
+
* @param {String} [fnName]
|
|
644
|
+
* @return {Function}
|
|
645
|
+
*/
|
|
646
|
+
|
|
647
|
+
function withArgs(cb, fnName) {
|
|
648
|
+
return setFnName(function () {
|
|
649
|
+
var args = Array.from(arguments);
|
|
650
|
+
var right = args.splice(cb.length - 1);
|
|
651
|
+
return cb.apply(null, args.concat([right]));
|
|
652
|
+
}, fnName || cb.name);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
var cache = createCache(20);
|
|
656
|
+
/**
|
|
657
|
+
* @param {boolean} [isDraft]
|
|
658
|
+
* @returns Vest output object.
|
|
659
|
+
*/
|
|
660
|
+
|
|
661
|
+
var produce = function produce(isDraft) {
|
|
662
|
+
var _context$use = context.use(),
|
|
663
|
+
stateRef = _context$use.stateRef;
|
|
664
|
+
|
|
665
|
+
var _useTestObjects = useTestObjects(),
|
|
666
|
+
testObjects = _useTestObjects[0];
|
|
667
|
+
|
|
668
|
+
var ctxRef = {
|
|
669
|
+
stateRef: stateRef
|
|
670
|
+
};
|
|
671
|
+
return cache([testObjects, isDraft], context.bind(ctxRef, function () {
|
|
672
|
+
return [[HAS_ERRORS, has, SEVERITY_GROUP_ERROR], [HAS_WARNINGS, has, SEVERITY_GROUP_WARN], ['getErrors', getFailures, SEVERITY_GROUP_ERROR], ['getWarnings', getFailures, SEVERITY_GROUP_WARN], ['hasErrorsByGroup', hasByGroup, SEVERITY_GROUP_ERROR], ['hasWarningsByGroup', hasByGroup, SEVERITY_GROUP_WARN], ['getErrorsByGroup', getByGroup, SEVERITY_GROUP_ERROR], ['getWarningsByGroup', getByGroup, SEVERITY_GROUP_WARN]].concat([['isValid', isValid]], isDraft ? [] : [['done', withArgs(done)]]).reduce(function (properties, _ref) {
|
|
673
|
+
var name = _ref[0],
|
|
674
|
+
fn = _ref[1],
|
|
675
|
+
severityKey = _ref[2];
|
|
676
|
+
properties[name] = context.bind(ctxRef, fn, severityKey);
|
|
677
|
+
return properties;
|
|
678
|
+
}, genTestsSummary());
|
|
679
|
+
}));
|
|
680
|
+
};
|
|
681
|
+
/**
|
|
682
|
+
* Registers done callbacks.
|
|
683
|
+
* @param {string} [fieldName]
|
|
684
|
+
* @param {Function} doneCallback
|
|
685
|
+
* @register {Object} Vest output object.
|
|
686
|
+
*/
|
|
687
|
+
|
|
688
|
+
function done(args) {
|
|
689
|
+
var _args$reverse = args.reverse(),
|
|
690
|
+
callback = _args$reverse[0],
|
|
691
|
+
fieldName = _args$reverse[1];
|
|
692
|
+
|
|
693
|
+
var _context$use2 = context.use(),
|
|
694
|
+
stateRef = _context$use2.stateRef;
|
|
695
|
+
|
|
696
|
+
var output = produce(); // If we do not have any test runs for the current field
|
|
697
|
+
|
|
698
|
+
var shouldSkipRegistration = fieldName && (!output.tests[fieldName] || output.tests[fieldName].testCount === 0);
|
|
699
|
+
|
|
700
|
+
if (!isFunction(callback) || shouldSkipRegistration) {
|
|
701
|
+
return output;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
var cb = context.bind({
|
|
705
|
+
stateRef: stateRef
|
|
706
|
+
}, function () {
|
|
707
|
+
return callback(produce(
|
|
708
|
+
/*isDraft:*/
|
|
709
|
+
true));
|
|
710
|
+
}); // is suite finished || field name exists, and test is finished
|
|
711
|
+
|
|
712
|
+
var shouldRunCallback = !hasRemainingTests() || fieldName && !hasRemainingTests(fieldName);
|
|
713
|
+
|
|
714
|
+
if (shouldRunCallback) {
|
|
715
|
+
cb();
|
|
716
|
+
return output;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
var _useTestCallbacks = useTestCallbacks(),
|
|
720
|
+
setTestCallbacks = _useTestCallbacks[1];
|
|
721
|
+
|
|
722
|
+
setTestCallbacks(function (current) {
|
|
723
|
+
if (fieldName) {
|
|
724
|
+
current.fieldCallbacks[fieldName] = (current.fieldCallbacks[fieldName] || []).concat(cb);
|
|
725
|
+
} else {
|
|
726
|
+
current.doneCallbacks.push(cb);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
return current;
|
|
730
|
+
});
|
|
731
|
+
return output;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function isValid() {
|
|
735
|
+
var result = produce();
|
|
736
|
+
|
|
737
|
+
if (result.hasErrors()) {
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
var _useTestObjects2 = useTestObjects(),
|
|
742
|
+
testObjects = _useTestObjects2[0];
|
|
743
|
+
|
|
744
|
+
if (testObjects.length === 0) {
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
var _useOptionalFields = useOptionalFields(),
|
|
749
|
+
optionalFields = _useOptionalFields[0];
|
|
750
|
+
|
|
751
|
+
var _usePending = usePending(),
|
|
752
|
+
_usePending$ = _usePending[0],
|
|
753
|
+
pending = _usePending$.pending,
|
|
754
|
+
lagging = _usePending$.lagging;
|
|
755
|
+
|
|
756
|
+
if (isNotEmpty(pending.concat(lagging).filter(function (testObject) {
|
|
757
|
+
return !testObject.isWarning;
|
|
758
|
+
}))) {
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
for (var test in result.tests) {
|
|
763
|
+
if (!optionalFields[test] && result.tests[test].testCount === 0) {
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
return true;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Initializes a validation suite, creates a validation context.
|
|
773
|
+
* @param {String} [name] Identifier for validation suite.
|
|
774
|
+
* @param {Function} tests Validation suite body.
|
|
775
|
+
* @returns {Function} validator function.
|
|
776
|
+
*/
|
|
777
|
+
|
|
778
|
+
var createSuite = withArgs(function (args) {
|
|
779
|
+
var _args$reverse = args.reverse(),
|
|
780
|
+
tests = _args$reverse[0],
|
|
781
|
+
name = _args$reverse[1];
|
|
782
|
+
|
|
783
|
+
if (!isFunction(tests)) {
|
|
784
|
+
throwError('Suite initialization error. Expected `tests` to be a function.');
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
var handlers = [];
|
|
788
|
+
var state = createState(function () {
|
|
789
|
+
handlers.forEach(function (fn) {
|
|
790
|
+
return fn({
|
|
791
|
+
suiteState: stateRef,
|
|
792
|
+
type: 'suiteStateUpdate'
|
|
793
|
+
});
|
|
794
|
+
});
|
|
795
|
+
});
|
|
796
|
+
var stateRef = createStateRef(state, {
|
|
797
|
+
suiteId: id(),
|
|
798
|
+
name: name
|
|
799
|
+
});
|
|
800
|
+
/*
|
|
801
|
+
context.bind returns our `validate` function
|
|
802
|
+
We then wrap it with defineProperties to add
|
|
803
|
+
the `get`, and `reset` functions.
|
|
804
|
+
*/
|
|
805
|
+
|
|
806
|
+
var suite = context.bind({
|
|
807
|
+
stateRef: stateRef
|
|
808
|
+
}, function () {
|
|
809
|
+
var _useTestObjects = useTestObjects(),
|
|
810
|
+
previousTestObjects = _useTestObjects[0];
|
|
811
|
+
|
|
812
|
+
var _useCarryOverTests = useCarryOverTests(),
|
|
813
|
+
setCarryOverTests = _useCarryOverTests[1];
|
|
814
|
+
|
|
815
|
+
var _usePending = usePending(),
|
|
816
|
+
pending = _usePending[0].pending,
|
|
817
|
+
setPending = _usePending[1];
|
|
818
|
+
|
|
819
|
+
state.reset();
|
|
820
|
+
setCarryOverTests(function () {
|
|
821
|
+
return previousTestObjects;
|
|
822
|
+
}); // Move all the active pending tests to the lagging array
|
|
823
|
+
|
|
824
|
+
setPending({
|
|
825
|
+
lagging: pending,
|
|
826
|
+
pending: []
|
|
827
|
+
}); // Run the consumer's callback
|
|
828
|
+
|
|
829
|
+
tests.apply(null, arguments);
|
|
830
|
+
return produce();
|
|
831
|
+
});
|
|
832
|
+
suite.get = context.bind({
|
|
833
|
+
stateRef: stateRef
|
|
834
|
+
}, produce,
|
|
835
|
+
/*isDraft:*/
|
|
836
|
+
true);
|
|
837
|
+
suite.reset = state.reset;
|
|
838
|
+
suite.remove = context.bind({
|
|
839
|
+
stateRef: stateRef
|
|
840
|
+
}, function (name) {
|
|
841
|
+
var _useTestObjects2 = useTestObjects(),
|
|
842
|
+
testObjects = _useTestObjects2[0]; // We're mutating the array in `cancel`, so we have to first copy it.
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
asArray(testObjects).forEach(function (testObject) {
|
|
846
|
+
if (testObject.fieldName === name) {
|
|
847
|
+
testObject.cancel();
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
suite.subscribe = function (handler) {
|
|
853
|
+
if (!isFunction(handler)) return;
|
|
854
|
+
handlers.push(handler);
|
|
855
|
+
handler({
|
|
856
|
+
type: 'suiteSubscribeInit',
|
|
857
|
+
suiteState: stateRef
|
|
858
|
+
});
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
return suite;
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
function _unsupportedIterableToArray(o, minLen) {
|
|
865
|
+
if (!o) return;
|
|
866
|
+
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
867
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
868
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
869
|
+
if (n === "Map" || n === "Set") return Array.from(o);
|
|
870
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function _arrayLikeToArray(arr, len) {
|
|
874
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
875
|
+
|
|
876
|
+
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
|
877
|
+
|
|
878
|
+
return arr2;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
function _createForOfIteratorHelperLoose(o, allowArrayLike) {
|
|
882
|
+
var it;
|
|
883
|
+
|
|
884
|
+
if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
|
|
885
|
+
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
|
|
886
|
+
if (it) o = it;
|
|
887
|
+
var i = 0;
|
|
888
|
+
return function () {
|
|
889
|
+
if (i >= o.length) return {
|
|
890
|
+
done: true
|
|
891
|
+
};
|
|
892
|
+
return {
|
|
893
|
+
done: false,
|
|
894
|
+
value: o[i++]
|
|
895
|
+
};
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
it = o[Symbol.iterator]();
|
|
903
|
+
return it.next.bind(it);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Stores values and configuration passed down to compound rules.
|
|
908
|
+
*
|
|
909
|
+
* @param {Object} content
|
|
910
|
+
*/
|
|
911
|
+
|
|
912
|
+
function EnforceContext(content) {
|
|
913
|
+
assign(this, content);
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Sets an EnforceContext config `failFast`
|
|
917
|
+
*
|
|
918
|
+
* @param {Boolean} failFast
|
|
919
|
+
* @return {EnforceContext}
|
|
920
|
+
*/
|
|
921
|
+
|
|
922
|
+
EnforceContext.prototype.setFailFast = function (failFast) {
|
|
923
|
+
this.failFast = !!failFast;
|
|
924
|
+
return this;
|
|
925
|
+
};
|
|
926
|
+
/**
|
|
927
|
+
* Extracts the literal value from an EnforceContext object
|
|
928
|
+
* @param {*} value
|
|
929
|
+
* @return {*}
|
|
930
|
+
*/
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
EnforceContext.unwrap = function unwrap(value) {
|
|
934
|
+
return EnforceContext.is(value) ? value.value : value;
|
|
935
|
+
};
|
|
936
|
+
/**
|
|
937
|
+
* Wraps a literal value within a context.
|
|
938
|
+
* @param {*} value
|
|
939
|
+
* @return {EnforceContext}
|
|
940
|
+
*/
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
EnforceContext.wrap = function wrap(value) {
|
|
944
|
+
return EnforceContext.is(value) ? value : new EnforceContext({
|
|
945
|
+
value: value
|
|
946
|
+
});
|
|
947
|
+
};
|
|
948
|
+
/**
|
|
949
|
+
* Checks whether a given value is an EnforceContext instance
|
|
950
|
+
*
|
|
951
|
+
* @param {*} value
|
|
952
|
+
* @returns {boolean}
|
|
953
|
+
*/
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
EnforceContext.is = function is(value) {
|
|
957
|
+
return value instanceof EnforceContext;
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
function isBoolean(value) {
|
|
961
|
+
return !!value === value;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
var isNotBoolean = bindNot(isBoolean);
|
|
965
|
+
|
|
966
|
+
function isUndefined(value) {
|
|
967
|
+
return value === undefined;
|
|
968
|
+
}
|
|
969
|
+
var isNotUndefined = bindNot(isUndefined);
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* Stores a rule result in an easy to inspect and manipulate structure.
|
|
973
|
+
*
|
|
974
|
+
* @param {boolean|RuleResult} ruleRunResult
|
|
975
|
+
*/
|
|
976
|
+
|
|
977
|
+
function RuleResult(ruleRunResult) {
|
|
978
|
+
if (isUndefined(ruleRunResult)) {
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
if (isBoolean(ruleRunResult)) {
|
|
983
|
+
this.setFailed(!ruleRunResult);
|
|
984
|
+
} else {
|
|
985
|
+
this.extend(ruleRunResult);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Determines whether a given value is a RuleResult instance
|
|
990
|
+
* @param {*} res
|
|
991
|
+
* @return {boolean}
|
|
992
|
+
*/
|
|
993
|
+
|
|
994
|
+
RuleResult.is = function (res) {
|
|
995
|
+
return res instanceof RuleResult;
|
|
996
|
+
};
|
|
997
|
+
/**
|
|
998
|
+
* Marks the current result object as an array
|
|
999
|
+
*/
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
RuleResult.prototype.asArray = function () {
|
|
1003
|
+
this.isArray = true;
|
|
1004
|
+
return this;
|
|
1005
|
+
};
|
|
1006
|
+
/**
|
|
1007
|
+
* @param {string} key
|
|
1008
|
+
* @param {value} value
|
|
1009
|
+
* @return {RuleResult} current instance
|
|
1010
|
+
*/
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
RuleResult.prototype.setAttribute = function (key, value) {
|
|
1014
|
+
this[key] = value;
|
|
1015
|
+
return this;
|
|
1016
|
+
};
|
|
1017
|
+
/**
|
|
1018
|
+
* @param {boolean} failed
|
|
1019
|
+
* @return {RuleResult} current instance
|
|
1020
|
+
*/
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
RuleResult.prototype.setFailed = function (failed) {
|
|
1024
|
+
this.setAttribute(this.warn ? HAS_WARNINGS : HAS_ERRORS, failed);
|
|
1025
|
+
return this.setAttribute('failed', failed);
|
|
1026
|
+
};
|
|
1027
|
+
/**
|
|
1028
|
+
* Adds a nested result object
|
|
1029
|
+
*
|
|
1030
|
+
* @param {string} key
|
|
1031
|
+
* @param {RuleResult} child
|
|
1032
|
+
*/
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
RuleResult.prototype.setChild = function (key, child) {
|
|
1036
|
+
if (isNull(child)) {
|
|
1037
|
+
return null;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
var isWarning = this[HAS_WARNINGS] || child[HAS_WARNINGS] || child.warn || this.warn;
|
|
1041
|
+
this.setAttribute(HAS_WARNINGS, isWarning && child.failed || false);
|
|
1042
|
+
this.setAttribute(HAS_ERRORS, this[HAS_ERRORS] || child[HAS_ERRORS] || !isWarning && child.failed || false);
|
|
1043
|
+
this.setFailed(this.failed || child.failed);
|
|
1044
|
+
this.children = this.children || {};
|
|
1045
|
+
this.children[key] = child;
|
|
1046
|
+
return child;
|
|
1047
|
+
};
|
|
1048
|
+
/**
|
|
1049
|
+
* Retrieves a child by its key
|
|
1050
|
+
*
|
|
1051
|
+
* @param {string} key
|
|
1052
|
+
* @return {RuleResult|undefined}
|
|
1053
|
+
*/
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
RuleResult.prototype.getChild = function (key) {
|
|
1057
|
+
return (this.children || {})[key];
|
|
1058
|
+
};
|
|
1059
|
+
/**
|
|
1060
|
+
* Extends current instance with a new provided result
|
|
1061
|
+
* @param {Boolean|RuleResult} newRes
|
|
1062
|
+
*/
|
|
1063
|
+
|
|
1064
|
+
|
|
1065
|
+
RuleResult.prototype.extend = function (newRes) {
|
|
1066
|
+
if (isNull(newRes)) {
|
|
1067
|
+
return this;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
var res = RuleResult.is(newRes) ? newRes : new RuleResult().setAttribute('warn', !!this.warn).setFailed(!newRes);
|
|
1071
|
+
var failed = this.failed || res.failed;
|
|
1072
|
+
var children = mergeChildren(res, this).children;
|
|
1073
|
+
assign(this, res);
|
|
1074
|
+
|
|
1075
|
+
if (!isEmpty(children)) {
|
|
1076
|
+
this.children = children;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
this.setFailed(failed);
|
|
1080
|
+
this.setAttribute(HAS_WARNINGS, !!(this[HAS_WARNINGS] || res[HAS_WARNINGS]));
|
|
1081
|
+
this.setAttribute(HAS_ERRORS, !!(this[HAS_ERRORS] || res[HAS_ERRORS]));
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
Object.defineProperty(RuleResult.prototype, 'pass', {
|
|
1085
|
+
get: function get() {
|
|
1086
|
+
return !this.failed;
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
/**
|
|
1090
|
+
* Deeply merge the nested children of compound rules
|
|
1091
|
+
*
|
|
1092
|
+
* @param {?RuleResult} base
|
|
1093
|
+
* @param {?RuleResult} add
|
|
1094
|
+
* @return {RuleResult}
|
|
1095
|
+
*/
|
|
1096
|
+
|
|
1097
|
+
function mergeChildren(base, add) {
|
|
1098
|
+
var isRuleResultBase = RuleResult.is(base);
|
|
1099
|
+
var isRuleResultAdd = RuleResult.is(add); // If both base and add are result objects
|
|
1100
|
+
|
|
1101
|
+
if (isRuleResultBase && isRuleResultAdd) {
|
|
1102
|
+
// Use failed if either is failing
|
|
1103
|
+
base.setFailed(base.failed || add.failed); // If neither has a children object, or the children object is
|
|
1104
|
+
|
|
1105
|
+
if (isEmpty(base.children) && isEmpty(add.children)) {
|
|
1106
|
+
return base;
|
|
1107
|
+
} // If both have a children object
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
if (base.children && add.children) {
|
|
1111
|
+
// Merge all the "right side" children back to base
|
|
1112
|
+
for (var key in base.children) {
|
|
1113
|
+
mergeChildren(base.children[key], add.children[key]);
|
|
1114
|
+
} // If a child exists in "add" but not in "base", just copy the child as is
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
for (var _key in add.children) {
|
|
1118
|
+
if (!hasOwnProperty(base.children, _key)) {
|
|
1119
|
+
base.setChild(_key, add.children[_key]);
|
|
1120
|
+
}
|
|
1121
|
+
} // Return the modified base object
|
|
1122
|
+
|
|
1123
|
+
|
|
1124
|
+
return base; // If base has no children (but add does)
|
|
1125
|
+
} else if (!base.children) {
|
|
1126
|
+
// Use add's children
|
|
1127
|
+
base.children = add.children; // If add has no children
|
|
1128
|
+
} else if (!add.children) {
|
|
1129
|
+
// return base as is
|
|
1130
|
+
return base;
|
|
1131
|
+
} // If only base is `RuleResult`
|
|
1132
|
+
|
|
1133
|
+
} else if (isRuleResultBase) {
|
|
1134
|
+
// Return base as is
|
|
1135
|
+
return base; // If only add is RuleResult
|
|
1136
|
+
} else if (isRuleResultAdd) {
|
|
1137
|
+
// Return add as is
|
|
1138
|
+
return add;
|
|
1139
|
+
} // That's a weird case. Let's fail. Very unlikely.
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
return new RuleResult(false);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
var RUN_RULE = 'run';
|
|
1146
|
+
var TEST_RULE = 'test';
|
|
1147
|
+
var MODE_ALL = 'all';
|
|
1148
|
+
var MODE_ONE = 'one';
|
|
1149
|
+
var MODE_ANY = 'any';
|
|
1150
|
+
|
|
1151
|
+
/**
|
|
1152
|
+
* Determines whether we should bail out of an enforcement.
|
|
1153
|
+
*
|
|
1154
|
+
* @param {EnforceContext} ctx
|
|
1155
|
+
* @param {RuleResult} result
|
|
1156
|
+
*/
|
|
1157
|
+
|
|
1158
|
+
function shouldFailFast(ctx, result) {
|
|
1159
|
+
if (result.pass || result.warn) {
|
|
1160
|
+
return false;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
return !!EnforceContext.is(ctx) && ctx.failFast;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/**
|
|
1167
|
+
* Runs multiple enforce rules that are passed to compounds.
|
|
1168
|
+
* Example: enforce.allOf(enforce.ruleOne(), enforce.ruleTwo().ruleThree())
|
|
1169
|
+
*
|
|
1170
|
+
* @param {{run: Function}[]} ruleGroups
|
|
1171
|
+
* @param {*} value
|
|
1172
|
+
* @return {RuleResult}
|
|
1173
|
+
*/
|
|
1174
|
+
|
|
1175
|
+
function runLazyRules(ruleGroups, value) {
|
|
1176
|
+
var result = new RuleResult(true);
|
|
1177
|
+
|
|
1178
|
+
for (var _iterator = _createForOfIteratorHelperLoose(asArray(ruleGroups)), _step; !(_step = _iterator()).done;) {
|
|
1179
|
+
var chain = _step.value;
|
|
1180
|
+
|
|
1181
|
+
if (shouldFailFast(value, result)) {
|
|
1182
|
+
break;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
result.extend(runLazyRule(chain, value));
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
return result;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Runs a single lazy rule
|
|
1192
|
+
*
|
|
1193
|
+
* @param {{run: Function}} ruleGroup
|
|
1194
|
+
* @param {*} value
|
|
1195
|
+
* @return {boolean|RuleResult}
|
|
1196
|
+
*/
|
|
1197
|
+
|
|
1198
|
+
function runLazyRule(ruleGroup, value) {
|
|
1199
|
+
return ruleGroup[RUN_RULE](value);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/**
|
|
1203
|
+
* Runs chains of rules
|
|
1204
|
+
*
|
|
1205
|
+
* @param {EnforceContext} value
|
|
1206
|
+
* @param {[{test: Function, run: Function}]} rules
|
|
1207
|
+
* @param {RuleResult} options
|
|
1208
|
+
*/
|
|
1209
|
+
|
|
1210
|
+
function runCompoundChain(value, rules, options) {
|
|
1211
|
+
var result = new RuleResult(true);
|
|
1212
|
+
|
|
1213
|
+
if (isEmpty(rules)) {
|
|
1214
|
+
result.setFailed(true);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
var failedResults = [];
|
|
1218
|
+
var count = 0;
|
|
1219
|
+
|
|
1220
|
+
for (var _iterator = _createForOfIteratorHelperLoose(rules), _step; !(_step = _iterator()).done;) {
|
|
1221
|
+
var chain = _step.value;
|
|
1222
|
+
// Inner result for each iteration
|
|
1223
|
+
var currentResult = runLazyRule(chain, value);
|
|
1224
|
+
|
|
1225
|
+
if (isNull(currentResult)) {
|
|
1226
|
+
return null;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
var pass = currentResult.pass;
|
|
1230
|
+
|
|
1231
|
+
if (pass) {
|
|
1232
|
+
count++;
|
|
1233
|
+
} else {
|
|
1234
|
+
failedResults.push(currentResult);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
if (options) {
|
|
1238
|
+
// "anyOf" is a special case.
|
|
1239
|
+
// It shouldn't extend with failed results,
|
|
1240
|
+
// that's why we continue
|
|
1241
|
+
if (options.mode === MODE_ANY) {
|
|
1242
|
+
if (pass) {
|
|
1243
|
+
result.extend(currentResult);
|
|
1244
|
+
break;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
result.extend(currentResult); // MODE_ALL: All must pass.
|
|
1251
|
+
// If one failed, exit.
|
|
1252
|
+
|
|
1253
|
+
if (options.mode === MODE_ALL) {
|
|
1254
|
+
if (!pass) {
|
|
1255
|
+
break;
|
|
1256
|
+
}
|
|
1257
|
+
} // MODE_ONE: only one must pass.
|
|
1258
|
+
// If more than one passed, exit.
|
|
1259
|
+
|
|
1260
|
+
|
|
1261
|
+
if (options.mode === MODE_ONE) {
|
|
1262
|
+
result.setFailed(count !== 1);
|
|
1263
|
+
|
|
1264
|
+
if (count > 1) {
|
|
1265
|
+
break;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
} else {
|
|
1269
|
+
result.extend(currentResult);
|
|
1270
|
+
|
|
1271
|
+
if (pass) {
|
|
1272
|
+
break;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
if (result.pass && count === 0) {
|
|
1278
|
+
result.setFailed(true); // In some cases we do not want to extend failures, for example - in ANY
|
|
1279
|
+
// when there is a valid response, so we do it before returning
|
|
1280
|
+
|
|
1281
|
+
failedResults.forEach(function (failedResult) {
|
|
1282
|
+
return result.extend(failedResult);
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
return result;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* Runs a chain of rules, making sure that all assertions pass
|
|
1291
|
+
*
|
|
1292
|
+
* @param {EnforceContext} value
|
|
1293
|
+
* @param {[{test: Function, run: Function}]} ruleChains
|
|
1294
|
+
* @return {RuleResult}
|
|
1295
|
+
*/
|
|
1296
|
+
|
|
1297
|
+
function allOf(value, rules) {
|
|
1298
|
+
return runCompoundChain(value, rules, {
|
|
1299
|
+
mode: MODE_ALL
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
var allOf$1 = withArgs(allOf);
|
|
1304
|
+
|
|
1305
|
+
/**
|
|
1306
|
+
* Runs chains of rules, making sure
|
|
1307
|
+
* that at least one assertion passes
|
|
1308
|
+
*
|
|
1309
|
+
* @param {EnforceContext} value
|
|
1310
|
+
* @param {[{test: Function, run: Function}]} ruleChains
|
|
1311
|
+
* @return {RuleResult}
|
|
1312
|
+
*/
|
|
1313
|
+
|
|
1314
|
+
function anyOf(value, ruleChains) {
|
|
1315
|
+
return runCompoundChain(value, ruleChains, {
|
|
1316
|
+
mode: MODE_ANY
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
var anyOf$1 = withArgs(anyOf);
|
|
1321
|
+
|
|
1322
|
+
function isArray(value) {
|
|
1323
|
+
return Boolean(Array.isArray(value));
|
|
1324
|
+
}
|
|
1325
|
+
var isNotArray = bindNot(isArray);
|
|
1326
|
+
|
|
1327
|
+
/**
|
|
1328
|
+
* Asserts that each element in an array passes
|
|
1329
|
+
* at least one of the provided rule chain
|
|
1330
|
+
*
|
|
1331
|
+
* @param {EnforceContext} value
|
|
1332
|
+
* @param {[{test: Function, run: Function}]} ruleChains
|
|
1333
|
+
* @return {RuleResult}
|
|
1334
|
+
*/
|
|
1335
|
+
|
|
1336
|
+
function isArrayOf(value, ruleChains) {
|
|
1337
|
+
var plainValue = EnforceContext.unwrap(value);
|
|
1338
|
+
var result = new RuleResult(true).asArray(); // Fails if current value is not an array
|
|
1339
|
+
|
|
1340
|
+
if (isNotArray(plainValue)) {
|
|
1341
|
+
return result.setFailed(true);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
for (var i = 0; i < plainValue.length; i++) {
|
|
1345
|
+
// Set result per each item in the array
|
|
1346
|
+
result.setChild(i, runCompoundChain(new EnforceContext({
|
|
1347
|
+
value: plainValue[i],
|
|
1348
|
+
obj: plainValue,
|
|
1349
|
+
key: i
|
|
1350
|
+
}).setFailFast(value.failFast), ruleChains, {
|
|
1351
|
+
mode: MODE_ANY
|
|
1352
|
+
}));
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
return result;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
var isArrayOf$1 = withArgs(isArrayOf);
|
|
1359
|
+
|
|
1360
|
+
/**
|
|
1361
|
+
* @param {EnforceContext} value
|
|
1362
|
+
* @param {[{test: Function, run: Function}]} ruleChains
|
|
1363
|
+
* @return {RuleResult}
|
|
1364
|
+
*/
|
|
1365
|
+
|
|
1366
|
+
function oneOf(value, rules) {
|
|
1367
|
+
return runCompoundChain(value, rules, {
|
|
1368
|
+
mode: MODE_ONE
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
var oneOf$1 = withArgs(oneOf);
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
* @param {Array} ObjectEntry Object and key leading to current value
|
|
1376
|
+
* @param {Function[]} rules Rules to validate the value with
|
|
1377
|
+
*/
|
|
1378
|
+
|
|
1379
|
+
function optional$1(inputObject, ruleGroups) {
|
|
1380
|
+
var obj = inputObject.obj,
|
|
1381
|
+
key = inputObject.key; // If current value is not defined, undefined or null
|
|
1382
|
+
// Pass without further assertions
|
|
1383
|
+
|
|
1384
|
+
if (!hasOwnProperty(obj, key) || isUndefined(obj[key] || isNull(obj[key]))) {
|
|
1385
|
+
return true;
|
|
1386
|
+
} // Pass if exists but no assertions
|
|
1387
|
+
|
|
1388
|
+
|
|
1389
|
+
if (isEmpty(ruleGroups)) {
|
|
1390
|
+
return true;
|
|
1391
|
+
} // Run chain with `all` mode
|
|
1392
|
+
|
|
1393
|
+
|
|
1394
|
+
return runCompoundChain(obj[key], ruleGroups, {
|
|
1395
|
+
mode: MODE_ALL
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
var optional$2 = withArgs(optional$1);
|
|
1400
|
+
|
|
1401
|
+
/**
|
|
1402
|
+
* @param {EnforceContext} inputObject Data object that gets validated
|
|
1403
|
+
* @param {Object} shapeObj Shape definition
|
|
1404
|
+
* @param {Object} options
|
|
1405
|
+
* @param {boolean} options.loose Ignore extra keys not defined in shapeObj
|
|
1406
|
+
*/
|
|
1407
|
+
|
|
1408
|
+
function shape(inputObject, shapeObj, options) {
|
|
1409
|
+
// Extract the object from context
|
|
1410
|
+
var obj = EnforceContext.unwrap(inputObject); // Create a new result object
|
|
1411
|
+
|
|
1412
|
+
var result = new RuleResult(true); // Iterate over the shape keys
|
|
1413
|
+
|
|
1414
|
+
for (var key in shapeObj) {
|
|
1415
|
+
var current = shapeObj[key];
|
|
1416
|
+
var value = obj[key];
|
|
1417
|
+
|
|
1418
|
+
if (shouldFailFast(value, result)) {
|
|
1419
|
+
break;
|
|
1420
|
+
} // Set each key in the result object
|
|
1421
|
+
|
|
1422
|
+
|
|
1423
|
+
result.setChild(key, runLazyRule(current, new EnforceContext({
|
|
1424
|
+
value: value,
|
|
1425
|
+
obj: obj,
|
|
1426
|
+
key: key
|
|
1427
|
+
}).setFailFast(inputObject.failFast)));
|
|
1428
|
+
} // If mode is not loose
|
|
1429
|
+
|
|
1430
|
+
|
|
1431
|
+
if (!(options || {}).loose) {
|
|
1432
|
+
// Check that each key in the input object exists in the shape
|
|
1433
|
+
for (var _key in obj) {
|
|
1434
|
+
if (!hasOwnProperty(shapeObj, _key)) {
|
|
1435
|
+
return result.setFailed(true);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
return result;
|
|
1441
|
+
}
|
|
1442
|
+
var loose = function loose(obj, shapeObj) {
|
|
1443
|
+
return shape(obj, shapeObj, {
|
|
1444
|
+
loose: true
|
|
1445
|
+
});
|
|
1446
|
+
};
|
|
1447
|
+
|
|
1448
|
+
var compounds = {
|
|
1449
|
+
allOf: allOf$1,
|
|
1450
|
+
anyOf: anyOf$1,
|
|
1451
|
+
isArrayOf: isArrayOf$1,
|
|
1452
|
+
loose: loose,
|
|
1453
|
+
oneOf: oneOf$1,
|
|
1454
|
+
optional: optional$2,
|
|
1455
|
+
shape: shape
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1458
|
+
/**
|
|
1459
|
+
* Takes a value. If it is a function, runs it and returns the result.
|
|
1460
|
+
* Otherwise, returns the value as is.
|
|
1461
|
+
*
|
|
1462
|
+
* @param {Function|*} value Value to return. Run it if a function.
|
|
1463
|
+
* @param {Any[]} [args] Arguments to pass if a function
|
|
1464
|
+
* @return {Any}
|
|
1465
|
+
*/
|
|
1466
|
+
|
|
1467
|
+
function optionalFunctionValue(value, args) {
|
|
1468
|
+
return isFunction(value) ? value.apply(null, args) : value;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
function message(value, msg) {
|
|
1472
|
+
return optionalFunctionValue(msg, [EnforceContext.unwrap(value)]);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
function warn$1(_, isWarn) {
|
|
1476
|
+
if (isWarn === void 0) {
|
|
1477
|
+
isWarn = true;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
return isWarn;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
function when(value, condition, bail) {
|
|
1484
|
+
var shouldBail = !optionalFunctionValue(condition, [EnforceContext.unwrap(value)].concat(EnforceContext.is(value) ? [value.key, value.obj] : []));
|
|
1485
|
+
return bail(shouldBail);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
var ruleMeta = {
|
|
1489
|
+
warn: warn$1,
|
|
1490
|
+
message: message,
|
|
1491
|
+
when: when
|
|
1492
|
+
};
|
|
1493
|
+
|
|
1494
|
+
function isStringValue (v) {
|
|
1495
|
+
return String(v) === v;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
function endsWith(value, arg1) {
|
|
1499
|
+
return isStringValue(value) && isStringValue(arg1) && value.endsWith(arg1);
|
|
1500
|
+
}
|
|
1501
|
+
var doesNotEndWith = bindNot(endsWith);
|
|
1502
|
+
|
|
1503
|
+
function equals(value, arg1) {
|
|
1504
|
+
return value === arg1;
|
|
1505
|
+
}
|
|
1506
|
+
var notEquals = bindNot(equals);
|
|
1507
|
+
|
|
1508
|
+
function greaterThan(value, arg1) {
|
|
1509
|
+
return isNumeric(value) && isNumeric(arg1) && Number(value) > Number(arg1);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
function greaterThanOrEquals(value, arg1) {
|
|
1513
|
+
return isNumeric(value) && isNumeric(arg1) && Number(value) >= Number(arg1);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
function inside(value, arg1) {
|
|
1517
|
+
if (Array.isArray(arg1) && /^[s|n|b]/.test(typeof value)) {
|
|
1518
|
+
return arg1.indexOf(value) !== -1;
|
|
1519
|
+
} // both value and arg1 are strings
|
|
1520
|
+
|
|
1521
|
+
|
|
1522
|
+
if (isStringValue(arg1) && isStringValue(value)) {
|
|
1523
|
+
return arg1.indexOf(value) !== -1;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
return false;
|
|
1527
|
+
}
|
|
1528
|
+
var notInside = bindNot(inside);
|
|
1529
|
+
|
|
1530
|
+
function lessThanOrEquals(value, arg1) {
|
|
1531
|
+
return isNumeric(value) && isNumeric(arg1) && Number(value) <= Number(arg1);
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
function isBetween(value, min, max) {
|
|
1535
|
+
return greaterThanOrEquals(value, min) && lessThanOrEquals(value, max);
|
|
1536
|
+
}
|
|
1537
|
+
var isNotBetween = bindNot(isBetween);
|
|
1538
|
+
|
|
1539
|
+
function isBlank(value) {
|
|
1540
|
+
return typeof value === 'string' && value.trim() === '';
|
|
1541
|
+
}
|
|
1542
|
+
var isNotBlank = bindNot(isBlank);
|
|
1543
|
+
|
|
1544
|
+
/**
|
|
1545
|
+
* Validates that a given value is an even number
|
|
1546
|
+
* @param {Number|String} value Value to be validated
|
|
1547
|
+
* @return {Boolean}
|
|
1548
|
+
*/
|
|
1549
|
+
|
|
1550
|
+
var isEven = function isEven(value) {
|
|
1551
|
+
if (isNumeric(value)) {
|
|
1552
|
+
return value % 2 === 0;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
return false;
|
|
1556
|
+
};
|
|
1557
|
+
|
|
1558
|
+
function isNaN$1(value) {
|
|
1559
|
+
return Number.isNaN(value);
|
|
1560
|
+
}
|
|
1561
|
+
var isNotNaN = bindNot(isNaN$1);
|
|
1562
|
+
|
|
1563
|
+
function isNegative(value) {
|
|
1564
|
+
if (isNumeric(value)) {
|
|
1565
|
+
return Number(value) < 0;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
return false;
|
|
1569
|
+
}
|
|
1570
|
+
var isPositive = bindNot(isNegative);
|
|
1571
|
+
|
|
1572
|
+
function isNumber(value) {
|
|
1573
|
+
return Boolean(typeof value === 'number');
|
|
1574
|
+
}
|
|
1575
|
+
var isNotNumber = bindNot(isNumber);
|
|
1576
|
+
|
|
1577
|
+
/**
|
|
1578
|
+
* Validates that a given value is an odd number
|
|
1579
|
+
* @param {Number|String} value Value to be validated
|
|
1580
|
+
* @return {Boolean}
|
|
1581
|
+
*/
|
|
1582
|
+
|
|
1583
|
+
var isOdd = function isOdd(value) {
|
|
1584
|
+
if (isNumeric(value)) {
|
|
1585
|
+
return value % 2 !== 0;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
return false;
|
|
1589
|
+
};
|
|
1590
|
+
|
|
1591
|
+
var isNotString = bindNot(isStringValue);
|
|
1592
|
+
|
|
1593
|
+
function isTruthy(value) {
|
|
1594
|
+
return !!value;
|
|
1595
|
+
}
|
|
1596
|
+
var isFalsy = bindNot(isTruthy);
|
|
1597
|
+
|
|
1598
|
+
function lessThan(value, arg1) {
|
|
1599
|
+
return isNumeric(value) && isNumeric(arg1) && Number(value) < Number(arg1);
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
function longerThan(value, arg1) {
|
|
1603
|
+
return value.length > Number(arg1);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
function longerThanOrEquals(value, arg1) {
|
|
1607
|
+
return value.length >= Number(arg1);
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
function matches(value, regex) {
|
|
1611
|
+
if (regex instanceof RegExp) {
|
|
1612
|
+
return regex.test(value);
|
|
1613
|
+
} else if (isStringValue(regex)) {
|
|
1614
|
+
return new RegExp(regex).test(value);
|
|
1615
|
+
} else {
|
|
1616
|
+
return false;
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
var notMatches = bindNot(matches);
|
|
1620
|
+
|
|
1621
|
+
function numberEquals(value, arg1) {
|
|
1622
|
+
return isNumeric(value) && isNumeric(arg1) && Number(value) === Number(arg1);
|
|
1623
|
+
}
|
|
1624
|
+
var numberNotEquals = bindNot(numberEquals);
|
|
1625
|
+
|
|
1626
|
+
function shorterThan(value, arg1) {
|
|
1627
|
+
return value.length < Number(arg1);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
function shorterThanOrEquals(value, arg1) {
|
|
1631
|
+
return value.length <= Number(arg1);
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
function startsWith(value, arg1) {
|
|
1635
|
+
return isStringValue(value) && isStringValue(arg1) && value.startsWith(arg1);
|
|
1636
|
+
}
|
|
1637
|
+
var doesNotStartWith = bindNot(startsWith);
|
|
1638
|
+
|
|
1639
|
+
function rules() {
|
|
1640
|
+
return {
|
|
1641
|
+
doesNotEndWith: doesNotEndWith,
|
|
1642
|
+
doesNotStartWith: doesNotStartWith,
|
|
1643
|
+
endsWith: endsWith,
|
|
1644
|
+
equals: equals,
|
|
1645
|
+
greaterThan: greaterThan,
|
|
1646
|
+
greaterThanOrEquals: greaterThanOrEquals,
|
|
1647
|
+
gt: greaterThan,
|
|
1648
|
+
gte: greaterThanOrEquals,
|
|
1649
|
+
inside: inside,
|
|
1650
|
+
isArray: isArray,
|
|
1651
|
+
isBetween: isBetween,
|
|
1652
|
+
isBoolean: isBoolean,
|
|
1653
|
+
isBlank: isBlank,
|
|
1654
|
+
isEmpty: isEmpty,
|
|
1655
|
+
isEven: isEven,
|
|
1656
|
+
isFalsy: isFalsy,
|
|
1657
|
+
isNaN: isNaN$1,
|
|
1658
|
+
isNegative: isNegative,
|
|
1659
|
+
isNotArray: isNotArray,
|
|
1660
|
+
isNotBetween: isNotBetween,
|
|
1661
|
+
isNotBlank: isNotBlank,
|
|
1662
|
+
isNotBoolean: isNotBoolean,
|
|
1663
|
+
isNotEmpty: isNotEmpty,
|
|
1664
|
+
isNotNaN: isNotNaN,
|
|
1665
|
+
isNotNull: isNotNull,
|
|
1666
|
+
isNotNumber: isNotNumber,
|
|
1667
|
+
isNotNumeric: isNotNumeric,
|
|
1668
|
+
isNotString: isNotString,
|
|
1669
|
+
isNotUndefined: isNotUndefined,
|
|
1670
|
+
isNull: isNull,
|
|
1671
|
+
isNumber: isNumber,
|
|
1672
|
+
isNumeric: isNumeric,
|
|
1673
|
+
isOdd: isOdd,
|
|
1674
|
+
isPositive: isPositive,
|
|
1675
|
+
isString: isStringValue,
|
|
1676
|
+
isTruthy: isTruthy,
|
|
1677
|
+
isUndefined: isUndefined,
|
|
1678
|
+
lengthEquals: lengthEquals,
|
|
1679
|
+
lengthNotEquals: lengthNotEquals,
|
|
1680
|
+
lessThan: lessThan,
|
|
1681
|
+
lessThanOrEquals: lessThanOrEquals,
|
|
1682
|
+
longerThan: longerThan,
|
|
1683
|
+
longerThanOrEquals: longerThanOrEquals,
|
|
1684
|
+
lt: lessThan,
|
|
1685
|
+
lte: lessThanOrEquals,
|
|
1686
|
+
matches: matches,
|
|
1687
|
+
notEquals: notEquals,
|
|
1688
|
+
notInside: notInside,
|
|
1689
|
+
notMatches: notMatches,
|
|
1690
|
+
numberEquals: numberEquals,
|
|
1691
|
+
numberNotEquals: numberNotEquals,
|
|
1692
|
+
shorterThan: shorterThan,
|
|
1693
|
+
shorterThanOrEquals: shorterThanOrEquals,
|
|
1694
|
+
startsWith: startsWith
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
var runtimeRules = assign(rules(), compounds, ruleMeta);
|
|
1699
|
+
|
|
1700
|
+
/**
|
|
1701
|
+
* Determines whether a given string is a name of a rule
|
|
1702
|
+
*
|
|
1703
|
+
* @param {string} name
|
|
1704
|
+
* @return {boolean}
|
|
1705
|
+
*/
|
|
1706
|
+
|
|
1707
|
+
var isRule = function isRule(name) {
|
|
1708
|
+
return hasOwnProperty(runtimeRules, name) && isFunction(runtimeRules[name]);
|
|
1709
|
+
};
|
|
1710
|
+
|
|
1711
|
+
var GLOBAL_OBJECT = Function('return this')();
|
|
1712
|
+
|
|
1713
|
+
var proxySupported = function proxySupported() {
|
|
1714
|
+
return isFunction(GLOBAL_OBJECT.Proxy);
|
|
1715
|
+
};
|
|
1716
|
+
|
|
1717
|
+
function genRuleProxy(target, output) {
|
|
1718
|
+
if (proxySupported()) {
|
|
1719
|
+
return new Proxy(target, {
|
|
1720
|
+
get: function get(target, fnName) {
|
|
1721
|
+
// A faster bailout when we access `run` and `test`
|
|
1722
|
+
if (hasOwnProperty(target, fnName)) {
|
|
1723
|
+
return target[fnName];
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
if (isRule(fnName)) {
|
|
1727
|
+
return output(fnName);
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
return target[fnName];
|
|
1731
|
+
}
|
|
1732
|
+
});
|
|
1733
|
+
} else {
|
|
1734
|
+
var _loop = function _loop(fnName) {
|
|
1735
|
+
if (!isFunction(target[fnName])) {
|
|
1736
|
+
var _Object$definePropert;
|
|
1737
|
+
|
|
1738
|
+
Object.defineProperties(target, (_Object$definePropert = {}, _Object$definePropert[fnName] = {
|
|
1739
|
+
get: function get() {
|
|
1740
|
+
return output(fnName);
|
|
1741
|
+
}
|
|
1742
|
+
}, _Object$definePropert));
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
|
|
1746
|
+
/**
|
|
1747
|
+
* This method is REALLY not recommended as it is slow and iterates over
|
|
1748
|
+
* all the rules for each direct enforce reference. We only use it as a
|
|
1749
|
+
* lightweight alternative for the much faster proxy interface
|
|
1750
|
+
*/
|
|
1751
|
+
for (var fnName in runtimeRules) {
|
|
1752
|
+
_loop(fnName);
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
return target;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
/**
|
|
1760
|
+
* Determines whether a given rule is a compound.
|
|
1761
|
+
*
|
|
1762
|
+
* @param {Function} rule
|
|
1763
|
+
* @return {boolean}
|
|
1764
|
+
*/
|
|
1765
|
+
|
|
1766
|
+
function isCompound(rule) {
|
|
1767
|
+
return hasOwnProperty(compounds, rule.name);
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
/**
|
|
1771
|
+
* Creates a rule of lazily called rules.
|
|
1772
|
+
* Each rule gets added a `.run()` property
|
|
1773
|
+
* which runs all the accumulated rules in
|
|
1774
|
+
* the chain against the supplied value
|
|
1775
|
+
*
|
|
1776
|
+
* @param {string} ruleName
|
|
1777
|
+
* @return {{run: Function}}
|
|
1778
|
+
*/
|
|
1779
|
+
|
|
1780
|
+
function bindLazyRule(ruleName) {
|
|
1781
|
+
var registeredRules = []; // Chained rules
|
|
1782
|
+
|
|
1783
|
+
var meta = []; // Meta properties to add onto the rule context
|
|
1784
|
+
// Appends a function to the registeredRules array.
|
|
1785
|
+
// It gets called every time the consumer usess chaining
|
|
1786
|
+
// so, for example - enforce.isArray() <- this calles addFn
|
|
1787
|
+
|
|
1788
|
+
var addFn = function addFn(ruleName) {
|
|
1789
|
+
return withArgs(function (args) {
|
|
1790
|
+
var rule = runtimeRules[ruleName]; // Add a meta function
|
|
1791
|
+
|
|
1792
|
+
if (ruleMeta[rule.name] === rule) {
|
|
1793
|
+
meta.push(function (value, ruleResult, bail) {
|
|
1794
|
+
ruleResult.setAttribute(rule.name, rule(value, args[0], bail));
|
|
1795
|
+
});
|
|
1796
|
+
} else {
|
|
1797
|
+
// Register a rule
|
|
1798
|
+
registeredRules.push(setFnName(function (value) {
|
|
1799
|
+
return rule.apply(null, [// If the rule is compound - wraps the value with context
|
|
1800
|
+
// Otherwise - unwraps it
|
|
1801
|
+
isCompound(rule) ? EnforceContext.wrap(value) : EnforceContext.unwrap(value)].concat(args));
|
|
1802
|
+
}, ruleName));
|
|
1803
|
+
} // set addFn as the proxy handler
|
|
1804
|
+
|
|
1805
|
+
|
|
1806
|
+
var returnvalue = genRuleProxy({}, addFn);
|
|
1807
|
+
|
|
1808
|
+
returnvalue[RUN_RULE] = function (value) {
|
|
1809
|
+
var result = new RuleResult(true);
|
|
1810
|
+
var bailed = false; // Run meta chains
|
|
1811
|
+
|
|
1812
|
+
meta.forEach(function (fn) {
|
|
1813
|
+
fn(value, result, function (shouldBail) {
|
|
1814
|
+
return bailed = shouldBail;
|
|
1815
|
+
});
|
|
1816
|
+
});
|
|
1817
|
+
|
|
1818
|
+
if (bailed) {
|
|
1819
|
+
return null;
|
|
1820
|
+
} // Iterate over all the registered rules
|
|
1821
|
+
// This runs the function that's inside `addFn`
|
|
1822
|
+
|
|
1823
|
+
|
|
1824
|
+
for (var _iterator = _createForOfIteratorHelperLoose(registeredRules), _step; !(_step = _iterator()).done;) {
|
|
1825
|
+
var fn = _step.value;
|
|
1826
|
+
|
|
1827
|
+
try {
|
|
1828
|
+
result.extend(fn(value)); // If a chained rule fails, exit. We failed.
|
|
1829
|
+
|
|
1830
|
+
if (!result.pass) {
|
|
1831
|
+
break;
|
|
1832
|
+
}
|
|
1833
|
+
} catch (e) {
|
|
1834
|
+
result.setFailed(true);
|
|
1835
|
+
break;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
return result;
|
|
1840
|
+
};
|
|
1841
|
+
|
|
1842
|
+
returnvalue[TEST_RULE] = function (value) {
|
|
1843
|
+
return returnvalue[RUN_RULE](EnforceContext.wrap(value).setFailFast(true)).pass;
|
|
1844
|
+
};
|
|
1845
|
+
|
|
1846
|
+
return returnvalue;
|
|
1847
|
+
}, ruleName);
|
|
1848
|
+
}; // Returns the initial rule in the chain
|
|
1849
|
+
|
|
1850
|
+
|
|
1851
|
+
return addFn(ruleName);
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
function bindExtend(enforce, Enforce) {
|
|
1855
|
+
enforce.extend = function (customRules) {
|
|
1856
|
+
assign(runtimeRules, customRules);
|
|
1857
|
+
|
|
1858
|
+
if (!proxySupported()) {
|
|
1859
|
+
genRuleProxy(Enforce, bindLazyRule);
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
return enforce;
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
function validateResult(result, rule) {
|
|
1867
|
+
// if result is boolean, or if result.pass is boolean
|
|
1868
|
+
if (isBoolean(result) || result && isBoolean(result.pass)) {
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
throwError(rule.name + 'wrong return value');
|
|
1873
|
+
} // for easier testing and mocking
|
|
1874
|
+
|
|
1875
|
+
function getDefaultResult(value) {
|
|
1876
|
+
return {
|
|
1877
|
+
message: new Error("invalid " + typeof value + " value")
|
|
1878
|
+
};
|
|
1879
|
+
}
|
|
1880
|
+
/**
|
|
1881
|
+
* Transform the result of a rule into a standard format
|
|
1882
|
+
* @param {string} interfaceName to be used in the messages
|
|
1883
|
+
* @param {*} result of the rule
|
|
1884
|
+
* @param {Object} options
|
|
1885
|
+
* @param {function} options.rule
|
|
1886
|
+
* @param {*} options.value
|
|
1887
|
+
* @returns {Object} result
|
|
1888
|
+
* @returns {string} result.message
|
|
1889
|
+
* @returns {boolean} result.pass indicates if the test passes or not
|
|
1890
|
+
*/
|
|
1891
|
+
|
|
1892
|
+
function transformResult(result, _ref) {
|
|
1893
|
+
var rule = _ref.rule,
|
|
1894
|
+
value = _ref.value;
|
|
1895
|
+
var defaultResult = getDefaultResult(value);
|
|
1896
|
+
validateResult(result, rule); // if result is boolean
|
|
1897
|
+
|
|
1898
|
+
if (isBoolean(result)) {
|
|
1899
|
+
return defaultResult.pass = result, defaultResult;
|
|
1900
|
+
} else {
|
|
1901
|
+
defaultResult.pass = result.pass;
|
|
1902
|
+
|
|
1903
|
+
if (result.message) {
|
|
1904
|
+
defaultResult.message = optionalFunctionValue(result.message);
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
return defaultResult;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
/**
|
|
1912
|
+
* Run a single rule against enforced value (e.g. `isNumber()`)
|
|
1913
|
+
*
|
|
1914
|
+
* @param {Function} rule - rule to run
|
|
1915
|
+
* @param {Any} value
|
|
1916
|
+
* @param {Array} args list of arguments sent from consumer
|
|
1917
|
+
* @throws
|
|
1918
|
+
*/
|
|
1919
|
+
|
|
1920
|
+
function runner(rule, value, args) {
|
|
1921
|
+
if (args === void 0) {
|
|
1922
|
+
args = [];
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
var result;
|
|
1926
|
+
var isCompoundRule = isCompound(rule);
|
|
1927
|
+
var ruleValue = isCompoundRule ? EnforceContext.wrap(value).setFailFast(true) : EnforceContext.unwrap(value);
|
|
1928
|
+
result = rule.apply(null, [ruleValue].concat(args));
|
|
1929
|
+
|
|
1930
|
+
if (!isCompoundRule) {
|
|
1931
|
+
result = transformResult(result, {
|
|
1932
|
+
rule: rule,
|
|
1933
|
+
value: value
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
if (!result.pass) {
|
|
1938
|
+
throw result.message;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
/**
|
|
1943
|
+
* Adds `template` property to enforce.
|
|
1944
|
+
*
|
|
1945
|
+
* @param {Function} enforce
|
|
1946
|
+
*/
|
|
1947
|
+
|
|
1948
|
+
function bindTemplate(enforce) {
|
|
1949
|
+
enforce.template = withArgs(function (rules) {
|
|
1950
|
+
var template = function template(value) {
|
|
1951
|
+
runner(runLazyRules.bind(null, rules), value);
|
|
1952
|
+
var proxy = genRuleProxy({}, function (ruleName) {
|
|
1953
|
+
return withArgs(function (args) {
|
|
1954
|
+
runner(runtimeRules[ruleName], value, args);
|
|
1955
|
+
return proxy;
|
|
1956
|
+
});
|
|
1957
|
+
});
|
|
1958
|
+
return proxy;
|
|
1959
|
+
}; // `run` returns a deep ResultObject
|
|
1960
|
+
|
|
1961
|
+
|
|
1962
|
+
template[RUN_RULE] = function (value) {
|
|
1963
|
+
return runLazyRules(rules, value);
|
|
1964
|
+
}; // `test` returns a boolean
|
|
1965
|
+
|
|
1966
|
+
|
|
1967
|
+
template[TEST_RULE] = function (value) {
|
|
1968
|
+
return runLazyRules(rules, EnforceContext.wrap(value).setFailFast(true)).pass;
|
|
1969
|
+
};
|
|
1970
|
+
|
|
1971
|
+
return template;
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
var Enforce = function Enforce(value) {
|
|
1976
|
+
var proxy = genRuleProxy({}, function (ruleName) {
|
|
1977
|
+
return withArgs(function (args) {
|
|
1978
|
+
runner(runtimeRules[ruleName], value, args);
|
|
1979
|
+
return proxy;
|
|
1980
|
+
});
|
|
1981
|
+
});
|
|
1982
|
+
return proxy;
|
|
1983
|
+
};
|
|
1984
|
+
|
|
1985
|
+
var enforce = genRuleProxy(Enforce, bindLazyRule);
|
|
1986
|
+
bindExtend(enforce, Enforce);
|
|
1987
|
+
bindTemplate(enforce);
|
|
1988
|
+
|
|
1989
|
+
/**
|
|
1990
|
+
* @type {String} Error message to display when a hook was called outside of context.
|
|
1991
|
+
*/
|
|
1992
|
+
var ERROR_HOOK_CALLED_OUTSIDE = 'hook called outside of a running suite.';
|
|
1993
|
+
|
|
1994
|
+
/**
|
|
1995
|
+
* Adds a field or multiple fields to inclusion group.
|
|
1996
|
+
* @param {String[]|String} item Item to be added to inclusion group.
|
|
1997
|
+
*/
|
|
1998
|
+
|
|
1999
|
+
function only(item) {
|
|
2000
|
+
return addTo(EXCLUSION_GROUP_NAME_ONLY, EXCLUSION_ITEM_TYPE_TESTS, item);
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
only.group = function (item) {
|
|
2004
|
+
return addTo(EXCLUSION_GROUP_NAME_ONLY, EXCLUSION_ITEM_TYPE_GROUPS, item);
|
|
2005
|
+
};
|
|
2006
|
+
/**
|
|
2007
|
+
* Adds a field or multiple fields to exclusion group.
|
|
2008
|
+
* @param {String[]|String} item Item to be added to exclusion group.
|
|
2009
|
+
*/
|
|
2010
|
+
|
|
2011
|
+
|
|
2012
|
+
function skip(item) {
|
|
2013
|
+
return addTo(EXCLUSION_GROUP_NAME_SKIP, EXCLUSION_ITEM_TYPE_TESTS, item);
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
skip.group = function (item) {
|
|
2017
|
+
return addTo(EXCLUSION_GROUP_NAME_SKIP, EXCLUSION_ITEM_TYPE_GROUPS, item);
|
|
2018
|
+
};
|
|
2019
|
+
/**
|
|
2020
|
+
* Checks whether a certain test profile excluded by any of the exclusion groups.
|
|
2021
|
+
* @param {String} fieldName Field name to test.
|
|
2022
|
+
* @param {VestTest} Test Object reference.
|
|
2023
|
+
* @returns {Boolean}
|
|
2024
|
+
*/
|
|
2025
|
+
|
|
2026
|
+
|
|
2027
|
+
function isExcluded(testObject) {
|
|
2028
|
+
var fieldName = testObject.fieldName,
|
|
2029
|
+
groupName = testObject.groupName;
|
|
2030
|
+
|
|
2031
|
+
var _context$use = context.use(),
|
|
2032
|
+
exclusion = _context$use.exclusion;
|
|
2033
|
+
|
|
2034
|
+
var keyTests = exclusion[EXCLUSION_ITEM_TYPE_TESTS];
|
|
2035
|
+
var testValue = keyTests[fieldName]; // if test is skipped
|
|
2036
|
+
// no need to proceed
|
|
2037
|
+
|
|
2038
|
+
if (testValue === false) {
|
|
2039
|
+
return true;
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
var isTestIncluded = testValue === true; // If inside a group
|
|
2043
|
+
|
|
2044
|
+
if (groupName) {
|
|
2045
|
+
if (isGroupExcluded(groupName)) {
|
|
2046
|
+
return true; // field excluded by group
|
|
2047
|
+
// if group is `only`ed
|
|
2048
|
+
} else if (exclusion[EXCLUSION_ITEM_TYPE_GROUPS][groupName] === true) {
|
|
2049
|
+
if (isTestIncluded) {
|
|
2050
|
+
return false;
|
|
2051
|
+
} // If there is _ANY_ `only`ed test (and we already know this one isn't)
|
|
2052
|
+
|
|
2053
|
+
|
|
2054
|
+
if (hasIncludedTests(keyTests)) {
|
|
2055
|
+
return true; // Excluded implicitly
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
return keyTests[fieldName] === false;
|
|
2059
|
+
}
|
|
2060
|
+
} // if field is only'ed
|
|
2061
|
+
|
|
2062
|
+
|
|
2063
|
+
if (isTestIncluded) {
|
|
2064
|
+
return false;
|
|
2065
|
+
} // If there is _ANY_ `only`ed test (and we already know this one isn't) return true
|
|
2066
|
+
// Otherwise return false
|
|
2067
|
+
|
|
2068
|
+
|
|
2069
|
+
return hasIncludedTests(keyTests);
|
|
2070
|
+
}
|
|
2071
|
+
/**
|
|
2072
|
+
* Checks whether a given group is excluded from running.
|
|
2073
|
+
* @param {String} groupName
|
|
2074
|
+
* @return {Boolean}
|
|
2075
|
+
*/
|
|
2076
|
+
|
|
2077
|
+
function isGroupExcluded(groupName) {
|
|
2078
|
+
var _context$use2 = context.use(),
|
|
2079
|
+
exclusion = _context$use2.exclusion;
|
|
2080
|
+
|
|
2081
|
+
var keyGroups = exclusion[EXCLUSION_ITEM_TYPE_GROUPS];
|
|
2082
|
+
var groupPresent = hasOwnProperty(keyGroups, groupName); // When group is either only'ed or skipped
|
|
2083
|
+
|
|
2084
|
+
if (groupPresent) {
|
|
2085
|
+
// Return true if group is skipped and false if only'ed
|
|
2086
|
+
return keyGroups[groupName] === false;
|
|
2087
|
+
} // Group is not present
|
|
2088
|
+
|
|
2089
|
+
|
|
2090
|
+
for (var group in keyGroups) {
|
|
2091
|
+
// If any other group is only'ed
|
|
2092
|
+
if (keyGroups[group] === true) {
|
|
2093
|
+
return true;
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
return false;
|
|
2098
|
+
}
|
|
2099
|
+
/**
|
|
2100
|
+
* @type {String} Exclusion group name: only.
|
|
2101
|
+
*/
|
|
2102
|
+
|
|
2103
|
+
var EXCLUSION_GROUP_NAME_ONLY = 'only';
|
|
2104
|
+
/**
|
|
2105
|
+
* @type {String} Exclusion group name: skip.
|
|
2106
|
+
*/
|
|
2107
|
+
|
|
2108
|
+
var EXCLUSION_GROUP_NAME_SKIP = 'skip';
|
|
2109
|
+
/**
|
|
2110
|
+
* Adds fields to a specified exclusion group.
|
|
2111
|
+
* @param {String} exclusionGroup To add the fields to.
|
|
2112
|
+
* @param {String} itemType Whether the item is a group or a test.
|
|
2113
|
+
* @param {String[]|String} item A field name or a list of field names.
|
|
2114
|
+
*/
|
|
2115
|
+
|
|
2116
|
+
var addTo = function addTo(exclusionGroup, itemType, item) {
|
|
2117
|
+
var ctx = context.use();
|
|
2118
|
+
|
|
2119
|
+
if (!item) {
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
if (!ctx) {
|
|
2124
|
+
throwError(exclusionGroup + " " + ERROR_HOOK_CALLED_OUTSIDE);
|
|
2125
|
+
return;
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
asArray(item).forEach(function (itemName) {
|
|
2129
|
+
if (!isStringValue(itemName)) {
|
|
2130
|
+
return null;
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
ctx.exclusion[itemType][itemName] = exclusionGroup === EXCLUSION_GROUP_NAME_ONLY;
|
|
2134
|
+
});
|
|
2135
|
+
};
|
|
2136
|
+
/**
|
|
2137
|
+
* Checks if context has included tests
|
|
2138
|
+
* @param {Object} keyTests Object containing included and excluded tests
|
|
2139
|
+
* @returns {boolean}
|
|
2140
|
+
*/
|
|
2141
|
+
|
|
2142
|
+
|
|
2143
|
+
var hasIncludedTests = function hasIncludedTests(keyTests) {
|
|
2144
|
+
for (var test in keyTests) {
|
|
2145
|
+
if (keyTests[test] === true) {
|
|
2146
|
+
return true; // excluded implicitly
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
return false;
|
|
2151
|
+
};
|
|
2152
|
+
|
|
2153
|
+
// an if statement. The reason for it is to support version 4 api in version 3
|
|
2154
|
+
// so that someone reading the latest docs can still run the code.
|
|
2155
|
+
|
|
2156
|
+
function skipWhen(conditional, callback) {
|
|
2157
|
+
if (isFalsy(optionalFunctionValue(conditional))) {
|
|
2158
|
+
if (isFunction(callback)) {
|
|
2159
|
+
callback();
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
/**
|
|
2165
|
+
* @type {String} Error message to display when `warn` gets called outside of a test.
|
|
2166
|
+
*/
|
|
2167
|
+
|
|
2168
|
+
var ERROR_OUTSIDE_OF_TEST = 'warn called outside of a test.';
|
|
2169
|
+
/**
|
|
2170
|
+
* Sets a running test to warn only mode.
|
|
2171
|
+
*/
|
|
2172
|
+
|
|
2173
|
+
var warn = function warn() {
|
|
2174
|
+
var ctx = context.use();
|
|
2175
|
+
|
|
2176
|
+
if (!ctx) {
|
|
2177
|
+
throwError('warn ' + ERROR_HOOK_CALLED_OUTSIDE);
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
if (!ctx.currentTest) {
|
|
2182
|
+
throwError(ERROR_OUTSIDE_OF_TEST);
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
ctx.currentTest.warn();
|
|
2187
|
+
};
|
|
2188
|
+
|
|
2189
|
+
var throwGroupError = function throwGroupError() {
|
|
2190
|
+
return throwError("group initialization error. Incompatible argument passed to group.");
|
|
2191
|
+
};
|
|
2192
|
+
/**
|
|
2193
|
+
* Runs a group callback.
|
|
2194
|
+
* @param {string} groupName
|
|
2195
|
+
* @param {Function} tests
|
|
2196
|
+
*/
|
|
2197
|
+
|
|
2198
|
+
|
|
2199
|
+
var group = function group(groupName, tests) {
|
|
2200
|
+
if (!isStringValue(groupName)) {
|
|
2201
|
+
throwGroupError();
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
if (!isFunction(tests)) {
|
|
2205
|
+
throwGroupError();
|
|
2206
|
+
} // Running with the context applied
|
|
2207
|
+
|
|
2208
|
+
|
|
2209
|
+
context.bind({
|
|
2210
|
+
groupName: groupName
|
|
2211
|
+
}, tests)();
|
|
2212
|
+
};
|
|
2213
|
+
|
|
2214
|
+
function optional(optionals) {
|
|
2215
|
+
var _useOptionalFields = useOptionalFields(),
|
|
2216
|
+
setOptionalFields = _useOptionalFields[1];
|
|
2217
|
+
|
|
2218
|
+
setOptionalFields(function (state) {
|
|
2219
|
+
asArray(optionals).forEach(function (optionalField) {
|
|
2220
|
+
state[optionalField] = true;
|
|
2221
|
+
});
|
|
2222
|
+
return state;
|
|
2223
|
+
});
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
function isSameProfileTest(testObject1, testObject2) {
|
|
2227
|
+
return testObject1.fieldName === testObject2.fieldName && testObject1.groupName === testObject2.groupName;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
/**
|
|
2231
|
+
* Removes first found element from array
|
|
2232
|
+
* WARNING: Mutates array
|
|
2233
|
+
*
|
|
2234
|
+
* @param {any[]} array
|
|
2235
|
+
* @param {any} element
|
|
2236
|
+
*/
|
|
2237
|
+
var removeElementFromArray = function removeElementFromArray(array, element) {
|
|
2238
|
+
var index = array.indexOf(element);
|
|
2239
|
+
|
|
2240
|
+
if (index !== -1) {
|
|
2241
|
+
array.splice(index, 1);
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
return array;
|
|
2245
|
+
};
|
|
2246
|
+
|
|
2247
|
+
/**
|
|
2248
|
+
* Sets a test as pending in the state.
|
|
2249
|
+
* @param {VestTest} testObject
|
|
2250
|
+
*/
|
|
2251
|
+
|
|
2252
|
+
var setPending = function setPending(testObject) {
|
|
2253
|
+
var _usePending = usePending(),
|
|
2254
|
+
pendingState = _usePending[0],
|
|
2255
|
+
setPending = _usePending[1];
|
|
2256
|
+
|
|
2257
|
+
var lagging = asArray(pendingState.lagging).reduce(function (lagging, laggingTestObject) {
|
|
2258
|
+
/**
|
|
2259
|
+
* If the test is of the same profile
|
|
2260
|
+
* (same name + same group) we cancel
|
|
2261
|
+
* it. Otherwise, it is lagging.
|
|
2262
|
+
*/
|
|
2263
|
+
if (isSameProfileTest(testObject, laggingTestObject) && // This last case handles memoized tests
|
|
2264
|
+
// because that retain their od across runs
|
|
2265
|
+
laggingTestObject.id !== testObject.id) {
|
|
2266
|
+
laggingTestObject.cancel();
|
|
2267
|
+
} else {
|
|
2268
|
+
lagging.push(laggingTestObject);
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
return lagging;
|
|
2272
|
+
}, []);
|
|
2273
|
+
setPending(function (state) {
|
|
2274
|
+
return {
|
|
2275
|
+
lagging: lagging,
|
|
2276
|
+
pending: state.pending.concat(testObject)
|
|
2277
|
+
};
|
|
2278
|
+
});
|
|
2279
|
+
};
|
|
2280
|
+
/**
|
|
2281
|
+
* Removes a tests from the pending and lagging arrays.
|
|
2282
|
+
* @param {VestTest} testObject
|
|
2283
|
+
*/
|
|
2284
|
+
|
|
2285
|
+
var removePending = function removePending(testObject) {
|
|
2286
|
+
var _usePending2 = usePending(),
|
|
2287
|
+
setPending = _usePending2[1];
|
|
2288
|
+
|
|
2289
|
+
setPending(function (state) {
|
|
2290
|
+
return {
|
|
2291
|
+
pending: removeElementFromArray(state.pending, testObject),
|
|
2292
|
+
lagging: removeElementFromArray(state.lagging, testObject)
|
|
2293
|
+
};
|
|
2294
|
+
});
|
|
2295
|
+
};
|
|
2296
|
+
|
|
2297
|
+
/**
|
|
2298
|
+
* Removes test object from suite state
|
|
2299
|
+
* @param {VestTest} testObject
|
|
2300
|
+
*/
|
|
2301
|
+
|
|
2302
|
+
var removeTestFromState = (function (testObject) {
|
|
2303
|
+
var _useTestObjects = useTestObjects(),
|
|
2304
|
+
setTestObjects = _useTestObjects[1];
|
|
2305
|
+
|
|
2306
|
+
setTestObjects(function (testObjects) {
|
|
2307
|
+
return (// using asArray to clear the cache.
|
|
2308
|
+
asArray(removeElementFromArray(testObjects, testObject))
|
|
2309
|
+
);
|
|
2310
|
+
});
|
|
2311
|
+
});
|
|
2312
|
+
|
|
2313
|
+
/**
|
|
2314
|
+
* Describes a test call inside a Vest suite.
|
|
2315
|
+
* @param {String} fieldName Name of the field being tested.
|
|
2316
|
+
* @param {String} statement The message returned when failing.
|
|
2317
|
+
* @param {Promise|Function} testFn The actual test callback or promise.
|
|
2318
|
+
* @param {string} [group] The group in which the test runs.
|
|
2319
|
+
*/
|
|
2320
|
+
|
|
2321
|
+
function VestTest(_ref) {
|
|
2322
|
+
var fieldName = _ref.fieldName,
|
|
2323
|
+
statement = _ref.statement,
|
|
2324
|
+
testFn = _ref.testFn,
|
|
2325
|
+
group = _ref.group;
|
|
2326
|
+
var testObject = {
|
|
2327
|
+
cancel: cancel,
|
|
2328
|
+
fail: fail,
|
|
2329
|
+
failed: false,
|
|
2330
|
+
fieldName: fieldName,
|
|
2331
|
+
id: id(),
|
|
2332
|
+
isWarning: false,
|
|
2333
|
+
statement: statement,
|
|
2334
|
+
testFn: testFn,
|
|
2335
|
+
valueOf: valueOf,
|
|
2336
|
+
warn: warn
|
|
2337
|
+
};
|
|
2338
|
+
|
|
2339
|
+
if (group) {
|
|
2340
|
+
testObject.groupName = group;
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
return testObject;
|
|
2344
|
+
/**
|
|
2345
|
+
* @returns {Boolean} Current validity status of a test.
|
|
2346
|
+
*/
|
|
2347
|
+
|
|
2348
|
+
function valueOf() {
|
|
2349
|
+
return testObject.failed !== true;
|
|
2350
|
+
}
|
|
2351
|
+
/**
|
|
2352
|
+
* Sets a test to failed.
|
|
2353
|
+
*/
|
|
2354
|
+
|
|
2355
|
+
|
|
2356
|
+
function fail() {
|
|
2357
|
+
testObject.failed = true;
|
|
2358
|
+
}
|
|
2359
|
+
/**
|
|
2360
|
+
* Sets a current test's `isWarning` to true.
|
|
2361
|
+
*/
|
|
2362
|
+
|
|
2363
|
+
|
|
2364
|
+
function warn() {
|
|
2365
|
+
testObject.isWarning = true;
|
|
2366
|
+
}
|
|
2367
|
+
/**
|
|
2368
|
+
* Marks a test as canceled, removes it from the state.
|
|
2369
|
+
* This function needs to be called within a stateRef context.
|
|
2370
|
+
*/
|
|
2371
|
+
|
|
2372
|
+
|
|
2373
|
+
function cancel() {
|
|
2374
|
+
testObject.canceled = true;
|
|
2375
|
+
removePending(this);
|
|
2376
|
+
removeTestFromState(this);
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
/**
|
|
2381
|
+
*
|
|
2382
|
+
* @param {any[]} array
|
|
2383
|
+
* @param {() => boolean} predicate
|
|
2384
|
+
* @returns {[any[], any[]]}
|
|
2385
|
+
*/
|
|
2386
|
+
function partition(array, predicate) {
|
|
2387
|
+
return array.reduce(function (partitions, value, index) {
|
|
2388
|
+
partitions[predicate(value, index, array) ? 0 : 1].push(value);
|
|
2389
|
+
return partitions;
|
|
2390
|
+
}, [[], []]);
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
function mergeCarryOverTests(testObject) {
|
|
2394
|
+
var _useCarryOverTests = useCarryOverTests(),
|
|
2395
|
+
carryOverTests = _useCarryOverTests[0],
|
|
2396
|
+
setCarryOverTests = _useCarryOverTests[1];
|
|
2397
|
+
|
|
2398
|
+
var _useTestObjects = useTestObjects(),
|
|
2399
|
+
setTestObjects = _useTestObjects[1];
|
|
2400
|
+
|
|
2401
|
+
var _partition = partition(carryOverTests, function (carryOverTest) {
|
|
2402
|
+
return isSameProfileTest(carryOverTest, testObject);
|
|
2403
|
+
}),
|
|
2404
|
+
moveToTestObjects = _partition[0],
|
|
2405
|
+
keepInCarryOvers = _partition[1];
|
|
2406
|
+
|
|
2407
|
+
setCarryOverTests(function () {
|
|
2408
|
+
return keepInCarryOvers;
|
|
2409
|
+
});
|
|
2410
|
+
setTestObjects(function (testObjects) {
|
|
2411
|
+
return testObjects.concat(moveToTestObjects);
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
/**
|
|
2416
|
+
* Stores test object inside suite state.
|
|
2417
|
+
* @param {VestTest} testObject
|
|
2418
|
+
*/
|
|
2419
|
+
|
|
2420
|
+
var addTestToState = (function (testObject) {
|
|
2421
|
+
var _useTestObjects = useTestObjects(),
|
|
2422
|
+
setTestObjects = _useTestObjects[1];
|
|
2423
|
+
|
|
2424
|
+
setTestObjects(function (testObjects) {
|
|
2425
|
+
return testObjects.concat(testObject);
|
|
2426
|
+
});
|
|
2427
|
+
});
|
|
2428
|
+
|
|
2429
|
+
function isPromise(value) {
|
|
2430
|
+
return value && isFunction(value.then);
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
function callEach(arr) {
|
|
2434
|
+
return arr.forEach(function (fn) {
|
|
2435
|
+
return fn();
|
|
2436
|
+
});
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
/**
|
|
2440
|
+
* Runs async test.
|
|
2441
|
+
* @param {VestTest} testObject A VestTest instance.
|
|
2442
|
+
*/
|
|
2443
|
+
|
|
2444
|
+
var runAsyncTest = function runAsyncTest(testObject) {
|
|
2445
|
+
var asyncTest = testObject.asyncTest,
|
|
2446
|
+
statement = testObject.statement;
|
|
2447
|
+
|
|
2448
|
+
var _context$use = context.use(),
|
|
2449
|
+
stateRef = _context$use.stateRef;
|
|
2450
|
+
|
|
2451
|
+
var done = context.bind({
|
|
2452
|
+
stateRef: stateRef
|
|
2453
|
+
}, function () {
|
|
2454
|
+
removePending(testObject); // This is for cases in which the suite state was already reset
|
|
2455
|
+
|
|
2456
|
+
if (testObject.canceled) {
|
|
2457
|
+
return;
|
|
2458
|
+
} // Perform required done callback calls and cleanups after the test is finished
|
|
2459
|
+
|
|
2460
|
+
|
|
2461
|
+
runDoneCallbacks(testObject.fieldName);
|
|
2462
|
+
});
|
|
2463
|
+
var fail = context.bind({
|
|
2464
|
+
stateRef: stateRef
|
|
2465
|
+
}, function (rejectionMessage) {
|
|
2466
|
+
testObject.statement = isStringValue(rejectionMessage) ? rejectionMessage : statement;
|
|
2467
|
+
testObject.fail(); // Spreading the array to invalidate the cache
|
|
2468
|
+
|
|
2469
|
+
var _useTestObjects = useTestObjects(),
|
|
2470
|
+
setTestObjects = _useTestObjects[1];
|
|
2471
|
+
|
|
2472
|
+
setTestObjects(function (testObjects) {
|
|
2473
|
+
return testObjects.slice();
|
|
2474
|
+
});
|
|
2475
|
+
done();
|
|
2476
|
+
});
|
|
2477
|
+
|
|
2478
|
+
try {
|
|
2479
|
+
asyncTest.then(done, fail);
|
|
2480
|
+
} catch (e) {
|
|
2481
|
+
fail();
|
|
2482
|
+
}
|
|
2483
|
+
};
|
|
2484
|
+
/**
|
|
2485
|
+
* Runs done callback when async tests are finished running.
|
|
2486
|
+
* @param {string} [fieldName] Field name with associated callbacks.
|
|
2487
|
+
*/
|
|
2488
|
+
|
|
2489
|
+
|
|
2490
|
+
var runDoneCallbacks = function runDoneCallbacks(fieldName) {
|
|
2491
|
+
var _useTestCallbacks = useTestCallbacks(),
|
|
2492
|
+
_useTestCallbacks$ = _useTestCallbacks[0],
|
|
2493
|
+
fieldCallbacks = _useTestCallbacks$.fieldCallbacks,
|
|
2494
|
+
doneCallbacks = _useTestCallbacks$.doneCallbacks;
|
|
2495
|
+
|
|
2496
|
+
if (fieldName) {
|
|
2497
|
+
if (!hasRemainingTests(fieldName) && Array.isArray(fieldCallbacks[fieldName])) {
|
|
2498
|
+
callEach(fieldCallbacks[fieldName]);
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
if (!hasRemainingTests()) {
|
|
2503
|
+
callEach(doneCallbacks);
|
|
2504
|
+
}
|
|
2505
|
+
};
|
|
2506
|
+
|
|
2507
|
+
/**
|
|
2508
|
+
* Runs sync tests - or extracts promise.
|
|
2509
|
+
* @param {VestTest} testObject VestTest instance.
|
|
2510
|
+
* @returns {*} Result from test callback.
|
|
2511
|
+
*/
|
|
2512
|
+
|
|
2513
|
+
function runSyncTest(testObject) {
|
|
2514
|
+
return context.run({
|
|
2515
|
+
currentTest: testObject
|
|
2516
|
+
}, function () {
|
|
2517
|
+
var result;
|
|
2518
|
+
|
|
2519
|
+
try {
|
|
2520
|
+
result = testObject.testFn();
|
|
2521
|
+
} catch (e) {
|
|
2522
|
+
if (isUndefined(testObject.statement) && isStringValue(e)) {
|
|
2523
|
+
testObject.statement = e;
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
result = false;
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
if (result === false) {
|
|
2530
|
+
testObject.fail();
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
return result;
|
|
2534
|
+
});
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
/**
|
|
2538
|
+
* Registers test, if async - adds to pending array
|
|
2539
|
+
* @param {VestTest} testObject A VestTest Instance.
|
|
2540
|
+
*/
|
|
2541
|
+
|
|
2542
|
+
function registerTest(testObject) {
|
|
2543
|
+
addTestToState(testObject); // Run test callback.
|
|
2544
|
+
// If a promise is returned, set as async and
|
|
2545
|
+
// Move to pending list.
|
|
2546
|
+
|
|
2547
|
+
var result = runSyncTest(testObject);
|
|
2548
|
+
|
|
2549
|
+
try {
|
|
2550
|
+
// try catch for safe property access
|
|
2551
|
+
// in case object is an enforce chain
|
|
2552
|
+
if (isPromise(result)) {
|
|
2553
|
+
testObject.asyncTest = result;
|
|
2554
|
+
setPending(testObject);
|
|
2555
|
+
runAsyncTest(testObject);
|
|
2556
|
+
}
|
|
2557
|
+
} catch (_unused) {
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
/* eslint-disable jest/no-export */
|
|
2562
|
+
|
|
2563
|
+
function bindTestEach(test) {
|
|
2564
|
+
/**
|
|
2565
|
+
* Run multiple tests using a parameter table
|
|
2566
|
+
* @param {any[]} table Array of arrays with params for each run (will accept 1d-array and treat every item as size one array)
|
|
2567
|
+
* @param {String} fieldName Name of the field to test.
|
|
2568
|
+
* @param {String|function} [statement] The message returned in case of a failure. Follows printf syntax.
|
|
2569
|
+
* @param {function} testFn The actual test callback.
|
|
2570
|
+
* @return {VestTest[]} An array of VestTest instances.
|
|
2571
|
+
*/
|
|
2572
|
+
function each(table) {
|
|
2573
|
+
if (!Array.isArray(table)) {
|
|
2574
|
+
throwError('test.each: Expected table to be an array.');
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
return withArgs(function (fieldName, args) {
|
|
2578
|
+
var _args$reverse = args.reverse(),
|
|
2579
|
+
testFn = _args$reverse[0],
|
|
2580
|
+
statement = _args$reverse[1];
|
|
2581
|
+
|
|
2582
|
+
return table.map(function (item) {
|
|
2583
|
+
item = asArray(item);
|
|
2584
|
+
return test(optionalFunctionValue(fieldName, item), optionalFunctionValue(statement, item), function () {
|
|
2585
|
+
return testFn.apply(null, item);
|
|
2586
|
+
});
|
|
2587
|
+
});
|
|
2588
|
+
});
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
test.each = each;
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
/* eslint-disable jest/no-export */
|
|
2595
|
+
|
|
2596
|
+
function bindTestMemo(test) {
|
|
2597
|
+
var cache = createCache(100); // arbitrary cache size
|
|
2598
|
+
|
|
2599
|
+
/**
|
|
2600
|
+
* Caches, or returns an already cached test call
|
|
2601
|
+
* @param {String} fieldName Name of the field to test.
|
|
2602
|
+
* @param {String} [statement] The message returned in case of a failure.
|
|
2603
|
+
* @param {function} testFn The actual test callback.
|
|
2604
|
+
* @param {any[]} deps Dependency array.
|
|
2605
|
+
* @return {VestTest} A VestTest instance.
|
|
2606
|
+
*/
|
|
2607
|
+
|
|
2608
|
+
function memo(fieldName, args) {
|
|
2609
|
+
var _useSuiteId = useSuiteId(),
|
|
2610
|
+
suiteId = _useSuiteId[0];
|
|
2611
|
+
|
|
2612
|
+
var _args$reverse = args.reverse(),
|
|
2613
|
+
deps = _args$reverse[0],
|
|
2614
|
+
testFn = _args$reverse[1],
|
|
2615
|
+
msg = _args$reverse[2]; // Implicit dependency for more specificity
|
|
2616
|
+
|
|
2617
|
+
|
|
2618
|
+
var dependencies = [suiteId.id, fieldName].concat(deps);
|
|
2619
|
+
var cached = cache.get(dependencies);
|
|
2620
|
+
|
|
2621
|
+
if (isNull(cached)) {
|
|
2622
|
+
// Cache miss. Start fresh
|
|
2623
|
+
return cache(dependencies, test.bind(null, fieldName, msg, testFn));
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
var testObject = cached[1];
|
|
2627
|
+
|
|
2628
|
+
if (isExcluded(testObject)) {
|
|
2629
|
+
return testObject;
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
addTestToState(testObject);
|
|
2633
|
+
|
|
2634
|
+
if (testObject && isPromise(testObject.asyncTest)) {
|
|
2635
|
+
setPending(testObject);
|
|
2636
|
+
runAsyncTest(testObject);
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
return testObject;
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
test.memo = withArgs(memo);
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
/**
|
|
2646
|
+
* Test function used by consumer to provide their own validations.
|
|
2647
|
+
* @param {String} fieldName Name of the field to test.
|
|
2648
|
+
* @param {String} [statement] The message returned in case of a failure.
|
|
2649
|
+
* @param {function} testFn The actual test callback.
|
|
2650
|
+
* @return {VestTest} A VestTest instance.
|
|
2651
|
+
*
|
|
2652
|
+
* **IMPORTANT**
|
|
2653
|
+
* Changes to this function need to reflect in test.memo as well
|
|
2654
|
+
*/
|
|
2655
|
+
|
|
2656
|
+
var test = withArgs(function (fieldName, args) {
|
|
2657
|
+
var _args$reverse = args.reverse(),
|
|
2658
|
+
testFn = _args$reverse[0],
|
|
2659
|
+
statement = _args$reverse[1];
|
|
2660
|
+
|
|
2661
|
+
var _useSkippedTests = useSkippedTests(),
|
|
2662
|
+
setSkippedTests = _useSkippedTests[1];
|
|
2663
|
+
|
|
2664
|
+
var _context$use = context.use(),
|
|
2665
|
+
groupName = _context$use.groupName;
|
|
2666
|
+
|
|
2667
|
+
var testObject = VestTest({
|
|
2668
|
+
fieldName: fieldName,
|
|
2669
|
+
group: groupName,
|
|
2670
|
+
statement: statement,
|
|
2671
|
+
testFn: testFn
|
|
2672
|
+
});
|
|
2673
|
+
|
|
2674
|
+
if (isExcluded(testObject)) {
|
|
2675
|
+
setSkippedTests(function (skippedTests) {
|
|
2676
|
+
return skippedTests.concat(testObject);
|
|
2677
|
+
});
|
|
2678
|
+
mergeCarryOverTests(testObject);
|
|
2679
|
+
return testObject;
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
if (!isFunction(testFn)) {
|
|
2683
|
+
return testObject;
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
registerTest(testObject);
|
|
2687
|
+
return testObject;
|
|
2688
|
+
});
|
|
2689
|
+
bindTestEach(test);
|
|
2690
|
+
bindTestMemo(test);
|
|
2691
|
+
|
|
2692
|
+
var VERSION = "3.2.8";
|
|
2693
|
+
var vest = {
|
|
2694
|
+
VERSION: VERSION,
|
|
2695
|
+
create: createSuite,
|
|
2696
|
+
enforce: enforce,
|
|
2697
|
+
group: group,
|
|
2698
|
+
only: only,
|
|
2699
|
+
optional: optional,
|
|
2700
|
+
skip: skip,
|
|
2701
|
+
skipWhen: skipWhen,
|
|
2702
|
+
test: test,
|
|
2703
|
+
warn: warn
|
|
2704
|
+
};
|
|
2705
|
+
|
|
2706
|
+
return vest;
|
|
2707
|
+
|
|
2708
|
+
})));
|