@suffolklitlab/alkiln 5.13.2 → 5.13.3-feat-random-2
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/.github/workflows/github_server.yml +10 -8
- package/.github/workflows/playground.yml +5 -2
- package/.github/workflows/unit_tests.yml +1 -1
- package/CHANGELOG.md +32 -1
- package/_alkiln_meta/shift_logs.js +75 -0
- package/action.yml +89 -31
- package/action_for_github_server/action.yml +65 -29
- package/docs/4_environments.md +243 -0
- package/docs/artifacts_folder_name.md +135 -0
- package/docs/constrained_random_tests.md +254 -0
- package/docs/decisions/006_which_log_level_to_pick_2024_09_5.md +70 -0
- package/docs/decisions/007_getting_and_storing_logs.md +179 -0
- package/docs/decisions/move_runtime_config_to_sources_folder.md +5 -0
- package/docs/decisions/reports_and_logs_2024_08_11.md +161 -0
- package/docs/kinds_of_errors.md +26 -0
- package/docs/prioritize_constrained_random_tests.md +60 -0
- package/lib/docassemble/docassemble_api_interface.js +77 -51
- package/lib/docassemble/server_install.js +32 -9
- package/lib/docassemble/setup.js +40 -25
- package/lib/docassemble/takedown.js +33 -10
- package/lib/globals.js +10 -0
- package/lib/run_cucumber.js +141 -69
- package/lib/scope.js +101 -77
- package/lib/setup/TestGenerator.js +1755 -0
- package/lib/setup/artifacts.js +20 -0
- package/lib/setup/generate.js +42 -0
- package/lib/setup/get_Gherkin_AST.js +26 -0
- package/lib/setup/validate.js +89 -0
- package/lib/steps.js +132 -54
- package/lib/utils/Log.js +1145 -0
- package/lib/utils/_alkiln-constrained_random_tests_old.js +1776 -0
- package/lib/utils/files.js +18 -11
- package/lib/utils/get_error_stacks.js +16 -0
- package/lib/utils/reports.js +100 -34
- package/lib/utils/session_vars.js +107 -73
- package/lib/utils/set_sources_paths.js +98 -0
- package/lib/utils/time.js +44 -0
- package/package.json +18 -7
- package/tests/log_codes/check_codes.sh +18 -13
- package/tests/log_codes/log_code_expected_instances.txt +91 -9
- package/tests/unit_tests/constrained_random_answers/invalid.fixtures.js +177 -0
- package/tests/unit_tests/constrained_random_answers/invalid.test.js +251 -0
- package/tests/unit_tests/constrained_random_answers/valid.fixtures.js +936 -0
- package/tests/unit_tests/constrained_random_answers/valid.test.js +541 -0
- package/tests/unit_tests/example_of_chai_test_lifecycle.js +78 -0
- package/tests/unit_tests/getAllFields.test.js +5 -0
- package/tests/unit_tests/getMatchingRows.test.js +5 -0
- package/tests/unit_tests/log.test.js +557 -0
- package/tests/unit_tests/normalizeTable.test.js +5 -0
- package/lib/utils/log.js +0 -96
- /package/docs/{expected_inernal_results.md → expected_internal_results.md} +0 -0
package/lib/utils/files.js
CHANGED
|
@@ -21,20 +21,27 @@ files.readable_date = function() {
|
|
|
21
21
|
return fancy_str;
|
|
22
22
|
}; // Ends files.readable_date()
|
|
23
23
|
|
|
24
|
-
files.make_artifacts_folder = function (
|
|
25
|
-
/** Makes a folder in the root of the project
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
24
|
+
files.make_artifacts_folder = function (path) {
|
|
25
|
+
/** Makes a folder in the root of the project if it doesn't
|
|
26
|
+
* already exist, either with the given name or with a default name.
|
|
27
|
+
*
|
|
28
|
+
* Works for local development as well, without action.yml or workflow files.
|
|
29
|
+
* */
|
|
30
|
+
if (path) {
|
|
31
|
+
let name_parts = path.split(`/`);
|
|
32
|
+
let safe_parts = [];
|
|
33
|
+
for ( let part of name_parts ) {
|
|
34
|
+
safe_parts.push(safe_filename( part ));
|
|
35
|
+
}
|
|
36
|
+
path = safe_parts.join(`/`);
|
|
32
37
|
} else {
|
|
33
|
-
|
|
38
|
+
path = `alkiln-${ files.readable_date() }`
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
// `recursive` avoids errors if folder already exists
|
|
42
|
+
// https://nodejs.org/docs/latest-v18.x/api/fs.html#fspromisesmkdirpath-options
|
|
43
|
+
fs.mkdirSync( path, { recursive: true });
|
|
44
|
+
return path;
|
|
38
45
|
}; // Ends files.make_artifacts_folder()
|
|
39
46
|
|
|
40
47
|
module.exports = files;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module.exports = function get_error_stacks({ errors }) {
|
|
2
|
+
/** Given a list of Error instances, return a list of their stacks
|
|
3
|
+
*
|
|
4
|
+
* Discuss: Move this into `Log`. Potentially print each stack with the
|
|
5
|
+
* same icon as the message. `{to_stack: [Errors]}.
|
|
6
|
+
*
|
|
7
|
+
* @param {object} obj - Named arguments
|
|
8
|
+
* @param {[Error]} obj.errors - A list of Error instances
|
|
9
|
+
*
|
|
10
|
+
* @returns {[Error.stack]} - A list of Error instances' `.stack` properties
|
|
11
|
+
* */
|
|
12
|
+
let stacks = errors.map(function( error ) {
|
|
13
|
+
return error.stack;
|
|
14
|
+
});
|
|
15
|
+
return stacks;
|
|
16
|
+
};
|
package/lib/utils/reports.js
CHANGED
|
@@ -26,14 +26,14 @@ reports.addReportHeading = function (scope, { scenario }) {
|
|
|
26
26
|
tag_names.push( tag.name );
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
let heading = `\n
|
|
29
|
+
let heading = `\n━━━━━━━━━━━━━━━\n`
|
|
30
30
|
+ `Scenario: ${ scenario.pickle.name }`;
|
|
31
31
|
|
|
32
32
|
if ( tag_names.length > 0 ) {
|
|
33
33
|
heading += `\nTags: ${ tag_names.join(' ') }`;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
heading += `\n
|
|
36
|
+
heading += `\n━━━━━━━━━━━━━━━`;
|
|
37
37
|
|
|
38
38
|
reports.addToReport( scope, {
|
|
39
39
|
code: `ALK0176`,
|
|
@@ -57,7 +57,7 @@ reports.addToReport = function( scope, { type, code, value }) {
|
|
|
57
57
|
// thrown errors if needed.
|
|
58
58
|
|
|
59
59
|
// Debugging logs
|
|
60
|
-
let debug_line = getDebugLine({ type, code, value });
|
|
60
|
+
let debug_line = getDebugLine({ type, code, value, indent: global_indent });
|
|
61
61
|
if ( session_vars.get_debug() ) {
|
|
62
62
|
console.log( `Report: ${ debug_line }` );
|
|
63
63
|
}
|
|
@@ -66,24 +66,43 @@ reports.addToReport = function( scope, { type, code, value }) {
|
|
|
66
66
|
const MISC_ARTIFACTS_DIR = `_alkiln-misc_artifacts`;
|
|
67
67
|
if ( !scope.paths ) {
|
|
68
68
|
// Can this be usefully abstracted?
|
|
69
|
-
|
|
69
|
+
let date = files.readable_date();
|
|
70
|
+
scope.paths = {
|
|
71
|
+
debug_log: `${ MISC_ARTIFACTS_DIR }/unit_tests_debug_log-${ date }.txt`,
|
|
72
|
+
report_log: `${ MISC_ARTIFACTS_DIR }/unit_tests_report_log-${ date }.txt`,
|
|
73
|
+
};
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
try {
|
|
73
77
|
fs.appendFileSync( scope.paths.debug_log, `\nReport:\n${debug_line}` );
|
|
74
|
-
|
|
78
|
+
fs.appendFileSync( scope.paths.report_log, `\nReport:\n${debug_line}` );
|
|
79
|
+
} catch ( file_error ) {
|
|
75
80
|
fs.mkdirSync( MISC_ARTIFACTS_DIR );
|
|
76
81
|
fs.appendFileSync( scope.paths.debug_log, `\nReport:\n${debug_line}` );
|
|
82
|
+
fs.appendFileSync( scope.paths.report_log, `\nReport:\n${debug_line}` );
|
|
77
83
|
// An error in here should throw (for now)
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
// Report stuff
|
|
81
87
|
let scenario = reports.makeSureScenarioExists( scope );
|
|
82
|
-
scenario.lines.push({ type, code, value });
|
|
88
|
+
scenario.lines.push({ type, code, value, indent: global_indent });
|
|
83
89
|
|
|
84
|
-
return getLineText({ type, code, value });
|
|
90
|
+
return getLineText({ type, code, value, indent: global_indent });
|
|
85
91
|
}; // Ends reports.addToReport()
|
|
86
92
|
|
|
93
|
+
|
|
94
|
+
let indent_num = 0;
|
|
95
|
+
let global_indent = ``;
|
|
96
|
+
reports.indent = function() {
|
|
97
|
+
indent_num += 2;
|
|
98
|
+
global_indent = ` `.repeat( indent_num );
|
|
99
|
+
}
|
|
100
|
+
reports.outdent = function() {
|
|
101
|
+
indent_num = Math.max( 0, indent_num - 2 );
|
|
102
|
+
global_indent = ` `.repeat( indent_num );
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
87
106
|
reports.create = function( scope ) {
|
|
88
107
|
reports.makeSureReportExists( scope );
|
|
89
108
|
}
|
|
@@ -115,25 +134,66 @@ reports.saveFinalScenarioData = function( scope, { status=`in progress` }) {
|
|
|
115
134
|
this.setReportScenarioStatus( scope, { status });
|
|
116
135
|
let scenario = scope.report.get( scope.scenario_id );
|
|
117
136
|
let report = this.getPrintableScenario( scenario );
|
|
118
|
-
if ( scenario.status === `
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
else { this.others.push( report ); }
|
|
123
|
-
|
|
124
|
-
// Save to failures.txt (worry about optimization later)
|
|
125
|
-
if ( this.failures.length > 1 || this.others.length > 1 ) {
|
|
126
|
-
fs.writeFileSync(
|
|
127
|
-
scope.paths.failures,
|
|
128
|
-
this.title + this.getPrintableUnexpected( scope )
|
|
129
|
-
);
|
|
137
|
+
if ( scenario.status === `PASSED` ) {
|
|
138
|
+
this.successes.push( report );
|
|
139
|
+
} else {
|
|
140
|
+
this._save_unexpected( scope, { status: scenario.status, report });
|
|
130
141
|
}
|
|
142
|
+
// if ( scenario.status === `FAILED` ) {
|
|
143
|
+
// this.failures.push( report );
|
|
144
|
+
// }
|
|
145
|
+
// else if ( scenario.status === `PASSED` ) { this.successes.push( report ); }
|
|
146
|
+
// // else if ( scenario.status === `WARNING` ) { passed_reports += report; this.warnings.push( report ); }
|
|
147
|
+
// // else if ( scenario.status === `UNDEFINED` ) { passed_reports += report; this.undefineds.push( report ); }
|
|
148
|
+
// else { this.others.push( report ); }
|
|
149
|
+
|
|
150
|
+
// // Save to unexpected results files (worry about optimization later)
|
|
151
|
+
// if ( this.failures.length > 1 || this.others.length > 1 ) {
|
|
152
|
+
// let unexpected_results = this.title + this.getPrintableUnexpected( scope );
|
|
153
|
+
// fs.writeFileSync( scope.paths.report_of_unexpected, unexpected_results );
|
|
154
|
+
// fs.appendFileSync( scope.paths.debug_of_unexpected, unexpected_results );
|
|
155
|
+
// }
|
|
156
|
+
|
|
131
157
|
// Save to reports.txt (worry about optimization later)
|
|
132
158
|
fs.writeFileSync( scope.paths.report, this.getPrintableReport( scope ) );
|
|
133
159
|
|
|
134
160
|
return scenario;
|
|
135
161
|
}; // Ends reports.saveFinalScenarioData()
|
|
136
162
|
|
|
163
|
+
reports._save_unexpected = function(scope, { status, report }) {
|
|
164
|
+
/** Store the unexpected report and write to the unexpected report file.
|
|
165
|
+
*
|
|
166
|
+
* @param {Object} scope - State and methods of the current tests
|
|
167
|
+
* @param {Object} data
|
|
168
|
+
* @param {Object} data.status - Status of the current scenario
|
|
169
|
+
* @param {string} data.report - Report for the current scenario
|
|
170
|
+
* */
|
|
171
|
+
if ( status === `FAILED` ) {
|
|
172
|
+
this.failures.push( report );
|
|
173
|
+
// } else if ( status === `WARNING` ) { passed_reports += report; this.warnings.push( report );
|
|
174
|
+
// } else if ( status === `UNDEFINED` ) { passed_reports += report; this.undefineds.push( report ); }
|
|
175
|
+
} else {
|
|
176
|
+
this.others.push( report );
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// If it's our first unexpected result, add the title to the top of the page
|
|
180
|
+
let already_unexpected = ``;
|
|
181
|
+
try { already_unexpected = fs.readFileSync( scope.paths.report_of_unexpected ); }
|
|
182
|
+
catch ( ignore_non_critical_error ) {}
|
|
183
|
+
if ( already_unexpected === `` ) {
|
|
184
|
+
try { fs.appendFileSync( scope.paths.report_of_unexpected, this.title ); }
|
|
185
|
+
catch ( ignore_non_critical_error ) {}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
try { fs.appendFileSync( scope.paths.report_of_unexpected, report ); }
|
|
189
|
+
catch ( ignore_non_critical_error ) {}
|
|
190
|
+
|
|
191
|
+
return this;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
reports.progress = function ({ logs=[] }) {
|
|
195
|
+
// TODO: save stdout-style to report_log, object, and getting printable scenario
|
|
196
|
+
};
|
|
137
197
|
|
|
138
198
|
reports.setReportScenarioStatus = function( scope, { status=`PASSED` }) {
|
|
139
199
|
/** Add final status of scenario to scenario report object - passed, failed, etc. */
|
|
@@ -197,47 +257,53 @@ reports.getPrintableScenario = function( scenario ) {
|
|
|
197
257
|
/** Return a string generated from scenario list of report items. */
|
|
198
258
|
let report = ``;
|
|
199
259
|
for ( let line of scenario.lines ) {
|
|
200
|
-
let { type, code, value } = line;
|
|
201
|
-
report += `${ getLineText({ type, code, value }) }\n`;
|
|
260
|
+
let { type, code, value, indent } = line;
|
|
261
|
+
report += `${ getLineText({ type, code, value, indent }) }\n`;
|
|
202
262
|
}
|
|
203
263
|
return report;
|
|
204
264
|
}; // Ends reports.getPrintableScenario()
|
|
205
265
|
|
|
206
|
-
let getLineText = function ({ type=`no type`, code=`
|
|
207
|
-
|
|
266
|
+
let getLineText = function ({ type=`no type`, code=`ALK00rl`, value=``, indent=`` }) {
|
|
267
|
+
|
|
268
|
+
value = `${ value.replace( /\n/g, "\n" + indent ) }`;
|
|
269
|
+
|
|
270
|
+
if ( type === `as_is` ) { return `${ indent }${ value }`; }
|
|
271
|
+
|
|
272
|
+
let start = indent;
|
|
273
|
+
if ( type.includes(`outcome`) ) { start = `\n`; }
|
|
274
|
+
|
|
208
275
|
if ( type.includes(`page_id`) ) { start += `screen id: `; }
|
|
209
|
-
else if ( type.includes(`outcome`) ) { start += `\n`; } // extra new line
|
|
210
276
|
else if ( type.includes(`warning`) ) { start += `🔎 ${ code } WARNING: `; }
|
|
211
277
|
else if ( type.includes(`error`) ) { start += `\n🤕 ${ code } ERROR: `; } // extra new line
|
|
212
|
-
|
|
278
|
+
let result = `${ start }${ value }`;
|
|
279
|
+
return result;
|
|
213
280
|
}; // Ends getLine()
|
|
214
281
|
|
|
215
|
-
let getDebugLine = function ({ type=``, code=`
|
|
282
|
+
let getDebugLine = function ({ type=``, code=`ALK0278`, value=``, indent=`` }) {
|
|
216
283
|
/** In debug log lines, include all the information possible. */
|
|
217
284
|
|
|
218
285
|
// `plain` is usually used for visual clarity and shouldn't have
|
|
219
286
|
// codes and such associated with it. Return right away.
|
|
220
|
-
if ( type.includes(`plain`) ) { return value
|
|
287
|
+
if ( type.includes(`plain`) ) { return `${ indent }${ value }`; }
|
|
221
288
|
|
|
222
|
-
let start =
|
|
289
|
+
let start = indent;
|
|
223
290
|
|
|
224
291
|
// Get all the relevant emoji
|
|
225
|
-
if ( type.includes(`debug`) ) { start +=
|
|
292
|
+
if ( type.includes(`debug`) ) { start += `🐛`; }
|
|
226
293
|
if ( type.includes(`info`) ) { start += `💡`; }
|
|
227
294
|
if ( type.includes(`success`) ) { start += `🌈`; }
|
|
228
295
|
if ( type.includes(`warning`) ) { start += `🔎`; }
|
|
229
296
|
if ( type.includes(`error`) ) { start += `🤕`; }
|
|
230
297
|
// Default
|
|
231
|
-
if ( start ===
|
|
298
|
+
if ( start === indent ) { start += `?`; }
|
|
232
299
|
|
|
233
300
|
start += ` ${ code }`;
|
|
234
301
|
|
|
235
|
-
|
|
302
|
+
let description_parts = [];
|
|
236
303
|
let extra_types = type.replace(/debug|info|success|warning|error/i, ``).replace(/ +/g, ' ').trim();
|
|
237
|
-
if ( extra_types.length > 0 ) {
|
|
304
|
+
if ( extra_types.length > 0 ) { description_parts = extra_types.split(` `); }
|
|
238
305
|
|
|
239
|
-
// Get all the
|
|
240
|
-
let description_parts = [];
|
|
306
|
+
// Get all the main descriptive text
|
|
241
307
|
if ( type.includes(`debug`) ) { description_parts.push( `DEBUG` ); }
|
|
242
308
|
if ( type.includes(`info`) ) { description_parts.push( `INFO` ); }
|
|
243
309
|
if ( type.includes(`success`) ) { description_parts.push( `SUCCESS` ); }
|
|
@@ -3,19 +3,33 @@ const fg = require('fast-glob');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
require('dotenv').config();
|
|
5
5
|
|
|
6
|
+
/** WARNING: Avoid importing other project files into here. Every other
|
|
7
|
+
* file should be able to reference this one without worrying
|
|
8
|
+
* about circular references.
|
|
9
|
+
* */
|
|
10
|
+
|
|
11
|
+
/** This file provides user info variable getters and setters.
|
|
12
|
+
* E.g. server address. It often uses `process.env` vars or
|
|
13
|
+
* vars that have been built from `process.env` vars. It is
|
|
14
|
+
* meant to avoid duplication, make testing easier, and leave
|
|
15
|
+
* room for future manipulation.
|
|
16
|
+
*
|
|
17
|
+
* No other file should need to use `process.env`.
|
|
18
|
+
*
|
|
19
|
+
* We use `.get_foo()` functions partly because it makes the code
|
|
20
|
+
* easier to test.
|
|
21
|
+
* */
|
|
22
|
+
|
|
6
23
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* We have a bunch of `.get_foo()` functions partly because it makes the code
|
|
17
|
-
* much easier to test.
|
|
18
|
-
*/
|
|
24
|
+
* Discuss: We don't like logging directly to the console in here. Consider
|
|
25
|
+
* returning most logs (info, error, warning) so that the calling functions
|
|
26
|
+
* can use a `Log` to log them properly instead of logging to the console.
|
|
27
|
+
* Alternatively, move everything in here that `Log` uses into its own file
|
|
28
|
+
* so that `session_vars` can have its own `Log` instance. For example, put
|
|
29
|
+
* artifacts path name related functions in `Log`. That "get/set/delete"
|
|
30
|
+
* behavior would be inconsistent with other similar functions. Other places
|
|
31
|
+
* need to use artifacts path name too.
|
|
32
|
+
* */
|
|
19
33
|
|
|
20
34
|
const session_vars = {};
|
|
21
35
|
|
|
@@ -59,6 +73,18 @@ session_vars.get_setup_timeout_ms = function () {
|
|
|
59
73
|
return timeout;
|
|
60
74
|
};
|
|
61
75
|
|
|
76
|
+
session_vars.get_max_tests_per_generator_scenario = function() {
|
|
77
|
+
/** Returns the maximum number of tests to generate for any one generator
|
|
78
|
+
* Scenario.
|
|
79
|
+
*
|
|
80
|
+
* Assumes an average of 30 secs per test with a max of 20 min. for all tests
|
|
81
|
+
* for that one generator Scenario.
|
|
82
|
+
*
|
|
83
|
+
* @returns {int} - Max number of tests for one generator Scenario
|
|
84
|
+
* */
|
|
85
|
+
return process.env.MAX_RANDOM_TESTS_PER_SCENARIO || 40;
|
|
86
|
+
}
|
|
87
|
+
|
|
62
88
|
session_vars.get_da_server_url = function () {
|
|
63
89
|
try {
|
|
64
90
|
let url = process.env.SERVER_URL || process.env.BASE_URL;
|
|
@@ -136,7 +162,7 @@ session_vars.save_project_name = function ( project_name ) {
|
|
|
136
162
|
let file = fs.writeFileSync( runtime_config_path, JSON.stringify( json, null, 2 ) );
|
|
137
163
|
|
|
138
164
|
if ( session_vars.get_debug() ) {
|
|
139
|
-
console.
|
|
165
|
+
console.debug( `🐛 ALK0036 DEBUG: Stored name of Project "${ project_name }" locally.` );
|
|
140
166
|
}
|
|
141
167
|
return file;
|
|
142
168
|
}; // Ends save_project_name()
|
|
@@ -148,7 +174,7 @@ session_vars.get_project_name = function () {
|
|
|
148
174
|
}
|
|
149
175
|
let json = JSON.parse( fs.readFileSync( runtime_config_path ));
|
|
150
176
|
let project_name = json[ project_name_key ] || null;
|
|
151
|
-
if ( session_vars.get_debug() ) { console.
|
|
177
|
+
if ( session_vars.get_debug() ) { console.debug( `🐛 ALK0037 DEBUG: Project name from file is "${ project_name }".` ); }
|
|
152
178
|
return project_name;
|
|
153
179
|
}; // Ends get_project_name()
|
|
154
180
|
|
|
@@ -172,29 +198,41 @@ session_vars.delete_project_name = function () {
|
|
|
172
198
|
// const file_storing_artifact_path = `_alkiln_test_artifacts_dir_name.json`;
|
|
173
199
|
const artifacts_path_key = `artifacts_path`;
|
|
174
200
|
session_vars.save_artifacts_path_name = function ( path_name ) {
|
|
175
|
-
|
|
201
|
+
/** Save a key with the name of the current artifacts folder
|
|
202
|
+
* in case it doesn't exist.
|
|
203
|
+
* */
|
|
176
204
|
return session_vars.save_runtime_config_var({ key: artifacts_path_key, value: path_name });
|
|
177
205
|
}; // Ends save_artifacts_path_name()
|
|
178
206
|
session_vars.get_artifacts_path_name = function () {
|
|
179
|
-
|
|
207
|
+
/** Get the name of the current artifacts path. */
|
|
180
208
|
return session_vars.get_runtime_config_var( artifacts_path_key );
|
|
181
209
|
}; // Ends get_artifacts_path_name()
|
|
210
|
+
session_vars.delete_artifacts_path_name = function () {
|
|
211
|
+
/** Delete the runtime config value of the artifacts path. */
|
|
212
|
+
return session_vars.delete_runtime_config_var({ key: artifacts_path_key });
|
|
213
|
+
};
|
|
182
214
|
|
|
183
215
|
|
|
216
|
+
/** We use `da_install_method` for temp server vs. author's account (Playground)
|
|
217
|
+
* on author's server. For example, for deciding how to get the url of a
|
|
218
|
+
* server. See docs on 4 environments. TODO: Change `_method` to `_location`?
|
|
219
|
+
* */
|
|
184
220
|
const install_method_key = `da_install_method`;
|
|
185
221
|
session_vars.save_install_method = function ( install_method ) {
|
|
186
|
-
|
|
222
|
+
/** Save a key with the name of the current artifacts folder in case it
|
|
223
|
+
* doesn't exist. */
|
|
187
224
|
return session_vars.save_runtime_config_var({ key: install_method_key, value: install_method });
|
|
188
225
|
}; // Ends save_install_method()
|
|
189
226
|
session_vars.get_install_method = function () {
|
|
190
|
-
|
|
227
|
+
/** Get the name of the current install method. */
|
|
191
228
|
return session_vars.get_runtime_config_var( install_method_key );
|
|
192
229
|
}; // Ends get_install_method()
|
|
193
230
|
|
|
194
231
|
|
|
195
232
|
const repo_folder_key = `da_repo_folder_name`;
|
|
196
233
|
session_vars.save_repo_folder_name = function (repo_folder_name) {
|
|
197
|
-
/** Save a key with the name of the main package folder in the repository.
|
|
234
|
+
/** Save a key with the name of the main package folder in the repository. For
|
|
235
|
+
* example, EvictionDefense in the docassemble-EvictionDefense folder. */
|
|
198
236
|
return session_vars.save_runtime_config_var({ key: repo_folder_key, value: repo_folder_name });
|
|
199
237
|
}; // Ends session_vars.save_repo_folder_name()
|
|
200
238
|
session_vars.get_repo_folder_name = function () {
|
|
@@ -208,10 +246,11 @@ session_vars.save_runtime_config_var = function ({ key, value }) {
|
|
|
208
246
|
* the json object there. The functions calling this are keeping
|
|
209
247
|
* track of the key, so they're useful to keep around.
|
|
210
248
|
*
|
|
211
|
-
* @param
|
|
212
|
-
* @param
|
|
249
|
+
* @param {object} obj - Named arguments
|
|
250
|
+
* @param {str} obj.key - Key to use to store `value` in runtime config file
|
|
251
|
+
* @param {any} obj.value - Value to store in runtime config object
|
|
213
252
|
*
|
|
214
|
-
* @returns
|
|
253
|
+
* @returns {binary?} file Runtime config file
|
|
215
254
|
*/
|
|
216
255
|
|
|
217
256
|
// Ensure runtime config file exists
|
|
@@ -226,82 +265,77 @@ session_vars.save_runtime_config_var = function ({ key, value }) {
|
|
|
226
265
|
let file = fs.writeFileSync( runtime_config_path, JSON.stringify( json, null, 2 ) );
|
|
227
266
|
|
|
228
267
|
if ( session_vars.get_debug() ) {
|
|
229
|
-
console.
|
|
268
|
+
console.debug( `🐛 ALK0040 DEBUG: Stored runtime config var "${key}" locally as "${value}".` );
|
|
230
269
|
}
|
|
231
270
|
return file;
|
|
232
271
|
}; // Ends session_vars.save_runtime_config_var()
|
|
233
272
|
|
|
234
273
|
session_vars.get_runtime_config_var = function ( key ) {
|
|
235
274
|
/** Get the value of the given key from the runtime config file.
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
275
|
+
*
|
|
276
|
+
* @param {str} key - Key in runtime config file that contains the desired val
|
|
277
|
+
*
|
|
278
|
+
* @returns {any} - Value of given key in runtime config file or null
|
|
279
|
+
*/
|
|
241
280
|
try {
|
|
242
281
|
let json = JSON.parse( fs.readFileSync( runtime_config_path ));
|
|
243
282
|
let value = json[ key ];
|
|
283
|
+
console.log(`🐛🐛🐛🐛🐛 ALK00temp config key/value pair:`, key, value);
|
|
244
284
|
if ( session_vars.get_debug() ) {
|
|
245
|
-
console.
|
|
285
|
+
console.debug( `🐛 ALK0041 DEBUG: Runtime config var "${ key }" is "${ value }".` );
|
|
246
286
|
}
|
|
247
|
-
|
|
248
287
|
return value;
|
|
249
|
-
} catch (
|
|
288
|
+
} catch ( get_var_error ) {
|
|
289
|
+
|
|
290
|
+
console.debug(`🖊️ ALK0273 NOTE: Skipped getting runtime config var "${ key }".`, get_var_error.stack );
|
|
250
291
|
return null;
|
|
292
|
+
|
|
251
293
|
}
|
|
252
294
|
}; // Ends session_vars.get_runtime_config_var()
|
|
253
295
|
|
|
296
|
+
session_vars.delete_runtime_config_var = function ({ key }) {
|
|
297
|
+
/** Delete the runtime config var of the given `key`
|
|
298
|
+
*
|
|
299
|
+
* @param {object} obj - Named arguments
|
|
300
|
+
* @param {str} obj.key - Key in runtime config file object to delete
|
|
301
|
+
*
|
|
302
|
+
* @returns {null}
|
|
303
|
+
* */
|
|
304
|
+
try {
|
|
305
|
+
if ( !fs.existsSync( runtime_config_path )) { return null; }
|
|
306
|
+
|
|
307
|
+
let json = JSON.parse( fs.readFileSync( runtime_config_path ));
|
|
308
|
+
delete json[ key ];
|
|
309
|
+
fs.writeFileSync( runtime_config_path, JSON.stringify( json, null, 2 ));
|
|
310
|
+
return null;
|
|
254
311
|
|
|
255
|
-
|
|
256
|
-
/** Returns whether at least one of the given path exists. Store
|
|
257
|
-
* a list of the existing paths. Warn of any missing paths. Can be
|
|
258
|
-
* used for getting, e.g .feature files, files to upload, etc.
|
|
259
|
-
*
|
|
260
|
-
* Our code should ensure this gets run before cucumber tests get triggered.
|
|
261
|
-
*
|
|
262
|
-
* @param paths { [str] } List of paths to the 'sources' folder.
|
|
263
|
-
*
|
|
264
|
-
* @returns bool
|
|
265
|
-
*/
|
|
312
|
+
} catch ( delete_var_error ) {
|
|
266
313
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
let missing_paths = [];
|
|
270
|
-
for ( let path of paths ) {
|
|
271
|
-
let dirs = fg.sync(
|
|
272
|
-
[ path ],
|
|
273
|
-
{ onlyFiles: false, suppressErrors: true }
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
if ( dirs.length > 0 ) {
|
|
277
|
-
existing_paths.push( ...dirs );
|
|
278
|
-
} else {
|
|
279
|
-
missing_paths.push( path );
|
|
280
|
-
}
|
|
281
|
-
}
|
|
314
|
+
console.debug( `🖊️ ALK0274 NOTE: Skipped deleting runtime config var "${ key }".`, delete_var_error.stack );
|
|
315
|
+
return null
|
|
282
316
|
|
|
283
|
-
// If no paths were valid
|
|
284
|
-
if ( existing_paths.length <= 0 ) {
|
|
285
|
-
session_vars._sources_paths = null;
|
|
286
|
-
return false;
|
|
287
317
|
}
|
|
318
|
+
}; // Ends session_vars.delete_runtime_config_var()
|
|
288
319
|
|
|
289
|
-
console.info(
|
|
290
|
-
`🌈 ALK0042 SUCCESS: Did find "sources" file(s) at "${ existing_paths.join('", "') }".`
|
|
291
|
-
)
|
|
292
320
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
321
|
+
session_vars.set_sources_paths = function ( paths ) {
|
|
322
|
+
/** Set the globally accessible value of the paths to the sources folder.
|
|
323
|
+
* Saves and returns the argument it was given.
|
|
324
|
+
*
|
|
325
|
+
* @param paths {[string]} List of paths to the 'sources' folder.
|
|
326
|
+
*
|
|
327
|
+
* @returns {null | [string]}
|
|
328
|
+
* */
|
|
329
|
+
session_vars._sources_paths = paths;
|
|
330
|
+
return paths || null;
|
|
296
331
|
}; // End session_vars.set_sources_paths()
|
|
297
332
|
|
|
298
333
|
session_vars.get_sources_paths = function () {
|
|
299
|
-
/** Get the "sources" folder
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
return session_vars._sources_paths;
|
|
334
|
+
/** Get the previously stored "sources" folder paths.
|
|
335
|
+
*
|
|
336
|
+
* @returns {null | [string]}
|
|
337
|
+
* */
|
|
338
|
+
return session_vars._sources_paths || null;
|
|
305
339
|
};
|
|
306
340
|
|
|
307
341
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const fast_glob = require(`fast-glob`);
|
|
2
|
+
|
|
3
|
+
const session_vars = require(`./session_vars`);
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
module.exports = function set_sources_paths({ log }) {
|
|
7
|
+
/** Set the globally accessible value of the paths to the sources folder,
|
|
8
|
+
* where authors store tests and other relevant test files. Can throw an
|
|
9
|
+
* error.
|
|
10
|
+
*
|
|
11
|
+
* @param {object} obj - Named arguments
|
|
12
|
+
* @param {Log} obj.log - Logs and saves useful info for later debugging.
|
|
13
|
+
*
|
|
14
|
+
* @returns {[string]} - All paths found for package sources folders.
|
|
15
|
+
* */
|
|
16
|
+
session_vars.validateEnvironment();
|
|
17
|
+
|
|
18
|
+
let maybe_paths = get_potential_paths();
|
|
19
|
+
|
|
20
|
+
let sources_paths = get_sources_paths({ maybe_paths });
|
|
21
|
+
if ( sources_paths === 0 ) {
|
|
22
|
+
// If we don't throw, it'll be more confusing when no tests run
|
|
23
|
+
log.throw({ code: `ALK0050`, context: `sources`,
|
|
24
|
+
error: new ReferenceError(
|
|
25
|
+
`ALKiln can't find your "sources" folder(s) which should contain tests and their related files.`
|
|
26
|
+
),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
log.info({ code: `ALK0042`, context: `sources` },
|
|
31
|
+
`Did find "sources" file(s) at "${ sources_paths.join('", "') }".`
|
|
32
|
+
);
|
|
33
|
+
session_vars.set_sources_paths( sources_paths );
|
|
34
|
+
|
|
35
|
+
return sources_paths;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
function get_potential_paths() {
|
|
40
|
+
/** Use command line args and session vars to return a list of strings of
|
|
41
|
+
* possible sources files paths.
|
|
42
|
+
*
|
|
43
|
+
* @returns {[string]} - Potential sources paths.
|
|
44
|
+
* */
|
|
45
|
+
// `project` is the name in the server docassemble "Project" page
|
|
46
|
+
let project = session_vars.get_user_project_name();
|
|
47
|
+
let id = session_vars.get_user_id();
|
|
48
|
+
|
|
49
|
+
// process.argv is a list of strings of commands and args.
|
|
50
|
+
// Internal note: If using `npm run cucumber`, use `--` before
|
|
51
|
+
// `--sources=./foo`
|
|
52
|
+
let cli_args = require(`minimist`)( process.argv.slice(2), {
|
|
53
|
+
default: {
|
|
54
|
+
sources: [
|
|
55
|
+
`./docassemble/*/data/sources`, // In GitHub folder
|
|
56
|
+
`/usr/share/docassemble/files/playgroundsources/${id}/${project}/*.feature`, // From playground without S3
|
|
57
|
+
`/tmp/playgroundsources/${id}/${project}/*.feature` // From playground with S3
|
|
58
|
+
],
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Make sure `sources` is always a list
|
|
63
|
+
let potential_paths_list = cli_args.sources;
|
|
64
|
+
if ( !Array.isArray( cli_args.sources )) {
|
|
65
|
+
potential_paths_list = [ cli_args.sources ];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return potential_paths_list;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
function get_sources_paths({ maybe_paths }) {
|
|
73
|
+
/** Of the given possible paths, return the paths that actually exist. Maybe
|
|
74
|
+
* none.
|
|
75
|
+
*
|
|
76
|
+
* @param {object} obj - Named arguments
|
|
77
|
+
* @param {[string]} obj.maybe_paths - All possible paths
|
|
78
|
+
*
|
|
79
|
+
* @returns {[string]} - List of all existing paths
|
|
80
|
+
* */
|
|
81
|
+
// Check every path, tracking which exist and which are missing
|
|
82
|
+
let existing_paths = [];
|
|
83
|
+
let missing_paths = [];
|
|
84
|
+
for ( let path of maybe_paths ) {
|
|
85
|
+
let dirs = fast_glob.sync(
|
|
86
|
+
[ path ],
|
|
87
|
+
{ onlyFiles: false, suppressErrors: true }
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
if ( dirs.length > 0 ) {
|
|
91
|
+
existing_paths.push( ...dirs );
|
|
92
|
+
} else {
|
|
93
|
+
missing_paths.push( path );
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return existing_paths;
|
|
98
|
+
}
|