systemview 1.19.0 → 1.20.0
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/api/Connections.js +5 -0
- package/api/connections.json +1 -1
- package/api/index.js +16 -0
- package/build/asset-manifest.json +6 -8
- package/build/index.html +1 -1
- package/build/static/css/{main.2c749bba.css → main.3396e24d.css} +3 -3
- package/build/static/css/main.3396e24d.css.map +1 -0
- package/build/static/js/main.c3485d98.js +3 -0
- package/build/static/js/{main.e94b392a.js.LICENSE.txt → main.c3485d98.js.LICENSE.txt} +48 -26
- package/build/static/js/main.c3485d98.js.map +1 -0
- package/cli/index.js +51 -3
- package/cli/listTests.js +162 -0
- package/cli/openBrowser.js +22 -8
- package/cli/runTests.js +189 -36
- package/cli/utils/cli.js +37 -3
- package/cli/utils/resolveNamespace.js +16 -0
- package/cli/utils/truncate.js +31 -0
- package/package.json +1 -1
- package/build/static/css/main.2c749bba.css.map +0 -1
- package/build/static/js/422.3649b867.chunk.js +0 -2
- package/build/static/js/422.3649b867.chunk.js.map +0 -1
- package/build/static/js/main.e94b392a.js +0 -3
- package/build/static/js/main.e94b392a.js.map +0 -1
package/cli/index.js
CHANGED
|
@@ -2,18 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
4
4
|
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
5
7
|
const { input, flags, showHelp } = require("./utils/cli");
|
|
6
8
|
const init = require("./utils/init");
|
|
7
9
|
const log = require("./logger");
|
|
8
10
|
const launchApp = require("./launchApp");
|
|
9
11
|
const runTests = require("./runTests");
|
|
12
|
+
const listTests = require("./listTests");
|
|
10
13
|
const appIsRunning = require("./appIsRunning");
|
|
11
14
|
const openBrowser = require("./openBrowser");
|
|
12
15
|
const connectService = require("./connectService");
|
|
13
16
|
const probe = require("./probe");
|
|
14
17
|
const { HttpClient } = require("systemlynx");
|
|
18
|
+
const { createCookieClient } = require("./cookieClient");
|
|
15
19
|
|
|
16
20
|
const DEFAULT_PORT = 3000;
|
|
21
|
+
const VERSION = require("../package.json").version;
|
|
17
22
|
|
|
18
23
|
async function startApp() {
|
|
19
24
|
const port = isNaN(input[1]) ? DEFAULT_PORT : Number(input[1]);
|
|
@@ -35,6 +40,11 @@ async function startTest() {
|
|
|
35
40
|
verbose: flags.verbose,
|
|
36
41
|
manifest: flags.manifest,
|
|
37
42
|
headers: flags.headers,
|
|
43
|
+
bail: flags.bail,
|
|
44
|
+
dryRun: flags.dryRun,
|
|
45
|
+
phase: flags.phase,
|
|
46
|
+
index: flags.index,
|
|
47
|
+
skip: flags.skip,
|
|
38
48
|
});
|
|
39
49
|
process.exit(exitCode);
|
|
40
50
|
} catch (error) {
|
|
@@ -43,6 +53,18 @@ async function startTest() {
|
|
|
43
53
|
}
|
|
44
54
|
}
|
|
45
55
|
|
|
56
|
+
async function list() {
|
|
57
|
+
const project_code = input[1];
|
|
58
|
+
const namespace = input[2];
|
|
59
|
+
const url = `http://localhost:${DEFAULT_PORT}`;
|
|
60
|
+
const api = `${url}/systemview/api`;
|
|
61
|
+
if (!(await appIsRunning(api))) {
|
|
62
|
+
await launchApp(DEFAULT_PORT);
|
|
63
|
+
}
|
|
64
|
+
await listTests(url, project_code, namespace, { manifest: flags.manifest, verbose: flags.verbose });
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
|
|
46
68
|
async function open() {
|
|
47
69
|
const project_code = input[1];
|
|
48
70
|
const namespace = input[2];
|
|
@@ -51,7 +73,24 @@ async function open() {
|
|
|
51
73
|
if (!(await appIsRunning(api))) {
|
|
52
74
|
await launchApp(DEFAULT_PORT);
|
|
53
75
|
}
|
|
54
|
-
|
|
76
|
+
|
|
77
|
+
let connectedServices = [];
|
|
78
|
+
if (namespace && project_code) {
|
|
79
|
+
const manifestFile = flags.manifest || path.join(process.cwd(), "systemview.manifest.json");
|
|
80
|
+
if (fs.existsSync(manifestFile)) {
|
|
81
|
+
try {
|
|
82
|
+
const raw = JSON.parse(fs.readFileSync(manifestFile, "utf8"));
|
|
83
|
+
connectedServices = raw.services ? raw.services : [raw];
|
|
84
|
+
} catch {}
|
|
85
|
+
} else {
|
|
86
|
+
try {
|
|
87
|
+
const { SystemView } = await createCookieClient().loadService(api);
|
|
88
|
+
connectedServices = (await SystemView.getServices(project_code)) || [];
|
|
89
|
+
} catch {}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
openBrowser(ui, project_code, namespace, connectedServices);
|
|
55
94
|
process.exit(0);
|
|
56
95
|
}
|
|
57
96
|
|
|
@@ -85,8 +124,13 @@ async function quitApp() {
|
|
|
85
124
|
init();
|
|
86
125
|
const command = input[0];
|
|
87
126
|
|
|
88
|
-
if (
|
|
127
|
+
if (process.argv.includes("--version")) {
|
|
128
|
+
console.log(VERSION);
|
|
129
|
+
process.exit(0);
|
|
130
|
+
} else if (command === "open") {
|
|
89
131
|
await open();
|
|
132
|
+
} else if (command === "list") {
|
|
133
|
+
await list();
|
|
90
134
|
} else if (command === "help" || input.includes("help")) {
|
|
91
135
|
showHelp();
|
|
92
136
|
} else if (command === "test") {
|
|
@@ -97,7 +141,11 @@ async function quitApp() {
|
|
|
97
141
|
await connectService(input[1], input[2], { manifest: flags.manifest });
|
|
98
142
|
process.exit(0);
|
|
99
143
|
} else if (command === "probe") {
|
|
100
|
-
const exitCode = await probe(input[1], input[2], {
|
|
144
|
+
const exitCode = await probe(input[1], input[2], {
|
|
145
|
+
json: flags.json,
|
|
146
|
+
manifest: flags.manifest,
|
|
147
|
+
headers: flags.headers,
|
|
148
|
+
});
|
|
101
149
|
process.exit(exitCode || 0);
|
|
102
150
|
} else {
|
|
103
151
|
await startApp();
|
package/cli/listTests.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { createCookieClient } = require("./cookieClient");
|
|
4
|
+
const log = require("./logger");
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
|
|
7
|
+
module.exports = async function listTests(url, project_code, namespace, { manifest: manifestPath, verbose = false } = {}) {
|
|
8
|
+
const api = `${url}/systemview/api`;
|
|
9
|
+
const Client = createCookieClient();
|
|
10
|
+
|
|
11
|
+
if (!project_code) {
|
|
12
|
+
await listAllProjects(api, Client, verbose);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const connectedServices = await loadServices(api, project_code, manifestPath, Client);
|
|
17
|
+
if (!connectedServices.length) {
|
|
18
|
+
log.warn("No connected services found for project: " + project_code);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log("");
|
|
23
|
+
console.log(` ${chalk.bold(project_code)}`);
|
|
24
|
+
|
|
25
|
+
for (let si = 0; si < connectedServices.length; si++) {
|
|
26
|
+
const { serviceId, system } = connectedServices[si];
|
|
27
|
+
const isLastService = si === connectedServices.length - 1;
|
|
28
|
+
const servicePrefix = isLastService ? " └── " : " ├── ";
|
|
29
|
+
const childPad = isLastService ? " " : " │ ";
|
|
30
|
+
|
|
31
|
+
let testList = [];
|
|
32
|
+
try {
|
|
33
|
+
const svc = createCookieClient().createService(system.connectionData);
|
|
34
|
+
testList = await svc.Plugin.getTests();
|
|
35
|
+
} catch {
|
|
36
|
+
console.log(`${servicePrefix}${chalk.dim(serviceId)} ${chalk.dim("(could not load tests)")}`);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const filtered = namespace
|
|
41
|
+
? testList.filter(({ namespace: n }) =>
|
|
42
|
+
`${n.serviceId}.${n.moduleName}.${n.methodName}`.includes(namespace)
|
|
43
|
+
)
|
|
44
|
+
: testList;
|
|
45
|
+
|
|
46
|
+
if (!filtered.length) {
|
|
47
|
+
console.log(`${servicePrefix}${chalk.dim(serviceId)} ${chalk.dim("(no matching tests)")}`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const tree = {};
|
|
52
|
+
filtered.forEach(({ namespace: n, title }) => {
|
|
53
|
+
if (!tree[n.moduleName]) tree[n.moduleName] = {};
|
|
54
|
+
if (verbose) {
|
|
55
|
+
if (!tree[n.moduleName][n.methodName]) tree[n.moduleName][n.methodName] = [];
|
|
56
|
+
tree[n.moduleName][n.methodName].push(title);
|
|
57
|
+
} else {
|
|
58
|
+
tree[n.moduleName][n.methodName] = (tree[n.moduleName][n.methodName] || 0) + 1;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
console.log(`${servicePrefix}${chalk.bold(serviceId)}`);
|
|
63
|
+
|
|
64
|
+
const modules = Object.keys(tree);
|
|
65
|
+
modules.forEach((moduleName, mi) => {
|
|
66
|
+
const isLastModule = mi === modules.length - 1;
|
|
67
|
+
const modPrefix = childPad + (isLastModule ? "└── " : "├── ");
|
|
68
|
+
const modPad = childPad + (isLastModule ? " " : "│ ");
|
|
69
|
+
console.log(`${modPrefix}${moduleName}`);
|
|
70
|
+
|
|
71
|
+
const methods = Object.keys(tree[moduleName]);
|
|
72
|
+
methods.forEach((methodName, mei) => {
|
|
73
|
+
const isLastMethod = mei === methods.length - 1;
|
|
74
|
+
const methodPrefix = modPad + (isLastMethod ? "└── " : "├── ");
|
|
75
|
+
const methodPad = modPad + (isLastMethod ? " " : "│ ");
|
|
76
|
+
if (verbose) {
|
|
77
|
+
const titles = tree[moduleName][methodName];
|
|
78
|
+
console.log(`${methodPrefix}${chalk.cyan(methodName)}`);
|
|
79
|
+
titles.forEach((title, ti) => {
|
|
80
|
+
const isLastTitle = ti === titles.length - 1;
|
|
81
|
+
const titlePrefix = methodPad + (isLastTitle ? "└── " : "├── ");
|
|
82
|
+
console.log(`${titlePrefix}${chalk.dim(title)}`);
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
const count = tree[moduleName][methodName];
|
|
86
|
+
const countStr = chalk.dim(`${count} ${count === 1 ? "test" : "tests"}`);
|
|
87
|
+
console.log(`${methodPrefix}${chalk.cyan(methodName)} ${countStr}`);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log("");
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
async function listAllProjects(api, Client, verbose = false) {
|
|
97
|
+
let projects;
|
|
98
|
+
try {
|
|
99
|
+
const { SystemView } = await Client.loadService(api);
|
|
100
|
+
projects = await SystemView.getProjects();
|
|
101
|
+
} catch {
|
|
102
|
+
log.error("Could not connect to SystemView. Is it running?");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const codes = Object.keys(projects);
|
|
107
|
+
if (!codes.length) {
|
|
108
|
+
log.warn("No connected projects found.");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const total = codes.reduce((n, c) => n + projects[c].length, 0);
|
|
113
|
+
console.log("");
|
|
114
|
+
console.log(chalk.dim(` ${codes.length} ${codes.length === 1 ? "project" : "projects"}, ${total} ${total === 1 ? "service" : "services"}`));
|
|
115
|
+
console.log("");
|
|
116
|
+
codes.forEach((projectCode, pi) => {
|
|
117
|
+
console.log(` ${chalk.bold(projectCode)}`);
|
|
118
|
+
const services = projects[projectCode];
|
|
119
|
+
services.forEach(({ serviceId, serviceUrl, connectionData }, si) => {
|
|
120
|
+
const isLastService = si === services.length - 1;
|
|
121
|
+
const servicePrefix = isLastService ? " └── " : " ├── ";
|
|
122
|
+
const childPad = isLastService ? " " : " │ ";
|
|
123
|
+
|
|
124
|
+
if (verbose && connectionData && connectionData.modules) {
|
|
125
|
+
console.log(`${servicePrefix}${chalk.bold(serviceId)} ${chalk.dim(serviceUrl)}`);
|
|
126
|
+
const mods = connectionData.modules.filter((m) => m.name !== "Plugin");
|
|
127
|
+
mods.forEach((mod, mi) => {
|
|
128
|
+
const isLastMod = mi === mods.length - 1;
|
|
129
|
+
const modPrefix = childPad + (isLastMod ? "└── " : "├── ");
|
|
130
|
+
const modPad = childPad + (isLastMod ? " " : "│ ");
|
|
131
|
+
console.log(`${modPrefix}${mod.name}`);
|
|
132
|
+
const methods = mod.methods || [];
|
|
133
|
+
methods.forEach((m, mei) => {
|
|
134
|
+
const isLastMethod = mei === methods.length - 1;
|
|
135
|
+
const methodPrefix = modPad + (isLastMethod ? "└── " : "├── ");
|
|
136
|
+
console.log(`${methodPrefix}${chalk.cyan(m.fn)}`);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
console.log(`${servicePrefix}${serviceId} ${chalk.dim(serviceUrl)}`);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
if (pi < codes.length - 1) console.log("");
|
|
144
|
+
});
|
|
145
|
+
console.log("");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function loadServices(api, project_code, manifestPath, Client) {
|
|
149
|
+
const manifestFile = manifestPath || path.join(process.cwd(), "systemview.manifest.json");
|
|
150
|
+
if (fs.existsSync(manifestFile)) {
|
|
151
|
+
try {
|
|
152
|
+
const raw = JSON.parse(fs.readFileSync(manifestFile, "utf8"));
|
|
153
|
+
return raw.services ? raw.services : [raw];
|
|
154
|
+
} catch {}
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const { SystemView } = await Client.loadService(api);
|
|
158
|
+
return (await SystemView.getServices(project_code)) || [];
|
|
159
|
+
} catch {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
}
|
package/cli/openBrowser.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { exec } = require("child_process");
|
|
2
|
+
const resolveNamespace = require("./utils/resolveNamespace");
|
|
2
3
|
|
|
3
|
-
module.exports = function openBrowser(url, project_code, namespace) {
|
|
4
|
+
module.exports = function openBrowser(url, project_code, namespace, connectedServices) {
|
|
4
5
|
if (!url || typeof url !== "string" || url.trim() === "") {
|
|
5
6
|
console.error("Invalid URL provided");
|
|
6
7
|
return;
|
|
@@ -8,15 +9,28 @@ module.exports = function openBrowser(url, project_code, namespace) {
|
|
|
8
9
|
|
|
9
10
|
const browserCommand = process.platform === "win32" ? "start" : "open";
|
|
10
11
|
const code = project_code ? "/" + project_code : "";
|
|
12
|
+
|
|
13
|
+
if (namespace && connectedServices && connectedServices.length) {
|
|
14
|
+
const matches = resolveNamespace(namespace, connectedServices);
|
|
15
|
+
if (matches.length) {
|
|
16
|
+
matches.forEach(({ serviceId, moduleName, methodName }) => {
|
|
17
|
+
const nsp = `/${serviceId}/${moduleName}/${methodName}`;
|
|
18
|
+
const fullUrl = `${url}${code}${nsp}`;
|
|
19
|
+
console.log(`opening... ${fullUrl}`);
|
|
20
|
+
exec(`${browserCommand} ${fullUrl}`, (error, stdout, stderr) => {
|
|
21
|
+
if (error) console.error(`Failed to open browser: ${error.message}`);
|
|
22
|
+
else if (stderr) console.error(`Failed to open browser: ${stderr}`);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
11
29
|
const nsp = code && namespace ? "/" + namespace.replace(/\./g, "/") : "";
|
|
12
30
|
console.log(`opening... ${url}${code}${nsp}`);
|
|
13
31
|
exec(`${browserCommand} ${url}${code}${nsp}`, (error, stdout, stderr) => {
|
|
14
|
-
if (error) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
console.error(`Failed to open browser: ${stderr}`);
|
|
18
|
-
} else {
|
|
19
|
-
console.log("Browser opened successfully!");
|
|
20
|
-
}
|
|
32
|
+
if (error) console.error(`Failed to open browser: ${error.message}`);
|
|
33
|
+
else if (stderr) console.error(`Failed to open browser: ${stderr}`);
|
|
34
|
+
else console.log("Browser opened successfully!");
|
|
21
35
|
});
|
|
22
36
|
};
|
package/cli/runTests.js
CHANGED
|
@@ -5,12 +5,55 @@ const { initializeSavedTests } = require("../testing-utilities/transformTests");
|
|
|
5
5
|
const FullTestController = require("../testing-utilities/FullTestController");
|
|
6
6
|
const log = require("./logger");
|
|
7
7
|
const validationMessages = require("../testing-utilities/validtionMessages");
|
|
8
|
+
const { truncate, TruncationMarker } = require("./utils/truncate");
|
|
9
|
+
|
|
10
|
+
function formatTruncated(value, indent = 0) {
|
|
11
|
+
const pad = " ".repeat(indent);
|
|
12
|
+
const inner = " ".repeat(indent + 1);
|
|
13
|
+
if (value instanceof TruncationMarker) return chalk.dim(value.message);
|
|
14
|
+
if (value === null) return "null";
|
|
15
|
+
if (typeof value !== "object") return JSON.stringify(value);
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
if (!value.length) return "[]";
|
|
18
|
+
const items = value.map((v) =>
|
|
19
|
+
v instanceof TruncationMarker
|
|
20
|
+
? inner + chalk.dim(v.message)
|
|
21
|
+
: inner + formatTruncated(v, indent + 1)
|
|
22
|
+
);
|
|
23
|
+
return "[\n" + items.join(",\n") + "\n" + pad + "]";
|
|
24
|
+
}
|
|
25
|
+
const keys = Object.keys(value);
|
|
26
|
+
if (!keys.length) return "{}";
|
|
27
|
+
const entries = keys.map((k) => {
|
|
28
|
+
const v = value[k];
|
|
29
|
+
if (k === "__more__" && v instanceof TruncationMarker)
|
|
30
|
+
return inner + chalk.dim(v.message);
|
|
31
|
+
return inner + JSON.stringify(k) + ": " + formatTruncated(v, indent + 1);
|
|
32
|
+
});
|
|
33
|
+
return "{\n" + entries.join(",\n") + "\n" + pad + "}";
|
|
34
|
+
}
|
|
8
35
|
const { runFullTest } = new FullTestController();
|
|
9
36
|
|
|
10
37
|
const chalk = require("chalk");
|
|
11
38
|
const DIVIDER = chalk.dim(" " + "─".repeat(60));
|
|
39
|
+
const SUITE_DIVIDER = chalk.cyan(" " + "─".repeat(60));
|
|
12
40
|
|
|
13
|
-
module.exports = async function runTests(
|
|
41
|
+
module.exports = async function runTests(
|
|
42
|
+
url,
|
|
43
|
+
project_code,
|
|
44
|
+
namespace,
|
|
45
|
+
{
|
|
46
|
+
json = false,
|
|
47
|
+
verbose = false,
|
|
48
|
+
manifest: manifestPath,
|
|
49
|
+
headers: cliHeaders = {},
|
|
50
|
+
bail = false,
|
|
51
|
+
dryRun = false,
|
|
52
|
+
phase: phaseFilter = null,
|
|
53
|
+
index: indexFilter = undefined,
|
|
54
|
+
skip: skipPatterns = [],
|
|
55
|
+
} = {}
|
|
56
|
+
) {
|
|
14
57
|
const api = `${url}/systemview/api`;
|
|
15
58
|
|
|
16
59
|
if (!project_code) {
|
|
@@ -18,7 +61,11 @@ module.exports = async function runTests(url, project_code, namespace, { json =
|
|
|
18
61
|
return 1;
|
|
19
62
|
}
|
|
20
63
|
|
|
21
|
-
const { services: connectedServices, probeHeaders: manifestHeaders } = await resolveServices(
|
|
64
|
+
const { services: connectedServices, probeHeaders: manifestHeaders } = await resolveServices(
|
|
65
|
+
api,
|
|
66
|
+
project_code,
|
|
67
|
+
manifestPath
|
|
68
|
+
);
|
|
22
69
|
if (!connectedServices || !connectedServices.length) {
|
|
23
70
|
log.warn("No connected services found for project: " + project_code);
|
|
24
71
|
return 1;
|
|
@@ -36,11 +83,12 @@ module.exports = async function runTests(url, project_code, namespace, { json =
|
|
|
36
83
|
const allTestLists = await getTests(connectedServices, Client);
|
|
37
84
|
const testToRun = allTestLists
|
|
38
85
|
.map((list) =>
|
|
39
|
-
namespace
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
86
|
+
list.filter(({ namespace: n }) => {
|
|
87
|
+
const full = `${n.serviceId}.${n.moduleName}.${n.methodName}`;
|
|
88
|
+
const matchesInclude = !namespace || full.includes(namespace);
|
|
89
|
+
const matchesSkip = skipPatterns.length && skipPatterns.some((p) => full.includes(p));
|
|
90
|
+
return matchesInclude && !matchesSkip;
|
|
91
|
+
})
|
|
44
92
|
)
|
|
45
93
|
.filter((list) => list.length);
|
|
46
94
|
|
|
@@ -49,8 +97,24 @@ module.exports = async function runTests(url, project_code, namespace, { json =
|
|
|
49
97
|
return 0;
|
|
50
98
|
}
|
|
51
99
|
|
|
100
|
+
if (dryRun) {
|
|
101
|
+
const total = testToRun.reduce((n, list) => n + list.length, 0);
|
|
102
|
+
console.log("");
|
|
103
|
+
console.log(` Would run ${total} ${total === 1 ? "test" : "tests"}:`);
|
|
104
|
+
console.log("");
|
|
105
|
+
testToRun.forEach((list) => {
|
|
106
|
+
list.forEach(({ namespace: n, title }) => {
|
|
107
|
+
console.log(` ${chalk.cyan(`${n.serviceId}.${n.moduleName}.${n.methodName}`)} — "${title}"`);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
console.log("");
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
52
114
|
const jsonOutput = json ? { projectCode: project_code, passed: 0, failed: 0, tests: [] } : null;
|
|
53
115
|
let totalFailed = 0;
|
|
116
|
+
const serviceSummaries = [];
|
|
117
|
+
const allFailures = [];
|
|
54
118
|
|
|
55
119
|
for (const testList of testToRun) {
|
|
56
120
|
const { serviceId } = testList[0].namespace;
|
|
@@ -58,7 +122,14 @@ module.exports = async function runTests(url, project_code, namespace, { json =
|
|
|
58
122
|
|
|
59
123
|
try {
|
|
60
124
|
const initialized = initializeSavedTests(testList, connectedServices, Client);
|
|
61
|
-
const { passed, failed, jsonTests } = await runAllTests(
|
|
125
|
+
const { passed, failed, jsonTests, failures } = await runAllTests(
|
|
126
|
+
initialized,
|
|
127
|
+
url,
|
|
128
|
+
project_code,
|
|
129
|
+
json,
|
|
130
|
+
verbose,
|
|
131
|
+
{ bail, phaseFilter, indexFilter }
|
|
132
|
+
);
|
|
62
133
|
totalFailed += failed;
|
|
63
134
|
|
|
64
135
|
if (json) {
|
|
@@ -66,10 +137,11 @@ module.exports = async function runTests(url, project_code, namespace, { json =
|
|
|
66
137
|
jsonOutput.failed += failed;
|
|
67
138
|
jsonOutput.tests.push(...jsonTests);
|
|
68
139
|
} else {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
);
|
|
140
|
+
serviceSummaries.push({ serviceId, passed, failed });
|
|
141
|
+
allFailures.push(...failures);
|
|
72
142
|
}
|
|
143
|
+
|
|
144
|
+
if (bail && failed > 0) break;
|
|
73
145
|
} catch (error) {
|
|
74
146
|
log.error(`Unexpected error for ${serviceId}: ${error.message}`);
|
|
75
147
|
}
|
|
@@ -81,22 +153,67 @@ module.exports = async function runTests(url, project_code, namespace, { json =
|
|
|
81
153
|
console.log("");
|
|
82
154
|
log.info("TEST COMPLETE");
|
|
83
155
|
console.log("");
|
|
156
|
+
|
|
157
|
+
serviceSummaries.forEach(({ serviceId, passed, failed }) => {
|
|
158
|
+
const total = passed + failed;
|
|
159
|
+
log[failed ? "error" : "success"](
|
|
160
|
+
`${serviceId}: ${total} tests, ${passed} passed, ${failed} failed`
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (allFailures.length) {
|
|
165
|
+
console.log("");
|
|
166
|
+
log.warn("FAILED:");
|
|
167
|
+
allFailures.forEach(({ serviceId, moduleName, methodName, title, failedPhases }) => {
|
|
168
|
+
console.log("");
|
|
169
|
+
log.error(` ${serviceId}.${moduleName}.${methodName} — "${title}"`);
|
|
170
|
+
failedPhases.forEach(({ phase, actions }) => {
|
|
171
|
+
actions.forEach(({ actionTitle, errors }) => {
|
|
172
|
+
console.log(` ${phase}: "${actionTitle}"`);
|
|
173
|
+
errors.forEach((err) => console.log(` → ${validationMessages(err)}`));
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
console.log("");
|
|
178
|
+
}
|
|
84
179
|
}
|
|
85
180
|
|
|
86
181
|
return totalFailed > 0 ? 1 : 0;
|
|
87
182
|
};
|
|
88
183
|
|
|
89
|
-
const runAllTests = async (savedTests, url, project_code, json, verbose) => {
|
|
90
|
-
const
|
|
184
|
+
const runAllTests = async (savedTests, url, project_code, json, verbose, opts = {}) => {
|
|
185
|
+
const { bail = false, phaseFilter = null, indexFilter = undefined } = opts;
|
|
186
|
+
const sum = { passed: 0, failed: 0, jsonTests: [], failures: [] };
|
|
187
|
+
|
|
188
|
+
for (const { Before: B, Main: M, Events: E, After: A, title, namespace } of savedTests) {
|
|
189
|
+
let Before = [...B];
|
|
190
|
+
let Main = [...M];
|
|
191
|
+
let Events = [...E];
|
|
192
|
+
let After = [...A];
|
|
193
|
+
|
|
194
|
+
if (phaseFilter) {
|
|
195
|
+
const allowed = phaseFilter.split(",").map((p) => p.trim().toLowerCase());
|
|
196
|
+
if (!allowed.includes("before")) Before = [];
|
|
197
|
+
if (!allowed.includes("main")) Main = [];
|
|
198
|
+
if (!allowed.includes("events")) Events = [];
|
|
199
|
+
if (!allowed.includes("after")) After = [];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (indexFilter !== undefined) {
|
|
203
|
+
Before = Before.slice(indexFilter, indexFilter + 1);
|
|
204
|
+
Main = Main.slice(indexFilter, indexFilter + 1);
|
|
205
|
+
Events = Events.slice(indexFilter, indexFilter + 1);
|
|
206
|
+
After = After.slice(indexFilter, indexFilter + 1);
|
|
207
|
+
}
|
|
91
208
|
|
|
92
|
-
for (const { Before, Main, Events, After, title, namespace } of savedTests) {
|
|
93
|
-
const { moduleName, methodName, serviceId } = namespace;
|
|
94
209
|
const fullTest = [Before, Main, Events, After];
|
|
210
|
+
const { moduleName, methodName, serviceId } = namespace;
|
|
95
211
|
|
|
96
212
|
if (!json) {
|
|
97
|
-
console.log(
|
|
98
|
-
console.log(" " + chalk.bold.cyan(
|
|
99
|
-
console.log(
|
|
213
|
+
console.log(SUITE_DIVIDER);
|
|
214
|
+
console.log(" " + chalk.bold("Test: ") + chalk.bold.cyan(title));
|
|
215
|
+
console.log(SUITE_DIVIDER);
|
|
216
|
+
console.log(chalk.dim(` ${serviceId}.${moduleName}.${methodName} → ${url}/${project_code}/${serviceId}/${moduleName}/${methodName}`));
|
|
100
217
|
console.log("");
|
|
101
218
|
}
|
|
102
219
|
|
|
@@ -105,8 +222,24 @@ const runAllTests = async (savedTests, url, project_code, json, verbose) => {
|
|
|
105
222
|
const hasFailed =
|
|
106
223
|
countErrors(Before) + countErrors(Main) + countErrors(Events) + countErrors(After) > 0;
|
|
107
224
|
|
|
108
|
-
if (hasFailed)
|
|
109
|
-
|
|
225
|
+
if (hasFailed) {
|
|
226
|
+
sum.failed++;
|
|
227
|
+
const failedPhases = [];
|
|
228
|
+
[
|
|
229
|
+
{ phase: "Before", tests: Before },
|
|
230
|
+
{ phase: "Main", tests: Main },
|
|
231
|
+
{ phase: "Events", tests: Events },
|
|
232
|
+
{ phase: "After", tests: After },
|
|
233
|
+
].forEach(({ phase, tests }) => {
|
|
234
|
+
const actions = tests
|
|
235
|
+
.filter((t) => t.errors && t.errors.length)
|
|
236
|
+
.map((t) => ({ actionTitle: t.title, errors: t.errors }));
|
|
237
|
+
if (actions.length) failedPhases.push({ phase, actions });
|
|
238
|
+
});
|
|
239
|
+
sum.failures.push({ serviceId, moduleName, methodName, title, failedPhases });
|
|
240
|
+
} else {
|
|
241
|
+
sum.passed++;
|
|
242
|
+
}
|
|
110
243
|
|
|
111
244
|
if (json) {
|
|
112
245
|
sum.jsonTests.push({
|
|
@@ -121,47 +254,65 @@ const runAllTests = async (savedTests, url, project_code, json, verbose) => {
|
|
|
121
254
|
After: serializePhase(After),
|
|
122
255
|
});
|
|
123
256
|
} else {
|
|
124
|
-
|
|
125
|
-
logPhase(
|
|
126
|
-
logPhase(
|
|
127
|
-
logPhase(
|
|
257
|
+
const compact = !verbose;
|
|
258
|
+
logPhase(Before, "Before", verbose, compact, false);
|
|
259
|
+
logPhase(Main, "Main", verbose, false, true);
|
|
260
|
+
logPhase(Events, "Events", verbose, compact, false);
|
|
261
|
+
logPhase(After, "After", verbose, compact, false);
|
|
128
262
|
}
|
|
263
|
+
|
|
264
|
+
if (bail && hasFailed) break;
|
|
129
265
|
}
|
|
130
266
|
|
|
131
267
|
return sum;
|
|
132
268
|
};
|
|
133
269
|
|
|
134
|
-
function
|
|
135
|
-
return tests.reduce((n, t) => n + (t.errors ? t.errors.length : 0), 0);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function logPhase(tests, section, logSuccess = false, verbose = false) {
|
|
270
|
+
function logPhase(tests, section, verbose = false, compact = false, isMain = false) {
|
|
139
271
|
tests.forEach(({ errors, results, namespace, title, args }) => {
|
|
140
272
|
const { serviceId, moduleName, methodName } = namespace;
|
|
141
273
|
const failed = errors && errors.length;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
274
|
+
|
|
275
|
+
const label = isMain ? chalk.bold.green(section) : chalk.blue(section);
|
|
276
|
+
console.log(` ${label}: ${chalk.dim(title)}`);
|
|
277
|
+
if (failed) {
|
|
278
|
+
console.log(" " + chalk.bold.red(`${serviceId}.${moduleName}.${methodName}(...)`));
|
|
279
|
+
} else if (isMain) {
|
|
280
|
+
console.log(" " + chalk.bold.green(`${serviceId}.${moduleName}.${methodName}(...)`));
|
|
281
|
+
} else {
|
|
282
|
+
log.success(` ${serviceId}.${moduleName}.${methodName}(...)`);
|
|
283
|
+
}
|
|
284
|
+
|
|
147
285
|
if (verbose && args && args.length) {
|
|
148
286
|
console.log("");
|
|
149
287
|
args.forEach(({ name, input }) => {
|
|
150
288
|
console.log(` ${chalk.dim(name)}`, JSON.stringify(input));
|
|
151
289
|
});
|
|
152
290
|
}
|
|
291
|
+
|
|
153
292
|
console.log("");
|
|
154
|
-
|
|
293
|
+
if (compact) {
|
|
294
|
+
console.log(" results:", JSON.stringify(truncate(results)));
|
|
295
|
+
} else if (verbose) {
|
|
296
|
+
console.log(" results:", JSON.stringify(results, null, 2).replace(/\n/g, "\n "));
|
|
297
|
+
} else {
|
|
298
|
+
console.log(" results:", formatTruncated(truncate(results)).replace(/\n/g, "\n "));
|
|
299
|
+
}
|
|
155
300
|
console.log("");
|
|
301
|
+
|
|
156
302
|
if (failed) {
|
|
157
303
|
log.warn(` ${errors.length} validation error(s)`);
|
|
158
304
|
errors.forEach((err) => console.log(` → ${validationMessages(err)}`));
|
|
159
305
|
console.log("");
|
|
160
306
|
}
|
|
307
|
+
|
|
161
308
|
console.log(DIVIDER);
|
|
162
309
|
});
|
|
163
310
|
}
|
|
164
311
|
|
|
312
|
+
function countErrors(tests) {
|
|
313
|
+
return tests.reduce((n, t) => n + (t.errors ? t.errors.length : 0), 0);
|
|
314
|
+
}
|
|
315
|
+
|
|
165
316
|
function serializePhase(tests) {
|
|
166
317
|
return tests.map((test) => {
|
|
167
318
|
const { title, namespace, args, results, errors, evaluations } = test;
|
|
@@ -214,7 +365,9 @@ async function resolveServices(api, project_code, manifestPath) {
|
|
|
214
365
|
: [{ serviceId: raw.serviceId, system: raw.system, specList: raw.specList }];
|
|
215
366
|
|
|
216
367
|
if (project_code && raw.projectCode && raw.projectCode !== project_code) {
|
|
217
|
-
log.warn(
|
|
368
|
+
log.warn(
|
|
369
|
+
`Manifest projectCode "${raw.projectCode}" doesn't match "${project_code}", falling back to API`
|
|
370
|
+
);
|
|
218
371
|
} else {
|
|
219
372
|
return { services, probeHeaders: raw.probeHeaders || {} };
|
|
220
373
|
}
|