systemview 1.6.2 → 1.7.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,162 @@
1
+ const { validateResults } = require("./validators");
2
+ const { Client } = require("systemlynx");
3
+ const moment = require("moment");
4
+ const { getArrayNamespaces, getLastArrayNamespace, obj } = require("./test-helpers");
5
+
6
+ module.exports = function Test({
7
+ namespace,
8
+ args,
9
+ title,
10
+ shouldValidate = false,
11
+ savedEvaluations = [],
12
+ index,
13
+ editMode = true,
14
+ }) {
15
+ this.index = index;
16
+ this.connection = {};
17
+ this.title = title;
18
+ this.args = args || [];
19
+ this.editMode = editMode;
20
+ this.shouldValidate = shouldValidate || !!savedEvaluations.length;
21
+ this.namespace = namespace || {
22
+ serviceId: "",
23
+ moduleName: "",
24
+ methodName: "",
25
+ };
26
+ this.clearResults = () => {
27
+ this.results = null;
28
+ this.response_type = "";
29
+ this.test_start = null;
30
+ this.test_end = null;
31
+ this.evaluations = [];
32
+ this.savedEvaluations = obj(savedEvaluations).clone();
33
+ this.errors = [];
34
+ return this;
35
+ };
36
+
37
+ this.clearResults();
38
+
39
+ this.getErrors = () => {
40
+ this.errors = this.evaluations
41
+ .filter(({ save }) => save)
42
+ .reduce(
43
+ (sum, { errors, namespace }) =>
44
+ sum.concat(errors.map((e) => ({ ...e, namespace }))),
45
+ []
46
+ );
47
+ return this.errors;
48
+ };
49
+
50
+ this.validate = validateResults.bind(this);
51
+
52
+ this.runTest = async (_logger) => {
53
+ const logger = _logger || new TestLogger(this);
54
+ const { serviceId, moduleName, methodName } = this.namespace;
55
+ const args = this.args.map((arg) => arg.value());
56
+
57
+ this.test_start = moment().toJSON();
58
+ const Module = this.connection[serviceId][moduleName];
59
+ if (methodName === "on") {
60
+ const eventTest = (e) => {
61
+ this.results = e;
62
+ this.test_end = moment().toJSON();
63
+ this.response_type = "event";
64
+ this.shouldValidate && this.validate();
65
+ logger.end(this);
66
+ Module.$clearEvent(args[0], "eventTest");
67
+ };
68
+ logger.start(args);
69
+ Module.on(args[0], eventTest);
70
+ } else {
71
+ try {
72
+ logger.start(args);
73
+ this.results = await Module[methodName](...args);
74
+ this.test_end = moment().toJSON();
75
+ this.response_type = "results";
76
+ this.shouldValidate && this.validate();
77
+ logger.end(this);
78
+ } catch (error) {
79
+ this.test_end = moment().toJSON();
80
+ this.results = error;
81
+ this.response_type = "error";
82
+ this.shouldValidate && this.validate();
83
+ logger.end(this);
84
+ }
85
+ }
86
+ return this;
87
+ };
88
+
89
+ this.getConnection = (connectedServices) => {
90
+ const { serviceId } = this.namespace;
91
+
92
+ if (connectedServices.length > 0) {
93
+ const service = connectedServices.find(
94
+ (service) => service.serviceId === serviceId
95
+ );
96
+ if (!service) {
97
+ console.warn("connection data not found");
98
+ return this;
99
+ }
100
+ const { connectionData } = service.system;
101
+
102
+ this.connection[serviceId] = Client.createService(connectionData);
103
+ }
104
+
105
+ return this;
106
+ };
107
+
108
+ this.addEvaluation = (evaluation) => {
109
+ const savedEval = this.savedEvaluations.find(
110
+ ({ namespace }) => namespace === evaluation.namespace
111
+ );
112
+ if (savedEval) Object.assign(savedEval, evaluation);
113
+ else this.savedEvaluations.push(evaluation);
114
+ };
115
+ this.removeEvaluation = (namespace) => {
116
+ const index = this.savedEvaluations.findIndex((e) => e.namespace === namespace);
117
+ if (index > -1) return this.savedEvaluations.splice(index, 1)[0];
118
+ else return {};
119
+ };
120
+
121
+ this.addSavedIndices = (arrayNamespace, newArrayNamespace) => {
122
+ //break namespace into multiple array namespaces
123
+ const nspList = getArrayNamespaces(arrayNamespace);
124
+ this.evaluations.forEach((e) => {
125
+ if (nspList.includes(getLastArrayNamespace(e.namespace))) {
126
+ e.namespace = e.namespace.replace(arrayNamespace, newArrayNamespace);
127
+ e.indexed = true;
128
+ e.expected_type = undefined;
129
+ this.addEvaluation(e);
130
+ }
131
+ });
132
+ };
133
+ this.removeSavedIndices = (namespace) => {
134
+ this.savedEvaluations = this.savedEvaluations.filter(
135
+ (e) => !e.namespace.includes(namespace) //|| !e.indexed
136
+ );
137
+ };
138
+ };
139
+
140
+ function TestLogger(test) {
141
+ this.start = (args) => {
142
+ const { serviceId, moduleName, methodName } = test.namespace;
143
+
144
+ console.log(
145
+ `[${moment(this.test_start).format(
146
+ "L LTS"
147
+ )}]> [invoking]:${serviceId}.${moduleName}.${methodName}()`
148
+ );
149
+ console.log.apply({}, ["args:"].concat(args));
150
+ };
151
+ this.end = () => {
152
+ const { serviceId, moduleName, methodName } = test.namespace;
153
+ const { results, response_type } = test;
154
+ console.log(
155
+ `[${moment(this.test_end).format(
156
+ "L LTS"
157
+ )}]> [${response_type}]:${serviceId}.${moduleName}.${methodName}()`,
158
+ `${response_type}:`,
159
+ results
160
+ );
161
+ };
162
+ }
@@ -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,149 @@
1
+ const moment = require("moment");
2
+ moment.suppressDeprecationWarnings = true;
3
+ const rnb = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
4
+
5
+ const isTargetNamespace = (str) =>
6
+ /^(?:before|main|after)Test\.Action\d+\.(?:error|results)(?:\.(?![0-9])[a-zA-Z0-9$_]+(?:\[\d\])*)*$/.test(
7
+ str
8
+ );
9
+ const targetValueFnRegex =
10
+ /tv\((?:before|main|after)Test\.Action\d+\.(?:error|results)(?:\.(?![0-9])[a-zA-Z0-9$_]+(?:\[\d\])*)*\)/g;
11
+ const isTargetValueFn = (str) =>
12
+ /^tv\((?:before|main|after)Test\.Action\d+\.(?:error|results)(?:\.(?![0-9])[a-zA-Z0-9$_]+(?:\[\d\])*)*\)$/.test(
13
+ str
14
+ );
15
+ const isEqualArrays = (a, b) => a.join(".") === b.join("."); //specifically for arrays of strings
16
+ const isValidNamespace = (str) => /^(?![0-9])[a-zA-Z0-9$_]+$/.test(str); //_id
17
+ const startsWithNameAndArray = (str) => /^\w+(\[\d+\])+/.test(str); //users[0]
18
+ const isNameAndArray = (str) => /^\w+(\[\d+\])+$/.test(str); //users[0]
19
+ const endsWithArrayIndex = (str) => /\w+(\[\d+\])+$/.test(str); //users[0].docs[3]...
20
+ const getLastArrayNamespace = (str) => (str.match(/(\w+(\[\d+\])+)$/) || [str])[0];
21
+
22
+ const parseIndex = (nsp) => parseInt((nsp.match(/\[(\d+)\]$/) || [null, "0"])[1]);
23
+ const replaceFirstIndex = (nsp, insert = "0") => nsp.replace(/(\[\d+\])/, `[${insert}]`);
24
+ const replaceLastIndex = (nsp, insert = "0") => nsp.replace(/(\[\d+\])$/, `[${insert}]`);
25
+
26
+ const replaceAllIndices = (nsp, insert = "0") => nsp.replace(/(\[\d+\])/g, `[${insert}]`);
27
+
28
+ const getArrayNamespaces = (str) =>
29
+ str
30
+ .split(/(\w+(\[\d+\])+)/)
31
+ .filter(isNameAndArray)
32
+ .reduce((sum, nsp) => {
33
+ //split by indexes in case of nested arrays
34
+ const indices = nsp.split(/(\[\d+\])/).filter((n) => n);
35
+ const [name] = indices.splice(0, 1);
36
+ return sum.concat(
37
+ indices.map((index, i) => {
38
+ return name + indices.slice(0, i + 1).join("");
39
+ }, [])
40
+ );
41
+ }, []);
42
+ const switchArrayIndices = (nsp, replace) => {
43
+ // normalize nsp and replace nsp
44
+ const n = replaceAllIndices(nsp);
45
+ const r = replaceAllIndices(replace);
46
+ // match the normalized namespaces
47
+ if (n.substr(0, r.length) === r) {
48
+ // create new namespace by concatenating
49
+ const n = nsp.split(".");
50
+ const r = replace.split(".");
51
+ n.splice(...[0, r.length, ...r]);
52
+ return n.join(".");
53
+ } else return nsp;
54
+ };
55
+
56
+ //separate prop names from other prop names and indices (ie. 'test.results[0][0]'...);
57
+ const mapNamespace = (nsp) =>
58
+ nsp
59
+ .replace(/(?:\.|\[|\])/g, " ")
60
+ .split(" ")
61
+ .reduce((sum, str) => sum.concat(str.trim() || []), []);
62
+
63
+ const obj = function ObjectParser(obj) {
64
+ const parser = this || {};
65
+ parser.parse = (map) =>
66
+ map.reduce(([placeholder], key) => [placeholder?.[key], placeholder, key], [obj]);
67
+
68
+ parser.valueAt = (map) => parser.parse(map)[0];
69
+
70
+ parser.valueAtNsp = (nsp) => parser.valueAt(mapNamespace(nsp));
71
+
72
+ parser.parseNsp = (nsp) => parser.parse(mapNamespace(nsp));
73
+ //using JSON to create a deep copy in order to lose refs to original
74
+ parser.clone = () => JSON.parse(JSON.stringify(obj));
75
+
76
+ parser.isEmpty = () => Object.getOwnPropertyNames(obj).length === 0;
77
+
78
+ return parser;
79
+ };
80
+
81
+ const arr = function ArrayParser(arr) {
82
+ const parser = this || {};
83
+
84
+ parser.randomIndex = () => rnb(0, arr.length - 1);
85
+
86
+ parser.randomItem = () => arr[parser.randomIndex()];
87
+ return parser;
88
+ };
89
+
90
+ const isFn = /^\w+\(([^,)]*(,[^,)]*)*)\)$/;
91
+ const parseArgs = (str) => str.split(",").map((value) => value.trim());
92
+ const isFunction = (str) => isFn.test(str);
93
+ const isDateFunction = (str) => /^[dD]ate\(([^,)]*(,[^,)]*)*)\)$/.test(str);
94
+
95
+ const strFn = (str) => {
96
+ if (isDateFunction(str)) {
97
+ const [fullStr, args] = str.match(isFn);
98
+ return moment(args).toJSON();
99
+ }
100
+
101
+ return str;
102
+ };
103
+ function getType(value) {
104
+ switch (true) {
105
+ case typeof value === "object":
106
+ if (!value) return "null";
107
+ else if (Array.isArray(value)) return "array";
108
+ else return "object";
109
+ case typeof value === "string":
110
+ if (moment(value).isValid()) return "date";
111
+ else return "string";
112
+ case typeof value === "number":
113
+ return "number";
114
+ case typeof value === "boolean":
115
+ return "boolean";
116
+ case typeof value === "undefined":
117
+ return "undefined";
118
+ default:
119
+ return "?";
120
+ }
121
+ }
122
+ const isObjectLike = (value) =>
123
+ ["object", "array", "string"].indexOf(getType(value)) > -1;
124
+ module.exports = {
125
+ rnb,
126
+ isObjectLike,
127
+ isTargetNamespace,
128
+ targetValueFnRegex,
129
+ isTargetValueFn,
130
+ isEqualArrays,
131
+ isValidNamespace,
132
+ startsWithNameAndArray,
133
+ isNameAndArray,
134
+ endsWithArrayIndex,
135
+ getLastArrayNamespace,
136
+ parseIndex,
137
+ replaceFirstIndex,
138
+ replaceLastIndex,
139
+ replaceAllIndices,
140
+ getArrayNamespaces,
141
+ switchArrayIndices,
142
+ mapNamespace,
143
+ obj,
144
+ arr,
145
+ isFunction,
146
+ isDateFunction,
147
+ strFn,
148
+ getType,
149
+ };
@@ -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
+ };