@kumologica/sdk 3.3.0-beta9 → 3.4.0-beta2

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 (60) hide show
  1. package/cli/commands/open.js +6 -7
  2. package/cli/commands/test-commands/TestSuiteRunner.js +75 -0
  3. package/cli/commands/test-commands/lib/TestCaseRunner.js +105 -0
  4. package/cli/commands/test-commands/lib/index.js +12 -0
  5. package/cli/commands/test-commands/lib/reporters/index.js +120 -0
  6. package/cli/commands/test.js +92 -120
  7. package/package.json +4 -5
  8. package/src/app/lib/stores/store.js +15 -1
  9. package/src/app/lib/stores/user-preference-store.js +2 -0
  10. package/src/app/lib/stores/workspace-preference-store.js +42 -0
  11. package/src/app/main-process/editor-manager.js +11 -51
  12. package/src/app/main-process/main-window.js +10 -0
  13. package/src/app/main-process/menu.js +4 -2
  14. package/src/app/main-process/modal-home.js +5 -7
  15. package/src/app/main-process/modal-newproject.js +4 -6
  16. package/src/app/main-process/modal-newtab.js +4 -6
  17. package/src/app/main-process/modal-nodelibrary.js +4 -6
  18. package/src/app/main-process/modal-welcome.js +5 -8
  19. package/src/app/main.js +3 -1
  20. package/src/app/preload.js +28 -4
  21. package/src/app/ui/editor-api/lib/index.js +6 -9
  22. package/src/app/ui/editor-client/public/red/red.js +434 -176
  23. package/src/app/ui/editor-client/public/red/red.min.js +3 -3
  24. package/src/app/ui/editor-client/public/red/style.min.css +1 -1
  25. package/src/app/ui/editor-client/public/vendor/vendor.css +21 -1
  26. package/src/app/ui/editor-client/src/js/red.js +1 -1
  27. package/src/app/ui/editor-client/src/js/ui/clipboard.js +8 -0
  28. package/src/app/ui/editor-client/src/js/ui/header.js +2 -40
  29. package/src/app/ui/editor-client/src/js/ui/palette-explorer.js +328 -0
  30. package/src/app/ui/editor-client/src/js/ui/palette.js +10 -8
  31. package/src/app/ui/editor-client/src/js/ui/project-info.js +10 -8
  32. package/src/app/ui/editor-client/src/js/ui/search.js +147 -44
  33. package/src/app/ui/editor-client/src/js/ui/ui-settings.js +1 -1
  34. package/src/app/ui/editor-client/src/js/ui/view.js +2 -5
  35. package/src/app/ui/editor-client/src/js/validators.js +2 -2
  36. package/src/app/ui/editor-client/src/sass/dropdownMenu.scss +1 -1
  37. package/src/app/ui/editor-client/src/sass/editor.scss +1 -0
  38. package/src/app/ui/editor-client/src/sass/header.scss +16 -7
  39. package/src/app/ui/editor-client/src/sass/palette.scss +46 -5
  40. package/src/app/ui/editor-client/src/sass/project-info.scss +4 -3
  41. package/src/app/ui/editor-client/src/sass/search.scss +49 -21
  42. package/src/app/ui/editor-client/src/sass/style.scss +1 -0
  43. package/src/app/ui/editor-client/src/sass/ui/common/editableList.scss +3 -3
  44. package/src/app/ui/editor-client/src/sass/ui/common/searchBox.scss +1 -2
  45. package/src/app/ui/editor-client/src/sass/ui-settings.scss +5 -3
  46. package/src/app/ui/editor-client/src/vendor/jqtree/jqtree.css +21 -1
  47. package/src/app/ui/editor-client/templates/index.mst +89 -79
  48. package/src/server/DesignerServer.js +161 -0
  49. package/src/server/certificate.pem +23 -0
  50. package/src/server/private-key.pem +28 -0
  51. package/cli/commands/test-utils/TestSuiteController.js +0 -363
  52. package/cli/commands/test-utils/TestSuiteController.test.js +0 -171
  53. package/cli/commands/test-utils/util/output.js +0 -14
  54. package/cli/commands/test-utils/util/updates/index.js +0 -17
  55. package/cli/commands/test-utils/util/updates/pkg.js +0 -13
  56. package/cli/commands/test-utils/util/updates/templates/default-settings.js +0 -209
  57. package/src/app/ui/editor-client/src/js/ui/palette-navigator.js +0 -144
  58. /package/cli/commands/{test-utils → test-commands/lib}/fixtures/example3-flow.json +0 -0
  59. /package/cli/commands/{test-utils → test-commands/lib}/fixtures/package.json +0 -0
  60. /package/cli/commands/{test-utils → test-commands/lib}/fixtures/s3-event.js +0 -0
@@ -1,18 +1,17 @@
1
1
  const path = require('path');
2
- const { logError } = require('../utils/logger');
2
+ const { spawn } = require("child_process");
3
3
 
4
+ const { logError } = require('../utils/logger');
4
5
  const rootPath = path.join(__dirname, '..', '..',);
5
6
 
6
7
  function startElectron(projectDir) {
7
8
  console.debug(`Project directory: ${projectDir}`);
8
9
  console.debug(`Root directory: ${rootPath}`);
9
10
 
10
- const { spawn } = require("child_process");
11
- // TODO remove '..', '..' if not working from npm
12
- // const e = spawn(path.join(rootPath, '..', '..', "node_modules", ".bin", "electron"),
13
- const e = spawn(path.join(rootPath, "node_modules", ".bin", "electron"),
14
- ['.', projectDir],
15
- { cwd: rootPath, shell: true });
11
+ let electronPath = process.env["KUMOLOGICA_ELECTRON_PATH"] || path.join(rootPath, "node_modules", ".bin", "electron");
12
+ console.debug(`Electron path: ${electronPath}`);
13
+
14
+ const e = spawn( electronPath, ['.', projectDir], { cwd: rootPath, shell: true } );
16
15
 
17
16
  e.stdout.on("data", data => {
18
17
  console.log(data.toString().trim());
@@ -0,0 +1,75 @@
1
+ const { performance } = require('perf_hooks');
2
+ const { TestCaseRunner, InMemoryReporter, TerminalReporter } = require('./lib');
3
+ const { logError, logNotice, logInfo, logFatal } = require('../../utils/logger');
4
+
5
+
6
+ class TestSuiteRunner {
7
+ constructor(designerServer) {
8
+ this.designerServer = designerServer;
9
+ this.flowServer = this.designerServer.getFlowServer();
10
+ }
11
+ /**
12
+ *
13
+ * @param {*} testcases
14
+ * return an array containing found errors during execution, or undefined if successful
15
+ */
16
+ async runAll(testcases) {
17
+ logInfo(`TestCases - Executing\n`);
18
+ let testSuiteTimeStart = performance.now();
19
+
20
+ let totalPassedTestCases = 0;
21
+ let totalFailedCases = 0;
22
+ const totalTestCases = testcases.length;
23
+ for (let i = 0; i <= totalTestCases - 1; i++) {
24
+ let success = await this.runTestCase(testcases[i]);
25
+ if (success) {
26
+ totalPassedTestCases = totalPassedTestCases + 1;
27
+ } else {
28
+ totalFailedCases = totalFailedCases + 1;
29
+ }
30
+ }
31
+ let testSuiteTimeEnd = performance.now();
32
+ let testSuiteExecutionTimeInMs = testSuiteTimeEnd - testSuiteTimeStart;
33
+ this.printSummary(testSuiteExecutionTimeInMs, totalTestCases, totalFailedCases, totalPassedTestCases);
34
+ return 0;
35
+ }
36
+
37
+ /**
38
+ *
39
+ * @param {*} testcase
40
+ * @returns true if success, otherwise false
41
+ */
42
+ async runTestCase(testcase) {
43
+ let testcaseid = testcase.id;
44
+
45
+ const inMemReporter = new InMemoryReporter;
46
+ const terminalReporter = new TerminalReporter;
47
+
48
+ const testcaseRunner = new TestCaseRunner(this.flowServer, testcaseid, [inMemReporter, terminalReporter]);
49
+ try {
50
+ await testcaseRunner.runAsync();
51
+ return !inMemReporter.isFailedStatus()
52
+ } catch (err) {
53
+ console.log(err);
54
+ logFatal(`Unexpected error occurred while running testcase: "${testcase.name}" due to: ${err.message}.`)
55
+ }
56
+ }
57
+
58
+ printSummary(totalExecutionTimeInMs, totalTestCases, totalFailedCases, totalPassedTestCases) {
59
+ logInfo(`TestCases - Completed
60
+
61
+ Final Execution Statistics
62
+ -------------------------------
63
+ Execution Time: ${Math.floor(totalExecutionTimeInMs)} ms
64
+ Total Test Cases: ${totalTestCases}
65
+ Failed: ${totalFailedCases}
66
+ Passed: ${totalPassedTestCases}
67
+ -------------------------------
68
+ `);
69
+ }
70
+
71
+ }
72
+
73
+ module.exports = {
74
+ TestSuiteRunner
75
+ }
@@ -0,0 +1,105 @@
1
+ const got = require('got');
2
+ const { performance } = require('perf_hooks');
3
+ const { EventEmitter } = require('events').EventEmitter;
4
+
5
+ const { TerminalReporter, InMemoryReporter } = require('./reporters');
6
+
7
+ const TESTCASE_SIGNALS = {
8
+ TESTCASE_STOP: '__testrunner-stop__',
9
+ };
10
+
11
+ class TestCaseRunner {
12
+ constructor(flowServer, testCaseId, testReporters) {
13
+ this.flowServer = flowServer;
14
+ this.testCaseId = testCaseId;
15
+ this.testReporters = testReporters;
16
+
17
+ // Measuring the execution time
18
+ this.timeTestCaseStart = null;
19
+ this.timeTestCaseEnd = null;
20
+
21
+ // Managing runtime events
22
+ this.runtimeEventEmitter = this.flowServer.events;
23
+
24
+ // Signal the start/end of testcase execution
25
+ this.testCaseRunnerEmitter = new EventEmitter();
26
+
27
+ }
28
+
29
+ subscribeRuntimeEvents() {
30
+ let runtimeEventTypes = this.flowServer.eventTypes;
31
+
32
+ this.runtimeEventEmitter.on('runtime-event', async (data) => {
33
+ let p = data.payload;
34
+
35
+ // Attributes from p.testCaseResults
36
+ let testCaseID;
37
+ let testCaseDescription;
38
+ let target; // flow:*
39
+ let targetOutput; // { response: {}, error: {}},
40
+ let assertionResults; // [...]
41
+ let error;
42
+
43
+ switch (data.id) {
44
+ case runtimeEventTypes.TEST_TESTCASE_START:
45
+ this.timeTestCaseStart = performance.now();
46
+ ({ testCaseID, testCaseDescription } = p.testCaseResults);
47
+ this.testReporters.map(reporter => reporter.handleStart(testCaseID, testCaseDescription));
48
+ break;
49
+ case runtimeEventTypes.TEST_TESTCASE_END:
50
+ case runtimeEventTypes.TEST_TESTCASE_END_WITH_ERROR:
51
+ this.timeTestCaseEnd = performance.now();
52
+ ({ testCaseID, testCaseDescription, target, assertionResults, error, targetOutput } = p.testCaseResults); // targetOutput.response: { statusCode, headers, body } }
53
+ this.testReporters.map(reporter => reporter.handleResult(testCaseID, testCaseDescription, target, assertionResults, error, this.timeTestCaseStart, this.timeTestCaseEnd, targetOutput));
54
+ this.testCaseRunnerEmitter.emit(TESTCASE_SIGNALS.TESTCASE_STOP);
55
+ break;
56
+ default:
57
+ // do nothing
58
+ }
59
+
60
+ });
61
+
62
+ }
63
+
64
+ async runAsync() {
65
+ return new Promise(async (resolve, reject) => {
66
+ // Subscribe to the runtime events
67
+ this.subscribeRuntimeEvents();
68
+ // Wait for the end of the testcase execution
69
+ this.testCaseRunnerEmitter.on(TESTCASE_SIGNALS.TESTCASE_STOP, () => {
70
+ resolve();
71
+ });
72
+ try {
73
+ // Trigger the execution of the testcase
74
+ await this.invokeTest(this.testCaseId);
75
+ } catch (err) {
76
+ if (err && err.response && err.response.statusCode === 404 && err.response.body === '{"errorMessage":"Targeted node not found"}') {
77
+ reject(new Error('TestCaseID not found'));
78
+ } else {
79
+ console.log(`Error found in TestRunner due to`, err.toString());
80
+ resolve();
81
+ }
82
+ }
83
+
84
+ })
85
+
86
+ }
87
+
88
+ async invokeTest(testCaseId) {
89
+ let resp = await got({
90
+ url: `http://127.0.0.1:${this.flowServer.settings.port}`,
91
+ method: 'POST',
92
+ headers: { 'x-kumologica-testcasenode': `${testCaseId}` },
93
+ });
94
+ return resp;
95
+ }
96
+ }
97
+
98
+
99
+
100
+ module.exports = {
101
+ TestCaseRunner,
102
+ // Reporters
103
+ TerminalReporter,
104
+ InMemoryReporter
105
+ }
@@ -0,0 +1,12 @@
1
+ const {
2
+ TestCaseRunner,
3
+ // Reporters
4
+ TerminalReporter,
5
+ InMemoryReporter
6
+ } = require('./TestCaseRunner');
7
+
8
+ module.exports = {
9
+ TestCaseRunner,
10
+ TerminalReporter,
11
+ InMemoryReporter
12
+ }
@@ -0,0 +1,120 @@
1
+ const chalk = require('chalk');
2
+ const log = console.log;
3
+
4
+ const STATUS_PASSED = 'passed';
5
+ const STATUS_FAILED = 'failed';
6
+
7
+ class TestReporter {
8
+ handleStart(testCaseID, testCaseDescription) { /* TO BE IMPLEMENTED */ }
9
+ handleResult(testCaseID, testCaseDescription, target, assertionResults, error, timeStart, timeEnd, targetOutput) { /* TO BE IMPLEMENTED */ }
10
+ }
11
+
12
+ class InMemoryReporter extends TestReporter {
13
+ handleStart(testCaseID, testCaseDescription) {
14
+ this.testCaseID = testCaseID;
15
+ this.testCaseDescription = testCaseDescription;
16
+ }
17
+ handleResult(testCaseID, testCaseDescription, target, assertionResults, error, timeStart, timeEnd, targetOutput) {
18
+ this.assertionResults = assertionResults.map(({ status, description, diffReported }) => ({
19
+ status,
20
+ description,
21
+ diffReported
22
+ }));
23
+ this.executionTimeInMs = timeEnd - timeStart;
24
+ this.flowResponse = targetOutput.response;
25
+ }
26
+ isFailedStatus() {
27
+ let failedAssertions = this.assertionResults.filter(ar => ar.status === STATUS_FAILED);
28
+ return failedAssertions.length >= 1;
29
+ }
30
+ }
31
+
32
+ class TerminalReporter extends TestReporter {
33
+ handleStart(testCaseID, testCaseDescription) {
34
+ //console.log(`TestCase started: ${testCaseID} - ${testCaseDescription}`);
35
+ }
36
+ handleResult(testCaseID, testCaseDescription, target, assertionResults, error, timeStart, timeEnd, targetOutput) {
37
+ // Print header
38
+ this.printTestCaseHeader(testCaseDescription, target);
39
+
40
+ // Print assertions
41
+ this.printTestAssertionResult(assertionResults || []);
42
+
43
+ // Print status
44
+ if (!error) {
45
+ this.printTestCaseStatus(assertionResults);
46
+ } else {
47
+ this.printTestCaseWithError(testCaseDescription, target, error);
48
+ }
49
+
50
+ }
51
+ printTestCaseHeader(description, target) {
52
+ let prettyName = `${description}`; // mvp: only full flow being targeted > ${target}
53
+ const sep = '=';
54
+ let underline = sep.repeat(prettyName.length);
55
+ let headerLine = `- ${prettyName}\n ${underline}`;
56
+ log(chalk.cyan(headerLine));
57
+ log(` Results:`);
58
+ }
59
+ printTestAssertionResult(assertionResults) {
60
+ // Print Assertions
61
+ if (assertionResults.length === 0) {
62
+ log(chalk.dim(` (No assertions found)`));
63
+ } else {
64
+ assertionResults.map((ar, index) => {
65
+ this.printTestAssertion(ar, index);
66
+ });
67
+ }
68
+ }
69
+ printTestAssertion(ar, index) {
70
+ // console.log('Assertion:', ar)
71
+ let { description, selector, property, comparison, valueExpected, diffReported } = ar;
72
+ let status = diffReported ? STATUS_FAILED : STATUS_PASSED;
73
+ let icon;
74
+ if (status === STATUS_FAILED) {
75
+ icon = chalk.red('\u2716'); // cross mark
76
+ } else {
77
+ icon = chalk.green(`\u2713`); // tick
78
+ }
79
+ // Description
80
+ let descriptionName;
81
+ if (description && description.length > 0) {
82
+ descriptionName = description;
83
+ } else {
84
+ descriptionName = `${selector} - ${property} - ${comparison} - ${valueExpected}`;
85
+ }
86
+ let prettyName = ` ${index}. ${icon} ${descriptionName}`;
87
+ if (diffReported) {
88
+ prettyName = `${prettyName}
89
+ Diff report:\n${chalk.red(JSON.stringify(diffReported, null, 2))}`;
90
+ }
91
+ log(prettyName);
92
+ }
93
+
94
+ printTestCaseStatus(assertionResults) {
95
+ // See if any assertion has failed
96
+ let failedAssertions = assertionResults.filter(ar => ar.status === STATUS_FAILED);
97
+ let failedTestCase = failedAssertions.length >= 1 ? true : false;
98
+ // Print it nicely
99
+ let coloredStatus;
100
+ if (failedTestCase) {
101
+ coloredStatus = chalk.bgRed.white(` Failed `);
102
+ } else {
103
+ coloredStatus = chalk.bgGreen.white(` Passed `);
104
+ }
105
+ log(` Status: ${coloredStatus}\n`);
106
+ }
107
+
108
+ printTestCaseWithError(testCaseDescription, target, error) {
109
+ // Print header
110
+ this.printTestCaseHeader(testCaseDescription, target);
111
+ log(` ERROR: ${error}`);
112
+ log(` Status: ${chalk.bgRed.white(` Failed `)}\n`);
113
+ }
114
+
115
+ }
116
+
117
+ module.exports = {
118
+ TerminalReporter,
119
+ InMemoryReporter
120
+ }
@@ -1,151 +1,123 @@
1
- const chalk = require('chalk');
2
- const { Select } = require('enquirer');
3
- const fs = require('fs');
4
1
  const path = require('path');
2
+ const { Select } = require('enquirer');
5
3
  const wcmatch = require('wildcard-match');
6
-
7
- const { AppServer } = require('@kumologica/runtime');
4
+ const { util } = require('@kumologica/runtime');
8
5
  const { codegen } = require('@kumologica/builder');
9
6
 
10
- const { TestSuiteController } = require('./test-utils/TestSuiteController');
11
- const { logError } = require('../utils/logger');
12
-
13
- const log = console.log;
14
- const APP_SERVER_PORT = 1990;
15
-
16
- async function runTest(flowFilePath, testcaseSelected, iterative) {
17
- log(`\n> Starting runtime on port ${APP_SERVER_PORT}...`);
18
-
19
- let appServer = new AppServer({
20
- projectDir: path.dirname(flowFilePath),
21
- flowFilePath: flowFilePath,
22
- port: APP_SERVER_PORT,
23
- serverMode: AppServer.mode.HEADLESS_KUMOLOGICA,
24
- logLevel: AppServer.logLevel.ERROR,
25
- editorApi: undefined,
26
- startupEmitter: undefined
27
- });
28
-
29
- try {
30
- await appServer.start();
31
- log(`> FlowFile to be tested: ${chalk.bold(path.resolve(flowFilePath))} \n`);
32
- let testSuiteController = new TestSuiteController(appServer);
33
-
34
- // If testcase is null, default to universal wildcard
35
- testcaseSelected = testcaseSelected || "**";
36
-
37
- // Find out all testcases available on the flow
38
- let testcasesAvailable = testSuiteController.findTestCasesFromFlow(flowFilePath);
39
- if (!testcasesAvailable || (testcasesAvailable && testcasesAvailable.length === 0)) {
40
- logError(`No testcases found on flow file: ${flowFileAbsPath}`);
41
- process.exit(1);
42
- };
43
- let testcaseAvailableNames = testcasesAvailable.map(tc=>tc.name);
44
-
45
- // Capture the testcase from user on iterative mode
46
- if (iterative) {
47
- const prompt = new Select({
48
- name: 'testcase',
49
- message: 'What testcase do you want to run?',
50
- choices: [...testcaseAvailableNames, 'Run all TestCases...']
51
- });
52
- await prompt.run()
53
- .then(tc => {
54
- if (tc === 'Run all TestCases...'){
55
- testcaseSelected = "**";
56
- }else {
57
- testcaseSelected = tc;
58
- }
59
- })
60
- .catch(err => {
61
- logError(`Error found while running tests on iterative mode due to: `, err.message);
62
- process.exit(1);
63
- });
64
- }
7
+ const { TestSuiteRunner } = require('./test-commands/TestSuiteRunner');
8
+ const { DesignerServer } = require('../../src/server/DesignerServer');
9
+ const { logError, logNotice, logInfo, logFatal } = require('../utils/logger');
65
10
 
66
- // Filter all testcases to be part of the test suite
67
- const isMatch = wcmatch(testcaseSelected);
68
- let testCasesSelected = [];
69
- testcasesAvailable.forEach(async tc => {
70
- if (isMatch(tc.name)){
71
- testCasesSelected.push({ name: tc.name, id: tc.id });
72
- }
73
- });
11
+ const isDirectory = util.isDirectorySync;
74
12
 
75
- // Execute the testcasesIds if any, otherwise throw an error
76
- if (testCasesSelected.length === 0){
77
- logError(`No matched testcases found`);
78
- process.exit(1);
79
- } else {
80
- const errors = await testSuiteController.runTestSuite(testCasesSelected);
81
- process.exit(errors > 0);
82
- }
83
-
84
- } catch (err) {
85
- log(
86
- chalk.red(
87
- `Unexpected error occurred while starting server due to <${err.message}>`
88
- )
89
- );
90
- console.log(err);
91
- process.exit(1);
92
- }
93
- }
94
-
95
- function isDirectory(dir) {
96
- try{
97
- let stats = fs.statSync(dir);
98
- return stats.isDirectory();
99
- } catch (err) {
100
- return undefined;
101
- }
102
-
103
-
104
- }
105
13
 
106
14
  exports.command = 'test [project_directory]';
107
15
  exports.desc = `Run test suite`;
108
16
  exports.builder = (yargs) => {
109
17
  yargs.positional(`project_directory`, {
110
- type: 'string',
111
- describe: 'Path to a valid kumologica project directory or flow file. (Optional)'
18
+ type: 'string',
19
+ describe: 'Path to a valid kumologica project directory or flow file. (Optional)'
112
20
  })
113
21
  yargs.option(`testcase`, {
114
22
  describe: "Testcase name to run",
115
23
  type: 'string',
116
24
  alias: 't',
117
25
  nargs: 1
118
- });
119
- yargs.option(`iterative`, {
120
- describe: "Manually select the testcase to run from all available testcases",
121
- type: 'boolean',
122
- alias: 'i'
123
- });
26
+ });
27
+ yargs.option(`iterative`, {
28
+ describe: "Manually select the testcase to run from all available testcases",
29
+ type: 'boolean',
30
+ alias: 'i'
31
+ });
124
32
  }
125
33
  exports.handler = ({ project_directory, testcase, iterative }) => {
126
34
  let projectDirOrFile = project_directory || process.cwd();
127
35
  let projectFlowPath = projectDirOrFile;
128
36
 
129
37
  let isDir = isDirectory(projectDirOrFile);
130
- if (isDir){
38
+ if (isDir) {
131
39
  let flowFileName = codegen.findFlowFile(projectDirOrFile); // returns only the flowname
132
- if (!flowFileName){
133
- logError(`No flow found in directory: ${projectDirOrFile}`);
134
- process.exit(1);
135
- }else {
40
+ if (!flowFileName) {
41
+ logFatal(`No flow found in directory: ${projectDirOrFile}`);
42
+ } else {
136
43
  projectFlowPath = path.join(projectDirOrFile, flowFileName);
137
44
  }
138
- } else if (isDir === false){
45
+ } else if (isDir === false) {
139
46
  // do nothing as it was assumed to be a file
140
47
  } else {
141
- logError(`Directory does not exist: ${project_directory}`);
142
- process.exit(1);
48
+ logFatal(`Directory does not exist: ${project_directory}`);
143
49
  }
144
50
 
145
- try {
146
- runTest(projectFlowPath, testcase, iterative);
147
- } catch (e) {
148
- logError(e.message);
149
- process.exit(1);
150
- }
51
+ try {
52
+ runTestOnNewServer(projectFlowPath, testcase, iterative);
53
+ } catch (e) {
54
+ logFatal(e.message);
55
+ }
56
+ }
57
+
58
+ async function runTestOnNewServer(flowFilePath, testcaseSelected, iterative) {
59
+ let designerServer = new DesignerServer(
60
+ flowFilePath,
61
+ false,
62
+ {
63
+ loglevel: "error",
64
+ noadmin: true
65
+ });
66
+
67
+ try {
68
+ await designerServer.listen();
69
+ logInfo(`> Flow file: ${path.resolve(flowFilePath)} \n`);
70
+ let testSuiteRunner = new TestSuiteRunner(designerServer);
71
+
72
+ // If testcase is null, default to universal wildcard
73
+ testcaseSelected = testcaseSelected || "**";
74
+
75
+ // Find out all testcases available on the flow
76
+ let testcasesAvailable = codegen.findTestCasesFromFlow(flowFilePath);
77
+ if (!testcasesAvailable || (testcasesAvailable && testcasesAvailable.length === 0)) {
78
+ logFatal(`No testcases found on flow file: ${flowFileAbsPath}`);
79
+ };
80
+ let testcaseAvailableNames = testcasesAvailable.map(tc => tc.name);
81
+
82
+ // Capture the testcase from user on iterative mode
83
+ if (iterative) {
84
+ const prompt = new Select({
85
+ name: 'testcase',
86
+ message: 'What testcase do you want to run?',
87
+ choices: [...testcaseAvailableNames, 'Run all TestCases...']
88
+ });
89
+ await prompt.run()
90
+ .then(tc => {
91
+ if (tc === 'Run all TestCases...') {
92
+ testcaseSelected = "**";
93
+ } else {
94
+ testcaseSelected = tc;
95
+ }
96
+ })
97
+ .catch(err => {
98
+ logFatal(`Error found while running tests on iterative mode due to: `, err.message);
99
+ });
100
+ }
101
+
102
+ // Filter all testcases to be part of the test suite
103
+ const isMatch = wcmatch(testcaseSelected);
104
+ let testCasesSelected = [];
105
+ testcasesAvailable.forEach(async tc => {
106
+ if (isMatch(tc.name)) {
107
+ testCasesSelected.push({ name: tc.name, id: tc.id });
108
+ }
109
+ });
110
+
111
+ // Execute the testcasesIds if any, otherwise throw an error
112
+ if (testCasesSelected.length === 0) {
113
+ logFatal(`No matched testcases found`);
114
+ } else {
115
+ const errors = await testSuiteRunner.runAll(testCasesSelected);
116
+ process.exit(errors > 0);
117
+ }
118
+
119
+ } catch (err) {
120
+ console.log(err);
121
+ logFatal(`Unexpected error occurred while starting server due to <${err.message}>`);
122
+ }
151
123
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "productName": "Kumologica Designer",
4
4
  "copyright": "Copyright 2020 Kumologica Pty Ltd, All Rights Reserved.",
5
5
  "author": "Kumologica Pty Ltd <contact@kumologica.com>",
6
- "version": "3.3.0-beta9",
6
+ "version": "3.4.0-beta2",
7
7
  "description": "Kumologica Designer, harnessing Serverless for your cloud integration needs",
8
8
  "main": "src/app/main.js",
9
9
  "files": [
@@ -64,9 +64,9 @@
64
64
  "license": "Proprietary",
65
65
  "dependencies": {
66
66
  "@electron/remote": "^2.0.8",
67
- "@kumologica/builder": "3.3.0-beta9",
68
- "@kumologica/devkit": "3.3.0-beta9",
69
- "@kumologica/runtime": "3.3.0-beta9",
67
+ "@kumologica/builder": "3.4.0-beta2",
68
+ "@kumologica/devkit": "3.4.0-beta2",
69
+ "@kumologica/runtime": "3.4.0-beta2",
70
70
  "adm-zip": "0.4.13",
71
71
  "ajv": "8.10.0",
72
72
  "archive-type": "^4.0.0",
@@ -124,7 +124,6 @@
124
124
  "simplemde": "1.11.2",
125
125
  "smoketail": "0.2.2",
126
126
  "static-eval": "2.0.2",
127
- "tcp-port-used": "1.0.2",
128
127
  "url": "^0.11.0",
129
128
  "util": "0.12.1",
130
129
  "whatwg-url": "^11.0.0",
@@ -44,11 +44,13 @@ class Store {
44
44
  // ...and this will set it
45
45
  set(key, val) {
46
46
  this.data[key] = val;
47
+ let value = JSON.stringify(this.data);
48
+
47
49
  // Wait, I thought using the node.js' synchronous APIs was bad form?
48
50
  // We're not writing a server so there's not nearly the same IO demand on the process
49
51
  // Also if we used an async API and our app was quit before the asynchronous write had a chance to complete,
50
52
  // we might lose that data. Note that in a real app, we would try/catch this.
51
- fs.outputFileSync(this.path, JSON.stringify(this.data, null, 2));
53
+ fs.outputFileSync(this.path, value);
52
54
  }
53
55
 
54
56
  trim(key, size) {}
@@ -78,6 +80,18 @@ class Store {
78
80
  this.set(key, values);
79
81
  }
80
82
  }
83
+ remove(key, val) {
84
+ let values = this.get(key) || [];
85
+ if (!Array.isArray(values)) {
86
+ throw new Error(
87
+ `Key: ${key} be an array. File: ${this.configName} may be corrupted`
88
+ );
89
+ }
90
+ if (values.indexOf(val) >= 0) {
91
+ values.splice(values.indexOf(val), 1);
92
+ }
93
+ this.set(key, values);
94
+ }
81
95
  }
82
96
 
83
97
  function parseDataFile(filePath, defaults) {
@@ -4,6 +4,7 @@ const defaultKeys = {
4
4
  recentProjects: 'recentProjects',
5
5
  untitledProject: 'untitledProject'
6
6
  };
7
+
7
8
  const MAX_NUM_RECENT_PROJECTS = 10;
8
9
 
9
10
  const Store = require('./store');
@@ -25,6 +26,7 @@ class UserPreferenceStore extends Store {
25
26
  MAX_NUM_RECENT_PROJECTS
26
27
  );
27
28
  }
29
+
28
30
  getRecentProjects() {
29
31
  return super.get(defaultKeys.recentProjects);
30
32
  }