@suffolklitlab/alkiln 5.13.3 → 5.13.4
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 +7 -7
- package/.github/workflows/playground.yml +1 -1
- package/CHANGELOG.md +24 -0
- package/action.yml +70 -28
- package/action_for_github_server/action.yml +61 -29
- package/docs/4_environments.md +243 -0
- package/docs/artifacts_folder_name.md +135 -0
- package/docs/constrained_random_tests.md +320 -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 +3 -1
- package/docs/decisions/reports_and_logs_2024_08_11.md +161 -0
- package/docs/kinds_of_errors.md +26 -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/parse.js +36 -0
- package/lib/run_cucumber.js +115 -36
- package/lib/scope.js +60 -75
- package/lib/steps.js +70 -42
- package/lib/utils/Log.js +982 -0
- package/lib/utils/files.js +18 -11
- package/lib/utils/get_sources_paths.js +162 -0
- package/lib/utils/parse.js +1 -0
- package/lib/utils/proccess_command_line_args.js +267 -0
- package/lib/utils/reports.js +64 -17
- package/lib/utils/session_vars.js +34 -21
- package/lib/utils/time.js +44 -0
- package/package.json +1 -1
- package/tests/log_codes/check_codes.sh +2 -1
- package/tests/log_codes/log_code_expected_instances.txt +33 -9
- 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 +581 -0
- package/tests/unit_tests/normalizeTable.test.js +5 -0
- package/lib/utils/log.js +0 -96
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,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
fast_glob (fast glob)
|
|
3
|
+
|
|
4
|
+
files
|
|
5
|
+
session_vars
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
// let log = null;
|
|
10
|
+
|
|
11
|
+
// function set_log ( artifacts_path ) {
|
|
12
|
+
// log = Log( artifacts_path );
|
|
13
|
+
// }
|
|
14
|
+
|
|
15
|
+
// TODO: Remove after merging Log class PR
|
|
16
|
+
let artifacts_path = files.make_artifacts_folder();
|
|
17
|
+
session_vars.save_artifacts_path_name( artifacts_path );
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
function proccess_command_line_args({ log }) {
|
|
21
|
+
|
|
22
|
+
// Discuss: Wouldn't have to use log if we moved this up.
|
|
23
|
+
const environment = { cwd: process.cwd() };
|
|
24
|
+
log.info({ type: `run`, code: `ALK0049`, pre: `environment`, data: environment });
|
|
25
|
+
|
|
26
|
+
// We'll use the user info to construct the possible paths to /sources
|
|
27
|
+
let id = session_vars.get_user_id();
|
|
28
|
+
// `project` is the name in the server docassemble "Project" page
|
|
29
|
+
let project = session_vars.get_user_project_name();
|
|
30
|
+
|
|
31
|
+
// process.argv is a list of strings of commands and args.
|
|
32
|
+
// Internal note: If using `npm run cucumber`, use `--` before
|
|
33
|
+
// `--sources=./foo`
|
|
34
|
+
let command_line_args = require(`minimist`)( process.argv.slice(2), {
|
|
35
|
+
default: {
|
|
36
|
+
sources: [
|
|
37
|
+
`./docassemble/*/data/sources`, // In GitHub folder
|
|
38
|
+
`/usr/share/docassemble/files/playgroundsources/${id}/${project}/*.feature`, // From playground without S3
|
|
39
|
+
`/tmp/playgroundsources/${id}/${project}/*.feature` // From playground with S3
|
|
40
|
+
],
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
let processed = {};
|
|
45
|
+
processed.sources = get_sources_paths({ command_line_args });
|
|
46
|
+
let tags = get_tags({ command_line_args });
|
|
47
|
+
|
|
48
|
+
return command_line_args;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
function get_sources_paths({ command_line_args }) {
|
|
53
|
+
|
|
54
|
+
// Make sure `sources` is always a list
|
|
55
|
+
let sources_list = command_line_args.sources;
|
|
56
|
+
if ( typeof command_line_args.sources === `string` ) {
|
|
57
|
+
sources_list = [ command_line_args.sources ];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check every path, tracking which exist and which are missing
|
|
61
|
+
let existing_paths = [];
|
|
62
|
+
let missing_paths = [];
|
|
63
|
+
for ( let path of paths ) {
|
|
64
|
+
let dirs = fast_glob.sync(
|
|
65
|
+
[ path ],
|
|
66
|
+
{ onlyFiles: false, suppressErrors: true }
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if ( dirs.length > 0 ) {
|
|
70
|
+
existing_paths.push( ...dirs );
|
|
71
|
+
} else {
|
|
72
|
+
missing_paths.push( path );
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If no paths were valid
|
|
77
|
+
if ( existing_paths.length <= 0 ) {
|
|
78
|
+
// session_vars.set_sources_paths( null );
|
|
79
|
+
|
|
80
|
+
// Discuss: Wouldn't have to use log if we moved this up.
|
|
81
|
+
// If we don't throw, it'll be more confusing when no tests run
|
|
82
|
+
let msg = `ALKiln can't find your "sources" folder(s). See above for details.`;
|
|
83
|
+
let code = `ALK0050`
|
|
84
|
+
log.error({ type: `run`, code: code, pre: msg });
|
|
85
|
+
// pass error up instead?
|
|
86
|
+
throw new ReferenceError(`🤕 ${ code } run ERROR: ${ msg }`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// TODO: move this out a level
|
|
90
|
+
console.info(
|
|
91
|
+
`lightbulb ALK0042 INFO: Did find "sources" file(s) at "${ existing_paths.join('", "') }".`
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
// session_vars.set_sources_paths( existing_paths );
|
|
95
|
+
return existing_paths;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
function get_tags({ command_line_args }) {
|
|
100
|
+
// Normalize tag expressions, if there are any Joining and splitting handles
|
|
101
|
+
// both tags surrounded with `""` or those without `""`
|
|
102
|
+
let tags = command_line_args[`_`].join(` `).split(` `);
|
|
103
|
+
// If there was no tag expression, `tags` is ['']. Remove that item.
|
|
104
|
+
tags = tags.filter(function(entry) { return entry !== ""; })
|
|
105
|
+
if ( tags.length > 0 ) {
|
|
106
|
+
let joined_tags = '';
|
|
107
|
+
// Cucumber expects tags in boolean expressions, like (@a0 or @a1) and
|
|
108
|
+
// @random. If we get a list of only tags, then "or" them all together
|
|
109
|
+
if (tags.every((val) => val.startsWith(`@`))) {
|
|
110
|
+
joined_tags = tags.join(` or `);
|
|
111
|
+
}
|
|
112
|
+
// If we get a list of seemingly no tags, assume they meant to use tags
|
|
113
|
+
else if (tags.every((val) => !val.startsWith(`@`))) {
|
|
114
|
+
joined_tags = tags.map((val) => `@` + val).join(` or `);
|
|
115
|
+
}
|
|
116
|
+
// If it's a mix of tags and no tags, it's probably a boolean expression!
|
|
117
|
+
else {
|
|
118
|
+
joined_tags = tags.join(` `);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// config.tags = joined_tags;
|
|
122
|
+
return joined_tags;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* get sources paths
|
|
131
|
+
* generate feature files
|
|
132
|
+
* try to parse everything
|
|
133
|
+
* pass on the feature files?
|
|
134
|
+
*
|
|
135
|
+
* When running run_cucumber, pass whether files have yet been parsed
|
|
136
|
+
* Alternatively, have 2 setup steps? The second one parses before run_cucumber
|
|
137
|
+
* That seems maybe better...? Have to pass on the feature files somehow, though
|
|
138
|
+
*
|
|
139
|
+
* setup_server_stuff
|
|
140
|
+
* get_feature_files
|
|
141
|
+
* run_cucumber(with files list somehow... runtime_config?)
|
|
142
|
+
* */
|
|
143
|
+
|
|
144
|
+
function get_feature_paths() {
|
|
145
|
+
// Turn all the paths to the sources folders into paths to the feature files
|
|
146
|
+
let sources_paths = session_vars.get_sources_paths();
|
|
147
|
+
let feature_paths = [];
|
|
148
|
+
|
|
149
|
+
// Get all the paths for the right feature files (for cucumber to use)
|
|
150
|
+
for ( let sources_path of sources_paths ) {
|
|
151
|
+
const generators_prefix = `alkiln_random`;
|
|
152
|
+
constrained_random_tests.create({ generators_prefix, sources_path });
|
|
153
|
+
// Ignore feature files that we parsed ourselves
|
|
154
|
+
let glob = fast_glob.sync(
|
|
155
|
+
`${ sources_path }/**/*.feature`,
|
|
156
|
+
{ ignore: [`${ sources_path }/${ generators_prefix }*.feature`] }
|
|
157
|
+
);
|
|
158
|
+
feature_paths.push( ...glob );
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
parse.js
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* before everything else
|
|
3
|
+
* error if there are generator errors
|
|
4
|
+
*
|
|
5
|
+
* problem:
|
|
6
|
+
*
|
|
7
|
+
* because of the 3 (4) env of ALKiln, have to be able to generate and error in
|
|
8
|
+
* 2 places: run_cucumber, at the start of the GitHub action
|
|
9
|
+
*
|
|
10
|
+
* If there's an error, no problem (nothing will run twice)
|
|
11
|
+
*
|
|
12
|
+
* Otherwise (success, warnings, etc.), generation/parsing will be run 2x. Do we
|
|
13
|
+
* care?
|
|
14
|
+
*
|
|
15
|
+
* Note that generation cleans out its folder before generating.
|
|
16
|
+
*
|
|
17
|
+
* Warnings are the problem if we log them at the top (not if we log them along
|
|
18
|
+
* with the other test errors/warnings).
|
|
19
|
+
*
|
|
20
|
+
* Error:
|
|
21
|
+
* Action: Errors, nothing else is run
|
|
22
|
+
* Local/Playground: Errors, nothing else is run
|
|
23
|
+
* TODO: Need to generate error files?
|
|
24
|
+
*
|
|
25
|
+
* Success:
|
|
26
|
+
* Action: Generate twice
|
|
27
|
+
* Local/Playground: Generate once
|
|
28
|
+
*
|
|
29
|
+
* Warnings (same warnings):
|
|
30
|
+
* Action: Generate, warn?, generate, warn?, warn at end of test run
|
|
31
|
+
* Do we need a "dry run" env var?
|
|
32
|
+
* Local/Playground: Generate, warn, warn at end of test run
|
|
33
|
+
*
|
|
34
|
+
* Maybe run_cucumber needs an idea of whether it's running as the first
|
|
35
|
+
* operation or as a follow-up
|
|
36
|
+
* `is_followup` (general state of affairs)
|
|
37
|
+
* `don't_generate` (a command of a specific action to take)
|
|
38
|
+
* previous_steps = [`pre_parse`, `server_setup`/`github_playground_setup`]
|
|
39
|
+
*
|
|
40
|
+
* Maybe do this completely separately and call it in different places in
|
|
41
|
+
* different processes.
|
|
42
|
+
*
|
|
43
|
+
*
|
|
44
|
+
* action needs:
|
|
45
|
+
* pre-setup needs:
|
|
46
|
+
* `generate` which needs:
|
|
47
|
+
* (really??) trigger_any_parsing_errors which needs:
|
|
48
|
+
* feature files list/glob which needs:
|
|
49
|
+
* sources paths which needs
|
|
50
|
+
* command line args
|
|
51
|
+
* and
|
|
52
|
+
* prints environment PROBLEM TO REPEAT
|
|
53
|
+
* potentially errors
|
|
54
|
+
* and
|
|
55
|
+
* potentially errors
|
|
56
|
+
*
|
|
57
|
+
* run_cucumber needs
|
|
58
|
+
* pre-setup
|
|
59
|
+
* (maybe) generate which needs:
|
|
60
|
+
* (maybe) trigger_any_parsing_errors etc.
|
|
61
|
+
* (maybe) trigger_any_parsing_errors which needs:
|
|
62
|
+
* feature files, etc.
|
|
63
|
+
*
|
|
64
|
+
* feature files list/glob which needs:
|
|
65
|
+
* sources paths which needs
|
|
66
|
+
* command line args
|
|
67
|
+
* and
|
|
68
|
+
* prints environment PROBLEM TO REPEAT
|
|
69
|
+
* potentially errors (not a problem. won't run if the other errors)
|
|
70
|
+
* tags needs
|
|
71
|
+
* command line args
|
|
72
|
+
*
|
|
73
|
+
* ## Problem 1 - printing environment in 2 places
|
|
74
|
+
* Fix: Move print environment into run_cucumber
|
|
75
|
+
*
|
|
76
|
+
* ====
|
|
77
|
+
*
|
|
78
|
+
* ## Problem 2 - parsing in both places.
|
|
79
|
+
* Parsing errors will prevent duplicate parsing.
|
|
80
|
+
* What about when there are no errors?
|
|
81
|
+
* Fix: I guess we can check for parsing errors again without harm, though it'll
|
|
82
|
+
* take an extra second. Errors will only be parsed once because they'll
|
|
83
|
+
* prevent execution.
|
|
84
|
+
*
|
|
85
|
+
* ## Problem 3 - generating in both places
|
|
86
|
+
* Fix: flag? Or maybe re-generating isn't a problem if it doesn't take too long
|
|
87
|
+
*
|
|
88
|
+
* ## Problem 4
|
|
89
|
+
* Parse before generating or after generating?
|
|
90
|
+
* - Can generating create files that have parsing errors without already
|
|
91
|
+
* having parsing errors? If so, parse before.
|
|
92
|
+
* - Generating can create its own errors, though. So generate before?
|
|
93
|
+
* - Also, errors for generated tests may be confusing because author may think,
|
|
94
|
+
* "I didn't write those tests."
|
|
95
|
+
* Maybe:
|
|
96
|
+
* 1. test all files before generating
|
|
97
|
+
* 2. generate (and pass back errors or just throw?)
|
|
98
|
+
* 3. test generated files and throw just generator process errors and parsing
|
|
99
|
+
* errors
|
|
100
|
+
*
|
|
101
|
+
* All:
|
|
102
|
+
* 0. Get command line args
|
|
103
|
+
* 1. Get sources
|
|
104
|
+
* 2. Get feature files
|
|
105
|
+
* 3. Parse all current (can throw) (accumulated errors?)
|
|
106
|
+
* 4. Generate (with possible errors) (can throw accumulated errors)
|
|
107
|
+
* 5. Parse generated (can throw)
|
|
108
|
+
* Should those throw all together? Probably not. They can affect each other.
|
|
109
|
+
*
|
|
110
|
+
* run_cucumber also independently needs:
|
|
111
|
+
* 1. sources
|
|
112
|
+
* 2. feature files
|
|
113
|
+
* */
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* get sources paths
|
|
117
|
+
* generate feature files
|
|
118
|
+
* try to parse everything
|
|
119
|
+
* pass on the feature files?
|
|
120
|
+
*
|
|
121
|
+
* When running run_cucumber, pass whether files have yet been parsed
|
|
122
|
+
* Alternatively, have 2 setup steps? The second one parses before run_cucumber
|
|
123
|
+
* That seems maybe better...? Have to pass on the feature files somehow, though
|
|
124
|
+
*
|
|
125
|
+
* setup_server_stuff
|
|
126
|
+
* get_feature_files
|
|
127
|
+
* run_cucumber(with files list somehow... runtime_config?)
|
|
128
|
+
* */
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
fast_glob (fast glob)
|
|
132
|
+
|
|
133
|
+
files
|
|
134
|
+
session_vars
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
// let log = null;
|
|
138
|
+
|
|
139
|
+
// function set_log ( artifacts_path ) {
|
|
140
|
+
// log = Log( artifacts_path );
|
|
141
|
+
// }
|
|
142
|
+
|
|
143
|
+
// TODO: Remove after merging Log class PR
|
|
144
|
+
let artifacts_path = files.make_artifacts_folder();
|
|
145
|
+
session_vars.save_artifacts_path_name( artifacts_path );
|
|
146
|
+
|
|
147
|
+
function get_command_line_args({ log }) {
|
|
148
|
+
|
|
149
|
+
// We'll use the user info to construct the possible paths to /sources
|
|
150
|
+
let id = session_vars.get_user_id();
|
|
151
|
+
// `project` is the name in the author's server's docassemble "Project" page
|
|
152
|
+
let project = session_vars.get_user_project_name();
|
|
153
|
+
|
|
154
|
+
// process.argv is a list of strings of commands and args.
|
|
155
|
+
// Internal note: If using `npm run cucumber`, use `--` before
|
|
156
|
+
// `--sources=./foo`
|
|
157
|
+
let command_line_args = require(`minimist`)( process.argv.slice(2), {
|
|
158
|
+
default: {
|
|
159
|
+
sources: [
|
|
160
|
+
`./docassemble/*/data/sources`, // In GitHub folder
|
|
161
|
+
`/usr/share/docassemble/files/playgroundsources/${id}/${project}/*.feature`, // From playground without S3
|
|
162
|
+
`/tmp/playgroundsources/${id}/${project}/*.feature` // From playground with S3
|
|
163
|
+
],
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// let processed = {};
|
|
168
|
+
// processed.sources = get_sources_paths({ command_line_args });
|
|
169
|
+
// let tags = get_tags({ command_line_args });
|
|
170
|
+
|
|
171
|
+
return command_line_args;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
function get_sources_paths({ command_line_args }) {
|
|
176
|
+
|
|
177
|
+
// Make sure `sources` is always a list
|
|
178
|
+
let sources_list = command_line_args.sources;
|
|
179
|
+
if ( typeof command_line_args.sources === `string` ) {
|
|
180
|
+
sources_list = [ command_line_args.sources ];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check every path, tracking which exist and which are missing
|
|
184
|
+
let existing_paths = [];
|
|
185
|
+
let missing_paths = [];
|
|
186
|
+
for ( let path of paths ) {
|
|
187
|
+
let dirs = fast_glob.sync(
|
|
188
|
+
[ path ],
|
|
189
|
+
{ onlyFiles: false, suppressErrors: true }
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
if ( dirs.length > 0 ) {
|
|
193
|
+
existing_paths.push( ...dirs );
|
|
194
|
+
} else {
|
|
195
|
+
missing_paths.push( path );
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// If no paths were valid
|
|
200
|
+
if ( existing_paths.length <= 0 ) {
|
|
201
|
+
// session_vars.set_sources_paths( null );
|
|
202
|
+
|
|
203
|
+
// Discuss: Wouldn't have to use log if we moved this up.
|
|
204
|
+
// If we don't throw, it'll be more confusing when no tests run
|
|
205
|
+
let msg = `ALKiln can't find your "sources" folder(s). See above for details.`;
|
|
206
|
+
let code = `ALK0050`
|
|
207
|
+
log.error({ type: `run`, code: code, pre: msg });
|
|
208
|
+
// pass error up instead?
|
|
209
|
+
throw new ReferenceError(`🤕 ${ code } run ERROR: ${ msg }`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// TODO: move this out a level
|
|
213
|
+
console.info(
|
|
214
|
+
`lightbulb ALK0042 INFO: Did find "sources" file(s) at "${ existing_paths.join('", "') }".`
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
// session_vars.set_sources_paths( existing_paths );
|
|
218
|
+
return existing_paths;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
function get_tags({ command_line_args }) {
|
|
223
|
+
// Normalize tag expressions, if there are any Joining and splitting handles
|
|
224
|
+
// both tags surrounded with `""` or those without `""`
|
|
225
|
+
let tags = command_line_args[`_`].join(` `).split(` `);
|
|
226
|
+
// If there was no tag expression, `tags` is ['']. Remove that item.
|
|
227
|
+
tags = tags.filter(function(entry) { return entry !== ""; })
|
|
228
|
+
if ( tags.length > 0 ) {
|
|
229
|
+
let joined_tags = '';
|
|
230
|
+
// Cucumber expects tags in boolean expressions, like (@a0 or @a1) and
|
|
231
|
+
// @random. If we get a list of only tags, then "or" them all together
|
|
232
|
+
if (tags.every((val) => val.startsWith(`@`))) {
|
|
233
|
+
joined_tags = tags.join(` or `);
|
|
234
|
+
}
|
|
235
|
+
// If we get a list of seemingly no tags, assume they meant to use tags
|
|
236
|
+
else if (tags.every((val) => !val.startsWith(`@`))) {
|
|
237
|
+
joined_tags = tags.map((val) => `@` + val).join(` or `);
|
|
238
|
+
}
|
|
239
|
+
// If it's a mix of tags and no tags, it's probably a boolean expression!
|
|
240
|
+
else {
|
|
241
|
+
joined_tags = tags.join(` `);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// config.tags = joined_tags;
|
|
245
|
+
return joined_tags;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function get_feature_paths() {
|
|
252
|
+
// Turn all the paths to the sources folders into paths to the feature files
|
|
253
|
+
let sources_paths = session_vars.get_sources_paths();
|
|
254
|
+
let feature_paths = [];
|
|
255
|
+
|
|
256
|
+
// Get all the paths for the right feature files (for cucumber to use)
|
|
257
|
+
for ( let sources_path of sources_paths ) {
|
|
258
|
+
const generators_prefix = `alkiln_random`;
|
|
259
|
+
constrained_random_tests.create({ generators_prefix, sources_path });
|
|
260
|
+
// Ignore feature files that we parsed ourselves
|
|
261
|
+
let glob = fast_glob.sync(
|
|
262
|
+
`${ sources_path }/**/*.feature`,
|
|
263
|
+
{ ignore: [`${ sources_path }/${ generators_prefix }*.feature`] }
|
|
264
|
+
);
|
|
265
|
+
feature_paths.push( ...glob );
|
|
266
|
+
}
|
|
267
|
+
}
|
package/lib/utils/reports.js
CHANGED
|
@@ -66,14 +66,20 @@ 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
|
|
|
@@ -115,25 +121,66 @@ reports.saveFinalScenarioData = function( scope, { status=`in progress` }) {
|
|
|
115
121
|
this.setReportScenarioStatus( scope, { status });
|
|
116
122
|
let scenario = scope.report.get( scope.scenario_id );
|
|
117
123
|
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
|
-
);
|
|
124
|
+
if ( scenario.status === `PASSED` ) {
|
|
125
|
+
this.successes.push( report );
|
|
126
|
+
} else {
|
|
127
|
+
this._save_unexpected( scope, { status: scenario.status, report });
|
|
130
128
|
}
|
|
129
|
+
// if ( scenario.status === `FAILED` ) {
|
|
130
|
+
// this.failures.push( report );
|
|
131
|
+
// }
|
|
132
|
+
// else if ( scenario.status === `PASSED` ) { this.successes.push( report ); }
|
|
133
|
+
// // else if ( scenario.status === `WARNING` ) { passed_reports += report; this.warnings.push( report ); }
|
|
134
|
+
// // else if ( scenario.status === `UNDEFINED` ) { passed_reports += report; this.undefineds.push( report ); }
|
|
135
|
+
// else { this.others.push( report ); }
|
|
136
|
+
|
|
137
|
+
// // Save to unexpected results files (worry about optimization later)
|
|
138
|
+
// if ( this.failures.length > 1 || this.others.length > 1 ) {
|
|
139
|
+
// let unexpected_results = this.title + this.getPrintableUnexpected( scope );
|
|
140
|
+
// fs.writeFileSync( scope.paths.report_of_unexpected, unexpected_results );
|
|
141
|
+
// fs.appendFileSync( scope.paths.debug_of_unexpected, unexpected_results );
|
|
142
|
+
// }
|
|
143
|
+
|
|
131
144
|
// Save to reports.txt (worry about optimization later)
|
|
132
145
|
fs.writeFileSync( scope.paths.report, this.getPrintableReport( scope ) );
|
|
133
146
|
|
|
134
147
|
return scenario;
|
|
135
148
|
}; // Ends reports.saveFinalScenarioData()
|
|
136
149
|
|
|
150
|
+
reports._save_unexpected = function(scope, { status, report }) {
|
|
151
|
+
/** Store the unexpected report and write to the unexpected report file.
|
|
152
|
+
*
|
|
153
|
+
* @param {Object} scope - State and methods of the current tests
|
|
154
|
+
* @param {Object} data
|
|
155
|
+
* @param {Object} data.status - Status of the current scenario
|
|
156
|
+
* @param {string} data.report - Report for the current scenario
|
|
157
|
+
* */
|
|
158
|
+
if ( status === `FAILED` ) {
|
|
159
|
+
this.failures.push( report );
|
|
160
|
+
// } else if ( status === `WARNING` ) { passed_reports += report; this.warnings.push( report );
|
|
161
|
+
// } else if ( status === `UNDEFINED` ) { passed_reports += report; this.undefineds.push( report ); }
|
|
162
|
+
} else {
|
|
163
|
+
this.others.push( report );
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// If it's our first unexpected result, add the title to the top of the page
|
|
167
|
+
let already_unexpected = ``;
|
|
168
|
+
try { already_unexpected = fs.readFileSync( scope.paths.report_of_unexpected ); }
|
|
169
|
+
catch ( ignore_non_critical_error ) {}
|
|
170
|
+
if ( already_unexpected === `` ) {
|
|
171
|
+
try { fs.appendFileSync( scope.paths.report_of_unexpected, this.title ); }
|
|
172
|
+
catch ( ignore_non_critical_error ) {}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try { fs.appendFileSync( scope.paths.report_of_unexpected, report ); }
|
|
176
|
+
catch ( ignore_non_critical_error ) {}
|
|
177
|
+
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
reports.progress = function ({ logs=[] }) {
|
|
182
|
+
// TODO: save stdout-style to report_log, object, and getting printable scenario
|
|
183
|
+
};
|
|
137
184
|
|
|
138
185
|
reports.setReportScenarioStatus = function( scope, { status=`PASSED` }) {
|
|
139
186
|
/** Add final status of scenario to scenario report object - passed, failed, etc. */
|
|
@@ -203,7 +250,7 @@ reports.getPrintableScenario = function( scenario ) {
|
|
|
203
250
|
return report;
|
|
204
251
|
}; // Ends reports.getPrintableScenario()
|
|
205
252
|
|
|
206
|
-
let getLineText = function ({ type=`no type`, code=`
|
|
253
|
+
let getLineText = function ({ type=`no type`, code=`ALK00rl`, value=`` }) {
|
|
207
254
|
let start = ``;
|
|
208
255
|
if ( type.includes(`page_id`) ) { start += `screen id: `; }
|
|
209
256
|
else if ( type.includes(`outcome`) ) { start += `\n`; } // extra new line
|
|
@@ -212,7 +259,7 @@ let getLineText = function ({ type=`no type`, code=`ALK0000`, value=`` }) {
|
|
|
212
259
|
return `${ start }${ value }`;
|
|
213
260
|
}; // Ends getLine()
|
|
214
261
|
|
|
215
|
-
let getDebugLine = function ({ type=``, code=`
|
|
262
|
+
let getDebugLine = function ({ type=``, code=`ALK00rd`, value=`` }) {
|
|
216
263
|
/** In debug log lines, include all the information possible. */
|
|
217
264
|
|
|
218
265
|
// `plain` is usually used for visual clarity and shouldn't have
|
|
@@ -222,7 +269,7 @@ let getDebugLine = function ({ type=``, code=`ALK0000`, value=`` }) {
|
|
|
222
269
|
let start = ``;
|
|
223
270
|
|
|
224
271
|
// Get all the relevant emoji
|
|
225
|
-
if ( type.includes(`debug`) ) { start +=
|
|
272
|
+
if ( type.includes(`debug`) ) { start += `🐛`; }
|
|
226
273
|
if ( type.includes(`info`) ) { start += `💡`; }
|
|
227
274
|
if ( type.includes(`success`) ) { start += `🌈`; }
|
|
228
275
|
if ( type.includes(`warning`) ) { start += `🔎`; }
|