@suffolklitlab/alkiln 5.2.1 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/lib/scope.js +85 -202
- package/lib/steps.js +66 -49
- package/lib/utils/reports.js +147 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -44,6 +44,18 @@ Format:
|
|
|
44
44
|
-->
|
|
45
45
|
<!-- ## [Unreleased] -->
|
|
46
46
|
|
|
47
|
+
## [5.3.0] - 2023-10-14
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
- Screenshots now come with an HTML file of the same page. Pages can be examined for their full errors and for accessibility details. Note that styles are weird because we don't control the page with js like docassemble does. Otherwise hidden elements are visible, etc. See [#763](https://github.com/SuffolkLITLab/ALKiln/issues/763).
|
|
51
|
+
|
|
52
|
+
### Fixed
|
|
53
|
+
- Fixed and improved error for accessibility failures. See [#744](https://github.com/SuffolkLITLab/ALKiln/issues/744).
|
|
54
|
+
|
|
55
|
+
### Internal
|
|
56
|
+
- Refatored report functions into their own file. See https://github.com/SuffolkLITLab/ALKiln/issues/770.
|
|
57
|
+
- Changed the format of the aXe file name to shorten it by avoiding repeating the scenario name.
|
|
58
|
+
|
|
47
59
|
## [5.2.1] - 2023-09-29
|
|
48
60
|
|
|
49
61
|
### Added
|
package/lib/scope.js
CHANGED
|
@@ -17,6 +17,7 @@ const { waitForTimeout } = require('./utils/time');
|
|
|
17
17
|
const files = require('./utils/files' );
|
|
18
18
|
const server = require('./docassemble/docassemble_api_interface');
|
|
19
19
|
const wrapPromiseWithTimeout = require('./cucumber_8_shim');
|
|
20
|
+
const reports = require(`./utils/reports`);
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
let ordinal_to_integer = {
|
|
@@ -82,152 +83,10 @@ let toBase64 = function ( utf8_str, exclude_padding=true) {
|
|
|
82
83
|
|
|
83
84
|
let proxies_regex = /(\bx\.)|(\bx\[)|(\[[ijklmn]\])/g;
|
|
84
85
|
let da_base64_regex = /^[A-Za-z0-9+/]+$/;
|
|
85
|
-
let misc_artifacts_dir = `_alkiln-misc_artifacts`;
|
|
86
86
|
|
|
87
87
|
module.exports = {
|
|
88
88
|
trigger_not_needed_flag: `ALKiln: no trigger variable needed`,
|
|
89
89
|
|
|
90
|
-
addToReport: async function( scope, { type, value }) {
|
|
91
|
-
/* Add an item to a specific list in a scenario's report.
|
|
92
|
-
* Create report if necessary.
|
|
93
|
-
*
|
|
94
|
-
* The report has a key for each scenario and each of those
|
|
95
|
-
* keys should correspond to a list containing the relevant data. */
|
|
96
|
-
if ( session_vars.get_debug() ) { console.log(`Report:\ntype: ${ type }, value: ${ value }\n`) }
|
|
97
|
-
let scenario = await scope.makeSureReportPartsExist( scope );
|
|
98
|
-
scenario.lines.push({ type, value });
|
|
99
|
-
|
|
100
|
-
// TODO: Not sure how to use .add_to_debug_log() here considering possibly dynamic filepath
|
|
101
|
-
// log.add_to_debug_log(`Report:\ntype: ${ type }, value: ${ value }\n`);
|
|
102
|
-
|
|
103
|
-
// Ensure filepath for saving local unit test logs
|
|
104
|
-
if ( !scope.paths ) {
|
|
105
|
-
// TODO: would it be useful to put in existing artifacts folder if possible.
|
|
106
|
-
// Can this be usefully abstracted?
|
|
107
|
-
scope.paths = { debug_log: `${ misc_artifacts_dir }/unit_tests_logs-${ files.readable_date() }.txt` };
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
fs.appendFileSync(scope.paths.debug_log, `\nReport:\ntype: ${ type }, value: ${ value }`);
|
|
112
|
-
} catch (err) {
|
|
113
|
-
fs.mkdirSync( misc_artifacts_dir );
|
|
114
|
-
fs.appendFileSync(scope.paths.debug_log, `\nReport:\ntype: ${ type }, value: ${ value }`);
|
|
115
|
-
}
|
|
116
|
-
return scenario;
|
|
117
|
-
|
|
118
|
-
}, // Ends scope.addToReport()
|
|
119
|
-
|
|
120
|
-
makeSureReportPartsExist: async function( scope ) {
|
|
121
|
-
/* If a part of the report needs to exist right now, but doesn't exist,
|
|
122
|
-
* create it. */
|
|
123
|
-
if ( !scope.report ) { scope.report = new Map(); }
|
|
124
|
-
if ( !scope.scenario_id ) {
|
|
125
|
-
scope.scenario_id = `_report_${ Date.now() }`;
|
|
126
|
-
}
|
|
127
|
-
if ( !scope.report.get( scope.scenario_id )) {
|
|
128
|
-
scope.report.set( scope.scenario_id, {} )
|
|
129
|
-
scope.report.get( scope.scenario_id ).status = `in progress`;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
let scenario = scope.report.get( scope.scenario_id );
|
|
133
|
-
if ( !scenario.lines ) { scenario.lines = []; }
|
|
134
|
-
|
|
135
|
-
return scenario;
|
|
136
|
-
}, // Ends scope.makeSureReportPartsExist()
|
|
137
|
-
|
|
138
|
-
setReportScenarioStatus: async function( scope, { status=`PASSED` }) {
|
|
139
|
-
/* Add final status of scenario to scenario report object - passed, failed, etc. */
|
|
140
|
-
scope.report.get( scope.scenario_id ).status = status;
|
|
141
|
-
}, // Ends scope.setReportScenarioStatus()
|
|
142
|
-
|
|
143
|
-
convertToOriginalStoryTableRow: async function( scope, { row_data }) {
|
|
144
|
-
/* Returns the original data of row into a string formatted as a story table row.
|
|
145
|
-
* Should this show the actual row data that was used instead? That could
|
|
146
|
-
* be pretty confusing to most devs. */
|
|
147
|
-
let source_row = row_data.source || row_data;
|
|
148
|
-
let original = source_row.original;
|
|
149
|
-
let var_str = await scope.toTrimmedJSON( scope, { str: original.var });
|
|
150
|
-
let value_str = await scope.toTrimmedJSON( scope, { str: original.value });
|
|
151
|
-
let trigger_str = await scope.toTrimmedJSON( scope, { str: original.trigger });
|
|
152
|
-
if ( trigger_str === scope.trigger_not_needed_flag ) {
|
|
153
|
-
// This text was added by ALKiln itself. It's not part of the user's data
|
|
154
|
-
trigger_str = ``;
|
|
155
|
-
}
|
|
156
|
-
return `${ ' '.repeat(6) }| ${ var_str } | ${ value_str } | ${ trigger_str } |`;
|
|
157
|
-
}, // Ends scope.convertToOriginalStoryTableRows()
|
|
158
|
-
|
|
159
|
-
toTrimmedJSON: async function( scope, { str='' }) {
|
|
160
|
-
/* Turn into JSON string then trim quotes from the start and end. */
|
|
161
|
-
let json = JSON.stringify( str );
|
|
162
|
-
let trimmed = json.replace( /^"/, '' ).replace( /"$/, '' ).replace( /\\\\/g, `\\`);
|
|
163
|
-
return trimmed;
|
|
164
|
-
}, // Ends scope.toTrimmedJSON()
|
|
165
|
-
|
|
166
|
-
addReportHeading: async function (scope, { scenario }) {
|
|
167
|
-
/** Return printable heading based on Scenario name and tags.
|
|
168
|
-
* Allow characters from other languages.
|
|
169
|
-
*/
|
|
170
|
-
if ( session_vars.get_debug() ) { console.log( scenario.pickle.name ); }
|
|
171
|
-
|
|
172
|
-
// Collect all tag names
|
|
173
|
-
let tag_names = [];
|
|
174
|
-
for ( let tag of scenario.pickle.tags ) {
|
|
175
|
-
tag_names.push( tag.name );
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
let heading = `\n---------------\n`
|
|
179
|
-
+ `Scenario: ${ scenario.pickle.name }`;
|
|
180
|
-
|
|
181
|
-
if ( tag_names.length > 0 ) {
|
|
182
|
-
heading += `\nTags: ${ tag_names.join(' ') }`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
heading += `\n---------------`;
|
|
186
|
-
|
|
187
|
-
await scope.addToReport( scope, {
|
|
188
|
-
type: `heading`,
|
|
189
|
-
value: heading,
|
|
190
|
-
});
|
|
191
|
-
}, // Ends scope.addReportHeading()
|
|
192
|
-
|
|
193
|
-
getPrintableReport: async function( scope ) {
|
|
194
|
-
/* Return a string generated from test report data. */
|
|
195
|
-
let report = `Assembly Line Kiln Automated Testing Report - ${ (new Date()).toUTCString() }\n`;
|
|
196
|
-
|
|
197
|
-
// First list failed scenarios, then non-passed scenarios, then passed scenarios
|
|
198
|
-
let failed_reports = ``;
|
|
199
|
-
let passed_reports = ``;
|
|
200
|
-
let other_reports = ``;
|
|
201
|
-
for ( let scenario of scope.report ) {
|
|
202
|
-
|
|
203
|
-
let report = await scope.getPrintableScenario( scope, { scenario });
|
|
204
|
-
let [ scenario_id, data ] = scenario;
|
|
205
|
-
if ( data.status === `FAILED` ) { failed_reports += report; }
|
|
206
|
-
else if ( data.status === `PASSED` ) { passed_reports += report; }
|
|
207
|
-
else { other_reports += report; }
|
|
208
|
-
} // ends for every scenario
|
|
209
|
-
|
|
210
|
-
if ( failed_reports !== `` ) { report += `\n\n===============================\n===============================\nFailed scenarios:\n${ failed_reports }`; }
|
|
211
|
-
if ( other_reports !== `` ) { report += `\n\n===============================\n===============================\nScenarios that did not pass:\n${ other_reports }`; }
|
|
212
|
-
if ( passed_reports !== `` ) { report += `\n\n===============================\n===============================\nPassed scenarios:\n${ passed_reports }`; }
|
|
213
|
-
|
|
214
|
-
return report;
|
|
215
|
-
}, // Ends scope.getPrintableReport()
|
|
216
|
-
|
|
217
|
-
getPrintableScenario: async function( scope, { scenario }) {
|
|
218
|
-
/* Return a string generated from scenario list of report items. */
|
|
219
|
-
let [ scenario_id, data ] = scenario;
|
|
220
|
-
let report = ``;
|
|
221
|
-
for ( let line of data.lines ) {
|
|
222
|
-
if ( line.type === `page_id` ) { report += `screen id: `; }
|
|
223
|
-
else if ( line.type === `outcome` ) { report += `\n`; }
|
|
224
|
-
else if ( line.type === `warning` ) { report += `WARNING: `; }
|
|
225
|
-
else if ( line.type === `error` ) { report += `\nERROR: ` }
|
|
226
|
-
report += `${ line.value }\n`;
|
|
227
|
-
}
|
|
228
|
-
return report;
|
|
229
|
-
}, // Ends scope.getPrintableScenario()
|
|
230
|
-
|
|
231
90
|
getSafeScenarioBaseFilename: async function( scope, { scenario }) {
|
|
232
91
|
/** Return a string that's safe to be a filename. Include the Scenario
|
|
233
92
|
* description and tags.
|
|
@@ -275,6 +134,15 @@ module.exports = {
|
|
|
275
134
|
return safe_name;
|
|
276
135
|
}, // Ends scope.getSafeScenarioFilename()
|
|
277
136
|
|
|
137
|
+
getA11yPath: async function( scope ) {
|
|
138
|
+
// Get a unique path for a json file
|
|
139
|
+
let { id } = await scope.examinePageID( scope, 'none to match' );
|
|
140
|
+
let short_id = `${ id }`.substring(0, 20);
|
|
141
|
+
// Need timestamp in case of a loop where the same page gets assessed for a11y multiple times
|
|
142
|
+
let a11y_path = path.join( scope.paths.scenario, `aXe_failure-${ short_id }-${ Date.now() }.json` );
|
|
143
|
+
return a11y_path;
|
|
144
|
+
},
|
|
145
|
+
|
|
278
146
|
getJSONFilename: async function( scope ) {
|
|
279
147
|
// Get a unique path for a json file
|
|
280
148
|
let { id } = await scope.examinePageID( scope, 'none to match' );
|
|
@@ -356,7 +224,7 @@ module.exports = {
|
|
|
356
224
|
let error_text_prop = await error_elem.getProperty( 'textContent' );
|
|
357
225
|
let system_error_text = await error_text_prop.jsonValue();
|
|
358
226
|
|
|
359
|
-
|
|
227
|
+
reports.addToReport(scope, { type: `error`, value: system_error_text });
|
|
360
228
|
throw system_error_text;
|
|
361
229
|
};
|
|
362
230
|
|
|
@@ -386,7 +254,7 @@ module.exports = {
|
|
|
386
254
|
// first in user-triggered cases - users used to be able to access this almost
|
|
387
255
|
// directly, but they can't anymore. Our functions should know to only pass good things.
|
|
388
256
|
let msg = `Cannot find the element to tap.`;
|
|
389
|
-
if ( !elem ) {
|
|
257
|
+
if ( !elem ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
390
258
|
|
|
391
259
|
expect( elem, msg ).to.exist;
|
|
392
260
|
|
|
@@ -425,7 +293,7 @@ module.exports = {
|
|
|
425
293
|
} else {
|
|
426
294
|
// Throw any non-timeout error
|
|
427
295
|
let err_msg = `Error occurred when ALKiln tried to tap "${ tapperText }".`
|
|
428
|
-
|
|
296
|
+
reports.addToReport( scope, { type: `error`, value: err_msg });
|
|
429
297
|
throw error;
|
|
430
298
|
} // ends if error is timeout error
|
|
431
299
|
|
|
@@ -517,7 +385,7 @@ module.exports = {
|
|
|
517
385
|
await scope.get_server_reload_promise( scope );
|
|
518
386
|
} else {
|
|
519
387
|
// Otherwise just throw the error
|
|
520
|
-
|
|
388
|
+
reports.addToReport( scope, {
|
|
521
389
|
type: `error`,
|
|
522
390
|
value: non_reload_report_msg });
|
|
523
391
|
throw error;
|
|
@@ -579,7 +447,7 @@ module.exports = {
|
|
|
579
447
|
+ `${ Math.round( ms_till_server_timeout/1000 ) } seconds to respond. `
|
|
580
448
|
+ `If the server was just reloading and needed more time, try setting the `
|
|
581
449
|
+ `"SERVER_RELOAD_TIMEOUT_SECONDS" GitHub secret to a longer amount of time.`
|
|
582
|
-
|
|
450
|
+
reports.addToReport( scope, { type: `error`, value: timeout_message });
|
|
583
451
|
}
|
|
584
452
|
|
|
585
453
|
// Once server has responded, throw an error to end the test. After the first
|
|
@@ -599,8 +467,8 @@ module.exports = {
|
|
|
599
467
|
let reload_failure_msg = `The test was unable to continue because the `
|
|
600
468
|
+ `server was reloading. ALKiln will try this Scenario a total of 2 times. An `
|
|
601
469
|
+ `error will still be printed for this attempt.`;
|
|
602
|
-
|
|
603
|
-
//
|
|
470
|
+
reports.addToReport( scope, { type: `error`, value: reload_failure_msg });
|
|
471
|
+
// reports.addToReport( scope, { type: `error`, value: timeout_message });
|
|
604
472
|
// clearTimeout( server_timeout_id );
|
|
605
473
|
throw( reload_failure_msg );
|
|
606
474
|
}, // Ends scope.throw_server_was_reloading_error()
|
|
@@ -673,7 +541,7 @@ module.exports = {
|
|
|
673
541
|
let elem = await scope.page.$(`#${tab_id}`);
|
|
674
542
|
let error_msg = `Couldn't find the tab with id "#${tab_id}" on the page, is there a typo?`;
|
|
675
543
|
if ( !elem ) {
|
|
676
|
-
|
|
544
|
+
reports.addToReport(scope, { type: `error`, value: error_msg });
|
|
677
545
|
}
|
|
678
546
|
expect( elem, error_msg ).to.exist;
|
|
679
547
|
await scope.tapElement( scope, elem, tab_id );
|
|
@@ -683,7 +551,7 @@ module.exports = {
|
|
|
683
551
|
/** Return if there were any violations and the file containing aXe output with more details of the checks */
|
|
684
552
|
let { id } = await scope.examinePageID( scope, 'none to match' );
|
|
685
553
|
if ( scope.page_id !== id ) {
|
|
686
|
-
|
|
554
|
+
reports.addToReport(scope, { type: `page_id`, value: `${ id }`, });
|
|
687
555
|
scope.page_id = id;
|
|
688
556
|
}
|
|
689
557
|
const axe = new AxePuppeteer(scope.page);
|
|
@@ -701,16 +569,15 @@ module.exports = {
|
|
|
701
569
|
let axe_path = null;
|
|
702
570
|
const passed = axe_result.violations.length == 0;
|
|
703
571
|
if (!passed) {
|
|
704
|
-
|
|
705
|
-
axe_path = `${ scope.paths.scenario }/${ axe_filename }`;
|
|
572
|
+
axe_path = await scope.getA11yPath( scope );
|
|
706
573
|
fs.writeFileSync( axe_path, JSON.stringify( axe_result, null, 2 ));
|
|
707
574
|
let msg = `Found potential accessibility issues on the ${ scope.page_id } screen. Details in ${ axe_path }.`;
|
|
708
|
-
|
|
575
|
+
reports.addToReport( scope, {
|
|
709
576
|
type: `error`,
|
|
710
577
|
value: msg
|
|
711
578
|
});
|
|
712
579
|
} else {
|
|
713
|
-
|
|
580
|
+
reports.addToReport(scope, { type: `info`, value: `Accessibility on ${ scope.page_id } passed!`})
|
|
714
581
|
}
|
|
715
582
|
return {
|
|
716
583
|
passed: axe_result.violations.length == 0,
|
|
@@ -722,7 +589,7 @@ module.exports = {
|
|
|
722
589
|
let elem = await scope.page.$(`${selector}`);
|
|
723
590
|
let error_msg = `Couldn't find "${selector}" on the page, is there a typo?`;
|
|
724
591
|
if ( !elem ) {
|
|
725
|
-
|
|
592
|
+
reports.addToReport(scope, { type: `error`, value: error_msg });
|
|
726
593
|
}
|
|
727
594
|
expect( elem, error_msg ).to.exist;
|
|
728
595
|
await scope.tapElement( scope, elem, '', waitForTimeout );
|
|
@@ -748,7 +615,7 @@ module.exports = {
|
|
|
748
615
|
interview_url = `${ base_url }${ file_name }`;
|
|
749
616
|
}
|
|
750
617
|
|
|
751
|
-
|
|
618
|
+
reports.addToReport(scope,{
|
|
752
619
|
type: `row`,
|
|
753
620
|
value: `Trying to load the interview at "${interview_url}"`
|
|
754
621
|
});
|
|
@@ -772,7 +639,7 @@ module.exports = {
|
|
|
772
639
|
} else {
|
|
773
640
|
|
|
774
641
|
// Throw any non-timeout error
|
|
775
|
-
|
|
642
|
+
reports.addToReport( scope, {
|
|
776
643
|
type: `error`,
|
|
777
644
|
value: `Error occurred when ALKiln tried to go to the interview url`
|
|
778
645
|
});
|
|
@@ -784,7 +651,7 @@ module.exports = {
|
|
|
784
651
|
let load_result = await scope.getLoadData( scope );
|
|
785
652
|
// Errors, or reaching timeout, causes an exception and is shown to the developer at the end
|
|
786
653
|
if ( load_result.error ) {
|
|
787
|
-
|
|
654
|
+
reports.addToReport(scope, { type: `error`, value: `On final attempt to load interview, got "${ load_result.error }"` });
|
|
788
655
|
expect( load_result.error ).to.equal( '' );
|
|
789
656
|
}
|
|
790
657
|
|
|
@@ -854,7 +721,7 @@ module.exports = {
|
|
|
854
721
|
if (!var_data && ! raw_var_data) {
|
|
855
722
|
let no_data_msg = `ALKiln Internal error: normalizeTable called w/o var_data or raw_var_data:` +
|
|
856
723
|
` open an issue at https://github.com/SuffolkLITLab/ALKiln/issues/new if you see this!`;
|
|
857
|
-
|
|
724
|
+
reports.addToReport( scope, { type: `error`, value: no_data_msg });
|
|
858
725
|
throw Error(no_data_msg);
|
|
859
726
|
}
|
|
860
727
|
|
|
@@ -869,7 +736,7 @@ module.exports = {
|
|
|
869
736
|
let bad_table_msg = `Your Story Table definition needs to be changed. The best practice`
|
|
870
737
|
+ ` is to make your table 3 columns wide with a first row in this format: "| var | value | trigger |".`
|
|
871
738
|
+ ` The first row of your table is actually "${ raw_array[0] }"`;
|
|
872
|
-
|
|
739
|
+
reports.addToReport( scope, { type: `error`, value: bad_table_msg})
|
|
873
740
|
throw Error(bad_table_msg);
|
|
874
741
|
} else if (row_raw.length == 2) {
|
|
875
742
|
var_data.push({var: row_raw[0], value: row_raw[1]});
|
|
@@ -889,7 +756,7 @@ module.exports = {
|
|
|
889
756
|
actual_var_value = process.env[row.flags.secret_name];
|
|
890
757
|
if ( actual_var_value === undefined ) {
|
|
891
758
|
let not_in_env_msg = `The GitHub SECRET "${ row.flags.secret_name }" doesn't exist. Check for a typo and check your workflow file.`;
|
|
892
|
-
|
|
759
|
+
reports.addToReport( scope, { type: `error`, value: not_in_env_msg })
|
|
893
760
|
throw ReferenceError(`Could not find "${row.flags.secret_name }": ${not_in_env_msg}.`)
|
|
894
761
|
}
|
|
895
762
|
}
|
|
@@ -941,7 +808,7 @@ module.exports = {
|
|
|
941
808
|
let encoded_trigger_var = $( trigger_elem ).data( `variable` ); // This should come out to 'None' at the very least
|
|
942
809
|
if ( !encoded_trigger_var ) {
|
|
943
810
|
// TODO: Add documentation and then link to it in this message.
|
|
944
|
-
|
|
811
|
+
reports.addToReport(scope, { type: `warning`, value: `You are missing an element containing the data for the page's trigger variable. Some fields, like loops with index variables, may not work correctly in these tests. See https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#a-missing-trigger-variable.` });
|
|
945
812
|
encoded_trigger_var = toBase64( `None` );
|
|
946
813
|
}
|
|
947
814
|
let trigger = fromBase64( encoded_trigger_var );
|
|
@@ -1080,7 +947,7 @@ module.exports = {
|
|
|
1080
947
|
// Exceptions: Some specific fields can be duplicates without being dangerous
|
|
1081
948
|
if ( matching_nodes.length > 1 && !selector.includes(`[class="file-caption-name"]`) ) {
|
|
1082
949
|
let msg = `${ matching_nodes.length } items on this page matched the base64 encoded name '${ selector }'. You may be setting the same variable in multiple places on this page.`;
|
|
1083
|
-
|
|
950
|
+
reports.addToReport(scope, { type: `warning`, value: msg });
|
|
1084
951
|
}
|
|
1085
952
|
|
|
1086
953
|
return selector;
|
|
@@ -1359,7 +1226,7 @@ module.exports = {
|
|
|
1359
1226
|
+ `variable or a generic object, but the page does not `
|
|
1360
1227
|
+ `contain the trigger variable's name in its HTML. See `
|
|
1361
1228
|
+ `https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#a-missing-trigger-variable.`;
|
|
1362
|
-
|
|
1229
|
+
reports.addToReport(scope, { type: `warning`, value: page_trigger_warning });
|
|
1363
1230
|
}
|
|
1364
1231
|
|
|
1365
1232
|
for ( let row_that_matches_this_field of rows_that_match_field_names ) {
|
|
@@ -1373,13 +1240,13 @@ module.exports = {
|
|
|
1373
1240
|
// a useful message.
|
|
1374
1241
|
|
|
1375
1242
|
// First, make the row data more readable to humans.
|
|
1376
|
-
let row_as_story_table =
|
|
1243
|
+
let row_as_story_table = reports.convertToOriginalStoryTableRow( scope, { row_data: row_that_matches_this_field });
|
|
1377
1244
|
let row_trigger_warning = `This story table row needs a value in the "trigger" `
|
|
1378
1245
|
+ `column, but it is empty. The test might get unexpected results. See `
|
|
1379
1246
|
+ `https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#trigger.`
|
|
1380
1247
|
+ `\nThe row's data:\n${ row_as_story_table }`
|
|
1381
1248
|
+ `\nThis field's data:\n${ JSON.stringify( field )}`;
|
|
1382
|
-
|
|
1249
|
+
reports.addToReport(scope, { type: `warning`, value: row_trigger_warning });
|
|
1383
1250
|
rows_without_a_trigger_name.push( row_that_matches_this_field );
|
|
1384
1251
|
} else {
|
|
1385
1252
|
let triggers_match = await scope.matchNeutralizedQuotes( scope, { value1: page_trigger_name, value2: table_row_trigger_name });
|
|
@@ -1405,7 +1272,7 @@ module.exports = {
|
|
|
1405
1272
|
// First, make the row data more readable to humans.
|
|
1406
1273
|
let conflicts_as_story_table_rows = [];
|
|
1407
1274
|
for ( let row of rows_with_conflicting_trigger_names ) {
|
|
1408
|
-
let table_str =
|
|
1275
|
+
let table_str = reports.convertToOriginalStoryTableRow( scope, { row_data: row });
|
|
1409
1276
|
conflicts_as_story_table_rows.push( table_str );
|
|
1410
1277
|
}
|
|
1411
1278
|
|
|
@@ -1416,7 +1283,7 @@ module.exports = {
|
|
|
1416
1283
|
+ `https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#trigger.`
|
|
1417
1284
|
+ `\nThe data of row(s) with conflicting trigger name(s):\n${ conflicts_as_story_table_rows.join( `\n` )}`
|
|
1418
1285
|
+ `\nThis field's data:\n${ JSON.stringify( field )}`;
|
|
1419
|
-
|
|
1286
|
+
reports.addToReport(scope, {
|
|
1420
1287
|
type: `warning`,
|
|
1421
1288
|
value: all_trigger_names_conflict_msg
|
|
1422
1289
|
});
|
|
@@ -1433,7 +1300,7 @@ module.exports = {
|
|
|
1433
1300
|
// First, make the row data more readable to humans.
|
|
1434
1301
|
let rows_as_story_table = [];
|
|
1435
1302
|
for ( let row of final_matching_rows ) {
|
|
1436
|
-
let table_str =
|
|
1303
|
+
let table_str = reports.convertToOriginalStoryTableRow( scope, { row_data: row });
|
|
1437
1304
|
rows_as_story_table.push( table_str );
|
|
1438
1305
|
}
|
|
1439
1306
|
|
|
@@ -1443,7 +1310,7 @@ module.exports = {
|
|
|
1443
1310
|
+ `https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#trigger.`
|
|
1444
1311
|
+ `\nThe matching rows:\n${ rows_as_story_table.join(`\n`) }`
|
|
1445
1312
|
+ `\nThis field's data:\n${ JSON.stringify( field )}`;
|
|
1446
|
-
|
|
1313
|
+
reports.addToReport(scope, { type: `warning`, value: multiple_matches_msg });
|
|
1447
1314
|
}
|
|
1448
1315
|
|
|
1449
1316
|
if ( session_vars.get_debug() ) {
|
|
@@ -1617,7 +1484,7 @@ module.exports = {
|
|
|
1617
1484
|
// If it doesn't have the right format
|
|
1618
1485
|
if ( answer === null || !answer.includes(`/`) ) {
|
|
1619
1486
|
// Return field unused.
|
|
1620
|
-
|
|
1487
|
+
reports.addToReport( scope, {
|
|
1621
1488
|
type: `warning`,
|
|
1622
1489
|
value: `Invalid date. Value given: "${ answer }".`
|
|
1623
1490
|
});
|
|
@@ -1823,7 +1690,7 @@ module.exports = {
|
|
|
1823
1690
|
// allow a typo where someone forgot to include a space after a comma.
|
|
1824
1691
|
let uploads_names = answer.split(`,`);
|
|
1825
1692
|
if ( uploads_names.length === 0 ) {
|
|
1826
|
-
|
|
1693
|
+
reports.addToReport( scope, {
|
|
1827
1694
|
type: `error`,
|
|
1828
1695
|
value: `Your list of files to upload is empty.`
|
|
1829
1696
|
});
|
|
@@ -1855,7 +1722,7 @@ module.exports = {
|
|
|
1855
1722
|
|
|
1856
1723
|
// Make sure sources directory exists and has files in it
|
|
1857
1724
|
if ( dirs.length === 0 ) {
|
|
1858
|
-
|
|
1725
|
+
reports.addToReport( scope, {
|
|
1859
1726
|
type: `warning`,
|
|
1860
1727
|
value: `The test files need to be in the "sources" directory, `
|
|
1861
1728
|
+ `but ALKiln couldn't find the directory or it was empty.`
|
|
@@ -1887,7 +1754,7 @@ module.exports = {
|
|
|
1887
1754
|
} // ends for each file
|
|
1888
1755
|
|
|
1889
1756
|
if ( missing_files.length > 0 ) {
|
|
1890
|
-
|
|
1757
|
+
reports.addToReport( scope, {
|
|
1891
1758
|
type: `warning`,
|
|
1892
1759
|
value: `ALKiln could not find "${ missing_files.join('", "') }" in any of these folders: "${ dirs.join('", "') }".`
|
|
1893
1760
|
});
|
|
@@ -1944,7 +1811,7 @@ module.exports = {
|
|
|
1944
1811
|
* takes a while to load. */
|
|
1945
1812
|
let canvas = await scope.page.waitForSelector( `#dasigcanvas` );
|
|
1946
1813
|
let msg = `Could not find a signature field.`;
|
|
1947
|
-
if ( !canvas ) {
|
|
1814
|
+
if ( !canvas ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
1948
1815
|
expect( canvas, msg ).to.exist;
|
|
1949
1816
|
|
|
1950
1817
|
// Provides functionality to draw name arg on canvas
|
|
@@ -2017,7 +1884,7 @@ module.exports = {
|
|
|
2017
1884
|
// Only record the page id once for each page to which we navigate
|
|
2018
1885
|
let { id } = await scope.examinePageID( scope, 'none to match' );
|
|
2019
1886
|
if ( scope.page_id !== id ) {
|
|
2020
|
-
|
|
1887
|
+
reports.addToReport(scope, { type: `page_id`, value: `${ id }`, });
|
|
2021
1888
|
scope.page_id = id;
|
|
2022
1889
|
}
|
|
2023
1890
|
|
|
@@ -2047,9 +1914,9 @@ module.exports = {
|
|
|
2047
1914
|
|
|
2048
1915
|
something_new_was_set = true;
|
|
2049
1916
|
field.was_set = true;
|
|
2050
|
-
|
|
1917
|
+
reports.addToReport(scope, {
|
|
2051
1918
|
type: `row`,
|
|
2052
|
-
value: `${
|
|
1919
|
+
value: `${ reports.convertToOriginalStoryTableRow(scope, { row_data: row_used }) }`
|
|
2053
1920
|
});
|
|
2054
1921
|
|
|
2055
1922
|
// Accumulate number of times this row was used. Esp important for `.target_number`
|
|
@@ -2072,9 +1939,9 @@ module.exports = {
|
|
|
2072
1939
|
let row_used = await scope.setVariable( scope, { field, var_data, });
|
|
2073
1940
|
if ( row_used ) {
|
|
2074
1941
|
|
|
2075
|
-
|
|
1942
|
+
reports.addToReport(scope, {
|
|
2076
1943
|
type: `row`,
|
|
2077
|
-
value: `${
|
|
1944
|
+
value: `${ reports.convertToOriginalStoryTableRow(scope, { row_data: row_used }) }`
|
|
2078
1945
|
});
|
|
2079
1946
|
|
|
2080
1947
|
// Esp important for `.target_number`
|
|
@@ -2116,13 +1983,13 @@ module.exports = {
|
|
|
2116
1983
|
if ( row.source ) { source_row = row.source; }
|
|
2117
1984
|
let original_row = source_row.original;
|
|
2118
1985
|
let msg = `Did not find a field on this page for the variable "${ original_row.var }" that could be set to "${ original_row.value }"`;
|
|
2119
|
-
|
|
1986
|
+
reports.addToReport(scope, { type: `warning`, value: msg });
|
|
2120
1987
|
}
|
|
2121
1988
|
}
|
|
2122
1989
|
if ( num_unused_rows > 0 ) {
|
|
2123
1990
|
let { id } = await scope.examinePageID( scope, 'none to match' );
|
|
2124
1991
|
let msg = `Missing variable or variables on page "${id}"`;
|
|
2125
|
-
|
|
1992
|
+
reports.addToReport(scope, { type: `error`, value: msg });
|
|
2126
1993
|
expect( num_unused_rows, msg ).to.equal( 0 );
|
|
2127
1994
|
}
|
|
2128
1995
|
}
|
|
@@ -2312,7 +2179,7 @@ module.exports = {
|
|
|
2312
2179
|
// probably add a confusing message to the report, so mutating seems, sadly, more useful.
|
|
2313
2180
|
var_datum.value = `False`; // The original value is still preserved
|
|
2314
2181
|
// Warn the developer not to use `.there_is_another`.
|
|
2315
|
-
|
|
2182
|
+
reports.addToReport( scope, { type: `warning`,
|
|
2316
2183
|
value: 'The attribute `.there_is_another` is invalid in story table tests. Replace it with '
|
|
2317
2184
|
+ '`.target_number` in your `var` and `trigger` columns. Set the `value` to the number of items in '
|
|
2318
2185
|
+ `that list. This test will now set this row's \`value\` to \`False\`. See `
|
|
@@ -2346,10 +2213,10 @@ module.exports = {
|
|
|
2346
2213
|
} else if ( value.match( /(true|yes|checked|check|selected|select)/i )) {
|
|
2347
2214
|
if ( from_story_table && !row.artificial ) {
|
|
2348
2215
|
if ( row.var.match( /\.there_is_another$/ ) ) {
|
|
2349
|
-
|
|
2216
|
+
reports.addToReport( scope, { type: `warning`, value: `The attribute \`.there_is_another\` is invalid in story table tests. Replace it with \`.target_number\` in your \`var\` column. Set the \`value\` to the number of items in that list. This test will now set this row's \`value\` to \`False\`. See https://suffolklitlab.github.io/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing#there_is_another-loop. The row data is\n${ JSON.stringify( row.original )}` });
|
|
2350
2217
|
} else {
|
|
2351
2218
|
// If `target_number` is set to True instead of a number
|
|
2352
|
-
|
|
2219
|
+
reports.addToReport( scope, { type: `warning`, value: `${ val_to_print } value is not a valid value for ${ row.var } here. This test will default to \`False\` to avoid problems.` });
|
|
2353
2220
|
}
|
|
2354
2221
|
return `False`;
|
|
2355
2222
|
} else { return `True`; }
|
|
@@ -2357,7 +2224,7 @@ module.exports = {
|
|
|
2357
2224
|
} else if ( isNaN( desired_num )) { // Story table loop will end
|
|
2358
2225
|
// I can't figure out how to trigger this warning, so maybe we're
|
|
2359
2226
|
// already handling everything we need to
|
|
2360
|
-
|
|
2227
|
+
reports.addToReport( scope, { type: `warning`, value: `${ val_to_print } value is not a valid value for ${ row.var } here. This test will default to \`False\` to avoid problems.` });
|
|
2361
2228
|
return `False`;
|
|
2362
2229
|
|
|
2363
2230
|
// Check that the number of elements in the list hits our desired number.
|
|
@@ -2408,7 +2275,7 @@ module.exports = {
|
|
|
2408
2275
|
}
|
|
2409
2276
|
|
|
2410
2277
|
let timeout_err_message = `The test tried to continue to the next page, but it took longer than ${ (scope.timeout/1000)/60 } min. Check the picture of this page. If a secret variable was used, there will be no picture.`
|
|
2411
|
-
if ( timedOut ) {
|
|
2278
|
+
if ( timedOut ) { reports.addToReport(scope, { type: `error`, value: timeout_err_message }); }
|
|
2412
2279
|
expect( timedOut, timeout_err_message ).to.be.false;
|
|
2413
2280
|
|
|
2414
2281
|
clearTimeout( cont_timeout );
|
|
@@ -2452,13 +2319,13 @@ module.exports = {
|
|
|
2452
2319
|
/* Throw a cucumber error with the error that appeared on the page. */
|
|
2453
2320
|
if ( error_handlers.system_error !== undefined ) {
|
|
2454
2321
|
let sys_err_message = `The test tried to continue to the next page after ${ id }, but there was a system error. Check the picture of this page.`
|
|
2455
|
-
|
|
2322
|
+
reports.addToReport(scope, { type: `error`, value: sys_err_message });
|
|
2456
2323
|
expect( was_system_error, sys_err_message ).to.be.false;
|
|
2457
2324
|
|
|
2458
2325
|
} else {
|
|
2459
2326
|
// If it wasn't a system error, it was a user error
|
|
2460
2327
|
let user_err_message = `The test tried to continue to the next page after ${ id }, but the user got an error. Check the picture of this page.`
|
|
2461
|
-
|
|
2328
|
+
reports.addToReport(scope, { type: `error`, value: user_err_message });
|
|
2462
2329
|
expect( true, user_err_message ).to.be.false;
|
|
2463
2330
|
}
|
|
2464
2331
|
|
|
@@ -2480,6 +2347,22 @@ module.exports = {
|
|
|
2480
2347
|
fullPage: fullPage,
|
|
2481
2348
|
});
|
|
2482
2349
|
|
|
2350
|
+
let html_path = path;
|
|
2351
|
+
if (path.endsWith(".jpg")) {
|
|
2352
|
+
html_path = html_path.substring(0, html_path.length - 4) + ".html";
|
|
2353
|
+
} else {
|
|
2354
|
+
html_path = html_path + ".html";
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
// Also save the HTML of the page
|
|
2358
|
+
await scope.page.content().then(content => {
|
|
2359
|
+
let server_url = session_vars.get_da_server_url();
|
|
2360
|
+
content = content.replaceAll(/"(\/static\/.*\.css\?v=[^"]+")/g, server_url + "$1");
|
|
2361
|
+
content = content.replaceAll(/"(\/static\/.*\.js\?v=[^"]+")/g, server_url + "$1");
|
|
2362
|
+
content = content.replaceAll(/"(\/packagestatic\/.*\.css\?v=[^"]+")/g, server_url + "$1");
|
|
2363
|
+
content = content.replaceAll(/"(\/packagestatic\/.*\.js\?v=[^"]+")/g, server_url + "$1");
|
|
2364
|
+
fs.writeFileSync(html_path, content)
|
|
2365
|
+
});
|
|
2483
2366
|
}, // Ends scope.take_a_screenshot()
|
|
2484
2367
|
|
|
2485
2368
|
|
|
@@ -2519,7 +2402,7 @@ module.exports = {
|
|
|
2519
2402
|
});
|
|
2520
2403
|
|
|
2521
2404
|
// In future, may be saved to artifact file
|
|
2522
|
-
|
|
2405
|
+
reports.addToReport( scope, {
|
|
2523
2406
|
type: `json`,
|
|
2524
2407
|
value: JSON.stringify( data, null, 2 )
|
|
2525
2408
|
});
|
|
@@ -2535,7 +2418,7 @@ module.exports = {
|
|
|
2535
2418
|
let response = await linkPage.goto( actual_url, { waitUntil: 'domcontentloaded' });
|
|
2536
2419
|
|
|
2537
2420
|
let msg = `The "${ linkText }" link is broken.`;
|
|
2538
|
-
if ( !response.ok() ) {
|
|
2421
|
+
if ( !response.ok() ) { reports.addToReport( scope, { type: `error`, value: msg }); }
|
|
2539
2422
|
expect( response.ok(), msg ).to.be.true;
|
|
2540
2423
|
|
|
2541
2424
|
// This may cause problems if the linked page is the interview page
|
|
@@ -2584,7 +2467,7 @@ module.exports = {
|
|
|
2584
2467
|
const [link] = await scope.page.$x(`//a[contains(text(), "${phrase}")]`);
|
|
2585
2468
|
|
|
2586
2469
|
let msg = `The term "${ phrase }" seems to be missing.`;
|
|
2587
|
-
if ( !link ) {
|
|
2470
|
+
if ( !link ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
2588
2471
|
expect( link, msg ).to.exist;
|
|
2589
2472
|
|
|
2590
2473
|
await scope.tapElement( scope, link );
|
|
@@ -2611,7 +2494,7 @@ module.exports = {
|
|
|
2611
2494
|
let [elem] = await scope.page.$x(`//a[contains(@href, "${ filename }")]`);
|
|
2612
2495
|
|
|
2613
2496
|
let msg = `"${ filename }" seems to be missing. Cannot find a link to that document.`;
|
|
2614
|
-
if ( !elem ) {
|
|
2497
|
+
if ( !elem ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
2615
2498
|
expect( elem, msg ).to.exist;
|
|
2616
2499
|
|
|
2617
2500
|
scope.toDownload = filename;
|
|
@@ -2647,7 +2530,7 @@ module.exports = {
|
|
|
2647
2530
|
name_parts_slice.push(name_parts_last);
|
|
2648
2531
|
name_parts = name_parts_slice;
|
|
2649
2532
|
|
|
2650
|
-
|
|
2533
|
+
reports.addToReport(scope, {
|
|
2651
2534
|
type: `warning`,
|
|
2652
2535
|
value: `The name "${name_str}" has more than 4 parts, but 4 is the maximum allowed. The test will set the name to "${name_parts.join(" ")}".`
|
|
2653
2536
|
});
|
|
@@ -2733,7 +2616,7 @@ module.exports = {
|
|
|
2733
2616
|
await scope.handle_page_timeout_error( scope, { non_reload_report_msg, error });
|
|
2734
2617
|
} else {
|
|
2735
2618
|
// Throw any non-timeout error
|
|
2736
|
-
|
|
2619
|
+
reports.addToReport( scope, { type: `error`, value: err_msg });
|
|
2737
2620
|
throw error;
|
|
2738
2621
|
} // ends if error is timeout error
|
|
2739
2622
|
|
|
@@ -2747,11 +2630,11 @@ module.exports = {
|
|
|
2747
2630
|
// this is complex
|
|
2748
2631
|
let email_msg = `The email address GitHub SECRET "${ email_secret_name }" doesn't exist. Check for a typo and check your workflow file.`
|
|
2749
2632
|
if ( email === undefined ) {
|
|
2750
|
-
|
|
2633
|
+
reports.addToReport( scope, { type: `error`, value: email_msg })
|
|
2751
2634
|
}
|
|
2752
2635
|
let password_msg = `The password GitHub SECRET "${ password_secret_name }" doesn't exist. Check for a typo and check your workflow file.`
|
|
2753
2636
|
if ( password === undefined ) {
|
|
2754
|
-
|
|
2637
|
+
reports.addToReport( scope, { type: `error`, value: password_msg })
|
|
2755
2638
|
}
|
|
2756
2639
|
|
|
2757
2640
|
expect( email, email_msg ).to.not.equal( undefined );
|
|
@@ -2768,7 +2651,7 @@ module.exports = {
|
|
|
2768
2651
|
// TODO: use a Promise.race to fail faster.
|
|
2769
2652
|
await scope.page.waitForSelector( `.datopcenter .alert-success` );
|
|
2770
2653
|
|
|
2771
|
-
|
|
2654
|
+
reports.addToReport( scope, {
|
|
2772
2655
|
type: `row`,
|
|
2773
2656
|
value: `Signed into ${ session_vars.get_da_server_url() }/user/sign-in successfully.`
|
|
2774
2657
|
});
|
|
@@ -2910,7 +2793,7 @@ module.exports = {
|
|
|
2910
2793
|
if ( type !== `` && type !== `hidden` ) {
|
|
2911
2794
|
// Field types that we can't yet handle, but plan to in future
|
|
2912
2795
|
if ( type === `file` ) {
|
|
2913
|
-
|
|
2796
|
+
reports.addToReport( scope, {
|
|
2914
2797
|
type: `warning`,
|
|
2915
2798
|
value: `Sorry, ALKiln's random input tests cannot yet handle "${ type }" fields.`
|
|
2916
2799
|
});
|
|
@@ -3016,7 +2899,7 @@ module.exports = {
|
|
|
3016
2899
|
*/
|
|
3017
2900
|
let current_report_obj = scope.report.get( scope.scenario_id );
|
|
3018
2901
|
let scenario = [ scope.scenario_id, current_report_obj ]; // Mimic how loop works with `Map`
|
|
3019
|
-
let report =
|
|
2902
|
+
let report = reports.getPrintableScenario( scenario );
|
|
3020
2903
|
let all_are_included = true;
|
|
3021
2904
|
for ( let one_expectation of expected ) {
|
|
3022
2905
|
if ( !report.includes( one_expectation )) {
|
|
@@ -3036,7 +2919,7 @@ module.exports = {
|
|
|
3036
2919
|
*/
|
|
3037
2920
|
let current_report_obj = scope.report.get( scope.scenario_id );
|
|
3038
2921
|
let scenario = [ scope.scenario_id, current_report_obj ]; // Mimic how loop works with `Map`
|
|
3039
|
-
let report =
|
|
2922
|
+
let report = reports.getPrintableScenario( scenario );
|
|
3040
2923
|
let none_included = true;
|
|
3041
2924
|
for ( let one_expectation of not_expected ) {
|
|
3042
2925
|
if ( report.includes( one_expectation )) {
|
package/lib/steps.js
CHANGED
|
@@ -21,6 +21,7 @@ const scope = require('./scope');
|
|
|
21
21
|
const log = require('./utils/log');
|
|
22
22
|
const session_vars = require('./utils/session_vars');
|
|
23
23
|
const files = require('./utils/files' );
|
|
24
|
+
const reports = require(`./utils/reports`);
|
|
24
25
|
|
|
25
26
|
/* Of Note:
|
|
26
27
|
- We're using `*=` for selectors because sometimes da text has funny characters in it that are hard to anticipate
|
|
@@ -125,7 +126,7 @@ Before(async (scenario) => {
|
|
|
125
126
|
scope.expected_not_in_report = null;
|
|
126
127
|
scope.server_reload_promise = null;
|
|
127
128
|
|
|
128
|
-
|
|
129
|
+
reports.addReportHeading(scope, {scenario});
|
|
129
130
|
// Make folder for this Scenario in the all-tests artifacts folder
|
|
130
131
|
scope.base_filename = await scope.getSafeScenarioBaseFilename(scope, {scenario});
|
|
131
132
|
// Add a date for uniqueness in case dev has accidentally copied a Scenario description
|
|
@@ -353,7 +354,7 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
|
|
|
353
354
|
resolve();
|
|
354
355
|
} catch ( err ) {
|
|
355
356
|
let err_msg = `Error occurred when tried to answer fields on question "${ id }".`
|
|
356
|
-
|
|
357
|
+
reports.addToReport( scope, { type: `error`, value: err_msg });
|
|
357
358
|
// Throw error instead?
|
|
358
359
|
reject( err );
|
|
359
360
|
} finally {
|
|
@@ -372,7 +373,7 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
|
|
|
372
373
|
|
|
373
374
|
// Add report strings from supported table, not an altered one
|
|
374
375
|
for ( let row of supported_table ) {
|
|
375
|
-
row.report_str = `${
|
|
376
|
+
row.report_str = `${ reports.convertToOriginalStoryTableRow(scope, { row_data: row }) }`;
|
|
376
377
|
}
|
|
377
378
|
// Sort into alphabetical order, as is recommended for writing story tables
|
|
378
379
|
let sorted_rows = supported_table.sort( function ( row1, row2 ) {
|
|
@@ -383,13 +384,13 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
|
|
|
383
384
|
|
|
384
385
|
// Add full used table to report so it can be copied if needed.
|
|
385
386
|
// TODO: Should we/How do we get this in the final scenario report instead of here?
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
387
|
+
reports.addToReport( scope, { type: `note`, value: `\n${ ' '.repeat(2) }Rows that got set:` });
|
|
388
|
+
reports.addToReport( scope, { type: `step`, value: `${ ' '.repeat(4) }And I get to the question id "${ target_id }" with this data:` });
|
|
389
|
+
reports.addToReport(scope, { type: `row`, value: `${ ' '.repeat(6) }| var | value | trigger |`, });
|
|
389
390
|
let at_least_one_row_was_NOT_used = false;
|
|
390
391
|
for ( let row of sorted_rows ) {
|
|
391
392
|
if ( row.times_used > 0 ) {
|
|
392
|
-
|
|
393
|
+
reports.addToReport(scope, { type: `row`, value: row.report_str, });
|
|
393
394
|
} else {
|
|
394
395
|
at_least_one_row_was_NOT_used = true;
|
|
395
396
|
}
|
|
@@ -397,17 +398,17 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
|
|
|
397
398
|
|
|
398
399
|
// Add unused rows if needed
|
|
399
400
|
if ( at_least_one_row_was_NOT_used ) {
|
|
400
|
-
|
|
401
|
+
reports.addToReport( scope, { type: `note`, value: `${ ' '.repeat(2) }Unused rows:` });
|
|
401
402
|
for ( let row of sorted_rows ) {
|
|
402
403
|
if ( row.times_used === 0 ) {
|
|
403
|
-
|
|
404
|
+
reports.addToReport(scope, { type: `row`, value: row.report_str, });
|
|
404
405
|
}
|
|
405
406
|
}
|
|
406
407
|
} else {
|
|
407
|
-
|
|
408
|
+
reports.addToReport(scope, { type: `note`, value: `${ ' '.repeat(2) }All rows were used` });
|
|
408
409
|
}
|
|
409
410
|
|
|
410
|
-
|
|
411
|
+
reports.addToReport(scope, { type: `note`, value: `` });
|
|
411
412
|
});
|
|
412
413
|
|
|
413
414
|
//#####################################
|
|
@@ -457,7 +458,7 @@ Then(/the (?:question|page|screen) id should be "([^"]+)"/i, { timeout: -1 }, as
|
|
|
457
458
|
// No question id exists
|
|
458
459
|
let no_id_msg = `Did not find any question id.`;
|
|
459
460
|
if ( !question_has_id ) {
|
|
460
|
-
|
|
461
|
+
reports.addToReport( scope, { type: `error`, value: no_id_msg });
|
|
461
462
|
}
|
|
462
463
|
expect( question_has_id , no_id_msg ).to.be.true;
|
|
463
464
|
|
|
@@ -465,7 +466,7 @@ Then(/the (?:question|page|screen) id should be "([^"]+)"/i, { timeout: -1 }, as
|
|
|
465
466
|
let no_match_msg = `The question id was supposed to be "${ question_id }"`
|
|
466
467
|
+ `, but it's actually "${ id }".`;
|
|
467
468
|
if ( !id_matches ) {
|
|
468
|
-
|
|
469
|
+
reports.addToReport( scope, { type: `error`, value: no_match_msg });
|
|
469
470
|
}
|
|
470
471
|
expect( id_matches, no_match_msg ).to.be.true;
|
|
471
472
|
|
|
@@ -478,7 +479,7 @@ Then(/I ?(?:should)? see the phrase "([^"]+)"/i, async ( phrase ) => { // √
|
|
|
478
479
|
const bodyText = await scope.page.$eval('body', elem => elem.innerText);
|
|
479
480
|
|
|
480
481
|
let msg = `The text "${ phrase }" SHOULD be on this page, but it's NOT.`;
|
|
481
|
-
if ( !bodyText.includes( phrase )) {
|
|
482
|
+
if ( !bodyText.includes( phrase )) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
482
483
|
expect( bodyText, msg ).to.contain( phrase );
|
|
483
484
|
|
|
484
485
|
await scope.afterStep(scope);
|
|
@@ -492,7 +493,7 @@ Then(/I should not see the phrase "([^"]+)"/i, async ( phrase ) => { // √
|
|
|
492
493
|
const bodyText = await scope.page.$eval('body', elem => elem.innerText);
|
|
493
494
|
|
|
494
495
|
let msg = `The text "${ phrase }" should NOT be on this page, but it IS here.`;
|
|
495
|
-
if ( bodyText.includes( phrase )) {
|
|
496
|
+
if ( bodyText.includes( phrase )) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
496
497
|
expect( bodyText, msg ).not.to.contain( phrase );
|
|
497
498
|
|
|
498
499
|
await scope.afterStep(scope);
|
|
@@ -502,7 +503,7 @@ Then('an element should have the id {string}', async ( id ) => { // √
|
|
|
502
503
|
const element = await scope.page.$('#' + id);
|
|
503
504
|
|
|
504
505
|
let msg = `No element on this page has the ID "${ id }".`;
|
|
505
|
-
if ( !element ) {
|
|
506
|
+
if ( !element ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
506
507
|
expect( element, msg ).to.exist;
|
|
507
508
|
|
|
508
509
|
await scope.afterStep(scope);
|
|
@@ -557,7 +558,7 @@ Then(/I (don't|can't|don’t|can’t|cannot|do not) continue/, async (unused) =>
|
|
|
557
558
|
* Can other things trigger navigation? Re: other inputs things
|
|
558
559
|
* like 'Enter' acting like a click is a test for docassemble */
|
|
559
560
|
let msg = `The page should have stopped the user from continuing, but the user was able to continue.`;
|
|
560
|
-
if ( scope.navigated ) {
|
|
561
|
+
if ( scope.navigated ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
561
562
|
expect( scope.navigated, msg ).to.be.false;
|
|
562
563
|
|
|
563
564
|
await scope.afterStep(scope);
|
|
@@ -568,12 +569,12 @@ Then('I will be told an answer is invalid', async () => { // √
|
|
|
568
569
|
let { was_found, error_handlers } = await scope.checkForError( scope );
|
|
569
570
|
|
|
570
571
|
let no_error_msg = `No error message was found on the page`;
|
|
571
|
-
if ( !was_found ) {
|
|
572
|
+
if ( !was_found ) { reports.addToReport(scope, { type: `error`, value: no_error_msg }); }
|
|
572
573
|
expect( was_found, no_error_msg ).to.be.true;
|
|
573
574
|
|
|
574
575
|
let was_user_error = error_handlers.complex_user_error !== undefined || error_handlers.simple_user_error !== undefined;
|
|
575
576
|
let not_user_error_msg = `The error was a system error, not an invalid user answer. Check for the picture of the page's error if the step did not use any secret variables.`;
|
|
576
|
-
if ( !was_user_error ) {
|
|
577
|
+
if ( !was_user_error ) { reports.addToReport(scope, { type: `error`, value: not_user_error_msg }); }
|
|
577
578
|
expect( was_user_error, not_user_error_msg ).to.be.true;
|
|
578
579
|
|
|
579
580
|
await scope.afterStep(scope);
|
|
@@ -584,7 +585,7 @@ Then('I arrive at the next page', async () => { // √
|
|
|
584
585
|
* Can other things trigger navigation? Re: other inputs things
|
|
585
586
|
* like 'Enter' acting like a click is a test for docassemble */
|
|
586
587
|
let msg = `User did not arrive at the next page.`
|
|
587
|
-
if ( !scope.navigated ) {
|
|
588
|
+
if ( !scope.navigated ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
588
589
|
expect( scope.navigated, msg ).to.be.true;
|
|
589
590
|
await scope.afterStep(scope);
|
|
590
591
|
});
|
|
@@ -595,7 +596,7 @@ Then('I should see the link {string}', async ( linkText ) => { // √
|
|
|
595
596
|
let [link] = await scope.page.$x(`//a[contains(text(), "${linkText}")]`);
|
|
596
597
|
|
|
597
598
|
let msg = `Cannot find a link with the text "${ linkText }".`;
|
|
598
|
-
if ( !link ) {
|
|
599
|
+
if ( !link ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
599
600
|
expect( link, msg ).to.exist;
|
|
600
601
|
|
|
601
602
|
await scope.afterStep(scope);
|
|
@@ -606,7 +607,7 @@ Then('I should see the link to {string}', async ( url ) => { // √
|
|
|
606
607
|
let link = await scope.page.$(`*[href="${ url }"]`);
|
|
607
608
|
|
|
608
609
|
let msg = `Cannot find a link to "${ url }".`;
|
|
609
|
-
if ( !link ) {
|
|
610
|
+
if ( !link ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
610
611
|
expect(link, msg ).to.exist;
|
|
611
612
|
|
|
612
613
|
await scope.afterStep(scope);
|
|
@@ -622,7 +623,7 @@ Then(/the "([^"]+)" link leads to "([^"]+)"/, async (linkText, expected_url) =>
|
|
|
622
623
|
let actual_url = await prop_obj.jsonValue();
|
|
623
624
|
|
|
624
625
|
let msg = `Cannot find a link with the text "${ linkText }" leading to ${ expected_url }.`;
|
|
625
|
-
if ( actual_url !== expected_url ) {
|
|
626
|
+
if ( actual_url !== expected_url ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
626
627
|
expect( actual_url ).to.equal( expected_url );
|
|
627
628
|
|
|
628
629
|
await scope.afterStep(scope);
|
|
@@ -641,7 +642,7 @@ Then(/the "([^"]+)" link opens in (a new window|the same window)/, async (linkTe
|
|
|
641
642
|
|| ( !should_open_a_new_window && !opens_a_new_window );
|
|
642
643
|
|
|
643
644
|
let msg = `The link "${ linkText }" does NOT open in ${ which_window }.`;
|
|
644
|
-
if ( !hasCorrectWindowTarget ) {
|
|
645
|
+
if ( !hasCorrectWindowTarget ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
645
646
|
expect( hasCorrectWindowTarget, msg ).to.be.true;
|
|
646
647
|
|
|
647
648
|
await scope.afterStep(scope);
|
|
@@ -674,7 +675,7 @@ Then(/(?:the )?text(?: in)?(?: the)?(?: JSON)?(?: var)?(?:iable)?(?: )?"([^"]+)"
|
|
|
674
675
|
|
|
675
676
|
// Add warning to report if value is not as expected
|
|
676
677
|
if ( actual_value !== expected_value ) {
|
|
677
|
-
|
|
678
|
+
reports.addToReport( scope, {
|
|
678
679
|
type: `error`,
|
|
679
680
|
value: `The variable "${ var_name }" was not equal to the expected `
|
|
680
681
|
+ `value on the "${ scope.page_id }" screen. Check `
|
|
@@ -781,7 +782,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
781
782
|
let random_input_path = `${ scope.paths.scenario }/random${ timestamp }`
|
|
782
783
|
scope.paths.random_screenshots = random_input_path;
|
|
783
784
|
fs.mkdirSync( scope.paths.random_screenshots );
|
|
784
|
-
|
|
785
|
+
reports.addToReport( scope, {
|
|
785
786
|
type: 'row',
|
|
786
787
|
value: `Testing interview with random input (in ${ random_input_path })`
|
|
787
788
|
});
|
|
@@ -816,7 +817,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
816
817
|
let has_content = await scope.has_interview_content( scope );
|
|
817
818
|
|
|
818
819
|
if ( !has_content ) {
|
|
819
|
-
|
|
820
|
+
reports.addToReport( scope, {
|
|
820
821
|
type: `error`,
|
|
821
822
|
value: no_content_err
|
|
822
823
|
});
|
|
@@ -825,7 +826,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
825
826
|
} else {
|
|
826
827
|
let timeout_message = `The page with an id of "${ id }" took too long`
|
|
827
828
|
+ ` to fill with random answers (over ${ scope.timeout/1000/60 } min).`
|
|
828
|
-
|
|
829
|
+
reports.addToReport( scope, { type: `error`, value: timeout_message });
|
|
829
830
|
reject( timeout_message );
|
|
830
831
|
}
|
|
831
832
|
|
|
@@ -866,7 +867,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
866
867
|
// Make sure to only throw once
|
|
867
868
|
if ( !has_thrown ) {
|
|
868
869
|
has_thrown = true;
|
|
869
|
-
|
|
870
|
+
reports.addToReport( scope, {
|
|
870
871
|
type: `error`,
|
|
871
872
|
value: no_content_err
|
|
872
873
|
});
|
|
@@ -890,7 +891,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
890
891
|
// Throw a specific error if there's no interview content
|
|
891
892
|
let has_content = await scope.has_interview_content( scope );
|
|
892
893
|
if ( !has_content ) {
|
|
893
|
-
|
|
894
|
+
reports.addToReport( scope, {
|
|
894
895
|
type: `error`,
|
|
895
896
|
value: no_content_err
|
|
896
897
|
});
|
|
@@ -900,7 +901,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
900
901
|
} else {
|
|
901
902
|
let err_msg = `The page with the question id "${ id }" errored when trying to fill`
|
|
902
903
|
+ ` it with random answers: ${ error.name }`
|
|
903
|
-
|
|
904
|
+
reports.addToReport( scope, { type: `error`, value: err_msg });
|
|
904
905
|
reject( error );
|
|
905
906
|
} // ends if !has_content
|
|
906
907
|
|
|
@@ -1098,24 +1099,29 @@ Given(/the final Scenario status should be "(passed|failed)"/i, async ( expected
|
|
|
1098
1099
|
|
|
1099
1100
|
After(async function(scenario) {
|
|
1100
1101
|
|
|
1102
|
+
let changeable_test_status = scenario.result.status;
|
|
1101
1103
|
// The accessibility failures don't happen til the end, so the rest of the test can continue
|
|
1102
1104
|
if ( scope.check_all_for_a11y && !scope.passed_all_for_a11y) {
|
|
1103
1105
|
// The fact that the scenario failed has already been noted earlier in the report
|
|
1104
|
-
|
|
1106
|
+
changeable_test_status = `FAILED`;
|
|
1107
|
+
scope.addToReport(scope, {
|
|
1108
|
+
type: `error`,
|
|
1109
|
+
value: `Accessibility standards weren't met. See information above.`
|
|
1110
|
+
});
|
|
1105
1111
|
}
|
|
1106
1112
|
|
|
1107
1113
|
// Record status before internal report tests can interfere with values
|
|
1108
|
-
|
|
1114
|
+
reports.setReportScenarioStatus( scope, { status: changeable_test_status });
|
|
1109
1115
|
|
|
1110
|
-
if (
|
|
1116
|
+
if (changeable_test_status !== `PASSED`) {
|
|
1111
1117
|
if ( session_vars.get_debug() ) {
|
|
1112
|
-
console.log( `Non-passed status occurred while running test:`,
|
|
1118
|
+
console.log( `Non-passed status occurred while running test:`, changeable_test_status );
|
|
1113
1119
|
}
|
|
1114
1120
|
|
|
1115
1121
|
if ( scope.page ) {
|
|
1116
1122
|
|
|
1117
1123
|
if ( !!scope.disable_error_screenshot ) {
|
|
1118
|
-
|
|
1124
|
+
reports.addToReport(scope, {
|
|
1119
1125
|
type: `row`,
|
|
1120
1126
|
value: `For security, ALKiln will avoid creating a picture of the page for this error. It's possible a secret is being used on this screen.`
|
|
1121
1127
|
});
|
|
@@ -1139,21 +1145,19 @@ After(async function(scenario) {
|
|
|
1139
1145
|
} // ends if scope.page exists
|
|
1140
1146
|
|
|
1141
1147
|
// This has to come after the security message or security message doesn't print. Not sure why.
|
|
1142
|
-
if (
|
|
1143
|
-
|
|
1148
|
+
if (changeable_test_status === `FAILED`) {
|
|
1149
|
+
reports.addToReport(scope, { type: `outcome`, value: `**-- Scenario Failed --**` });
|
|
1144
1150
|
}
|
|
1145
1151
|
|
|
1146
1152
|
} // ends if not passed
|
|
1147
1153
|
|
|
1148
|
-
let original_scenario_status = scenario.result.status;
|
|
1149
|
-
|
|
1150
1154
|
// Internal tests of this framework
|
|
1151
1155
|
if ( scope.expected_status ) {
|
|
1152
1156
|
let expected = scope.expected_status;
|
|
1153
1157
|
scope.expected_status = null; // reset for next Scenario
|
|
1154
|
-
expect(
|
|
1158
|
+
expect( changeable_test_status ).to.equal( expected );
|
|
1155
1159
|
// If the status was as expected, pass this criteria
|
|
1156
|
-
|
|
1160
|
+
changeable_test_status = `PASSED`;
|
|
1157
1161
|
}
|
|
1158
1162
|
|
|
1159
1163
|
if ( scope.expected_in_report && scope.expected_in_report.length > 0) {
|
|
@@ -1162,8 +1166,8 @@ After(async function(scenario) {
|
|
|
1162
1166
|
scope.expected_in_report = null;
|
|
1163
1167
|
let all_were_included = await scope.reportIncludesAllExpected( scope, { expected });
|
|
1164
1168
|
|
|
1165
|
-
if ( all_were_included ) {
|
|
1166
|
-
else {
|
|
1169
|
+
if ( all_were_included ) { changeable_test_status = `PASSED`; }
|
|
1170
|
+
else { changeable_test_status = `FAILED`; }
|
|
1167
1171
|
}
|
|
1168
1172
|
|
|
1169
1173
|
if (scope.expected_not_in_report && scope.expected_not_in_report.length > 0) {
|
|
@@ -1173,9 +1177,9 @@ After(async function(scenario) {
|
|
|
1173
1177
|
let all_were_not_there = await scope.reportDoesNotInclude( scope, { not_expected });
|
|
1174
1178
|
|
|
1175
1179
|
if ( all_were_not_there ) {
|
|
1176
|
-
|
|
1180
|
+
changeable_test_status = `PASSED`;
|
|
1177
1181
|
} else {
|
|
1178
|
-
|
|
1182
|
+
changeable_test_status = `FAILED`;
|
|
1179
1183
|
}
|
|
1180
1184
|
|
|
1181
1185
|
}
|
|
@@ -1199,7 +1203,7 @@ After(async function(scenario) {
|
|
|
1199
1203
|
} else {
|
|
1200
1204
|
// Throw any non-timeout error
|
|
1201
1205
|
let err_msg = `Error occurred when ALKiln tried to sign out in preparation for the next test.`
|
|
1202
|
-
|
|
1206
|
+
reports.addToReport( scope, { type: `error`, value: err_msg });
|
|
1203
1207
|
throw error;
|
|
1204
1208
|
} // ends if error is timeout error
|
|
1205
1209
|
|
|
@@ -1210,16 +1214,29 @@ After(async function(scenario) {
|
|
|
1210
1214
|
// Add report text to Scenario folder after everything has been added to the report
|
|
1211
1215
|
let current_report_obj = scope.report.get( scope.scenario_id );
|
|
1212
1216
|
let scenario_report_obj = [ scope.scenario_id, current_report_obj ]; // Mimic how loop works with `Map`
|
|
1213
|
-
let report =
|
|
1217
|
+
let report = reports.getPrintableScenario( scenario_report_obj );
|
|
1214
1218
|
// Save the report as in the Scenario folder
|
|
1215
1219
|
fs.writeFileSync( scope.paths.scenario_report, report );
|
|
1220
|
+
|
|
1221
|
+
if ( changeable_test_status === `FAILED` ) {
|
|
1222
|
+
// If the result object status wasn't failure and we still want
|
|
1223
|
+
// the tests to fail, as with a11y tests, we can't mess with the
|
|
1224
|
+
// cucumber result.status object because it will cause unwanted
|
|
1225
|
+
// errors. We have to throw an error here instead.
|
|
1226
|
+
if ( scenario.result.status !== `FAILED` ) {
|
|
1227
|
+
throw new Error(`The test failed. See the report for more information.`);
|
|
1228
|
+
}
|
|
1229
|
+
} else {
|
|
1230
|
+
// Other types of errors should be ok
|
|
1231
|
+
scenario.result.status = changeable_test_status;
|
|
1232
|
+
}
|
|
1216
1233
|
});
|
|
1217
1234
|
|
|
1218
1235
|
AfterAll(async function() {
|
|
1219
1236
|
// Stop collecting server response statuses
|
|
1220
1237
|
scope.stop_tracking_server_status = true;
|
|
1221
1238
|
|
|
1222
|
-
let report =
|
|
1239
|
+
let report = reports.getPrintableReport( scope );
|
|
1223
1240
|
// Log the report. Can't get this.attach() or this.log() to work in `After()` or here.
|
|
1224
1241
|
// May need newest version of cucumberjs
|
|
1225
1242
|
console.log( `\n\n${ report }` );
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
const fs = require(`fs`);
|
|
2
|
+
|
|
3
|
+
// Ours
|
|
4
|
+
const session_vars = require(`./session_vars`);
|
|
5
|
+
const files = require(`./files` );
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module.exports = reports = {};
|
|
9
|
+
|
|
10
|
+
reports.addReportHeading = function (scope, { scenario }) {
|
|
11
|
+
/** Return printable heading based on Scenario name and tags.
|
|
12
|
+
* Allow characters from other languages.
|
|
13
|
+
*/
|
|
14
|
+
if ( session_vars.get_debug() ) { console.log(`reports.addReportHeading(): ${scenario.pickle.name} \n`); }
|
|
15
|
+
|
|
16
|
+
// Collect all tag names
|
|
17
|
+
let tag_names = [];
|
|
18
|
+
for ( let tag of scenario.pickle.tags ) {
|
|
19
|
+
tag_names.push( tag.name );
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let heading = `\n---------------\n`
|
|
23
|
+
+ `Scenario: ${ scenario.pickle.name }`;
|
|
24
|
+
|
|
25
|
+
if ( tag_names.length > 0 ) {
|
|
26
|
+
heading += `\nTags: ${ tag_names.join(' ') }`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
heading += `\n---------------`;
|
|
30
|
+
|
|
31
|
+
reports.addToReport( scope, {
|
|
32
|
+
type: `heading`,
|
|
33
|
+
value: heading,
|
|
34
|
+
});
|
|
35
|
+
}; // Ends reports.addReportHeading()
|
|
36
|
+
|
|
37
|
+
reports.addToReport = function( scope, { type, value }) {
|
|
38
|
+
/** Add an item to a specific list in a scenario's report.
|
|
39
|
+
* Create report if necessary.
|
|
40
|
+
*
|
|
41
|
+
* The report has a key for each scenario and each of those
|
|
42
|
+
* keys should correspond to a list containing the relevant data. */
|
|
43
|
+
if ( session_vars.get_debug() ) { console.log(`reports.addToReport():\ntype: ${ type }, value: ${ value }\n`) }
|
|
44
|
+
let scenario = makeSureReportPartsExist( scope );
|
|
45
|
+
scenario.lines.push({ type, value });
|
|
46
|
+
|
|
47
|
+
const MISC_ARTIFACTS_DIR = `_alkiln-misc_artifacts`;
|
|
48
|
+
// Ensure filepath for saving local unit test logs
|
|
49
|
+
if ( !scope.paths ) {
|
|
50
|
+
// TODO: would it be useful to put in existing artifacts folder if possible.
|
|
51
|
+
// Can this be usefully abstracted?
|
|
52
|
+
scope.paths = { debug_log: `${ MISC_ARTIFACTS_DIR }/unit_tests_logs-${ files.readable_date() }.txt` };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
fs.appendFileSync(scope.paths.debug_log, `\nReport:\ntype: ${ type }, value: ${ value }`);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
fs.mkdirSync( MISC_ARTIFACTS_DIR );
|
|
59
|
+
fs.appendFileSync(scope.paths.debug_log, `\nReport:\ntype: ${ type }, value: ${ value }`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return scenario;
|
|
63
|
+
}; // Ends reports.addToReport()
|
|
64
|
+
|
|
65
|
+
let makeSureReportPartsExist = function( scope ) {
|
|
66
|
+
/** If a part of the report needs to exist right now, but doesn't exist,
|
|
67
|
+
* create it. */
|
|
68
|
+
if ( !scope.report ) { scope.report = new Map(); }
|
|
69
|
+
if ( !scope.scenario_id ) {
|
|
70
|
+
scope.scenario_id = `_report_${ Date.now() }`;
|
|
71
|
+
}
|
|
72
|
+
if ( !scope.report.get( scope.scenario_id )) {
|
|
73
|
+
scope.report.set( scope.scenario_id, {} )
|
|
74
|
+
scope.report.get( scope.scenario_id ).status = `in progress`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let scenario = scope.report.get( scope.scenario_id );
|
|
78
|
+
if ( !scenario.lines ) { scenario.lines = []; }
|
|
79
|
+
|
|
80
|
+
return scenario;
|
|
81
|
+
}; // Ends makeSureReportPartsExist()
|
|
82
|
+
|
|
83
|
+
reports.setReportScenarioStatus = function( scope, { status=`PASSED` }) {
|
|
84
|
+
/** Add final status of scenario to scenario report object - passed, failed, etc. */
|
|
85
|
+
scope.report.get( scope.scenario_id ).status = status;
|
|
86
|
+
}; // Ends reports.setReportScenarioStatus()
|
|
87
|
+
|
|
88
|
+
reports.convertToOriginalStoryTableRow = function( scope, { row_data }) {
|
|
89
|
+
/** Returns the original data of row into a string formatted as a story table row.
|
|
90
|
+
* Should this show the actual row data that was used instead? That could
|
|
91
|
+
* be pretty confusing to most devs. */
|
|
92
|
+
let source_row = row_data.source || row_data;
|
|
93
|
+
let original = source_row.original;
|
|
94
|
+
let var_str = trimJSON( original.var );
|
|
95
|
+
let value_str = trimJSON( original.value );
|
|
96
|
+
let trigger_str = trimJSON( original.trigger );
|
|
97
|
+
if ( trigger_str === scope.trigger_not_needed_flag ) {
|
|
98
|
+
// This text was added by ALKiln itself. It's not part of the user's data
|
|
99
|
+
trigger_str = ``;
|
|
100
|
+
}
|
|
101
|
+
return `${ ' '.repeat(6) }| ${ var_str } | ${ value_str } | ${ trigger_str } |`;
|
|
102
|
+
}; // Ends reports.convertToOriginalStoryTableRows()
|
|
103
|
+
|
|
104
|
+
let trimJSON = function( str=`` ) {
|
|
105
|
+
/** Turn into JSON string then trim quotes from the start and end. */
|
|
106
|
+
let json = JSON.stringify( str );
|
|
107
|
+
let trimmed = json.replace( /^"/, `` ).replace( /"$/, `` ).replace( /\\\\/g, `\\`);
|
|
108
|
+
return trimmed;
|
|
109
|
+
}; // Ends trimJSON()
|
|
110
|
+
|
|
111
|
+
reports.getPrintableReport = function( scope ) {
|
|
112
|
+
/** Return a string generated from test report data. */
|
|
113
|
+
let report = `Assembly Line Kiln Automated Testing Report - ${ (new Date()).toUTCString() }\n`;
|
|
114
|
+
|
|
115
|
+
// First list failed scenarios, then non-passed scenarios, then passed scenarios
|
|
116
|
+
let failed_reports = ``;
|
|
117
|
+
let passed_reports = ``;
|
|
118
|
+
let other_reports = ``;
|
|
119
|
+
for ( let scenario of scope.report ) {
|
|
120
|
+
|
|
121
|
+
let report = reports.getPrintableScenario( scenario );
|
|
122
|
+
let [ scenario_id, data ] = scenario;
|
|
123
|
+
if ( data.status === `FAILED` ) { failed_reports += report; }
|
|
124
|
+
else if ( data.status === `PASSED` ) { passed_reports += report; }
|
|
125
|
+
else { other_reports += report; }
|
|
126
|
+
}; // ends for every scenario
|
|
127
|
+
|
|
128
|
+
if ( failed_reports !== `` ) { report += `\n\n===============================\n===============================\nFailed scenarios:\n${ failed_reports }`; }
|
|
129
|
+
if ( other_reports !== `` ) { report += `\n\n===============================\n===============================\nScenarios that did not pass:\n${ other_reports }`; }
|
|
130
|
+
if ( passed_reports !== `` ) { report += `\n\n===============================\n===============================\nPassed scenarios:\n${ passed_reports }`; }
|
|
131
|
+
|
|
132
|
+
return report;
|
|
133
|
+
}; // Ends reports.getPrintableReport()
|
|
134
|
+
|
|
135
|
+
reports.getPrintableScenario = function( scenario ) {
|
|
136
|
+
/** Return a string generated from scenario list of report items. */
|
|
137
|
+
let [ scenario_id, data ] = scenario;
|
|
138
|
+
let report = ``;
|
|
139
|
+
for ( let line of data.lines ) {
|
|
140
|
+
if ( line.type === `page_id` ) { report += `screen id: `; }
|
|
141
|
+
else if ( line.type === `outcome` ) { report += `\n`; }
|
|
142
|
+
else if ( line.type === `warning` ) { report += `WARNING: `; }
|
|
143
|
+
else if ( line.type === `error` ) { report += `\nERROR: ` }
|
|
144
|
+
report += `${ line.value }\n`;
|
|
145
|
+
}
|
|
146
|
+
return report;
|
|
147
|
+
}; // Ends reports.getPrintableScenario()
|