vest 4.0.0-dev-cc5cf5 → 4.0.0-dev-31f012

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +52 -44
  2. package/README.md +7 -1
  3. package/dist/cjs/classnames.development.js +3 -3
  4. package/dist/cjs/classnames.production.js +1 -1
  5. package/dist/cjs/promisify.development.js +1 -1
  6. package/dist/cjs/promisify.production.js +1 -1
  7. package/dist/cjs/vest.development.js +394 -180
  8. package/dist/cjs/vest.production.js +1 -1
  9. package/dist/es/classnames.development.js +3 -3
  10. package/dist/es/classnames.production.js +1 -1
  11. package/dist/es/promisify.development.js +1 -1
  12. package/dist/es/promisify.production.js +1 -1
  13. package/dist/es/vest.development.js +394 -180
  14. package/dist/es/vest.production.js +1 -1
  15. package/dist/umd/classnames.development.js +3 -3
  16. package/dist/umd/classnames.production.js +1 -1
  17. package/dist/umd/promisify.development.js +1 -1
  18. package/dist/umd/promisify.production.js +1 -1
  19. package/dist/umd/vest.development.js +394 -180
  20. package/dist/umd/vest.production.js +1 -1
  21. package/package.json +1 -1
  22. package/testUtils/testObjects.ts +11 -2
  23. package/types/classnames.d.ts +2 -2
  24. package/types/vest.d.ts +10 -3
  25. package/docs/.nojekyll +0 -0
  26. package/docs/README.md +0 -107
  27. package/docs/_assets/favicon.ico +0 -0
  28. package/docs/_assets/vest-logo.png +0 -0
  29. package/docs/_sidebar.md +0 -14
  30. package/docs/cross_field_validations.md +0 -34
  31. package/docs/enforce.md +0 -11
  32. package/docs/exclusion.md +0 -129
  33. package/docs/getting_started.md +0 -72
  34. package/docs/group.md +0 -142
  35. package/docs/index.html +0 -41
  36. package/docs/migration.md +0 -202
  37. package/docs/n4s/rules.md +0 -1282
  38. package/docs/node.md +0 -36
  39. package/docs/optional.md +0 -103
  40. package/docs/result.md +0 -249
  41. package/docs/state.md +0 -102
  42. package/docs/test.md +0 -172
  43. package/docs/utilities.md +0 -109
  44. package/docs/warn.md +0 -82
@@ -230,8 +230,16 @@
230
230
  return isNumeric(value) && isNumeric(gte) && Number(value) >= Number(gte);
231
231
  }
232
232
 
233
+ // The module is named "isArrayValue" since it
234
+ // is conflicting with a nested npm dependency.
235
+ // We may need to revisit this in the future.
236
+ function isArray(value) {
237
+ return Boolean(Array.isArray(value));
238
+ }
239
+ var isNotArray = bindNot(isArray);
240
+
233
241
  function inside(value, arg1) {
234
- if (Array.isArray(arg1)) {
242
+ if (isArray(arg1)) {
235
243
  return arg1.indexOf(value) !== -1;
236
244
  }
237
245
  // both value and arg1 are strings
@@ -242,14 +250,6 @@
242
250
  }
243
251
  var notInside = bindNot(inside);
244
252
 
245
- // The module is named "isArrayValue" since it
246
- // is conflicting with a nested npm dependency.
247
- // We may need to revisit this in the future.
248
- function isArray(value) {
249
- return Boolean(Array.isArray(value));
250
- }
251
- var isNotArray = bindNot(isArray);
252
-
253
253
  function lessThanOrEquals(value, lte) {
254
254
  return isNumeric(value) && isNumeric(lte) && Number(value) <= Number(lte);
255
255
  }
@@ -602,7 +602,7 @@
602
602
 
603
603
  function isProxySupported() {
604
604
  try {
605
- return typeof Proxy === 'function';
605
+ return isFunction(Proxy);
606
606
  }
607
607
  catch (_a) {
608
608
  return false;
@@ -623,7 +623,7 @@
623
623
  return ruleReturn(result);
624
624
  }
625
625
  else {
626
- return ruleReturn(result.pass, optionalFunctionValue.apply(void 0, __spreadArray([result.message, ruleName, value], args, false)));
626
+ return ruleReturn(result.pass, optionalFunctionValue.apply(void 0, __spreadArray([result.message, ruleName, value], args)));
627
627
  }
628
628
  }
629
629
  function validateResult(result) {
@@ -657,9 +657,9 @@
657
657
  for (var _i = 0; _i < arguments.length; _i++) {
658
658
  args[_i] = arguments[_i];
659
659
  }
660
- var transformedResult = transformResult.apply(void 0, __spreadArray([ctx$1.run({ value: value }, function () { return rule.apply(void 0, __spreadArray([value], args, false)); }),
660
+ var transformedResult = transformResult.apply(void 0, __spreadArray([ctx$1.run({ value: value }, function () { return rule.apply(void 0, __spreadArray([value], args)); }),
661
661
  ruleName,
662
- value], args, false));
662
+ value], args));
663
663
  if (!transformedResult.pass) {
664
664
  if (isEmpty(transformedResult.message)) {
665
665
  throwError("enforce/" + ruleName + " failed with " + JSON.stringify(value));
@@ -689,7 +689,7 @@
689
689
  }
690
690
  var rule = getRule(ruleName);
691
691
  registeredRules.push(function (value) {
692
- return transformResult.apply(void 0, __spreadArray([rule.apply(void 0, __spreadArray([value], args, false)), ruleName, value], args, false));
692
+ return transformResult.apply(void 0, __spreadArray([rule.apply(void 0, __spreadArray([value], args)), ruleName, value], args));
693
693
  });
694
694
  var proxy = {
695
695
  run: function (value) {
@@ -796,10 +796,6 @@
796
796
  }
797
797
  var enforce = genEnforce();
798
798
 
799
- function asArray(possibleArg) {
800
- return [].concat(possibleArg);
801
- }
802
-
803
799
  /**
804
800
  * @returns a unique numeric id.
805
801
  */
@@ -836,15 +832,16 @@
836
832
  return initKey(key, initialState);
837
833
  }
838
834
  function reset() {
835
+ var prev = current();
839
836
  state.references = [];
840
837
  registrations.forEach(function (_a, index) {
841
838
  var initialValue = _a[0];
842
- return initKey(index, initialValue);
839
+ return initKey(index, initialValue, prev[index]);
843
840
  });
844
841
  }
845
- function initKey(key, initialState) {
842
+ function initKey(key, initialState, prevState) {
846
843
  current().push();
847
- set(key, optionalFunctionValue(initialState));
844
+ set(key, optionalFunctionValue(initialState, prevState));
848
845
  return function useStateKey() {
849
846
  return [
850
847
  current()[key],
@@ -870,18 +867,74 @@
870
867
  }
871
868
  }
872
869
 
870
+ var IsolateTypes;
871
+ (function (IsolateTypes) {
872
+ IsolateTypes[IsolateTypes["DEFAULT"] = 0] = "DEFAULT";
873
+ IsolateTypes[IsolateTypes["SUITE"] = 1] = "SUITE";
874
+ IsolateTypes[IsolateTypes["EACH"] = 2] = "EACH";
875
+ IsolateTypes[IsolateTypes["SKIP_WHEN"] = 3] = "SKIP_WHEN";
876
+ IsolateTypes[IsolateTypes["GROUP"] = 4] = "GROUP";
877
+ })(IsolateTypes || (IsolateTypes = {}));
878
+
873
879
  function createStateRef(state, _a) {
874
880
  var suiteId = _a.suiteId;
875
881
  return {
876
882
  optionalFields: state.registerStateKey(function () { return ({}); }),
877
- prevTestObjects: state.registerStateKey(function () { return []; }),
878
- suiteId: state.registerStateKey(function () { return suiteId; }),
883
+ suiteId: state.registerStateKey(suiteId),
879
884
  testCallbacks: state.registerStateKey(function () { return ({
880
885
  fieldCallbacks: {},
881
886
  doneCallbacks: []
882
887
  }); }),
883
- testObjects: state.registerStateKey(function () { return []; }),
884
- testObjectsCursor: state.registerStateKey(function () { return 0; })
888
+ testObjects: state.registerStateKey(function (prev) {
889
+ return {
890
+ prev: (prev ? prev.current : []),
891
+ current: []
892
+ };
893
+ })
894
+ };
895
+ }
896
+
897
+ function asArray(possibleArg) {
898
+ return [].concat(possibleArg);
899
+ }
900
+
901
+ function last(values) {
902
+ var valuesArray = asArray(values);
903
+ var _a = valuesArray, l = _a.length, _b = l - 1, lastValue = _a[_b];
904
+ return lastValue;
905
+ }
906
+
907
+ function createCursor() {
908
+ var storage = {
909
+ cursor: []
910
+ };
911
+ function addLevel() {
912
+ storage.cursor.push(0);
913
+ }
914
+ function removeLevel() {
915
+ storage.cursor.pop();
916
+ }
917
+ function cursorAt() {
918
+ return last(storage.cursor);
919
+ }
920
+ function getCursor() {
921
+ return asArray(storage.cursor);
922
+ }
923
+ function next() {
924
+ storage.cursor[storage.cursor.length - 1]++;
925
+ return last(storage.cursor);
926
+ }
927
+ function reset() {
928
+ storage.cursor = [0];
929
+ }
930
+ reset();
931
+ return {
932
+ addLevel: addLevel,
933
+ cursorAt: cursorAt,
934
+ getCursor: getCursor,
935
+ next: next,
936
+ removeLevel: removeLevel,
937
+ reset: reset
885
938
  };
886
939
  }
887
940
 
@@ -889,6 +942,8 @@
889
942
  return parentContext
890
943
  ? null
891
944
  : assign({}, {
945
+ isolate: { type: IsolateTypes.DEFAULT },
946
+ testCursor: createCursor(),
892
947
  exclusion: {
893
948
  tests: {},
894
949
  groups: {}
@@ -896,11 +951,49 @@
896
951
  }, ctxRef);
897
952
  });
898
953
 
899
- function nonMatchingFieldName(testObject, fieldName) {
900
- return !!fieldName && !matchingFieldName(testObject, fieldName);
954
+ // This is sort of a map/filter in one function.
955
+ // Normally, behaves like a nested-array map
956
+ // Returning `null` will drop the element from the array
957
+ function transform(array, cb) {
958
+ var res = [];
959
+ for (var _i = 0, array_1 = array; _i < array_1.length; _i++) {
960
+ var v = array_1[_i];
961
+ if (isArray(v)) {
962
+ res.push(transform(v, cb));
963
+ }
964
+ else {
965
+ var output = cb(v);
966
+ if (isNotNull(output)) {
967
+ res.push(output);
968
+ }
969
+ }
970
+ }
971
+ return res;
901
972
  }
902
- function matchingFieldName(testObject, fieldName) {
903
- return !!(fieldName && testObject.fieldName === fieldName);
973
+ function valueAtPath(array, path) {
974
+ return getCurrent(array, path)[last(path)];
975
+ }
976
+ function setValueAtPath(array, path, value) {
977
+ var current = getCurrent(array, path);
978
+ current[last(path)] = value;
979
+ return array;
980
+ }
981
+ function flatten(values) {
982
+ return asArray(values).reduce(function (acc, value) {
983
+ if (isArray(value)) {
984
+ return acc.concat(flatten(value));
985
+ }
986
+ return asArray(acc).concat(value);
987
+ }, []);
988
+ }
989
+ function getCurrent(array, path) {
990
+ var current = array;
991
+ for (var _i = 0, _a = path.slice(0, -1); _i < _a.length; _i++) {
992
+ var p = _a[_i];
993
+ current[p] = defaultTo(current[p], []);
994
+ current = current[p];
995
+ }
996
+ return current;
904
997
  }
905
998
 
906
999
  /**
@@ -959,89 +1052,101 @@
959
1052
  function useTestObjects() {
960
1053
  return useStateRef().testObjects();
961
1054
  }
962
- function usePrevTestObjects() {
963
- return useStateRef().prevTestObjects();
964
- }
965
- function useCursorAt() {
966
- return useStateRef().testObjectsCursor();
967
- }
968
- function useSetNextCursorAt() {
969
- var _a = useCursorAt(), setCursorAt = _a[1];
970
- setCursorAt(function (cursorAt) { return cursorAt + 1; });
971
- }
972
1055
  // STATE ACTIONS
973
1056
  function useRefreshTestObjects() {
974
1057
  var _a = useTestObjects(), setTestObjects = _a[1];
975
- setTestObjects(function (testObjects) { return testObjects.slice(0); });
1058
+ setTestObjects(function (_a) {
1059
+ var current = _a.current, prev = _a.prev;
1060
+ return ({
1061
+ prev: prev,
1062
+ current: asArray(current)
1063
+ });
1064
+ });
976
1065
  }
977
- function useSetTestAtCursor(testObject) {
978
- var cursorAt = useCursorAt()[0];
979
- var _a = useTestObjects(), testObjects = _a[0], setTestObjects = _a[1];
980
- if (testObject === testObjects[cursorAt]) {
981
- return;
982
- }
983
- setTestObjects(function (testObjects) {
984
- var newTestsOrder = testObjects.slice(0);
985
- newTestsOrder[cursorAt] = testObject;
986
- return newTestsOrder;
1066
+ function useSetTests(handler) {
1067
+ var _a = useTestObjects(), testObjects = _a[1];
1068
+ testObjects(function (_a) {
1069
+ var current = _a.current, prev = _a.prev;
1070
+ return ({
1071
+ prev: prev,
1072
+ current: asArray(handler(current))
1073
+ });
987
1074
  });
988
1075
  }
989
- // DERIVED VALUES
990
- var omittedFieldsCache = createCache();
1076
+ // Derived state
1077
+ function useAllIncomplete() {
1078
+ var current = useTestObjects()[0].current;
1079
+ return flatten(transform(current, function (testObject) {
1080
+ return testObject.isPending() ? testObject : null;
1081
+ }));
1082
+ }
991
1083
  function useOmittedFields() {
992
- var testObjects = useTestObjects()[0];
993
- return omittedFieldsCache([testObjects], function () {
994
- return testObjects.reduce(function (omittedFields, testObject) {
995
- if (omittedFields[testObject.fieldName]) {
996
- return omittedFields;
997
- }
998
- if (testObject.isOmitted()) {
999
- omittedFields[testObject.fieldName] = true;
1000
- }
1084
+ var testObjects = useTestsFlat();
1085
+ return testObjects.reduce(function (omittedFields, testObject) {
1086
+ if (omittedFields[testObject.fieldName]) {
1001
1087
  return omittedFields;
1002
- }, {});
1003
- });
1088
+ }
1089
+ if (testObject.isOmitted()) {
1090
+ omittedFields[testObject.fieldName] = true;
1091
+ }
1092
+ return omittedFields;
1093
+ }, {});
1004
1094
  }
1005
- var incompleteCache = createCache();
1006
- function useAllIncomplete() {
1007
- var testObjects = useTestObjects()[0];
1008
- return incompleteCache([testObjects], function () {
1009
- return testObjects.filter(function (testObject) { return testObject.isPending(); });
1010
- });
1095
+ var flatCache = createCache();
1096
+ function useTestsFlat() {
1097
+ var current = useTestObjects()[0].current;
1098
+ return flatCache([current], function () { return flatten(current); });
1011
1099
  }
1012
1100
 
1013
- function omitOptionalTests() {
1014
- var _a = useTestObjects(), setTestObjects = _a[1];
1015
- var optionalFields = useOptionalFields()[0];
1016
- if (isEmpty(optionalFields)) {
1101
+ function usePath() {
1102
+ var context = ctx.useX();
1103
+ return context.testCursor.getCursor();
1104
+ }
1105
+ function useCursorAt() {
1106
+ var context = ctx.useX();
1107
+ return context.testCursor.cursorAt();
1108
+ }
1109
+ function moveForward() {
1110
+ var context = ctx.useX();
1111
+ return context.testCursor.next();
1112
+ }
1113
+ function addLevel() {
1114
+ var context = ctx.useX();
1115
+ context.testCursor.addLevel();
1116
+ }
1117
+ function removeLevel() {
1118
+ var context = ctx.useX();
1119
+ context.testCursor.removeLevel();
1120
+ }
1121
+
1122
+ function isolate(_a, callback) {
1123
+ var _b = _a.type, type = _b === void 0 ? IsolateTypes.DEFAULT : _b;
1124
+ if (!isFunction(callback)) {
1017
1125
  return;
1018
1126
  }
1019
- var shouldOmit = {};
1020
- setTestObjects(function (testObjects) {
1021
- return testObjects.map(function (testObject) {
1022
- var fieldName = testObject.fieldName;
1023
- if (shouldOmit.hasOwnProperty(fieldName)) {
1024
- omit(testObject);
1025
- }
1026
- else {
1027
- var optionalConfig = optionalFields[fieldName];
1028
- if (isFunction(optionalConfig)) {
1029
- shouldOmit[fieldName] = optionalConfig();
1030
- omit(testObject);
1031
- }
1032
- }
1033
- return testObject;
1034
- });
1127
+ var path = usePath();
1128
+ return ctx.run({ isolate: { type: type } }, function () {
1129
+ addLevel();
1130
+ useSetTests(function (tests) { return setValueAtPath(tests, path, []); });
1131
+ var res = callback();
1132
+ removeLevel();
1133
+ moveForward();
1134
+ return res;
1035
1135
  });
1036
- function omit(testObject) {
1037
- if (shouldOmit[testObject.fieldName]) {
1038
- testObject.omit();
1039
- }
1040
- }
1136
+ }
1137
+ function shouldAllowReorder() {
1138
+ return ctx.useX().isolate.type === IsolateTypes.EACH;
1139
+ }
1140
+
1141
+ function nonMatchingFieldName(testObject, fieldName) {
1142
+ return !!fieldName && !matchingFieldName(testObject, fieldName);
1143
+ }
1144
+ function matchingFieldName(testObject, fieldName) {
1145
+ return !!(fieldName && testObject.fieldName === fieldName);
1041
1146
  }
1042
1147
 
1043
1148
  /**
1044
- * Checks if a given tests, or the suite as a whole still have remaining tests.
1149
+ * Checks if a given field, or the suite as a whole still have remaining tests.
1045
1150
  */
1046
1151
  function hasRemainingTests(fieldName) {
1047
1152
  var allIncomplete = useAllIncomplete();
@@ -1060,7 +1165,7 @@
1060
1165
  * Reads the testObjects list and gets full validation result from it.
1061
1166
  */
1062
1167
  function genTestsSummary() {
1063
- var testObjects = useTestObjects()[0];
1168
+ var testObjects = useTestsFlat();
1064
1169
  var summary = {
1065
1170
  errorCount: 0,
1066
1171
  groups: {},
@@ -1180,7 +1285,7 @@
1180
1285
  * @returns suite or field's errors or warnings.
1181
1286
  */
1182
1287
  function getFailures(severityKey, fieldName) {
1183
- var testObjects = useTestObjects()[0];
1288
+ var testObjects = useTestsFlat();
1184
1289
  var failureMessages = collectFailureMessages(severityKey, testObjects, {
1185
1290
  fieldName: fieldName
1186
1291
  });
@@ -1202,7 +1307,7 @@
1202
1307
  if (!group) {
1203
1308
  throwError("get" + severityKey[0].toUpperCase() + severityKey.slice(1) + "ByGroup requires a group name. Received `" + group + "` instead.");
1204
1309
  }
1205
- var testObjects = useTestObjects()[0];
1310
+ var testObjects = useTestsFlat();
1206
1311
  var failureMessages = collectFailureMessages(severityKey, testObjects, {
1207
1312
  group: group,
1208
1313
  fieldName: fieldName
@@ -1233,7 +1338,7 @@
1233
1338
  return has('warnings', fieldName);
1234
1339
  }
1235
1340
  function has(severityKey, fieldName) {
1236
- var testObjects = useTestObjects()[0];
1341
+ var testObjects = useTestsFlat();
1237
1342
  return testObjects.some(function (testObject) {
1238
1343
  return hasFailuresLogic(testObject, severityKey, fieldName);
1239
1344
  });
@@ -1249,7 +1354,7 @@
1249
1354
  * Checks whether there are failures in a given group.
1250
1355
  */
1251
1356
  function hasByGroup(severityKey, group, fieldName) {
1252
- var testObjects = useTestObjects()[0];
1357
+ var testObjects = useTestsFlat();
1253
1358
  return testObjects.some(function (testObject) {
1254
1359
  return group === testObject.groupName
1255
1360
  ? hasFailuresLogic(testObject, severityKey, fieldName)
@@ -1265,7 +1370,7 @@
1265
1370
  if (result.hasErrors(fieldName)) {
1266
1371
  return false;
1267
1372
  }
1268
- var testObjects = useTestObjects()[0];
1373
+ var testObjects = useTestsFlat();
1269
1374
  if (isEmpty(testObjects)) {
1270
1375
  return false;
1271
1376
  }
@@ -1297,7 +1402,7 @@
1297
1402
  return !!fieldName && isEmpty(result.tests[fieldName]);
1298
1403
  }
1299
1404
  function noMissingTests(fieldName) {
1300
- var testObjects = useTestObjects()[0];
1405
+ var testObjects = useTestsFlat();
1301
1406
  var optionalFields = useOptionalFields()[0];
1302
1407
  return testObjects.every(function (testObject) {
1303
1408
  if (nonMatchingFieldName(testObject, fieldName)) {
@@ -1311,7 +1416,7 @@
1311
1416
 
1312
1417
  var cache$1 = createCache(20);
1313
1418
  function produceDraft() {
1314
- var testObjects = useTestObjects()[0];
1419
+ var testObjects = useTestsFlat();
1315
1420
  var ctxRef = { stateRef: useStateRef() };
1316
1421
  return cache$1([testObjects], ctx.bind(ctxRef, function () {
1317
1422
  return assign(genTestsSummary(), {
@@ -1332,7 +1437,7 @@
1332
1437
 
1333
1438
  var cache = createCache(20);
1334
1439
  function produceFullResult() {
1335
- var testObjects = useTestObjects()[0];
1440
+ var testObjects = useTestsFlat();
1336
1441
  var ctxRef = { stateRef: useStateRef() };
1337
1442
  return cache([testObjects], ctx.bind(ctxRef, function () {
1338
1443
  return assign({}, produceDraft(), {
@@ -1415,6 +1520,44 @@
1415
1520
  };
1416
1521
  }
1417
1522
 
1523
+ function omitOptionalTests() {
1524
+ var optionalFields = useOptionalFields()[0];
1525
+ if (isEmpty(optionalFields)) {
1526
+ return;
1527
+ }
1528
+ var shouldOmit = {};
1529
+ useSetTests(function (tests) {
1530
+ return transform(tests, function (testObject) {
1531
+ var fieldName = testObject.fieldName;
1532
+ if (shouldOmit.hasOwnProperty(fieldName)) {
1533
+ omit(testObject);
1534
+ }
1535
+ else {
1536
+ var optionalConfig = optionalFields[fieldName];
1537
+ if (isFunction(optionalConfig)) {
1538
+ shouldOmit[fieldName] = optionalConfig();
1539
+ omit(testObject);
1540
+ }
1541
+ }
1542
+ return testObject;
1543
+ });
1544
+ });
1545
+ function omit(testObject) {
1546
+ if (shouldOmit[testObject.fieldName]) {
1547
+ testObject.omit();
1548
+ }
1549
+ }
1550
+ }
1551
+
1552
+ /**
1553
+ * Removes test object from suite state
1554
+ */
1555
+ function removeTestFromState (testObject) {
1556
+ useSetTests(function (tests) {
1557
+ return transform(tests, function (test) { return (testObject !== test ? test : null); });
1558
+ });
1559
+ }
1560
+
1418
1561
  function callEach(arr) {
1419
1562
  return arr.forEach(function (fn) { return fn(); });
1420
1563
  }
@@ -1425,8 +1568,7 @@
1425
1568
  function runFieldCallbacks(fieldName) {
1426
1569
  var fieldCallbacks = useTestCallbacks()[0].fieldCallbacks;
1427
1570
  if (fieldName) {
1428
- if (!hasRemainingTests(fieldName) &&
1429
- Array.isArray(fieldCallbacks[fieldName])) {
1571
+ if (!hasRemainingTests(fieldName) && isArray(fieldCallbacks[fieldName])) {
1430
1572
  callEach(fieldCallbacks[fieldName]);
1431
1573
  }
1432
1574
  }
@@ -1443,6 +1585,8 @@
1443
1585
 
1444
1586
  function initBus() {
1445
1587
  var bus = createBus();
1588
+ // Report a the completion of a test. There may be other tests with the same
1589
+ // name that are still running, or not yet started.
1446
1590
  bus.on(Events.TEST_COMPLETED, function (testObject) {
1447
1591
  if (testObject.isCanceled()) {
1448
1592
  return;
@@ -1451,6 +1595,22 @@
1451
1595
  runFieldCallbacks(testObject.fieldName);
1452
1596
  runDoneCallbacks();
1453
1597
  });
1598
+ // Report that the suite completed its synchronous test run.
1599
+ // Async operations may still be running.
1600
+ bus.on(Events.SUITE_COMPLETED, function () {
1601
+ // Remove tests that are optional and need to be omitted
1602
+ omitOptionalTests();
1603
+ });
1604
+ // Removes a certain field from the state.
1605
+ bus.on(Events.REMOVE_FIELD, function (fieldName) {
1606
+ var testObjects = useTestsFlat();
1607
+ testObjects.forEach(function (testObject) {
1608
+ if (matchingFieldName(testObject, fieldName)) {
1609
+ testObject.cancel();
1610
+ removeTestFromState(testObject);
1611
+ }
1612
+ });
1613
+ });
1454
1614
  return bus;
1455
1615
  }
1456
1616
  function useBus() {
@@ -1463,42 +1623,48 @@
1463
1623
  var Events;
1464
1624
  (function (Events) {
1465
1625
  Events["TEST_COMPLETED"] = "test_completed";
1626
+ Events["REMOVE_FIELD"] = "remove_field";
1627
+ Events["SUITE_COMPLETED"] = "suite_completed";
1466
1628
  })(Events || (Events = {}));
1467
1629
 
1468
1630
  // eslint-disable-next-line max-lines-per-function
1469
1631
  function create(suiteCallback) {
1470
1632
  if (!isFunction(suiteCallback)) {
1471
- throwError('Suite initialization error. Expected `tests` to be a function.');
1633
+ throwError('vest.create: Expected callback to be a function.');
1472
1634
  }
1635
+ // Event bus initialization
1473
1636
  var bus = initBus();
1637
+ // State initialization
1474
1638
  var state = createState();
1639
+ // State reference - this holds the actual state values
1475
1640
  var stateRef = createStateRef(state, { suiteId: genId() });
1476
- var suite = assign(ctx.bind({ stateRef: stateRef, bus: bus }, function () {
1641
+ // Create base context reference. All hooks will derive their data from this
1642
+ var ctxRef = { stateRef: stateRef, bus: bus };
1643
+ var suite = assign(
1644
+ // Bind the suite body to the context
1645
+ ctx.bind(ctxRef, function () {
1477
1646
  var args = [];
1478
1647
  for (var _i = 0; _i < arguments.length; _i++) {
1479
1648
  args[_i] = arguments[_i];
1480
1649
  }
1481
- var prevTestObjects = useTestObjects()[0];
1482
- var _a = usePrevTestObjects(), setPrevTestObjects = _a[1];
1650
+ // Reset the state. Migrates current test objects to `prev` array.
1483
1651
  state.reset();
1484
- setPrevTestObjects(function () { return prevTestObjects; });
1485
- // Run the consumer's callback
1486
- suiteCallback.apply(void 0, args);
1487
- omitOptionalTests();
1488
- var res = produceFullResult();
1489
- return res;
1652
+ // Create a top level isolate
1653
+ isolate({ type: IsolateTypes.SUITE }, function () {
1654
+ // Run the consumer's callback
1655
+ suiteCallback.apply(void 0, args);
1656
+ });
1657
+ // Report the suite is done registering tests
1658
+ // Async tests may still be running
1659
+ bus.emit(Events.SUITE_COMPLETED);
1660
+ // Return the result
1661
+ return produceFullResult();
1490
1662
  }), {
1491
- get: ctx.bind({ stateRef: stateRef }, produceDraft),
1492
- remove: ctx.bind({ stateRef: stateRef }, function (name) {
1493
- var testObjects = useTestObjects()[0];
1494
- // We're mutating the array in `cancel`, so we have to first copy it.
1495
- asArray(testObjects).forEach(function (testObject) {
1496
- if (matchingFieldName(testObject, name)) {
1497
- testObject.cancel();
1498
- }
1499
- });
1500
- }),
1501
- reset: state.reset
1663
+ get: ctx.bind(ctxRef, produceDraft),
1664
+ reset: state.reset,
1665
+ remove: ctx.bind(ctxRef, function (fieldName) {
1666
+ bus.emit(Events.REMOVE_FIELD, fieldName);
1667
+ })
1502
1668
  });
1503
1669
  return suite;
1504
1670
  }
@@ -1512,16 +1678,20 @@
1512
1678
  * Adds a field or multiple fields to inclusion group.
1513
1679
  */
1514
1680
  function only(item) {
1515
- return addTo('only', 'tests', item);
1681
+ return addTo(0 /* ONLY */, 'tests', item);
1516
1682
  }
1517
- only.group = function (item) { return addTo('only', 'groups', item); };
1683
+ only.group = function (item) {
1684
+ return addTo(0 /* ONLY */, 'groups', item);
1685
+ };
1518
1686
  /**
1519
1687
  * Adds a field or multiple fields to exclusion group.
1520
1688
  */
1521
1689
  function skip(item) {
1522
- return addTo('skip', 'tests', item);
1690
+ return addTo(1 /* SKIP */, 'tests', item);
1523
1691
  }
1524
- skip.group = function (item) { return addTo('skip', 'groups', item); };
1692
+ skip.group = function (item) {
1693
+ return addTo(1 /* SKIP */, 'groups', item);
1694
+ };
1525
1695
  //Checks whether a certain test profile excluded by any of the exclusion groups.
1526
1696
  // eslint-disable-next-line complexity, max-statements
1527
1697
  function isExcluded(testObject) {
@@ -1593,7 +1763,8 @@
1593
1763
  if (!isStringValue(itemName)) {
1594
1764
  return;
1595
1765
  }
1596
- context.exclusion[itemType][itemName] = exclusionGroup === 'only';
1766
+ context.exclusion[itemType][itemName] =
1767
+ exclusionGroup === 0 /* ONLY */;
1597
1768
  });
1598
1769
  }
1599
1770
  /**
@@ -1608,6 +1779,12 @@
1608
1779
  return false;
1609
1780
  }
1610
1781
 
1782
+ function skipWhen(conditional, callback) {
1783
+ isolate({ type: IsolateTypes.SKIP_WHEN }, function () {
1784
+ ctx.run({ skipped: optionalFunctionValue(conditional) }, function () { return callback(); });
1785
+ });
1786
+ }
1787
+
1611
1788
  var ERROR_OUTSIDE_OF_TEST = "warn hook called outside of a test callback. It won't have an effect."
1612
1789
  ;
1613
1790
  /**
@@ -1621,10 +1798,6 @@
1621
1798
  ctx$1.currentTest.warn();
1622
1799
  }
1623
1800
 
1624
- function skipWhen(conditional, callback) {
1625
- ctx.run({ skipped: optionalFunctionValue(conditional) }, function () { return callback(); });
1626
- }
1627
-
1628
1801
  /**
1629
1802
  * Runs a group callback.
1630
1803
  */
@@ -1636,7 +1809,9 @@
1636
1809
  throwGroupError('callback must be a function');
1637
1810
  }
1638
1811
  // Running with the context applied
1639
- ctx.run({ groupName: groupName }, tests);
1812
+ isolate({ type: IsolateTypes.GROUP }, function () {
1813
+ ctx.run({ groupName: groupName }, tests);
1814
+ });
1640
1815
  }
1641
1816
  function throwGroupError(error) {
1642
1817
  throwError("Wrong arguments passed to group. Group " + error + ".");
@@ -1661,29 +1836,6 @@
1661
1836
  });
1662
1837
  }
1663
1838
 
1664
- /**
1665
- * Removes first found element from array
1666
- * WARNING: Mutates array
1667
- */
1668
- function removeElementFromArray(array, element) {
1669
- var index = array.indexOf(element);
1670
- if (index !== -1) {
1671
- array.splice(index, 1);
1672
- }
1673
- return array;
1674
- }
1675
-
1676
- /**
1677
- * Removes test object from suite state
1678
- */
1679
- function removeTestFromState (testObject) {
1680
- var _a = useTestObjects(), setTestObjects = _a[1];
1681
- setTestObjects(function (testObjects) {
1682
- // using asArray to clear the cache.
1683
- return asArray(removeElementFromArray(testObjects, testObject));
1684
- });
1685
- }
1686
-
1687
1839
  function shouldUseErrorAsMessage(message, error) {
1688
1840
  // kind of cheating with this safe guard, but it does the job
1689
1841
  return isUndefined(message) && isStringValue(error);
@@ -1745,11 +1897,17 @@
1745
1897
  return this.hasFailures() || this.isCanceled() || this.isPassing();
1746
1898
  };
1747
1899
  VestTest.prototype.skip = function () {
1900
+ if (this.isPending()) {
1901
+ // Without this condition, the test will be marked as skipped even if it is pending.
1902
+ // This means that it will not be counted in "allIncomplete" and its done callbacks
1903
+ // will not be called, or will be called prematurely.
1904
+ return;
1905
+ }
1748
1906
  this.setStatus(STATUS_SKIPPED);
1749
1907
  };
1750
1908
  VestTest.prototype.cancel = function () {
1751
1909
  this.setStatus(STATUS_CANCELED);
1752
- removeTestFromState(this);
1910
+ useRefreshTestObjects();
1753
1911
  };
1754
1912
  VestTest.prototype.omit = function () {
1755
1913
  this.setStatus(STATUS_OMITTED);
@@ -1873,7 +2031,7 @@
1873
2031
  * Registers test, if async - adds to pending array
1874
2032
  */
1875
2033
  function registerTest(testObject) {
1876
- var emit = useBus().emit;
2034
+ var bus = useBus();
1877
2035
  // Run test callback.
1878
2036
  // If a promise is returned, set as async and
1879
2037
  // Move to pending list.
@@ -1887,36 +2045,89 @@
1887
2045
  runAsyncTest(testObject);
1888
2046
  }
1889
2047
  else {
1890
- emit(Events.TEST_COMPLETED, testObject);
2048
+ bus.emit(Events.TEST_COMPLETED, testObject);
1891
2049
  }
1892
2050
  }
1893
- catch (_a) {
1894
- throwError("Your test function " + testObject.fieldName + " returned " + JSON.stringify(result) + ". Only \"false\" or a Promise are supported. Return values may cause unexpected behavior.");
2051
+ catch (e) {
2052
+ throwError("Your test function " + testObject.fieldName + " returned a value. Only \"false\" or Promise returns are supported.");
1895
2053
  }
1896
2054
  }
1897
2055
 
1898
- function useTestAtCursor(initialValue) {
1899
- var cursorAt = useCursorAt()[0];
1900
- var prevTestObjects = usePrevTestObjects()[0];
1901
- if (isNotEmpty(prevTestObjects[cursorAt]) &&
1902
- !isSameProfileTest(prevTestObjects[cursorAt], initialValue)) {
1903
- throwErrorDeferred("Vest Critical Error: Tests called in different order than previous run.\n The test at cursor " + cursorAt + " was not the same profile as the previous test.\n expected: " + JSON.stringify(prevTestObjects[cursorAt]) + "\n actual: " + JSON.stringify(initialValue) + "\n This usually happens when you conditionally call your tests using if/else.\n This might lead to unexpected behavior in your test results.\n Replacing if/else with skipWhen solves these issues.");
2056
+ /**
2057
+ * This module serves as the "collision detection" mechanism for Vest.
2058
+ * It is used to ensure that tests are not called in a different order than
2059
+ * they were called in the previous run.
2060
+ * If they are, it will throw a deferred error unless explicitly allowed.
2061
+ *
2062
+ * For now it seems pretty safe, and it covers most common use cases, but it can
2063
+ * be improved in the future both in terms of performance and scenarios it covers.
2064
+ */
2065
+ // eslint-disable-next-line max-statements, max-lines-per-function
2066
+ function useTestAtCursor(newTestObject) {
2067
+ var _a = useTestObjects(), testObjects = _a[0], setTestObjects = _a[1];
2068
+ var prevTests = testObjects.prev;
2069
+ if (isEmpty(prevTests)) {
2070
+ useSetTestAtCursor(newTestObject);
2071
+ return newTestObject;
2072
+ }
2073
+ var prevTest = useGetTestAtCursor(prevTests);
2074
+ if (shouldPurgePrevTest(prevTest, newTestObject)) {
2075
+ throwTestOrderError(prevTest, newTestObject);
2076
+ // Here we handle just the omission of tests in the middle of the test suite.
2077
+ // We need to also handle a case in which tests are added in between other tests.
2078
+ // At the moment all we can do is just splice the tests out of the array when this happens.
2079
+ // A viable solution would be to use something like React's key prop to identify tests regardless
2080
+ // of their position in the suite. https://reactjs.org/docs/lists-and-keys.html#keys
2081
+ var current = getCurrent(prevTests, usePath());
2082
+ var cursorAt = useCursorAt();
2083
+ current.splice(cursorAt);
2084
+ // We actually don't mind mutating the state directly (as can be seen above). There is no harm in it
2085
+ // since we're only touching the "prev" state. The reason we still use the setter function is
2086
+ // to prevent future headaches if we ever do need to rely on prev-state immutability.
2087
+ setTestObjects(function (_a) {
2088
+ var current = _a.current;
2089
+ return ({
2090
+ prev: prevTests,
2091
+ current: current
2092
+ });
2093
+ });
2094
+ // Need to see if this has any effect at all.
2095
+ prevTest = null;
1904
2096
  }
1905
- var nextTest = defaultTo(prevTestObjects[cursorAt], initialValue);
2097
+ var nextTest = defaultTo(prevTest, newTestObject);
1906
2098
  useSetTestAtCursor(nextTest);
1907
2099
  return nextTest;
1908
2100
  }
2101
+ function useSetTestAtCursor(testObject) {
2102
+ var cursorPath = usePath();
2103
+ useSetTests(function (tests) {
2104
+ return setValueAtPath(tests, cursorPath, testObject);
2105
+ });
2106
+ }
2107
+ function useGetTestAtCursor(tests) {
2108
+ var cursorPath = usePath();
2109
+ return valueAtPath(tests, cursorPath);
2110
+ }
2111
+ function shouldPurgePrevTest(prevTest, newTest) {
2112
+ return isNotEmpty(prevTest) && !isSameProfileTest(prevTest, newTest);
2113
+ }
2114
+ function throwTestOrderError(prevTest, newTestObject) {
2115
+ if (shouldAllowReorder()) {
2116
+ return;
2117
+ }
2118
+ throwErrorDeferred("Vest Critical Error: Tests called in different order than previous run.\n expected: " + prevTest.fieldName + "\n received: " + newTestObject.fieldName + "\n This happens when you conditionally call your tests using if/else.\n This might lead to incorrect validation results.\n Replacing if/else with skipWhen solves these issues.");
2119
+ }
1909
2120
 
1910
2121
  function registerPrevRunTest(testObject) {
1911
2122
  var prevRunTest = useTestAtCursor(testObject);
1912
2123
  if (isExcluded(testObject)) {
1913
2124
  testObject.skip();
1914
- useSetNextCursorAt();
2125
+ moveForward();
1915
2126
  return prevRunTest;
1916
2127
  }
1917
2128
  cancelOverriddenPendingTest(prevRunTest, testObject);
1918
2129
  useSetTestAtCursor(testObject);
1919
- useSetNextCursorAt();
2130
+ moveForward();
1920
2131
  registerTestObjectByTier(testObject);
1921
2132
  return testObject;
1922
2133
  }
@@ -1935,7 +2146,7 @@
1935
2146
  * Run multiple tests using a parameter table
1936
2147
  */
1937
2148
  function each(table) {
1938
- if (!Array.isArray(table)) {
2149
+ if (!isArray(table)) {
1939
2150
  throwError('test.each: Expected table to be an array.');
1940
2151
  }
1941
2152
  function eachReturn(fieldName) {
@@ -1944,9 +2155,12 @@
1944
2155
  args[_i - 1] = arguments[_i];
1945
2156
  }
1946
2157
  var _a = args.reverse(), testFn = _a[0], message = _a[1];
1947
- return table.map(function (item) {
1948
- item = asArray(item);
1949
- return test(optionalFunctionValue.apply(void 0, __spreadArray([fieldName], item, false)), optionalFunctionValue.apply(void 0, __spreadArray([message], item, false)), function () { return testFn.apply(void 0, item); });
2158
+ return isolate({ type: IsolateTypes.EACH }, function () {
2159
+ return table.map(function (item) {
2160
+ item = asArray(item);
2161
+ return test(optionalFunctionValue.apply(void 0, __spreadArray([fieldName], item)), optionalFunctionValue.apply(void 0, __spreadArray([message], item)), function () { return testFn.apply(void 0, item); } // eslint-disable-line max-nested-callbacks
2162
+ );
2163
+ });
1950
2164
  });
1951
2165
  }
1952
2166
  return eachReturn;
@@ -1964,7 +2178,7 @@
1964
2178
  args[_i - 1] = arguments[_i];
1965
2179
  }
1966
2180
  var suiteId = useSuiteId()[0];
1967
- var cursorAt = useCursorAt()[0];
2181
+ var cursorAt = useCursorAt();
1968
2182
  var _a = args.reverse(), deps = _a[0], testFn = _a[1], msg = _a[2];
1969
2183
  // Implicit dependency for more specificity
1970
2184
  var dependencies = [suiteId, fieldName, cursorAt].concat(deps);
@@ -2001,7 +2215,7 @@
2001
2215
  memo: bindTestMemo(testBase)
2002
2216
  });
2003
2217
 
2004
- var VERSION = "4.0.0-dev-cc5cf5";
2218
+ var VERSION = "4.0.0-dev-31f012";
2005
2219
 
2006
2220
  exports.VERSION = VERSION;
2007
2221
  exports.create = create;