@suffolklitlab/alkiln 5.11.0 → 5.11.2-feat-output-1
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 +40 -15
- package/.github/workflows/playground.yml +2 -7
- package/CHANGELOG.md +44 -2
- package/action.yml +49 -0
- package/lib/run_cucumber.js +25 -9
- package/lib/scope.js +2 -4
- package/lib/steps.js +95 -49
- package/lib/utils/log.js +5 -1
- package/lib/utils/reports.js +73 -25
- package/lib/utils/session_vars.js +1 -1
- package/package.json +1 -1
- package/tests/log_codes/check_codes.sh +1 -1
|
@@ -70,11 +70,11 @@ jobs:
|
|
|
70
70
|
- run: echo "ALKiln finished starting the isolated GitHub docassemble server"
|
|
71
71
|
shell: bash
|
|
72
72
|
|
|
73
|
-
#### Developer note: You can probably leave this out
|
|
74
|
-
# Optional debugging to explore things like docker issues
|
|
75
|
-
- name: Docker debug tmate session
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
# #### Developer note: You can probably leave this out
|
|
74
|
+
# # Optional debugging to explore things like docker issues
|
|
75
|
+
# - name: Docker debug tmate session
|
|
76
|
+
# if: ${{ failure() && github.event.inputs.enable_tmate == 'true' }}
|
|
77
|
+
# uses: mxschmitt/action-tmate@v3
|
|
78
78
|
|
|
79
79
|
#### Developer note: You can probably leave this out
|
|
80
80
|
- name: Run ALKiln - Env vars for our own tests from server output
|
|
@@ -93,25 +93,50 @@ jobs:
|
|
|
93
93
|
|
|
94
94
|
- name: "Run ALKiln tests"
|
|
95
95
|
if: ${{ success() }}
|
|
96
|
+
id: alkiln
|
|
96
97
|
#### Developer note: you'll need to replace `uses: ./`
|
|
97
98
|
#### with the path to ALKiln’s repository name and branch name. For example,
|
|
98
99
|
#### for version 5, use the branch name `v5`: `uses: suffolkLITLab/ALKiln@v5`
|
|
99
100
|
uses: ./
|
|
100
|
-
#### Developer note: You can probably leave this
|
|
101
|
+
#### Developer note: You can probably leave out this `env` section
|
|
101
102
|
env:
|
|
102
|
-
|
|
103
|
+
# Internal: docassemble changes sometimes break @a11y tests. That is
|
|
104
|
+
# a da problem, not ours. We will trigger these tests manually when we
|
|
105
|
+
# want to check up on this.
|
|
106
|
+
ALKILN_TAG_EXPRESSION: "${{ (github.event.inputs.tags && format('{0}', github.event.inputs.tags)) || '\(not @a11y\) and \(not @error\)' }}"
|
|
103
107
|
with:
|
|
104
108
|
#### Developer note: Required inputs. See
|
|
105
109
|
#### https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/writing/#sandbox-inputs
|
|
106
110
|
SERVER_URL: "${{ steps.github_server.outputs.SERVER_URL }}"
|
|
107
111
|
DOCASSEMBLE_DEVELOPER_API_KEY: "${{ steps.github_server.outputs.DOCASSEMBLE_DEVELOPER_API_KEY }}"
|
|
108
112
|
INSTALL_METHOD: "server"
|
|
109
|
-
#### Developer note:
|
|
110
|
-
####
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
#### Developer note: You can probably leave the rest out
|
|
114
|
+
#### To learn more, see https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/writing/#optional-inputs
|
|
115
|
+
ALKILN_TAG_EXPRESSION: "${{ $ALKILN_TAG_EXPRESSION }}"
|
|
116
|
+
ALKILN_VERSION: output
|
|
117
|
+
|
|
118
|
+
#### Developer note: Example of making an issue when tests fail, part 1
|
|
119
|
+
- name: Get ALKiln output files
|
|
120
|
+
run: |
|
|
121
|
+
echo "REPORT=${{ steps.alkiln.outputs.REPORT }}" >> $GITHUB_ENV
|
|
122
|
+
echo "UNEXPECTED_RESULTS=${{ steps.alkiln.outputs.UNEXPECTED_RESULTS }}" >> $GITHUB_ENV
|
|
123
|
+
echo "DEBUG_LOG=${{ steps.alkiln.outputs.DEBUG_LOG }}" >> $GITHUB_ENV
|
|
124
|
+
|
|
125
|
+
#### Developer note: Example of making an issue when tests fail, part 2
|
|
126
|
+
#### You can do this with any type of GitHub test
|
|
127
|
+
#### Change the name, title, body, and labels
|
|
128
|
+
#### To learn more, search online for actions-ecosystem/action-create-issue
|
|
129
|
+
- name: "If tests failed, create an issue"
|
|
130
|
+
if: ${{ failure() && '$UNEXPECTED_RESULTS' != 'false' }}
|
|
131
|
+
uses: actions-ecosystem/action-create-issue@v1
|
|
132
|
+
with:
|
|
133
|
+
github_token: ${{ secrets.github_token }}
|
|
134
|
+
title: "ALKiln tests failed"
|
|
135
|
+
body: |
|
|
136
|
+
An ALKiln test run failed. See the action at ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}.
|
|
137
|
+
|
|
138
|
+
### Report of unexpected results:
|
|
117
139
|
|
|
140
|
+
${{ env.UNEXPECTED_RESULTS }}
|
|
141
|
+
labels: |
|
|
142
|
+
testing
|
|
@@ -55,18 +55,13 @@ jobs:
|
|
|
55
55
|
# Place the root directory in this branch to access relative paths to action files
|
|
56
56
|
- uses: actions/checkout@v4
|
|
57
57
|
|
|
58
|
-
- name: Pause between triggers
|
|
59
|
-
if: (github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork)
|
|
60
|
-
shell: bash
|
|
61
|
-
run: sleep 60
|
|
62
|
-
|
|
63
58
|
#### Developer note: you'll need to replace `uses: ./`
|
|
64
59
|
#### with the path to ALKiln’s repository name and branch name. For example,
|
|
65
60
|
#### for version 5, use the branch name `v5`: `uses: suffolkLITLab/ALKiln@v5`
|
|
66
61
|
- uses: ./
|
|
67
62
|
#### Developer note: You can probably leave this out
|
|
68
63
|
env:
|
|
69
|
-
ALKILN_TAG_EXPRESSION: "${{ (github.event.inputs.tags && format('{0}', github.event.inputs.tags)) || 'not @a11y' }}"
|
|
64
|
+
ALKILN_TAG_EXPRESSION: "${{ (github.event.inputs.tags && format('{0}', github.event.inputs.tags)) || '\(not @a11y\) and \(not @error\)' }}"
|
|
70
65
|
with:
|
|
71
66
|
#### Developer note: Required ALKiln inputs. You can read more at
|
|
72
67
|
#### https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/writing/#githubNyou-inputs
|
|
@@ -77,6 +72,6 @@ jobs:
|
|
|
77
72
|
# Internal: docassemble changes sometimes break @a11y tests. That is
|
|
78
73
|
# a da problem, not ours. We will trigger these tests manually when we
|
|
79
74
|
# want to check up on this.
|
|
80
|
-
ALKILN_TAG_EXPRESSION: $ALKILN_TAG_EXPRESSION
|
|
75
|
+
ALKILN_TAG_EXPRESSION: "${{ $ALKILN_TAG_EXPRESSION }}"
|
|
81
76
|
#### Developer note: You can probably leave this out
|
|
82
77
|
# ALKILN_VERSION: url
|
package/CHANGELOG.md
CHANGED
|
@@ -43,9 +43,51 @@ Format:
|
|
|
43
43
|
-
|
|
44
44
|
-->
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
## [Unreleased]
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- New outputs from the ALKiln action - `REPORT`, `UNEXPECTED_RESULTS_PATH`, and `DEBUG_LOG_PATH`. Authors can choose to use these as they wish. For example, they can put the contents of these in issues they create from their workflow. Closes [#701](https://github.com/SuffolkLITLab/ALKiln/issues/701).
|
|
51
|
+
- New file with just failure results, including cucumber results when possible. It accumulates Scenario info as the tests run. Even if a test run stops early, there will still be some results.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- report.txt should accumulate over time instead of being created all at once at the end. Even if a test run stops early, there will still be some results.
|
|
56
|
+
- report.txt includes the cucumber output at the bottom as well.
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- Tag expression not being used correctly. This requires the author to wrap some tag expressions in quotes - those using complex syntax that includes things like parenthesis. For example, "(" or ")".
|
|
61
|
+
|
|
62
|
+
### Internal
|
|
63
|
+
|
|
64
|
+
- Removed our own workflow's pause between triggers
|
|
65
|
+
- Improved our use of ALKIN_TAG_EXPRESSION env var
|
|
66
|
+
-
|
|
67
|
+
|
|
68
|
+
## [5.11.2] - 2024-05-17
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- Added new debug log/code to reflect difference between a expected status mismatch and a cucumber status mismatch.
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
|
|
76
|
+
- Changed text of error to make it more clear.
|
|
77
|
+
- Changed logic of error finding to use expected status for users as well as internal tests. That needs discussion.
|
|
78
|
+
- Refactored code in that section.
|
|
79
|
+
- Wait till the end to throw some errors so the user can get as much info as possible about the failures.
|
|
80
|
+
|
|
81
|
+
### Internal
|
|
82
|
+
|
|
83
|
+
- Exclude another file from code checking (cucumber-report.txt)
|
|
84
|
+
|
|
85
|
+
## [5.11.1] - 2024-04-06
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
- Typo in run_cucumber.js
|
|
47
89
|
|
|
48
|
-
## [5.11] - 2024-04-05
|
|
90
|
+
## [5.11.0] - 2024-04-05
|
|
49
91
|
|
|
50
92
|
### Added
|
|
51
93
|
|
package/action.yml
CHANGED
|
@@ -34,6 +34,18 @@ inputs:
|
|
|
34
34
|
required: false
|
|
35
35
|
default: "0.0.17"
|
|
36
36
|
|
|
37
|
+
|
|
38
|
+
outputs:
|
|
39
|
+
REPORT:
|
|
40
|
+
description: 'report.txt contents or "false" if the file is missing'
|
|
41
|
+
value: ${{ steps.files.outputs.REPORT }}
|
|
42
|
+
UNEXPECTED_RESULTS:
|
|
43
|
+
description: 'unexpected_results.txt contents or "false" if the file is missing'
|
|
44
|
+
value: ${{ steps.files.outputs.UNEXPECTED_RESULTS }}
|
|
45
|
+
DEBUG_LOG:
|
|
46
|
+
description: 'debug_log.txt contents or "false" if the file is missing'
|
|
47
|
+
value: ${{ steps.files.outputs.DEBUG_LOG }}
|
|
48
|
+
|
|
37
49
|
runs:
|
|
38
50
|
using: "composite"
|
|
39
51
|
steps:
|
|
@@ -137,6 +149,43 @@ runs:
|
|
|
137
149
|
name: ${{ env.ARTIFACT_NAME }}
|
|
138
150
|
path: ./alkiln-*
|
|
139
151
|
|
|
152
|
+
# Download artifacts folder internally to create action output
|
|
153
|
+
- name: "💡 ALK0182 INFO: Get uploaded artifacts folder"
|
|
154
|
+
uses: actions/download-artifact@v4
|
|
155
|
+
with:
|
|
156
|
+
name: ${{ env.ARTIFACT_NAME }}
|
|
157
|
+
|
|
158
|
+
# Create output
|
|
159
|
+
- shell: bash
|
|
160
|
+
id: files
|
|
161
|
+
run: |
|
|
162
|
+
# 💡 ALK0183 INFO: Give file contents or 'false' to interview authors
|
|
163
|
+
REPORT_PATH="${ARTIFACT_NAME}/report.txt"
|
|
164
|
+
UNEXPECTED_RESULTS_PATH="${ARTIFACT_NAME}/unexpected_results.txt"
|
|
165
|
+
DEBUG_LOG_PATH="${ARTIFACT_NAME}/debug_log.txt"
|
|
166
|
+
|
|
167
|
+
# Get contents of files if they exist
|
|
168
|
+
if [ -f "$REPORT_PATH" ]; then
|
|
169
|
+
REPORT=$(cat "$REPORT_PATH")
|
|
170
|
+
else
|
|
171
|
+
REPORT="report.txt does not exist"
|
|
172
|
+
fi
|
|
173
|
+
if [ -f "$UNEXPECTED_RESULTS_PATH" ]; then
|
|
174
|
+
UNEXPECTED_RESULTS_PATH=$(cat "$UNEXPECTED_RESULTS_PATH")
|
|
175
|
+
else
|
|
176
|
+
UNEXPECTED_RESULTS_PATH="unexpected_results.txt does not exist"
|
|
177
|
+
fi
|
|
178
|
+
if [ -f "$DEBUG_LOG_PATH" ]; then
|
|
179
|
+
DEBUG_LOG_PATH=$(cat "$DEBUG_LOG_PATH")
|
|
180
|
+
else
|
|
181
|
+
DEBUG_LOG_PATH="debug_log.txt does not exist"
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
# Create the outputs
|
|
185
|
+
echo "REPORT=$REPORT" >> "$GITHUB_OUTPUT"
|
|
186
|
+
echo "UNEXPECTED_RESULTS=$UNEXPECTED_RESULTS" >> "$GITHUB_OUTPUT"
|
|
187
|
+
echo "DEBUG_LOG=$DEBUG_LOG" >> "$GITHUB_OUTPUT"
|
|
188
|
+
|
|
140
189
|
- shell: bash
|
|
141
190
|
run: |
|
|
142
191
|
echo "💡 ALK0034 INFO: ALkiln finished running tests"
|
package/lib/run_cucumber.js
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const cucumber_api = require('@cucumber/cucumber/api');
|
|
5
5
|
|
|
6
|
-
const log = require('./utils/log');
|
|
6
|
+
const log = require('./utils/log'); // These always log in the console
|
|
7
7
|
const session_vars = require('./utils/session_vars');
|
|
8
8
|
|
|
9
|
+
const SUMMARY_FILENAME = `cucumber-report.txt`;
|
|
10
|
+
|
|
9
11
|
function build_config( command_args ) {
|
|
10
12
|
// Turn all the paths to the sources folders into paths to the feature files
|
|
11
13
|
let sources_paths = session_vars.get_sources_paths();
|
|
@@ -17,13 +19,15 @@ function build_config( command_args ) {
|
|
|
17
19
|
const config = {
|
|
18
20
|
publishQuiet: true,
|
|
19
21
|
require: `./lib/index.js`,
|
|
20
|
-
format: [`progress`, `summary
|
|
22
|
+
format: [`progress`, `summary:${ SUMMARY_FILENAME }`],
|
|
21
23
|
paths: feature_paths,
|
|
22
24
|
retry: 1,
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
// Normalize tag expressions, if there are any
|
|
26
|
-
|
|
28
|
+
|
|
29
|
+
// Joining and splitting handles tags surrounded with `""` or those without `""`
|
|
30
|
+
let tags = command_args[`_`].join(` `).split(` `);
|
|
27
31
|
if (tags.length > 0) {
|
|
28
32
|
let joined_tags = '';
|
|
29
33
|
// Cucumber expects tags in boolean expressions, like (@a0 or @a1) and @random.
|
|
@@ -101,23 +105,24 @@ function build_config( command_args ) {
|
|
|
101
105
|
}, sources: {paths: []}}, environment);
|
|
102
106
|
|
|
103
107
|
log.info({ type: `run`, code: `ALK0053`, pre: `working directory`, data: codeDir });
|
|
104
|
-
log.info(type: `plain`, pre: `\n\n`);
|
|
108
|
+
log.info({ type: `plain`, pre: `\n\n` });
|
|
105
109
|
|
|
106
110
|
const { success } = await cucumber_api.runCucumber({...runConfiguration, support});
|
|
107
111
|
if ( success ) {
|
|
108
|
-
log.
|
|
112
|
+
log.success({ type: `run`, code: `ALK0054`, pre: `Tests succeeded!` });
|
|
109
113
|
} else {
|
|
110
|
-
log.error({ type: `run`, code: `ALK0055`, pre: `
|
|
114
|
+
log.error({ type: `run`, code: `ALK0055`, pre: `One or more tests got unexpected results` });
|
|
111
115
|
}
|
|
112
|
-
|
|
116
|
+
// Code is our custom code for proper completion with failed tests
|
|
117
|
+
const exitCode = success ? 0 : 2;
|
|
113
118
|
process.exitCode = exitCode;
|
|
114
119
|
|
|
115
120
|
// This is hardcoded information from `provide_config` above.
|
|
116
121
|
// This code isn't used for anything other than detecting and removing color codes.
|
|
117
|
-
let summary_file_name =
|
|
122
|
+
let summary_file_name = SUMMARY_FILENAME;
|
|
118
123
|
fs.readFile(summary_file_name, 'utf8', (err, text) => {
|
|
119
124
|
if (err) {
|
|
120
|
-
log.error({ type: `run`, code: `ALK0056`, pre: `cucumber report`, data: err });
|
|
125
|
+
log.error({ type: `internal run`, code: `ALK0056`, pre: `Failed to read cucumber report`, data: err });
|
|
121
126
|
return;
|
|
122
127
|
}
|
|
123
128
|
|
|
@@ -128,5 +133,16 @@ function build_config( command_args ) {
|
|
|
128
133
|
// Put it in the correct file in the correct folder
|
|
129
134
|
let artifacts_path = session_vars.get_artifacts_path_name();
|
|
130
135
|
fs.appendFileSync( `${ artifacts_path }/${ log.debug_log_file }`, `\n\n${text}`);
|
|
136
|
+
try {
|
|
137
|
+
// Add to a unexpected results file (that should exist already) if needed
|
|
138
|
+
if ( exitCode === 2 ) {
|
|
139
|
+
fs.appendFileSync(
|
|
140
|
+
`${ artifacts_path }/${ log.unexpected_filename }`,
|
|
141
|
+
`\n\n${ text }`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
} catch ( error ) {
|
|
145
|
+
log.error({ type: `internal run`, code: `ALK0181`, pre: `This is sometimes fine. ALKiln was unable to read ${ log.unexpected_filename }, which might just mean there was an unrelated failure above.`, data: error });
|
|
146
|
+
}
|
|
131
147
|
});
|
|
132
148
|
})();
|
package/lib/scope.js
CHANGED
|
@@ -3228,8 +3228,7 @@ module.exports = {
|
|
|
3228
3228
|
*
|
|
3229
3229
|
* @param expected {array} Arr of strs that should appear in the report.
|
|
3230
3230
|
*/
|
|
3231
|
-
let
|
|
3232
|
-
let scenario = [ scope.scenario_id, current_report_obj ]; // Mimic how loop works with `Map`
|
|
3231
|
+
let scenario = scope.report.get( scope.scenario_id );
|
|
3233
3232
|
let report = reports.getPrintableScenario( scenario );
|
|
3234
3233
|
let all_are_included = true;
|
|
3235
3234
|
for ( let one_expectation of expected ) {
|
|
@@ -3248,8 +3247,7 @@ module.exports = {
|
|
|
3248
3247
|
*
|
|
3249
3248
|
* @param not_expected {array} Arr of strs that should not appear in the report.
|
|
3250
3249
|
*/
|
|
3251
|
-
let
|
|
3252
|
-
let scenario = [ scope.scenario_id, current_report_obj ]; // Mimic how loop works with `Map`
|
|
3250
|
+
let scenario = scope.report.get( scope.scenario_id );
|
|
3253
3251
|
let report = reports.getPrintableScenario( scenario );
|
|
3254
3252
|
let none_included = true;
|
|
3255
3253
|
for ( let one_expectation of not_expected ) {
|
package/lib/steps.js
CHANGED
|
@@ -101,8 +101,11 @@ BeforeAll(async () => {
|
|
|
101
101
|
session_vars.save_artifacts_path_name( scope.paths.artifacts );
|
|
102
102
|
|
|
103
103
|
scope.paths.report = `${ scope.paths.artifacts }/report.txt`;
|
|
104
|
+
scope.paths.failures = `${ scope.paths.artifacts }/${ log.unexpected_filename }`;
|
|
104
105
|
scope.paths.debug_log = `${ scope.paths.artifacts }/${ log.debug_log_file }`;
|
|
105
106
|
fs.writeFileSync( scope.paths.debug_log, "" );
|
|
107
|
+
|
|
108
|
+
reports.create( scope );
|
|
106
109
|
});
|
|
107
110
|
|
|
108
111
|
Before(async (scenario) => {
|
|
@@ -1212,7 +1215,7 @@ After(async function(scenario) {
|
|
|
1212
1215
|
}
|
|
1213
1216
|
|
|
1214
1217
|
// Record status before internal report tests can interfere with values
|
|
1215
|
-
reports.
|
|
1218
|
+
reports.saveFinalScenarioData( scope, { status: changeable_test_status });
|
|
1216
1219
|
|
|
1217
1220
|
if (changeable_test_status !== `PASSED`) {
|
|
1218
1221
|
log.debug({
|
|
@@ -1256,13 +1259,13 @@ After(async function(scenario) {
|
|
|
1256
1259
|
} // ends if not passed
|
|
1257
1260
|
|
|
1258
1261
|
// Add report text to Scenario folder after everything has been added to the report
|
|
1259
|
-
let
|
|
1260
|
-
let scenario_report_obj = [ scope.scenario_id, current_report_obj ]; // Mimic how loop works with `Map`
|
|
1262
|
+
let scenario_report_obj = scope.report.get( scope.scenario_id );
|
|
1261
1263
|
let report = reports.getPrintableScenario( scenario_report_obj );
|
|
1262
1264
|
// Save the report as in the Scenario folder
|
|
1263
1265
|
fs.writeFileSync( scope.paths.scenario_report, report );
|
|
1266
|
+
// TODO: Save any cucumber failure message for the Scenario here (instead of waiting till run_cucumber.js)
|
|
1264
1267
|
|
|
1265
|
-
// ---------------- Check internal test results
|
|
1268
|
+
// ---------------- Check internal test results ----------------
|
|
1266
1269
|
|
|
1267
1270
|
if ( scope.expected_in_report && scope.expected_in_report.length > 0) {
|
|
1268
1271
|
let report_includes_all_expected_strings = await scope.reportIncludesAllExpected(
|
|
@@ -1284,74 +1287,120 @@ After(async function(scenario) {
|
|
|
1284
1287
|
scope.expected_not_in_report = null;
|
|
1285
1288
|
}
|
|
1286
1289
|
|
|
1287
|
-
//
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
// If the status was as expected, pass this Scenario
|
|
1292
|
-
changeable_test_status = `PASSED`;
|
|
1293
|
-
scope.expected_status = null; // reset for next Scenario
|
|
1290
|
+
// ---------------- Ends internal results check ----------------
|
|
1291
|
+
|
|
1292
|
+
if ( scope.expected_status === null ) {
|
|
1293
|
+
scope.expected_status = `PASSED`; // default expected status
|
|
1294
1294
|
}
|
|
1295
1295
|
|
|
1296
|
-
|
|
1296
|
+
let expected_status_was_met = true;
|
|
1297
|
+
let expected_status_err_msg = ``;
|
|
1298
|
+
// Avoid manipulating `UNDEFINED` and such
|
|
1299
|
+
if ( changeable_test_status === `PASSED`
|
|
1300
|
+
|| changeable_test_status === `FAILED` ) {
|
|
1301
|
+
|
|
1302
|
+
if ( changeable_test_status === scope.expected_status ) {
|
|
1303
|
+
// If the status was as expected, pass this Scenario. E.g. an internal
|
|
1304
|
+
// test was supposed to fail and it did. Or a test was suppose to pass
|
|
1305
|
+
// and it passed.
|
|
1306
|
+
changeable_test_status = `PASSED`;
|
|
1307
|
+
} else {
|
|
1308
|
+
// If a test that was supposed to fail passed (e.g. internal
|
|
1309
|
+
// test), it failed. If is was supposed to pass and failed, it also
|
|
1310
|
+
// failed. E.g. a11y failures let the Scenario pass initially, but
|
|
1311
|
+
// should cause a failure here.
|
|
1312
|
+
changeable_test_status = `FAILED`;
|
|
1313
|
+
// Flag this failure to trigger an error later, so cucumber shows
|
|
1314
|
+
// it as failed too.
|
|
1315
|
+
expected_status_was_met = false;
|
|
1316
|
+
// Keep it general. Users may see this message, e.g. for a11y tests.
|
|
1317
|
+
let msg = `Test failed. Expected status: ${scope.expected_status}. `
|
|
1318
|
+
+ `ALKiln's assessed status: ${ changeable_test_status }. `
|
|
1319
|
+
+ `See the report for details.`;
|
|
1320
|
+
let code = `ALK0099`;
|
|
1321
|
+
log.debug({ type: `error`, code: code, pre: msg, })
|
|
1322
|
+
expected_status_err_msg = `🤕 ${ code } ERROR: ${ msg }`
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
scope.expected_status = null; // reset for next Scenario
|
|
1326
|
+
|
|
1327
|
+
let need_to_force_failed_status = false;
|
|
1328
|
+
let force_failure_msg = `ALKiln assessed that the test failed. See the report for details.`;
|
|
1329
|
+
let force_failure_code = `ALK0180`;
|
|
1330
|
+
if ( changeable_test_status !== `FAILED` ) {
|
|
1331
|
+
// Other than `FAILED`, it's fine to set the scenario status to make
|
|
1332
|
+
// sure it matches up with ALKiln's assessed status.
|
|
1333
|
+
// WARNING: This may be impossible in future cucumber versions
|
|
1334
|
+
scenario.result.status = changeable_test_status;
|
|
1335
|
+
// TODO: Should we add (an info) debug log here too?
|
|
1297
1336
|
|
|
1337
|
+
// If assessed status is FAILED, but result.status is something else
|
|
1338
|
+
} else if ( scenario.result.status !== `FAILED` ) {
|
|
1339
|
+
// Make sure that tests which ALKiln has calculated as failed
|
|
1340
|
+
// cause a cucumber error later. Just changing the status to `FAILED`
|
|
1341
|
+
// causes unhelpful cucumber errors. TODO: Add to internal docs.
|
|
1342
|
+
log.debug({
|
|
1343
|
+
type: `error`,
|
|
1344
|
+
code: force_failure_code,
|
|
1345
|
+
pre: force_failure_msg
|
|
1346
|
+
});
|
|
1347
|
+
// TODO: Look into setDefinitionFunctionWrapper to catch errors
|
|
1348
|
+
// of failing tests that should indeed be failing:
|
|
1349
|
+
// https://github.com/cucumber/cucumber-js/blob/main/docs/support_files/api_reference.md#setdefinitionfunctionwrapperwrapper
|
|
1350
|
+
need_to_force_failed_status = true;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
let signout_succeeded = true;
|
|
1354
|
+
let signout_error_msg = ``;
|
|
1298
1355
|
// If there is a page open, then sign out and close it
|
|
1299
1356
|
if ( scope.page ) {
|
|
1300
1357
|
// Catch a possible reload error.
|
|
1301
|
-
|
|
1302
|
-
// TODO: implement and use scope.handle_possible_timeout()
|
|
1303
1358
|
try {
|
|
1304
1359
|
// puppeteer will ensure proper timeout.
|
|
1305
1360
|
await scope.page.goto(`${ session_vars.get_da_server_url() }/user/sign-out`, {waitUntil: `domcontentloaded`});
|
|
1306
1361
|
|
|
1307
1362
|
} catch ( error ) {
|
|
1363
|
+
signout_succeeded = false;
|
|
1308
1364
|
|
|
1309
1365
|
if ( error.name === `TimeoutError` ) {
|
|
1310
1366
|
// TODO: Should a more specific message about signing out get into the report no matter what?
|
|
1311
1367
|
// If so, what should the message be in order to not get confused with these other messages?
|
|
1312
1368
|
let non_reload_report_msg = `It took too long to sign out in preparation for the next test.`;
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1369
|
+
let code = `ALK0097`;
|
|
1370
|
+
try {
|
|
1371
|
+
await scope.handle_page_timeout_error( scope, { non_reload_report_data: {
|
|
1372
|
+
code: code,
|
|
1373
|
+
message: non_reload_report_msg,
|
|
1374
|
+
}, error });
|
|
1375
|
+
} catch {
|
|
1376
|
+
signout_error_msg = `🤕 ${ code } ERROR: ${ non_reload_report_msg }`;
|
|
1377
|
+
}
|
|
1317
1378
|
} else {
|
|
1318
1379
|
// Throw any non-timeout error
|
|
1319
|
-
let
|
|
1320
|
-
|
|
1321
|
-
|
|
1380
|
+
let other_err_msg = `Error occurred when ALKiln tried to sign out in preparation for the next test.`
|
|
1381
|
+
let code = `ALK0098`;
|
|
1382
|
+
reports.addToReport( scope, { type: `error`, code: code, value: other_err_msg });
|
|
1383
|
+
signout_error_msg = `🤕 ${ code } ERROR: ${ other_err_msg }`;
|
|
1322
1384
|
} // ends if error is timeout error
|
|
1323
1385
|
|
|
1324
1386
|
} // ends try/catch
|
|
1325
1387
|
|
|
1326
1388
|
} // ends if scope.page
|
|
1327
1389
|
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
// More internal testing stuff
|
|
1335
|
-
if ( changeable_test_status === `FAILED` ) {
|
|
1336
|
-
// If the result object status wasn't failure and we still want
|
|
1337
|
-
// the tests to fail, as with a11y tests, we can't mess with the
|
|
1338
|
-
// cucumber result.status object because it will cause unwanted
|
|
1339
|
-
// errors. We have to throw an error here instead.
|
|
1340
|
-
if ( scenario.result.status !== `FAILED` ) {
|
|
1341
|
-
let msg = `The test unexpectedly didn't fail. Scenario status: ${scenario.result.status}.`
|
|
1342
|
-
reports.addToReport( scope, { type: `error`, code: `ALK0099`, value: msg });
|
|
1343
|
-
throw new Error(msg);
|
|
1344
|
-
}
|
|
1345
|
-
} else {
|
|
1346
|
-
// Some internal test failures should be ok
|
|
1347
|
-
// TODO: Look into setDefinitionFunctionWrapper to catch errors
|
|
1348
|
-
// of failing tests that should indeed be failing:
|
|
1349
|
-
// https://github.com/cucumber/cucumber-js/blob/main/docs/support_files/api_reference.md#setdefinitionfunctionwrapperwrapper
|
|
1350
|
-
scenario.result.status = changeable_test_status;
|
|
1390
|
+
let errors = []
|
|
1391
|
+
if ( !expected_status_was_met ) { errors.push( expected_status_err_msg ); }
|
|
1392
|
+
if ( !signout_succeeded ) { errors.push( signout_error_msg ); }
|
|
1393
|
+
if ( !expected_status_was_met || !signout_succeeded ) {
|
|
1394
|
+
throw new Error( `${errors.join( errors, '\n')}` );
|
|
1351
1395
|
}
|
|
1352
|
-
// Ends more internal testing stuff
|
|
1353
1396
|
|
|
1354
|
-
//
|
|
1397
|
+
// Theoretically, we should never get here:
|
|
1398
|
+
// If everything else worked, if we need to force an internal test failure
|
|
1399
|
+
if ( need_to_force_failed_status ) {
|
|
1400
|
+
throw new Error( `🤕 ${force_failure_code} unreachable ERROR: ${force_failure_msg}` );
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
// Find the race condition.
|
|
1355
1404
|
log.debug({ type: `cucumber`, code: `ALK0100`, verbose: true, pre: `Scenario AfterStep() message`, data: scenario.result.message });
|
|
1356
1405
|
|
|
1357
1406
|
});
|
|
@@ -1365,9 +1414,6 @@ AfterAll(async function() {
|
|
|
1365
1414
|
// May need newest version of cucumberjs
|
|
1366
1415
|
console.log( `\n\n${ report }` );
|
|
1367
1416
|
|
|
1368
|
-
// Save/download the report as an artifact
|
|
1369
|
-
fs.writeFileSync( scope.paths.report, report );
|
|
1370
|
-
|
|
1371
1417
|
// If there is a browser open, then close it
|
|
1372
1418
|
if (scope.browser) {
|
|
1373
1419
|
await scope.browser.close();
|
package/lib/utils/log.js
CHANGED
|
@@ -2,6 +2,9 @@ const fs = require('fs');
|
|
|
2
2
|
const session_vars = require( './session_vars' );
|
|
3
3
|
|
|
4
4
|
// ? 🌈 💡 🔎 🤕 🐞
|
|
5
|
+
// Alternatives: 🩹 ❤️🩹 🚑
|
|
6
|
+
// TODO: return full message so it can be passed to manually
|
|
7
|
+
// thrown errors if needed.
|
|
5
8
|
|
|
6
9
|
let log = {};
|
|
7
10
|
module.exports = log;
|
|
@@ -89,4 +92,5 @@ log.add_to_debug_log = function(value) {
|
|
|
89
92
|
fs.appendFileSync(log.debug_log_file, value);
|
|
90
93
|
};
|
|
91
94
|
|
|
92
|
-
log.debug_log_file =
|
|
95
|
+
log.debug_log_file = `debug_log.txt`;
|
|
96
|
+
log.unexpected_filename = `unexpected_results.txt`;
|
package/lib/utils/reports.js
CHANGED
|
@@ -7,6 +7,13 @@ const files = require(`./files` );
|
|
|
7
7
|
|
|
8
8
|
module.exports = reports = {};
|
|
9
9
|
|
|
10
|
+
reports.title = `Assembly Line Kiln Automated Testing Report - ${ (new Date()).toUTCString() }\n`;
|
|
11
|
+
reports.failures = [ `\n\n===============================\n===============================\n🤕 Failed scenarios:\n` ];
|
|
12
|
+
reports.successes = [ `\n\n===============================\n===============================\n🌈 Passed scenarios:\n` ];
|
|
13
|
+
// reports.warnings = [ `\n\n===============================\n===============================\n🔎 Scenarios to check:\n` ];
|
|
14
|
+
// reports.undefineds = [ `\n\n===============================\n===============================\n[langs icon] Scenarios with typos:\n` ];
|
|
15
|
+
reports.others = [ `\n\n===============================\n===============================\n🔎 Other scenarios that did not pass:\n` ];
|
|
16
|
+
|
|
10
17
|
reports.addReportHeading = function (scope, { scenario }) {
|
|
11
18
|
/** Return printable heading based on Scenario name and tags.
|
|
12
19
|
* Allow characters from other languages.
|
|
@@ -46,6 +53,9 @@ reports.addToReport = function( scope, { type, code, value }) {
|
|
|
46
53
|
* @params code {string} (Optional) Error or warning code. Default
|
|
47
54
|
* is unspecified error or warning.
|
|
48
55
|
* */
|
|
56
|
+
// TODO: return full message so it can be passed to manually
|
|
57
|
+
// thrown errors if needed.
|
|
58
|
+
|
|
49
59
|
// Debugging logs
|
|
50
60
|
let debug_line = getDebugLine({ type, code, value });
|
|
51
61
|
if ( session_vars.get_debug() ) {
|
|
@@ -68,21 +78,30 @@ reports.addToReport = function( scope, { type, code, value }) {
|
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
// Report stuff
|
|
71
|
-
let scenario =
|
|
81
|
+
let scenario = reports.makeSureScenarioExists( scope );
|
|
72
82
|
scenario.lines.push({ type, code, value });
|
|
73
83
|
|
|
74
84
|
return scenario;
|
|
75
85
|
}; // Ends reports.addToReport()
|
|
76
86
|
|
|
77
|
-
|
|
87
|
+
reports.create = function( scope ) {
|
|
88
|
+
reports.makeSureReportExists( scope );
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
reports.makeSureReportExists = function( scope ) {
|
|
78
92
|
/** If a part of the report needs to exist right now, but doesn't exist,
|
|
79
93
|
* create it. */
|
|
80
94
|
if ( !scope.report ) { scope.report = new Map(); }
|
|
95
|
+
return this;
|
|
96
|
+
}; // Ends reports.makeSureReportExists()
|
|
97
|
+
|
|
98
|
+
reports.makeSureScenarioExists = function ( scope ) {
|
|
99
|
+
reports.makeSureReportExists( scope );
|
|
81
100
|
if ( !scope.scenario_id ) {
|
|
82
101
|
scope.scenario_id = `_report_${ Date.now() }`;
|
|
83
102
|
}
|
|
84
103
|
if ( !scope.report.get( scope.scenario_id )) {
|
|
85
|
-
scope.report.set( scope.scenario_id, {} )
|
|
104
|
+
scope.report.set( scope.scenario_id, {} );
|
|
86
105
|
scope.report.get( scope.scenario_id ).status = `in progress`;
|
|
87
106
|
}
|
|
88
107
|
|
|
@@ -90,11 +109,36 @@ let makeSureReportPartsExist = function( scope ) {
|
|
|
90
109
|
if ( !scenario.lines ) { scenario.lines = []; }
|
|
91
110
|
|
|
92
111
|
return scenario;
|
|
93
|
-
}; // Ends
|
|
112
|
+
}; // Ends reports.makeSureScenarioExists()
|
|
113
|
+
|
|
114
|
+
reports.saveFinalScenarioData = function( scope, { status=`in progress` }) {
|
|
115
|
+
this.setReportScenarioStatus( scope, { status });
|
|
116
|
+
let scenario = scope.report.get( scope.scenario_id );
|
|
117
|
+
let report = this.getPrintableScenario( scenario );
|
|
118
|
+
if ( scenario.status === `FAILED` ) { this.failures.push( report ); }
|
|
119
|
+
else if ( scenario.status === `PASSED` ) { this.successes.push( report ); }
|
|
120
|
+
// else if ( scenario.status === `WARNING` ) { passed_reports += report; this.warnings.push( report ); }
|
|
121
|
+
// else if ( scenario.status === `UNDEFINED` ) { passed_reports += report; this.undefineds.push( report ); }
|
|
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
|
+
);
|
|
130
|
+
}
|
|
131
|
+
// Save to reports.txt (worry about optimization later)
|
|
132
|
+
fs.writeFileSync( scope.paths.report, this.getPrintableReport( scope ) );
|
|
133
|
+
|
|
134
|
+
return scenario;
|
|
135
|
+
}; // Ends reports.saveFinalScenarioData()
|
|
136
|
+
|
|
94
137
|
|
|
95
138
|
reports.setReportScenarioStatus = function( scope, { status=`PASSED` }) {
|
|
96
139
|
/** Add final status of scenario to scenario report object - passed, failed, etc. */
|
|
97
140
|
scope.report.get( scope.scenario_id ).status = status;
|
|
141
|
+
return scope.report.get( scope.scenario_id );
|
|
98
142
|
}; // Ends reports.setReportScenarioStatus()
|
|
99
143
|
|
|
100
144
|
reports.convertToOriginalStoryTableRow = function( scope, { row_data }) {
|
|
@@ -121,34 +165,38 @@ let trimJSON = function( str=`` ) {
|
|
|
121
165
|
}; // Ends trimJSON()
|
|
122
166
|
|
|
123
167
|
reports.getPrintableReport = function( scope ) {
|
|
124
|
-
/** Return a string generated from test report data.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
let [ scenario_id, data ] = scenario;
|
|
135
|
-
if ( data.status === `FAILED` ) { failed_reports += report; }
|
|
136
|
-
else if ( data.status === `PASSED` ) { passed_reports += report; }
|
|
137
|
-
else { other_reports += report; }
|
|
138
|
-
}; // ends for every scenario
|
|
139
|
-
|
|
140
|
-
if ( failed_reports !== `` ) { report += `\n\n===============================\n===============================\n🤕 Failed scenarios:\n${ failed_reports }`; }
|
|
141
|
-
if ( other_reports !== `` ) { report += `\n\n===============================\n===============================\n🔎 Scenarios that did not pass:\n${ other_reports }`; }
|
|
142
|
-
if ( passed_reports !== `` ) { report += `\n\n===============================\n===============================\n🌈 Passed scenarios:\n${ passed_reports }`; }
|
|
168
|
+
/** Return a string generated from test report data.
|
|
169
|
+
*
|
|
170
|
+
* First list failed scenarios, then non-passed scenarios, then passed scenarios. */
|
|
171
|
+
let report = this.getTitle( scope );
|
|
172
|
+
|
|
173
|
+
if ( this.failures.length > 1 ) { report += this.failures.join( `` ); }
|
|
174
|
+
if ( this.successes.length > 1 ) { report += this.successes.join( `` ); }
|
|
175
|
+
// if ( this.warnings.length > 1 ) {}
|
|
176
|
+
// if ( this.undefineds.length > 1 ) {}
|
|
177
|
+
if ( this.others.length > 1 ) { report += this.others.join( `` ); }
|
|
143
178
|
|
|
144
179
|
return report;
|
|
145
180
|
}; // Ends reports.getPrintableReport()
|
|
146
181
|
|
|
182
|
+
reports.getTitle = function( scope ) { return this.title; }
|
|
183
|
+
|
|
184
|
+
reports.getPrintableUnexpected = function( scope ) {
|
|
185
|
+
// Note about `> 1`: The first line of the report section always exists
|
|
186
|
+
let output = ``;
|
|
187
|
+
if ( this.failures.length > 1 ) {
|
|
188
|
+
output += this.failures.join(``);
|
|
189
|
+
}
|
|
190
|
+
if ( this.others.length > 1 ) {
|
|
191
|
+
output += this.others.join(``);
|
|
192
|
+
}
|
|
193
|
+
return output;
|
|
194
|
+
}; // Ends reports.getPrintableUnexpected()
|
|
195
|
+
|
|
147
196
|
reports.getPrintableScenario = function( scenario ) {
|
|
148
197
|
/** Return a string generated from scenario list of report items. */
|
|
149
|
-
let [ scenario_id, data ] = scenario;
|
|
150
198
|
let report = ``;
|
|
151
|
-
for ( let line of
|
|
199
|
+
for ( let line of scenario.lines ) {
|
|
152
200
|
let { type, code, value } = line;
|
|
153
201
|
report += `${ getLineText({ type, code, value }) }\n`;
|
|
154
202
|
}
|
|
@@ -120,7 +120,7 @@ session_vars.save_project_name = function ( project_name ) {
|
|
|
120
120
|
// Warn local developers if file already exists
|
|
121
121
|
if ( fs.existsSync( runtime_config_path ) ) {
|
|
122
122
|
let old_project_name = session_vars.get_project_name();
|
|
123
|
-
let session_project_already_exists_msg = `🔎 ALK0035 WARNING: `
|
|
123
|
+
let session_project_already_exists_msg = `🔎 ALK0035 setup WARNING: `
|
|
124
124
|
+ `This session had a project name already: ${ old_project_name }. `
|
|
125
125
|
+ `A new project has now been created called ${ project_name }. `
|
|
126
126
|
+ `Remember to delete ${ old_project_name }`;
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@ fi
|
|
|
16
16
|
# The grepped files will include the list of all the codes that have been
|
|
17
17
|
# removed and are no longer used. Syntax used works with GitHub cli ([[:digit:]])
|
|
18
18
|
# https://stackoverflow.com/a/6901221
|
|
19
|
-
lines=$(grep -roh --exclude="tests/log_codes/check_codes.sh" --exclude="debug_log.txt" --exclude-dir="node_modules" --exclude-dir='alkiln-*' --exclude-dir='_alkiln*' 'ALK[[:digit:]][[:digit:]][[:digit:]][[:digit:]]' "$directory")
|
|
19
|
+
lines=$(grep -roh --exclude="tests/log_codes/check_codes.sh" --exclude="debug_log.txt" --exclude="cucumber-report.txt" --exclude-dir="node_modules" --exclude-dir='alkiln-*' --exclude-dir='_alkiln*' 'ALK[[:digit:]][[:digit:]][[:digit:]][[:digit:]]' "$directory")
|
|
20
20
|
sorted=$(echo "$lines" | sort -n)
|
|
21
21
|
|
|
22
22
|
|