@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/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
- await scope.addReportHeading(scope, {scenario});
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
- scope.addToReport( scope, { type: `error`, value: err_msg });
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 = `${ await scope.convertToOriginalStoryTableRow(scope, { row_data: row }) }`;
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
- await scope.addToReport( scope, { type: `note`, value: `\n${ ' '.repeat(2) }Rows that got set:` });
386
- await scope.addToReport( scope, { type: `step`, value: `${ ' '.repeat(4) }And I get to the question id "${ target_id }" with this data:` });
387
- await scope.addToReport(scope, { type: `row`, value: `${ ' '.repeat(6) }| var | value | trigger |`, });
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
- await scope.addToReport(scope, { type: `row`, value: row.report_str, });
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
- await scope.addToReport( scope, { type: `note`, value: `${ ' '.repeat(2) }Unused rows:` });
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
- await scope.addToReport(scope, { type: `row`, value: row.report_str, });
404
+ reports.addToReport(scope, { type: `row`, value: row.report_str, });
403
405
  }
404
406
  }
405
407
  } else {
406
- await scope.addToReport(scope, { type: `note`, value: `${ ' '.repeat(2) }All rows were used` });
408
+ reports.addToReport(scope, { type: `note`, value: `${ ' '.repeat(2) }All rows were used` });
407
409
  }
408
410
 
409
- await scope.addToReport(scope, { type: `note`, value: `` });
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
- await scope.addToReport( scope, { type: `error`, value: no_id_msg });
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
- await scope.addToReport( scope, { type: `error`, value: no_match_msg });
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 )) { await scope.addToReport(scope, { type: `error`, value: msg }); }
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 )) { await scope.addToReport(scope, { type: `error`, value: msg }); }
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 ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
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 ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
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 ) { await scope.addToReport(scope, { type: `error`, value: no_error_msg }); }
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 ) { await scope.addToReport(scope, { type: `error`, value: not_user_error_msg }); }
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 ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
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 ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
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 ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
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 ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
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 ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
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
- await scope.addToReport( scope, {
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
- await scope.addToReport( scope, {
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
- await scope.addToReport( scope, {
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
- await scope.addToReport( scope, { type: `error`, value: timeout_message });
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
- await scope.addToReport( scope, {
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
- await scope.addToReport( scope, {
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
- await scope.addToReport( scope, { type: `error`, value: err_msg });
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
- scenario.result.status = `FAILED`;
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
- await scope.setReportScenarioStatus( scope, { status: scenario.result.status });
1114
+ reports.setReportScenarioStatus( scope, { status: changeable_test_status });
1108
1115
 
1109
- if (scenario.result.status !== `PASSED`) {
1116
+ if (changeable_test_status !== `PASSED`) {
1110
1117
  if ( session_vars.get_debug() ) {
1111
- console.log( `Non-passed status occurred while running test:`, scenario.result.status );
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
- scope.addToReport(scope, {
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 (scenario.result.status === `FAILED`) {
1142
- await scope.addToReport(scope, { type: `outcome`, value: `**-- Scenario Failed --**` });
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( scenario.result.status ).to.equal( expected );
1158
+ expect( changeable_test_status ).to.equal( expected );
1154
1159
  // If the status was as expected, pass this criteria
1155
- scenario.result.status = `PASSED`;
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 ) { scenario.result.status = `PASSED`; }
1165
- else { scenario.result.status = `FAILED`; }
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
- scenario.result.status = `PASSED`;
1180
+ changeable_test_status = `PASSED`;
1176
1181
  } else {
1177
- scenario.result.status = `FAILED`;
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
- scope.addToReport( scope, { type: `error`, value: err_msg });
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 = await scope.getPrintableScenario( scope, { scenario: scenario_report_obj });
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 = await scope.getPrintableReport( scope );
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()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suffolklitlab/alkiln",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {