@suffolklitlab/alkiln 5.2.0 → 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 +25 -0
- package/lib/scope.js +166 -247
- package/lib/steps.js +67 -49
- package/lib/utils/reports.js +147 -0
- package/package.json +1 -1
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
|
|
@@ -203,6 +204,7 @@ Given(/I start the interview at "([^"]+)"/, {timeout: -1}, async (file_name) =>
|
|
|
203
204
|
// at any one time since each step completes synchronously
|
|
204
205
|
|
|
205
206
|
// If response has a file in it that matches the file we are downloading
|
|
207
|
+
// Sadly, 'content-disposition' isn't working. We're not sure what will.
|
|
206
208
|
if ( response.headers()['content-disposition'] && (response.headers()['content-disposition']).includes(`attachment;filename=${scope.toDownload}`) ) {
|
|
207
209
|
// We're choosing to avoid removing the event listener in case removal
|
|
208
210
|
// would prevent downloading other files. The listener should get
|
|
@@ -352,7 +354,7 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
|
|
|
352
354
|
resolve();
|
|
353
355
|
} catch ( err ) {
|
|
354
356
|
let err_msg = `Error occurred when tried to answer fields on question "${ id }".`
|
|
355
|
-
|
|
357
|
+
reports.addToReport( scope, { type: `error`, value: err_msg });
|
|
356
358
|
// Throw error instead?
|
|
357
359
|
reject( err );
|
|
358
360
|
} finally {
|
|
@@ -371,7 +373,7 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
|
|
|
371
373
|
|
|
372
374
|
// Add report strings from supported table, not an altered one
|
|
373
375
|
for ( let row of supported_table ) {
|
|
374
|
-
row.report_str = `${
|
|
376
|
+
row.report_str = `${ reports.convertToOriginalStoryTableRow(scope, { row_data: row }) }`;
|
|
375
377
|
}
|
|
376
378
|
// Sort into alphabetical order, as is recommended for writing story tables
|
|
377
379
|
let sorted_rows = supported_table.sort( function ( row1, row2 ) {
|
|
@@ -382,13 +384,13 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
|
|
|
382
384
|
|
|
383
385
|
// Add full used table to report so it can be copied if needed.
|
|
384
386
|
// TODO: Should we/How do we get this in the final scenario report instead of here?
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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 |`, });
|
|
388
390
|
let at_least_one_row_was_NOT_used = false;
|
|
389
391
|
for ( let row of sorted_rows ) {
|
|
390
392
|
if ( row.times_used > 0 ) {
|
|
391
|
-
|
|
393
|
+
reports.addToReport(scope, { type: `row`, value: row.report_str, });
|
|
392
394
|
} else {
|
|
393
395
|
at_least_one_row_was_NOT_used = true;
|
|
394
396
|
}
|
|
@@ -396,17 +398,17 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
|
|
|
396
398
|
|
|
397
399
|
// Add unused rows if needed
|
|
398
400
|
if ( at_least_one_row_was_NOT_used ) {
|
|
399
|
-
|
|
401
|
+
reports.addToReport( scope, { type: `note`, value: `${ ' '.repeat(2) }Unused rows:` });
|
|
400
402
|
for ( let row of sorted_rows ) {
|
|
401
403
|
if ( row.times_used === 0 ) {
|
|
402
|
-
|
|
404
|
+
reports.addToReport(scope, { type: `row`, value: row.report_str, });
|
|
403
405
|
}
|
|
404
406
|
}
|
|
405
407
|
} else {
|
|
406
|
-
|
|
408
|
+
reports.addToReport(scope, { type: `note`, value: `${ ' '.repeat(2) }All rows were used` });
|
|
407
409
|
}
|
|
408
410
|
|
|
409
|
-
|
|
411
|
+
reports.addToReport(scope, { type: `note`, value: `` });
|
|
410
412
|
});
|
|
411
413
|
|
|
412
414
|
//#####################################
|
|
@@ -456,7 +458,7 @@ Then(/the (?:question|page|screen) id should be "([^"]+)"/i, { timeout: -1 }, as
|
|
|
456
458
|
// No question id exists
|
|
457
459
|
let no_id_msg = `Did not find any question id.`;
|
|
458
460
|
if ( !question_has_id ) {
|
|
459
|
-
|
|
461
|
+
reports.addToReport( scope, { type: `error`, value: no_id_msg });
|
|
460
462
|
}
|
|
461
463
|
expect( question_has_id , no_id_msg ).to.be.true;
|
|
462
464
|
|
|
@@ -464,7 +466,7 @@ Then(/the (?:question|page|screen) id should be "([^"]+)"/i, { timeout: -1 }, as
|
|
|
464
466
|
let no_match_msg = `The question id was supposed to be "${ question_id }"`
|
|
465
467
|
+ `, but it's actually "${ id }".`;
|
|
466
468
|
if ( !id_matches ) {
|
|
467
|
-
|
|
469
|
+
reports.addToReport( scope, { type: `error`, value: no_match_msg });
|
|
468
470
|
}
|
|
469
471
|
expect( id_matches, no_match_msg ).to.be.true;
|
|
470
472
|
|
|
@@ -477,7 +479,7 @@ Then(/I ?(?:should)? see the phrase "([^"]+)"/i, async ( phrase ) => { // √
|
|
|
477
479
|
const bodyText = await scope.page.$eval('body', elem => elem.innerText);
|
|
478
480
|
|
|
479
481
|
let msg = `The text "${ phrase }" SHOULD be on this page, but it's NOT.`;
|
|
480
|
-
if ( !bodyText.includes( phrase )) {
|
|
482
|
+
if ( !bodyText.includes( phrase )) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
481
483
|
expect( bodyText, msg ).to.contain( phrase );
|
|
482
484
|
|
|
483
485
|
await scope.afterStep(scope);
|
|
@@ -491,7 +493,7 @@ Then(/I should not see the phrase "([^"]+)"/i, async ( phrase ) => { // √
|
|
|
491
493
|
const bodyText = await scope.page.$eval('body', elem => elem.innerText);
|
|
492
494
|
|
|
493
495
|
let msg = `The text "${ phrase }" should NOT be on this page, but it IS here.`;
|
|
494
|
-
if ( bodyText.includes( phrase )) {
|
|
496
|
+
if ( bodyText.includes( phrase )) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
495
497
|
expect( bodyText, msg ).not.to.contain( phrase );
|
|
496
498
|
|
|
497
499
|
await scope.afterStep(scope);
|
|
@@ -501,7 +503,7 @@ Then('an element should have the id {string}', async ( id ) => { // √
|
|
|
501
503
|
const element = await scope.page.$('#' + id);
|
|
502
504
|
|
|
503
505
|
let msg = `No element on this page has the ID "${ id }".`;
|
|
504
|
-
if ( !element ) {
|
|
506
|
+
if ( !element ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
505
507
|
expect( element, msg ).to.exist;
|
|
506
508
|
|
|
507
509
|
await scope.afterStep(scope);
|
|
@@ -556,7 +558,7 @@ Then(/I (don't|can't|don’t|can’t|cannot|do not) continue/, async (unused) =>
|
|
|
556
558
|
* Can other things trigger navigation? Re: other inputs things
|
|
557
559
|
* like 'Enter' acting like a click is a test for docassemble */
|
|
558
560
|
let msg = `The page should have stopped the user from continuing, but the user was able to continue.`;
|
|
559
|
-
if ( scope.navigated ) {
|
|
561
|
+
if ( scope.navigated ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
560
562
|
expect( scope.navigated, msg ).to.be.false;
|
|
561
563
|
|
|
562
564
|
await scope.afterStep(scope);
|
|
@@ -567,12 +569,12 @@ Then('I will be told an answer is invalid', async () => { // √
|
|
|
567
569
|
let { was_found, error_handlers } = await scope.checkForError( scope );
|
|
568
570
|
|
|
569
571
|
let no_error_msg = `No error message was found on the page`;
|
|
570
|
-
if ( !was_found ) {
|
|
572
|
+
if ( !was_found ) { reports.addToReport(scope, { type: `error`, value: no_error_msg }); }
|
|
571
573
|
expect( was_found, no_error_msg ).to.be.true;
|
|
572
574
|
|
|
573
575
|
let was_user_error = error_handlers.complex_user_error !== undefined || error_handlers.simple_user_error !== undefined;
|
|
574
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.`;
|
|
575
|
-
if ( !was_user_error ) {
|
|
577
|
+
if ( !was_user_error ) { reports.addToReport(scope, { type: `error`, value: not_user_error_msg }); }
|
|
576
578
|
expect( was_user_error, not_user_error_msg ).to.be.true;
|
|
577
579
|
|
|
578
580
|
await scope.afterStep(scope);
|
|
@@ -583,7 +585,7 @@ Then('I arrive at the next page', async () => { // √
|
|
|
583
585
|
* Can other things trigger navigation? Re: other inputs things
|
|
584
586
|
* like 'Enter' acting like a click is a test for docassemble */
|
|
585
587
|
let msg = `User did not arrive at the next page.`
|
|
586
|
-
if ( !scope.navigated ) {
|
|
588
|
+
if ( !scope.navigated ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
587
589
|
expect( scope.navigated, msg ).to.be.true;
|
|
588
590
|
await scope.afterStep(scope);
|
|
589
591
|
});
|
|
@@ -594,7 +596,7 @@ Then('I should see the link {string}', async ( linkText ) => { // √
|
|
|
594
596
|
let [link] = await scope.page.$x(`//a[contains(text(), "${linkText}")]`);
|
|
595
597
|
|
|
596
598
|
let msg = `Cannot find a link with the text "${ linkText }".`;
|
|
597
|
-
if ( !link ) {
|
|
599
|
+
if ( !link ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
598
600
|
expect( link, msg ).to.exist;
|
|
599
601
|
|
|
600
602
|
await scope.afterStep(scope);
|
|
@@ -605,7 +607,7 @@ Then('I should see the link to {string}', async ( url ) => { // √
|
|
|
605
607
|
let link = await scope.page.$(`*[href="${ url }"]`);
|
|
606
608
|
|
|
607
609
|
let msg = `Cannot find a link to "${ url }".`;
|
|
608
|
-
if ( !link ) {
|
|
610
|
+
if ( !link ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
609
611
|
expect(link, msg ).to.exist;
|
|
610
612
|
|
|
611
613
|
await scope.afterStep(scope);
|
|
@@ -621,7 +623,7 @@ Then(/the "([^"]+)" link leads to "([^"]+)"/, async (linkText, expected_url) =>
|
|
|
621
623
|
let actual_url = await prop_obj.jsonValue();
|
|
622
624
|
|
|
623
625
|
let msg = `Cannot find a link with the text "${ linkText }" leading to ${ expected_url }.`;
|
|
624
|
-
if ( actual_url !== expected_url ) {
|
|
626
|
+
if ( actual_url !== expected_url ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
625
627
|
expect( actual_url ).to.equal( expected_url );
|
|
626
628
|
|
|
627
629
|
await scope.afterStep(scope);
|
|
@@ -640,7 +642,7 @@ Then(/the "([^"]+)" link opens in (a new window|the same window)/, async (linkTe
|
|
|
640
642
|
|| ( !should_open_a_new_window && !opens_a_new_window );
|
|
641
643
|
|
|
642
644
|
let msg = `The link "${ linkText }" does NOT open in ${ which_window }.`;
|
|
643
|
-
if ( !hasCorrectWindowTarget ) {
|
|
645
|
+
if ( !hasCorrectWindowTarget ) { reports.addToReport(scope, { type: `error`, value: msg }); }
|
|
644
646
|
expect( hasCorrectWindowTarget, msg ).to.be.true;
|
|
645
647
|
|
|
646
648
|
await scope.afterStep(scope);
|
|
@@ -673,7 +675,7 @@ Then(/(?:the )?text(?: in)?(?: the)?(?: JSON)?(?: var)?(?:iable)?(?: )?"([^"]+)"
|
|
|
673
675
|
|
|
674
676
|
// Add warning to report if value is not as expected
|
|
675
677
|
if ( actual_value !== expected_value ) {
|
|
676
|
-
|
|
678
|
+
reports.addToReport( scope, {
|
|
677
679
|
type: `error`,
|
|
678
680
|
value: `The variable "${ var_name }" was not equal to the expected `
|
|
679
681
|
+ `value on the "${ scope.page_id }" screen. Check `
|
|
@@ -780,7 +782,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
780
782
|
let random_input_path = `${ scope.paths.scenario }/random${ timestamp }`
|
|
781
783
|
scope.paths.random_screenshots = random_input_path;
|
|
782
784
|
fs.mkdirSync( scope.paths.random_screenshots );
|
|
783
|
-
|
|
785
|
+
reports.addToReport( scope, {
|
|
784
786
|
type: 'row',
|
|
785
787
|
value: `Testing interview with random input (in ${ random_input_path })`
|
|
786
788
|
});
|
|
@@ -815,7 +817,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
815
817
|
let has_content = await scope.has_interview_content( scope );
|
|
816
818
|
|
|
817
819
|
if ( !has_content ) {
|
|
818
|
-
|
|
820
|
+
reports.addToReport( scope, {
|
|
819
821
|
type: `error`,
|
|
820
822
|
value: no_content_err
|
|
821
823
|
});
|
|
@@ -824,7 +826,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
824
826
|
} else {
|
|
825
827
|
let timeout_message = `The page with an id of "${ id }" took too long`
|
|
826
828
|
+ ` to fill with random answers (over ${ scope.timeout/1000/60 } min).`
|
|
827
|
-
|
|
829
|
+
reports.addToReport( scope, { type: `error`, value: timeout_message });
|
|
828
830
|
reject( timeout_message );
|
|
829
831
|
}
|
|
830
832
|
|
|
@@ -865,7 +867,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
865
867
|
// Make sure to only throw once
|
|
866
868
|
if ( !has_thrown ) {
|
|
867
869
|
has_thrown = true;
|
|
868
|
-
|
|
870
|
+
reports.addToReport( scope, {
|
|
869
871
|
type: `error`,
|
|
870
872
|
value: no_content_err
|
|
871
873
|
});
|
|
@@ -889,7 +891,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
889
891
|
// Throw a specific error if there's no interview content
|
|
890
892
|
let has_content = await scope.has_interview_content( scope );
|
|
891
893
|
if ( !has_content ) {
|
|
892
|
-
|
|
894
|
+
reports.addToReport( scope, {
|
|
893
895
|
type: `error`,
|
|
894
896
|
value: no_content_err
|
|
895
897
|
});
|
|
@@ -899,7 +901,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
899
901
|
} else {
|
|
900
902
|
let err_msg = `The page with the question id "${ id }" errored when trying to fill`
|
|
901
903
|
+ ` it with random answers: ${ error.name }`
|
|
902
|
-
|
|
904
|
+
reports.addToReport( scope, { type: `error`, value: err_msg });
|
|
903
905
|
reject( error );
|
|
904
906
|
} // ends if !has_content
|
|
905
907
|
|
|
@@ -1097,24 +1099,29 @@ Given(/the final Scenario status should be "(passed|failed)"/i, async ( expected
|
|
|
1097
1099
|
|
|
1098
1100
|
After(async function(scenario) {
|
|
1099
1101
|
|
|
1102
|
+
let changeable_test_status = scenario.result.status;
|
|
1100
1103
|
// The accessibility failures don't happen til the end, so the rest of the test can continue
|
|
1101
1104
|
if ( scope.check_all_for_a11y && !scope.passed_all_for_a11y) {
|
|
1102
1105
|
// The fact that the scenario failed has already been noted earlier in the report
|
|
1103
|
-
|
|
1106
|
+
changeable_test_status = `FAILED`;
|
|
1107
|
+
scope.addToReport(scope, {
|
|
1108
|
+
type: `error`,
|
|
1109
|
+
value: `Accessibility standards weren't met. See information above.`
|
|
1110
|
+
});
|
|
1104
1111
|
}
|
|
1105
1112
|
|
|
1106
1113
|
// Record status before internal report tests can interfere with values
|
|
1107
|
-
|
|
1114
|
+
reports.setReportScenarioStatus( scope, { status: changeable_test_status });
|
|
1108
1115
|
|
|
1109
|
-
if (
|
|
1116
|
+
if (changeable_test_status !== `PASSED`) {
|
|
1110
1117
|
if ( session_vars.get_debug() ) {
|
|
1111
|
-
console.log( `Non-passed status occurred while running test:`,
|
|
1118
|
+
console.log( `Non-passed status occurred while running test:`, changeable_test_status );
|
|
1112
1119
|
}
|
|
1113
1120
|
|
|
1114
1121
|
if ( scope.page ) {
|
|
1115
1122
|
|
|
1116
1123
|
if ( !!scope.disable_error_screenshot ) {
|
|
1117
|
-
|
|
1124
|
+
reports.addToReport(scope, {
|
|
1118
1125
|
type: `row`,
|
|
1119
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.`
|
|
1120
1127
|
});
|
|
@@ -1138,21 +1145,19 @@ After(async function(scenario) {
|
|
|
1138
1145
|
} // ends if scope.page exists
|
|
1139
1146
|
|
|
1140
1147
|
// This has to come after the security message or security message doesn't print. Not sure why.
|
|
1141
|
-
if (
|
|
1142
|
-
|
|
1148
|
+
if (changeable_test_status === `FAILED`) {
|
|
1149
|
+
reports.addToReport(scope, { type: `outcome`, value: `**-- Scenario Failed --**` });
|
|
1143
1150
|
}
|
|
1144
1151
|
|
|
1145
1152
|
} // ends if not passed
|
|
1146
1153
|
|
|
1147
|
-
let original_scenario_status = scenario.result.status;
|
|
1148
|
-
|
|
1149
1154
|
// Internal tests of this framework
|
|
1150
1155
|
if ( scope.expected_status ) {
|
|
1151
1156
|
let expected = scope.expected_status;
|
|
1152
1157
|
scope.expected_status = null; // reset for next Scenario
|
|
1153
|
-
expect(
|
|
1158
|
+
expect( changeable_test_status ).to.equal( expected );
|
|
1154
1159
|
// If the status was as expected, pass this criteria
|
|
1155
|
-
|
|
1160
|
+
changeable_test_status = `PASSED`;
|
|
1156
1161
|
}
|
|
1157
1162
|
|
|
1158
1163
|
if ( scope.expected_in_report && scope.expected_in_report.length > 0) {
|
|
@@ -1161,8 +1166,8 @@ After(async function(scenario) {
|
|
|
1161
1166
|
scope.expected_in_report = null;
|
|
1162
1167
|
let all_were_included = await scope.reportIncludesAllExpected( scope, { expected });
|
|
1163
1168
|
|
|
1164
|
-
if ( all_were_included ) {
|
|
1165
|
-
else {
|
|
1169
|
+
if ( all_were_included ) { changeable_test_status = `PASSED`; }
|
|
1170
|
+
else { changeable_test_status = `FAILED`; }
|
|
1166
1171
|
}
|
|
1167
1172
|
|
|
1168
1173
|
if (scope.expected_not_in_report && scope.expected_not_in_report.length > 0) {
|
|
@@ -1172,9 +1177,9 @@ After(async function(scenario) {
|
|
|
1172
1177
|
let all_were_not_there = await scope.reportDoesNotInclude( scope, { not_expected });
|
|
1173
1178
|
|
|
1174
1179
|
if ( all_were_not_there ) {
|
|
1175
|
-
|
|
1180
|
+
changeable_test_status = `PASSED`;
|
|
1176
1181
|
} else {
|
|
1177
|
-
|
|
1182
|
+
changeable_test_status = `FAILED`;
|
|
1178
1183
|
}
|
|
1179
1184
|
|
|
1180
1185
|
}
|
|
@@ -1198,7 +1203,7 @@ After(async function(scenario) {
|
|
|
1198
1203
|
} else {
|
|
1199
1204
|
// Throw any non-timeout error
|
|
1200
1205
|
let err_msg = `Error occurred when ALKiln tried to sign out in preparation for the next test.`
|
|
1201
|
-
|
|
1206
|
+
reports.addToReport( scope, { type: `error`, value: err_msg });
|
|
1202
1207
|
throw error;
|
|
1203
1208
|
} // ends if error is timeout error
|
|
1204
1209
|
|
|
@@ -1209,16 +1214,29 @@ After(async function(scenario) {
|
|
|
1209
1214
|
// Add report text to Scenario folder after everything has been added to the report
|
|
1210
1215
|
let current_report_obj = scope.report.get( scope.scenario_id );
|
|
1211
1216
|
let scenario_report_obj = [ scope.scenario_id, current_report_obj ]; // Mimic how loop works with `Map`
|
|
1212
|
-
let report =
|
|
1217
|
+
let report = reports.getPrintableScenario( scenario_report_obj );
|
|
1213
1218
|
// Save the report as in the Scenario folder
|
|
1214
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
|
+
}
|
|
1215
1233
|
});
|
|
1216
1234
|
|
|
1217
1235
|
AfterAll(async function() {
|
|
1218
1236
|
// Stop collecting server response statuses
|
|
1219
1237
|
scope.stop_tracking_server_status = true;
|
|
1220
1238
|
|
|
1221
|
-
let report =
|
|
1239
|
+
let report = reports.getPrintableReport( scope );
|
|
1222
1240
|
// Log the report. Can't get this.attach() or this.log() to work in `After()` or here.
|
|
1223
1241
|
// May need newest version of cucumberjs
|
|
1224
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()
|