systemview 1.5.2 → 1.6.3

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.
@@ -0,0 +1,134 @@
1
+ const Test = require("./Test.class");
2
+ const { Argument, TargetValue } = require("./Argument.class");
3
+ const { getType } = require("./test-helpers");
4
+
5
+ module.exports = function TestController({
6
+ TestSection,
7
+ setState,
8
+ section,
9
+ FullTest,
10
+ connectedServices,
11
+ }) {
12
+ this.runTest = async (testIndex) => {
13
+ const test = TestSection[testIndex];
14
+ //run only one test
15
+ await test.runTest();
16
+ setState([...TestSection]);
17
+ };
18
+
19
+ this.updateNamespace = (index, namespace) => {
20
+ TestSection[index].namespace = namespace;
21
+ TestSection[index].getConnection(connectedServices);
22
+ setState([...TestSection]);
23
+ };
24
+ this.addTest = (namespace, args, title) => {
25
+ TestSection.push(new Test({ namespace, args, title, editMode: true }));
26
+ setState([...TestSection]);
27
+ if (namespace) this.updateNamespace(TestSection.length - 1, namespace);
28
+ };
29
+ this.deleteTest = (index) => {
30
+ TestSection.splice(index, 1);
31
+ setState([...TestSection]);
32
+ };
33
+ this.addArg = (index) => {
34
+ const name = "arg" + (TestSection[0].args.length + 1);
35
+ TestSection[index].args.push(new Argument(name, FullTest));
36
+ setState([...TestSection]);
37
+ };
38
+ this.deleteArg = (index, arg_index) => {
39
+ TestSection[index].args.splice(arg_index, 1);
40
+ setState([...TestSection]);
41
+ };
42
+ this.editArg = (index, arg_index, arg) => {
43
+ arg.data_type = getType(arg.input);
44
+ TestSection[index].args[arg_index] = arg;
45
+ setState([...TestSection]);
46
+ };
47
+ this.resetResults = (index) => {
48
+ TestSection[index].clearResults();
49
+ setState([...TestSection]);
50
+ };
51
+
52
+ this.addTargetValue = (
53
+ testIndex,
54
+ arg_index,
55
+ target_namespace,
56
+ source_map,
57
+ source_index
58
+ ) => {
59
+ //check to see if target value already exists first
60
+ const arg = TestSection[testIndex].args[arg_index];
61
+ arg.addTargetValue(target_namespace, source_map, source_index);
62
+ setState([...TestSection]);
63
+ };
64
+ this.setTargetValue = (
65
+ testIndex,
66
+ arg_index,
67
+ target_index,
68
+ target_namespace,
69
+ source_map,
70
+ source_index
71
+ ) => {
72
+ const arg = TestSection[testIndex].args[arg_index];
73
+
74
+ arg.targetValues[target_index] = new TargetValue(
75
+ target_namespace.trim(),
76
+ source_map,
77
+ source_index
78
+ );
79
+ setState([...TestSection]);
80
+ };
81
+
82
+ this.parseTargetValues = (testIndex, arg_index, input, source_map) => {
83
+ const arg = TestSection[testIndex].args[arg_index];
84
+ arg.parseTargetValues(input, source_map).checkTargetNamespaces();
85
+ setState([...TestSection]);
86
+ };
87
+ this.checkTargetValues = (testIndex, arg_index) => {
88
+ const arg = TestSection[testIndex].args[arg_index];
89
+ arg.checkTargetNamespaces();
90
+ setState([...TestSection]);
91
+ };
92
+ this.updateTitle = (testIndex, title) => {
93
+ TestSection[testIndex].title = title;
94
+ setState([...TestSection]);
95
+ };
96
+ this.updateEvaluations = (testIndex, evaluations) => {
97
+ TestSection[testIndex].evaluations = evaluations;
98
+ setState([...TestSection]);
99
+ };
100
+ this.updateTests = () => {
101
+ setState([...TestSection]);
102
+ };
103
+ this.updateValidationStatus = (testIndex) => {
104
+ if (section !== 1) {
105
+ TestSection[testIndex].shouldValidate = !TestSection[testIndex].shouldValidate;
106
+ if (TestSection[testIndex].shouldValidate) TestSection[testIndex].validate();
107
+ else TestSection[testIndex].evaluations = [];
108
+ setState([...TestSection]);
109
+ }
110
+ };
111
+ this.getTargetSuggestions = (testIndex) => {
112
+ //get target value suggestion (namespaces) for previous test including sub test
113
+ const suggestions = [];
114
+ const test_names = ["beforeTest", "mainTest", "afterTest"];
115
+ //exclude all test sections following current section
116
+ const targetTests = FullTest.slice(0, section + 1);
117
+
118
+ targetTests.forEach((test_section, sIndex) => {
119
+ //also exclude current test and the tests that follow by the suggestions
120
+ const count = sIndex === section ? testIndex : test_section.length;
121
+ for (let i = 0; i < count; i++) {
122
+ suggestions.push(
123
+ `${test_names[sIndex]}.${"Action" + (i + 1) + "."}${
124
+ test_section[i].response_type || "results"
125
+ }`
126
+ );
127
+ }
128
+ });
129
+
130
+ return suggestions;
131
+ };
132
+
133
+ return this;
134
+ };
@@ -0,0 +1,148 @@
1
+ const moment = require("moment");
2
+ const rnb = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
3
+
4
+ const isTargetNamespace = (str) =>
5
+ /^(?:before|main|after)Test\.Action\d+\.(?:error|results)(?:\.(?![0-9])[a-zA-Z0-9$_]+(?:\[\d\])*)*$/.test(
6
+ str
7
+ );
8
+ const targetValueFnRegex =
9
+ /tv\((?:before|main|after)Test\.Action\d+\.(?:error|results)(?:\.(?![0-9])[a-zA-Z0-9$_]+(?:\[\d\])*)*\)/g;
10
+ const isTargetValueFn = (str) =>
11
+ /^tv\((?:before|main|after)Test\.Action\d+\.(?:error|results)(?:\.(?![0-9])[a-zA-Z0-9$_]+(?:\[\d\])*)*\)$/.test(
12
+ str
13
+ );
14
+ const isEqualArrays = (a, b) => a.join(".") === b.join("."); //specifically for arrays of strings
15
+ const isValidNamespace = (str) => /^(?![0-9])[a-zA-Z0-9$_]+$/.test(str); //_id
16
+ const startsWithNameAndArray = (str) => /^\w+(\[\d+\])+/.test(str); //users[0]
17
+ const isNameAndArray = (str) => /^\w+(\[\d+\])+$/.test(str); //users[0]
18
+ const endsWithArrayIndex = (str) => /\w+(\[\d+\])+$/.test(str); //users[0].docs[3]...
19
+ const getLastArrayNamespace = (str) => (str.match(/(\w+(\[\d+\])+)$/) || [str])[0];
20
+
21
+ const parseIndex = (nsp) => parseInt((nsp.match(/\[(\d+)\]$/) || [null, "0"])[1]);
22
+ const replaceFirstIndex = (nsp, insert = "0") => nsp.replace(/(\[\d+\])/, `[${insert}]`);
23
+ const replaceLastIndex = (nsp, insert = "0") => nsp.replace(/(\[\d+\])$/, `[${insert}]`);
24
+
25
+ const replaceAllIndices = (nsp, insert = "0") => nsp.replace(/(\[\d+\])/g, `[${insert}]`);
26
+
27
+ const getArrayNamespaces = (str) =>
28
+ str
29
+ .split(/(\w+(\[\d+\])+)/)
30
+ .filter(isNameAndArray)
31
+ .reduce((sum, nsp) => {
32
+ //split by indexes in case of nested arrays
33
+ const indices = nsp.split(/(\[\d+\])/).filter((n) => n);
34
+ const [name] = indices.splice(0, 1);
35
+ return sum.concat(
36
+ indices.map((index, i) => {
37
+ return name + indices.slice(0, i + 1).join("");
38
+ }, [])
39
+ );
40
+ }, []);
41
+ const switchArrayIndices = (nsp, replace) => {
42
+ // normalize nsp and replace nsp
43
+ const n = replaceAllIndices(nsp);
44
+ const r = replaceAllIndices(replace);
45
+ // match the normalized namespaces
46
+ if (n.substr(0, r.length) === r) {
47
+ // create new namespace by concatenating
48
+ const n = nsp.split(".");
49
+ const r = replace.split(".");
50
+ n.splice(...[0, r.length, ...r]);
51
+ return n.join(".");
52
+ } else return nsp;
53
+ };
54
+
55
+ //separate prop names from other prop names and indices (ie. 'test.results[0][0]'...);
56
+ const mapNamespace = (nsp) =>
57
+ nsp
58
+ .replace(/(?:\.|\[|\])/g, " ")
59
+ .split(" ")
60
+ .reduce((sum, str) => sum.concat(str.trim() || []), []);
61
+
62
+ const obj = function ObjectParser(obj) {
63
+ const parser = this || {};
64
+ parser.parse = (map) =>
65
+ map.reduce(([placeholder], key) => [placeholder?.[key], placeholder, key], [obj]);
66
+
67
+ parser.valueAt = (map) => parser.parse(map)[0];
68
+
69
+ parser.valueAtNsp = (nsp) => parser.valueAt(mapNamespace(nsp));
70
+
71
+ parser.parseNsp = (nsp) => parser.parse(mapNamespace(nsp));
72
+ //using JSON to create a deep copy in order to lose refs to original
73
+ parser.clone = () => JSON.parse(JSON.stringify(obj));
74
+
75
+ parser.isEmpty = () => Object.getOwnPropertyNames(obj).length === 0;
76
+
77
+ return parser;
78
+ };
79
+
80
+ const arr = function ArrayParser(arr) {
81
+ const parser = this || {};
82
+
83
+ parser.randomIndex = () => rnb(0, arr.length - 1);
84
+
85
+ parser.randomItem = () => arr[parser.randomIndex()];
86
+ return parser;
87
+ };
88
+
89
+ const isFn = /^\w+\(([^,)]*(,[^,)]*)*)\)$/;
90
+ const parseArgs = (str) => str.split(",").map((value) => value.trim());
91
+ const isFunction = (str) => isFn.test(str);
92
+ const isDateFunction = (str) => /^[dD]ate\(([^,)]*(,[^,)]*)*)\)$/.test(str);
93
+
94
+ const strFn = (str) => {
95
+ if (isDateFunction(str)) {
96
+ const [fullStr, args] = str.match(isFn);
97
+ return moment(args).toJSON();
98
+ }
99
+
100
+ return str;
101
+ };
102
+ function getType(value) {
103
+ switch (true) {
104
+ case typeof value === "object":
105
+ if (!value) return "null";
106
+ else if (Array.isArray(value)) return "array";
107
+ else return "object";
108
+ case typeof value === "string":
109
+ if (moment(value).isValid()) return "date";
110
+ else return "string";
111
+ case typeof value === "number":
112
+ return "number";
113
+ case typeof value === "boolean":
114
+ return "boolean";
115
+ case typeof value === "undefined":
116
+ return "undefined";
117
+ default:
118
+ return "?";
119
+ }
120
+ }
121
+ const isObjectLike = (value) =>
122
+ ["object", "array", "string"].indexOf(getType(value)) > -1;
123
+ module.exports = {
124
+ rnb,
125
+ isObjectLike,
126
+ isTargetNamespace,
127
+ targetValueFnRegex,
128
+ isTargetValueFn,
129
+ isEqualArrays,
130
+ isValidNamespace,
131
+ startsWithNameAndArray,
132
+ isNameAndArray,
133
+ endsWithArrayIndex,
134
+ getLastArrayNamespace,
135
+ parseIndex,
136
+ replaceFirstIndex,
137
+ replaceLastIndex,
138
+ replaceAllIndices,
139
+ getArrayNamespaces,
140
+ switchArrayIndices,
141
+ mapNamespace,
142
+ obj,
143
+ arr,
144
+ isFunction,
145
+ isDateFunction,
146
+ strFn,
147
+ getType,
148
+ };
@@ -0,0 +1,56 @@
1
+ const { Argument } = require("./Argument.class");
2
+ const Test = require("./Test.class");
3
+
4
+ function initializeSavedTests(savedTests, connectedServices) {
5
+ return savedTests.map((ft) => {
6
+ // context matters
7
+ const newTests = [];
8
+ const { title, namespace } = ft;
9
+
10
+ const Before = ft.Before.map((test) =>
11
+ resetTestClass(test, newTests, connectedServices, false)
12
+ );
13
+ const Main = ft.Main.map((test) =>
14
+ resetTestClass(test, newTests, connectedServices, false)
15
+ );
16
+ const Events = ft.Events.map((test) =>
17
+ resetTestClass(test, newTests, connectedServices, false)
18
+ );
19
+ const After = ft.After.map((test) =>
20
+ resetTestClass(test, newTests, connectedServices, false)
21
+ );
22
+ newTests.push(Before);
23
+ newTests.push(Main);
24
+ newTests.push(Events);
25
+ newTests.push(After);
26
+ return { Before, Main, Events, After, title, namespace };
27
+ });
28
+ }
29
+
30
+ const resetTestClass = (test, FullTest, connectedServices, editMode) => {
31
+ return new Test({
32
+ ...test,
33
+ args: test.args.map(
34
+ (arg) =>
35
+ new Argument(arg.name, FullTest, arg.input_type, arg.input, arg.targetValues)
36
+ ),
37
+ editMode,
38
+ }).getConnection(connectedServices);
39
+ };
40
+
41
+ const resetFullTest = (FullTest, connectedServices, editMode) => {
42
+ //context matters
43
+ const newTests = [[], [], [], []];
44
+ return FullTest.map((section, i) => {
45
+ return section.map((test) => {
46
+ const newTest = resetTestClass(test, newTests, connectedServices, editMode);
47
+ newTests[i].push(newTest);
48
+ return newTest;
49
+ });
50
+ });
51
+ };
52
+ module.exports = {
53
+ initializeSavedTests,
54
+ resetTestClass,
55
+ resetFullTest,
56
+ };
@@ -0,0 +1,228 @@
1
+ const moment = require("moment");
2
+ const {
3
+ arr,
4
+ obj,
5
+ parseIndex,
6
+ replaceLastIndex,
7
+ switchArrayIndices,
8
+ getType,
9
+ } = require("./test-helpers");
10
+
11
+ function evaluate(value, namespace, savedEval = {}, shouldSave) {
12
+ const type = getType(value);
13
+ const validations = savedEval.validations || [];
14
+ const expected_type = savedEval.expected_type || type;
15
+ const save = shouldSave || !!savedEval.save;
16
+ const indexed = savedEval.indexed;
17
+ const errors = getErrors({ type, value, validations, expected_type });
18
+ return {
19
+ namespace,
20
+ expected_type,
21
+ validations,
22
+ save,
23
+ indexed,
24
+ type,
25
+ value,
26
+ errors,
27
+ };
28
+ }
29
+
30
+ function validateResults() {
31
+ const { results, response_type, savedEvaluations, editMode } = this;
32
+ const savedEvalClone = [...savedEvaluations];
33
+ const shouldSave = !savedEvaluations.length;
34
+ const evaluations = [];
35
+ const errors = [];
36
+
37
+ function getSavedIndices(data, nsp) {
38
+ const randomIndex = () => {
39
+ // get all matching indices and rename them
40
+ // so they can be found later during getSavedEval
41
+ const index = arr(data).randomIndex();
42
+ const new_nsp = replaceLastIndex(nsp, index);
43
+ savedEvalClone.forEach((e) => {
44
+ if (!e.indexed) e.namespace = switchArrayIndices(e.namespace, new_nsp);
45
+ });
46
+ return index;
47
+ };
48
+ const savedIndices = savedEvalClone
49
+ .filter(({ namespace }) => {
50
+ return replaceLastIndex(namespace) === nsp;
51
+ })
52
+ .map((e) => {
53
+ if (e.indexed) {
54
+ return parseIndex(e.namespace);
55
+ } else {
56
+ return randomIndex();
57
+ }
58
+ });
59
+ return savedIndices.length ? savedIndices : [randomIndex()];
60
+ }
61
+ const getSavedEval = (nsp) => {
62
+ const i = savedEvalClone.findIndex(({ namespace }) => {
63
+ return replaceLastIndex(namespace) === replaceLastIndex(nsp);
64
+ });
65
+ return i > -1 ? savedEvalClone.splice(i, 1)[0] : {};
66
+ };
67
+ const addEvaluation = (evaluation) => {
68
+ evaluation.errors.forEach(
69
+ (e) => evaluation.save && errors.push({ ...e, namespace: evaluation.namespace })
70
+ );
71
+ evaluations.push(evaluation);
72
+ };
73
+
74
+ //evaluate based on the result only in edit mode
75
+ if (editMode)
76
+ (function recursiveEval(data, namespace) {
77
+ const evaluation = evaluate(data, namespace, getSavedEval(namespace), shouldSave);
78
+ addEvaluation(evaluation);
79
+ if (evaluation.type === "object") {
80
+ Object.getOwnPropertyNames(data).forEach((prop) => {
81
+ recursiveEval(data[prop], `${namespace}.${prop}`);
82
+ });
83
+ } else if (evaluation.type === "array") {
84
+ const indices = getSavedIndices(data, `${namespace}[0]`);
85
+ indices.forEach((index) => recursiveEval(data[index], `${namespace}[${index}]`));
86
+ }
87
+ })(results, response_type);
88
+
89
+ //evaluate based on the saved evaluations
90
+ const objParser = new obj({ [response_type]: results });
91
+ savedEvalClone.forEach(({ namespace, ...e }) => {
92
+ const value = objParser.valueAtNsp(namespace);
93
+ if (e.save) addEvaluation(evaluate(value, namespace, e));
94
+ });
95
+
96
+ Object.assign(this, {
97
+ evaluations: evaluations.sort((e1, e2) => e1.namespace.localeCompare(e2.namespace)),
98
+ errors,
99
+ });
100
+ }
101
+
102
+ function getErrors({ type, value, validations, expected_type }) {
103
+ if (type !== expected_type && expected_type !== "mixed")
104
+ return [{ name: "typeError", expected: expected_type, received: type }];
105
+
106
+ switch (type) {
107
+ case "number":
108
+ return validateNumber(value, validations);
109
+ case "date":
110
+ return validateDate(value, validations);
111
+ case "string":
112
+ return validateString(value, validations);
113
+ case "array":
114
+ return validateArray(value, validations);
115
+ case "boolean":
116
+ return validateBoolean(value, validations);
117
+ case "null":
118
+ case "undefined":
119
+ default:
120
+ return [];
121
+ }
122
+ }
123
+
124
+ const defaultValue = (data_type) => {
125
+ switch (data_type) {
126
+ case "string":
127
+ return "";
128
+ case "number":
129
+ return 0;
130
+ case "date":
131
+ return moment().toJSON();
132
+ case "boolean":
133
+ return false;
134
+ case "array":
135
+ return [];
136
+ case "object":
137
+ return {};
138
+ case "null":
139
+ return null;
140
+ case "target":
141
+ return "";
142
+ case "undefined":
143
+ default:
144
+ return undefined;
145
+ }
146
+ };
147
+
148
+ const validateLength = (item, validations) =>
149
+ validations.reduce((errors, { name, value }) => {
150
+ if (name === "lengthEquals" && item.length !== value)
151
+ return errors.concat({ name, expected: value, received: item.length });
152
+ if (name === "maxLength" && item.length > value)
153
+ return errors.concat({ name, expected: value, received: item.length });
154
+ if (name === "minLength" && item.length < value)
155
+ return errors.concat({ name, expected: value, received: item.length });
156
+ return errors;
157
+ }, []);
158
+
159
+ const validateArray = (arr, validations) =>
160
+ validations.reduce((errors, { name, value }) => {
161
+ if (name === "includes" && !arr.includes(value))
162
+ return errors.concat({ name, expected: value, received: arr });
163
+ return errors;
164
+ }, validateLength(arr, validations));
165
+
166
+ const validateString = (str, validations) =>
167
+ validations.reduce((errors, { name, value }) => {
168
+ if (name === "strEquals" && str !== value)
169
+ return errors.concat({ name, expected: value, received: str });
170
+ //str.match() returns null when there is no match
171
+ if ((name === "isLike") & !str.match(new RegExp(value, "gi")))
172
+ return errors.concat({ name, expected: value, received: str });
173
+ if (
174
+ name === "isOneOf" &&
175
+ typeof value === "string" &&
176
+ !value
177
+ .split(",")
178
+ .map((v) => v.trim())
179
+ .includes(str)
180
+ )
181
+ return errors.concat({ name, expected: value, received: str });
182
+ return errors;
183
+ }, validateLength(str, validations));
184
+
185
+ const validateNumber = (num, validations) =>
186
+ validations.reduce((errors, { name, value }) => {
187
+ if (name === "numEquals" && num !== value)
188
+ return errors.concat({ name, expected: value, received: num });
189
+ if (name === "max" && num > value)
190
+ return errors.concat({ name, expected: value, received: num });
191
+ if (name === "min" && num < value)
192
+ return errors.concat({ name, expected: value, received: num });
193
+ if (
194
+ name === "isOneOf" &&
195
+ typeof value === "string" &&
196
+ !value
197
+ .split(",")
198
+ .map((v) => parseInt(v))
199
+ .includes(num)
200
+ )
201
+ return errors.concat({ name, expected: value, received: num });
202
+ return errors;
203
+ }, []);
204
+
205
+ const validateBoolean = (bool, validations) =>
206
+ validations.reduce((errors, { name, value }) => {
207
+ if (name === "boolEquals" && bool !== value)
208
+ return errors.concat({ name, expected: value, received: bool });
209
+ return errors;
210
+ }, []);
211
+
212
+ const validateDate = (datetime, validations) =>
213
+ validations.reduce((errors, { name, value }) => {
214
+ if (name === "dateEquals" && !moment(datetime).isSame(value))
215
+ return errors.concat({ name, expected: value, received: datetime });
216
+ if (name === "maxDate" && moment(datetime).isAfter(value))
217
+ return errors.concat({ name, expected: value, received: datetime });
218
+ if (name === "minDate" && moment(datetime).isBefore(value))
219
+ return errors.concat({ name, expected: value, received: datetime });
220
+ return errors;
221
+ }, []);
222
+
223
+ module.exports = {
224
+ evaluate,
225
+ validateResults,
226
+ getErrors,
227
+ defaultValue,
228
+ };
@@ -0,0 +1,45 @@
1
+ const moment = require("moment/moment");
2
+
3
+ const vowels = ["a", "e", "i", "o", "u"];
4
+ const isVowel = (str) => vowels.includes((str + "").toLowerCase());
5
+ const an = (word) =>
6
+ word + "" === "undefined" ? "" : isVowel((word + "")[0]) ? "an" : "a";
7
+
8
+ module.exports = function validationMessage({ name, namespace, expected, received }) {
9
+ return errorMessages[name](namespace, expected, received);
10
+ };
11
+ const errorMessages = {
12
+ typeError: (namespace, expected, received) => {
13
+ const a = isVowel(expected) ? "an" : "a";
14
+ return `Expected ${namespace}to be ${a} ${expected}, received ${received}`;
15
+ },
16
+
17
+ lengthEquals: (namespace, expected, received) =>
18
+ `Expected ${namespace} to have a length of ${expected}, received ${received}`,
19
+ maxLength: (namespace, expected, received) =>
20
+ `Expected ${namespace} to have a maximum length of ${expected}, received ${received}`,
21
+ minLength: (namespace, expected, received) =>
22
+ `Expected ${namespace} to have a minimum length of ${expected}, received ${received}`,
23
+ includes: (namespace, expected, received) =>
24
+ `Expected ${namespace} to include the following value: ${expected}, received ${received}`,
25
+ isLike: (namespace, expected, received) =>
26
+ `Expected ${namespace} to be like the following expression: ${expected}, received ${received}`,
27
+ isOneOf: (namespace, expected, received) =>
28
+ `Expected ${namespace} to be one of the following values: ${expected}, received ${received}`,
29
+ strEquals: (namespace, expected, received) =>
30
+ `Expected ${namespace} to equal "${expected}"`,
31
+ numEquals: (namespace, expected, received) =>
32
+ `Expected ${namespace} to equal ${expected}, received ${received}`,
33
+ max: (namespace, expected, received) =>
34
+ `Expected ${namespace} to be less than ${expected}, received ${received}`,
35
+ min: (namespace, expected, received) =>
36
+ `Expected ${namespace} to be greater than ${expected}, received ${received}`,
37
+ boolEquals: (namespace, expected, received) =>
38
+ `Expected ${namespace} to be ${expected.toString()}`,
39
+ dateEquals: (namespace, expected, received) =>
40
+ `Expected ${namespace} to be ${moment(expected).format()}`,
41
+ minDate: (namespace, expected, received) =>
42
+ `Expected ${namespace} to be a date/time later than ${expected}, received ${received}`,
43
+ maxDate: (namespace, expected, received) =>
44
+ `Expected ${namespace} to be a date/time earlier than ${expected}, received ${received}`,
45
+ };
package/cli/runTest.js DELETED
File without changes