@suffolklitlab/alkiln 5.13.2 → 5.13.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.github/workflows/github_server.yml +7 -7
  2. package/.github/workflows/playground.yml +1 -1
  3. package/CHANGELOG.md +32 -1
  4. package/action.yml +70 -28
  5. package/action_for_github_server/action.yml +61 -29
  6. package/docs/4_environments.md +243 -0
  7. package/docs/artifacts_folder_name.md +135 -0
  8. package/docs/constrained_random_tests.md +347 -0
  9. package/docs/decisions/006_which_log_level_to_pick_2024_09_5.md +70 -0
  10. package/docs/decisions/007_getting_and_storing_logs.md +179 -0
  11. package/docs/decisions/move_runtime_config_to_sources_folder.md +3 -0
  12. package/docs/decisions/reports_and_logs_2024_08_11.md +161 -0
  13. package/docs/kinds_of_errors.md +26 -0
  14. package/docs/prioritize_constrained_random_tests.md +101 -0
  15. package/docs/seprate_file_generation_GitHub_action.md +11 -0
  16. package/lib/docassemble/docassemble_api_interface.js +77 -51
  17. package/lib/docassemble/server_install.js +32 -9
  18. package/lib/docassemble/setup.js +40 -25
  19. package/lib/docassemble/takedown.js +33 -10
  20. package/lib/parse.js +36 -0
  21. package/lib/run_cucumber.js +115 -36
  22. package/lib/scope.js +69 -77
  23. package/lib/steps.js +70 -42
  24. package/lib/utils/Log.js +982 -0
  25. package/lib/utils/files.js +18 -11
  26. package/lib/utils/get_sources_paths.js +162 -0
  27. package/lib/utils/parse.js +1 -0
  28. package/lib/utils/proccess_command_line_args.js +267 -0
  29. package/lib/utils/reports.js +64 -17
  30. package/lib/utils/session_vars.js +34 -21
  31. package/lib/utils/time.js +44 -0
  32. package/package.json +1 -1
  33. package/tests/log_codes/check_codes.sh +2 -1
  34. package/tests/log_codes/log_code_expected_instances.txt +33 -9
  35. package/tests/unit_tests/getAllFields.test.js +5 -0
  36. package/tests/unit_tests/getMatchingRows.test.js +5 -0
  37. package/tests/unit_tests/log.test.js +581 -0
  38. package/tests/unit_tests/normalizeTable.test.js +5 -0
  39. package/lib/utils/log.js +0 -96
package/lib/scope.js CHANGED
@@ -13,14 +13,12 @@ const pdf_reader = require('./utils/pdf_reader');
13
13
  const session_vars = require('./utils/session_vars');
14
14
  const get_base_interview_url = require(`./docassemble/get_base_interview_url`);
15
15
  const time = require('./utils/time');
16
- const log = require('./utils/log');
17
16
  const { waitForTimeout } = require('./utils/time');
18
17
  const files = require('./utils/files' );
19
18
  const da_api = require('./docassemble/docassemble_api_interface');
20
19
  const wrapPromiseWithTimeout = require('./cucumber_8_shim');
21
20
  const reports = require(`./utils/reports`);
22
21
 
23
-
24
22
  let ordinal_to_integer = {
25
23
  only: 0, first: 0, second: 1, third: 2, fourth: 3, fifth: 4,
26
24
  sixth: 5, seventh: 6, eighth: 7, ninth: 8, tenth: 9,
@@ -84,10 +82,16 @@ let toBase64 = function ( utf8_str, exclude_padding=true) {
84
82
 
85
83
  let proxies_regex = /(\bx\.)|(\bx\[)|(\[[ijklmn]\])/g;
86
84
  let da_base64_regex = /^[A-Za-z0-9+/]+$/;
85
+ let log = null;
87
86
 
88
87
  module.exports = {
89
88
  trigger_not_needed_flag: `ALKiln: no trigger variable needed`,
90
89
 
90
+
91
+ set_log: function ({ log_obj }) {
92
+ log = log_obj;
93
+ },
94
+
91
95
  getSafeScenarioBaseFilename: async function( scope, { scenario }) {
92
96
  /** Return a string that's safe to be a filename. Include the Scenario
93
97
  * description and tags.
@@ -514,13 +518,17 @@ module.exports = {
514
518
  },
515
519
 
516
520
  continue_button_selector: `fieldset.da-field-buttons button[type="submit"].btn-primary`,
521
+ continue_button_selector_backup: `.da-fieldset.da-field-buttons button[type="submit"].btn-primary`,
517
522
  signature_selector: `fieldset .dasigsave`,
523
+ signature_selector_backup: `.da-fieldset .dasigsave`,
518
524
  continue: async function ( scope ) {
519
525
  /* Presses whatever button it finds that might lead to the next page. */
520
526
  // Any selectors I find seem somewhat precarious.
521
527
  let elem = await Promise.race([
522
528
  scope.page.waitForSelector(scope.continue_button_selector), // other pages (this is the most consistent way)
529
+ scope.page.waitForSelector(scope.continue_button_selector_backup),
523
530
  scope.page.waitForSelector(scope.signature_selector), // signature page
531
+ scope.page.waitForSelector(scope.signature_selector_backup),
524
532
  ]);
525
533
  await elem.evaluate( el => { return el.className });
526
534
  // Waits for navigation or user error
@@ -540,9 +548,11 @@ module.exports = {
540
548
  // <button type="submit" class="btn btn-da btn-danger" name="X211bHRpcGxlX2Nob2ljZQ" value="0">exit</button>
541
549
  // `buttons:` can be used in question blocks as choices
542
550
  let regular = await scope.page.$(scope.continue_button_selector);
551
+ let regular_backup = await scope.page.$(scope.continue_button_selector_backup);
543
552
  let signature = await scope.page.$(scope.signature_selector);
553
+ let signature_backup = await scope.page.$(scope.signature_selector_backup);
544
554
 
545
- return regular !== null || signature !== null;
555
+ return regular !== null || regular_backup !== null || signature !== null || signature_backup !== null;
546
556
 
547
557
  }, // Ends scope.continue_exists()
548
558
 
@@ -692,7 +702,7 @@ module.exports = {
692
702
  });
693
703
  expect( load_result.error ).to.equal( '' );
694
704
  } else {
695
- reports.addToReport(scope, { type: `row info`, code: `ALK0117`, value: `Successfully found the interview at "${ interview_url }"` });
705
+ reports.addToReport(scope, { type: `row info`, code: `ALK0117`, value: `Successfully found the interview` });
696
706
  }
697
707
 
698
708
  return scope.page;
@@ -711,19 +721,19 @@ module.exports = {
711
721
  if ( !scenario.interviews ) { scenario.interviews = {}; }
712
722
  if ( !scenario.interviews[ vars.uid ] ) {
713
723
  scenario.interviews[ vars.uid ] = vars;
714
- log.debug({
715
- code: `ALK0185`, type: `info`,
716
- pre: `Saved new interview data. File path: ${ vars.i }. Session id: ${ vars.uid }.`
717
- });
724
+ log.debug({ code: `ALK0185`, context: `session_id_etc` },
725
+ `Saved new interview data. File path: ${ vars.i }. Session id: ${ vars.uid }.`
726
+ );
718
727
  } else {
719
- log.debug({
720
- code: `ALK0186`, type: `info`,
721
- pre: `Interview data already existed. File path: ${ vars.i }. Session id: ${ vars.uid }.`
722
- });
728
+ log.debug({ code: `ALK0186`, context: `session_id_etc` },
729
+ `Interview data already existed. File path: ${ vars.i }. Session id: ${ vars.uid }.`
730
+ );
723
731
  }
724
732
  return vars;
725
733
  } else {
726
- log.debug({ code: `ALK0187`, type: `info`, pre: `Found no interview data.` });
734
+ log.debug({ code: `ALK0187`, context: `session_id_etc` },
735
+ `Found no interview data.`
736
+ );
727
737
  return null;
728
738
  }
729
739
  }, // Ends scope.set_interview_session_data()
@@ -870,11 +880,9 @@ module.exports = {
870
880
  supported_table.push( result );
871
881
  } // ends for every row
872
882
 
873
- log.debug({
874
- code: `ALK0122`,
875
- type: `info`,
876
- pre: `scope.normalizedTable():\n ${JSON.stringify( supported_table )}`
877
- });
883
+ log.debug({ code: `ALK0122`, context: `table`, },
884
+ `scope.normalizedTable():`, supported_table
885
+ );
878
886
 
879
887
  return supported_table;
880
888
  }, // Ends scope.normalizeTable()
@@ -916,7 +924,7 @@ module.exports = {
916
924
  // All the different types of fields
917
925
  // buttons, canvases, inputs of all kinds, selects (dropdowns), textareas. Are there more?
918
926
  // Will deal with `option` once inside `select`
919
- let all_nodes = $( `#dasigpage, fieldset button, .daquestionactionbutton, fieldset input, .da-form-group input, .da-form-group select, form select, .da-form-group textarea` );
927
+ let all_nodes = $( `#dasigpage, fieldset button, .da-fieldset button, .daquestionactionbutton, fieldset input, da-fieldset input, .da-form-group input, .da-form-group select, form select, .da-form-group textarea` );
920
928
  for ( let node of all_nodes ) {
921
929
  // Decision: Do not abstract the below. There's too much data to pass around for it to make sense
922
930
  let $node = $( node );
@@ -1024,11 +1032,9 @@ module.exports = {
1024
1032
  fields.push( field_data );
1025
1033
  } // ends for every node
1026
1034
 
1027
- log.debug({
1028
- code: `ALK0124`,
1029
- type: `info`,
1030
- pre: `page fields:\n${ JSON.stringify( fields )}`
1031
- });
1035
+ log.debug({ code: `ALK0124`, context: `page fields`, },
1036
+ fields
1037
+ );
1032
1038
  return fields;
1033
1039
  }, // Ends scope.getAllFields()
1034
1040
 
@@ -1078,11 +1084,9 @@ module.exports = {
1078
1084
  /* Return variable names and values combined in ways that can
1079
1085
  * try to match table rows. Tables can have two properties:
1080
1086
  * variable name and value. */
1081
- log.debug({
1082
- code: `ALK0126`,
1083
- type: `info`,
1084
- pre: `scope.getPossibleTableRowMatchers() var_names: ${JSON.stringify(var_names)} values: ${JSON.stringify(values)}`
1085
- });
1087
+ log.debug({ code: `ALK0126`, context: `matching`, },
1088
+ `scope.getPossibleTableRowMatchers() var_names:`, var_names, `values:`, values
1089
+ );
1086
1090
  let possible_table_rows = [];
1087
1091
  for ( let var_name of var_names ) {
1088
1092
  // Inputs like textareas don't have values
@@ -1557,17 +1561,13 @@ module.exports = {
1557
1561
  reports.addToReport(scope, { type: `warning`, code: `ALK0130`, value: multiple_matches_msg });
1558
1562
  }
1559
1563
 
1560
- log.debug({
1561
- code: `ALK0131`,
1562
- type: `info`,
1563
- pre: `final_matching_rows:\n${JSON.stringify( final_matching_rows )}`
1564
- });
1564
+ log.debug({ code: `ALK0131`, context: `matching`, },
1565
+ `final_matching_rows:`, final_matching_rows
1566
+ );
1565
1567
  if ( final_matching_rows.length == 0) {
1566
- log.debug({
1567
- code: `ALK0132`,
1568
- type: `info`,
1569
- pre: `field:\n${JSON.stringify( field )}`
1570
- });
1568
+ log.debug({ code: `ALK0132`, context: `matching`, },
1569
+ `0 matching rows. Field:`, field
1570
+ );
1571
1571
  }
1572
1572
 
1573
1573
  return final_matching_rows;
@@ -1653,11 +1653,9 @@ module.exports = {
1653
1653
 
1654
1654
  get_handle_from_field: async function ( scope, { field }) {
1655
1655
  /** Get the element's puppeteer handle using the field's selector. */
1656
- log.debug({
1657
- code: `ALK0133`,
1658
- type: `info`,
1659
- pre: `scope.get_handle_from_field() field.selector: ${JSON.stringify(field.selector)}`
1660
- });
1656
+ log.debug({ code: `ALK0133`, },
1657
+ `scope.get_handle_from_field() field.selector:`, field.selector
1658
+ );
1661
1659
  let handle = await scope.page.evaluateHandle(( selector ) => {
1662
1660
  let elem = document.querySelector( selector );
1663
1661
  // If the element has a sibling that's a label, we need to
@@ -2169,7 +2167,9 @@ module.exports = {
2169
2167
  * @param ensure_all_vars_used {bool} [false] = Default false. If true, errors if any
2170
2168
  * object in var_data is not used during running of this function.
2171
2169
  */
2172
- log.debug({ type: `plain`, pre: `~~~~~~~~~~~~~~ scope.setFields() ~~~~~~~~~~~~~~` });
2170
+ log.debug({ code: `ALK0221`, before: `~~~~~~~~~~~~~~\n`, },
2171
+ `scope.setFields()`
2172
+ );
2173
2173
 
2174
2174
  // Only record the page id once for each page to which we navigate
2175
2175
  let { id } = await scope.examinePageID( scope, 'none to match' );
@@ -2213,8 +2213,7 @@ module.exports = {
2213
2213
  // Accumulate number of times this row was used. Esp important for `.target_number`
2214
2214
  await scope.increment_row_use( scope, { row: row_used });
2215
2215
 
2216
- process.stdout.write(`\x1b[36m${ '*' }\x1b[0m`); // assumes var was set if no error occurred
2217
-
2216
+ log.stdout({}, `\x1b[36m${ '*' }\x1b[0m`); // assumes var was set if no error occurred
2218
2217
  }
2219
2218
  } // ends for every non-submit-button field on the page
2220
2219
  } // ends while fields are still being set
@@ -2259,7 +2258,7 @@ module.exports = {
2259
2258
  // }
2260
2259
 
2261
2260
  did_navigate = true;
2262
- process.stdout.write(`\x1b[36m${ 'v' }\x1b[0m`); // pressed button successfully
2261
+ log.stdout({}, `\x1b[36m${ 'v' }\x1b[0m`); // pressed button successfully
2263
2262
  break;
2264
2263
 
2265
2264
  } // ends if row_used
@@ -2306,7 +2305,7 @@ module.exports = {
2306
2305
  // }
2307
2306
 
2308
2307
  did_navigate = true;
2309
- process.stdout.write(`\x1b[36m${ '>' }\x1b[0m`); // continued successfully
2308
+ log.stdout({}, `\x1b[36m${ '>' }\x1b[0m`); // continued successfully
2310
2309
  } // ends if !did_navigate && ensure_navigation
2311
2310
 
2312
2311
  if ( did_navigate && scope.check_all_for_a11y ) {
@@ -2635,6 +2634,7 @@ module.exports = {
2635
2634
 
2636
2635
  let fullPage = true;
2637
2636
  let signature_elem = await scope.page.$(scope.signature_selector);
2637
+ signature_elem = signature_elem || await scope.page.$(scope.signature_selector_backup);
2638
2638
  if ( signature_elem !== null ) {
2639
2639
  fullPage = false;
2640
2640
  }
@@ -3102,36 +3102,30 @@ module.exports = {
3102
3102
  let redirect_promise = scope.page.waitForResponse(function ( response ) {
3103
3103
  return response.status() === 302;
3104
3104
  }, { signal: controller.signal })
3105
- .catch(( error ) => {
3106
- log.debug({
3107
- code: `ALK0204`, type: `info`,
3108
- pre: `302 sign-in wait error: ${ error.name }`,
3109
- data: error,
3110
- });
3105
+ .catch(( catch_info ) => {
3106
+ log.debug({ code: `ALK0204`, context: `nav`, },
3107
+ `302 sign-in wait catch error:`, catch_info.name, catch_info,
3108
+ );
3111
3109
  });
3112
3110
 
3113
3111
  // Invalid credentials, no navigation, no sign in
3114
3112
  let wrong_sign_in_promise = scope.page.waitForResponse(function ( response ) {
3115
3113
  return response.status() >= 200 && response.status() < 300 && response.url().includes(`/sign-in`);
3116
3114
  }, { signal: controller.signal })
3117
- .catch(( error ) => {
3118
- log.debug({
3119
- code: `ALK0205`, type: `info`,
3120
- pre: `200s sign-in wait error: ${ error.name }`,
3121
- data: error,
3122
- });
3115
+ .catch(( catch_info ) => {
3116
+ log.debug({ code: `ALK0205`, context: `nav`, },
3117
+ `200s sign-in wait catch error:`, catch_info.name, catch_info,
3118
+ );
3123
3119
  });
3124
3120
 
3125
3121
  // System error
3126
3122
  let error_promise = scope.page.waitForResponse(function ( response ) {
3127
3123
  return response.status() >= 500 && response.status() < 600;
3128
3124
  }, { signal: controller.signal })
3129
- .catch(( error ) => {
3130
- log.debug({
3131
- code: `ALK0206`, type: `info`,
3132
- pre: `500s sign-in wait error: ${ error.name }`,
3133
- data: error,
3134
- });
3125
+ .catch(( catch_info ) => {
3126
+ log.debug({ code: `ALK0206`, context: `nav`, },
3127
+ `500s sign-in wait catch error:`, catch_info.name, catch_info,
3128
+ );
3135
3129
  });
3136
3130
 
3137
3131
  let click_promise = elem.click(); // MUST complete
@@ -3150,10 +3144,9 @@ module.exports = {
3150
3144
  // Clean up unresolved promises
3151
3145
  controller.abort();
3152
3146
 
3153
- log.debug({
3154
- code: `ALK0208`, type: `info`,
3155
- pre: `Sign-in winner:`, data: winner,
3156
- });
3147
+ log.debug({ code: `ALK0208`, context: `nav` },
3148
+ `Sign-in winner:`, winner
3149
+ );
3157
3150
 
3158
3151
  return winner;
3159
3152
  },
@@ -3176,7 +3169,7 @@ module.exports = {
3176
3169
  } else {
3177
3170
  // Try to give a random answer for that element tag
3178
3171
  await scope.set_random_input_for[ field.tag ]( scope, { field: field });
3179
- process.stdout.write(`\x1b[36m${ '*' }\x1b[0m`); // assumes var was set if no error occurred
3172
+ log.stdout({}, `\x1b[36m${ '*' }\x1b[0m`); // assumes var was set if no error occurred
3180
3173
  }
3181
3174
 
3182
3175
  } // End for all page fields
@@ -3212,11 +3205,10 @@ module.exports = {
3212
3205
  }
3213
3206
  if (field == null) {
3214
3207
  // Just skip it? Shouldn't happen but has before. Dig in more
3215
- log.debug({
3216
- code: `ALK0164`,
3217
- type: `info`,
3218
- pre: `A random button field choice was null. These were the fields to choose from:\n${ JSON.stringify(fields, null, 2) }`,
3219
- });
3208
+ log.debug({ code: `ALK0164`, context: `random`, },
3209
+ `A random button field choice was null. These were the fields to choose from:`,
3210
+ fields
3211
+ );
3220
3212
  return;
3221
3213
  }
3222
3214
  let handle = await scope.get_handle_from_field( scope, { field: field });
package/lib/steps.js CHANGED
@@ -5,6 +5,7 @@ const {
5
5
  Given,
6
6
  After,
7
7
  Before,
8
+ AfterStep,
8
9
  BeforeAll,
9
10
  AfterAll,
10
11
  setDefaultTimeout //,
@@ -18,7 +19,7 @@ const { v4: uuidv4 } = require('uuid');
18
19
 
19
20
  // Ours
20
21
  const scope = require('./scope');
21
- const log = require('./utils/log');
22
+ const Log = require(`./utils/Log`);
22
23
  const session_vars = require('./utils/session_vars');
23
24
  const files = require('./utils/files' );
24
25
  const reports = require(`./utils/reports`);
@@ -82,7 +83,9 @@ let click_with = {
82
83
  pc: 'click',
83
84
  };
84
85
 
85
- BeforeAll(async () => {
86
+ let log = null;
87
+
88
+ BeforeAll(async function() {
86
89
  scope.showif_timeout = 600;
87
90
  // TODO: Do these really need to be maps? As sets, they may make the output
88
91
  // more predictable to the user, but they add complexity.
@@ -95,29 +98,30 @@ BeforeAll(async () => {
95
98
  // Store path names for files and docs created for the tests
96
99
  scope.paths = {};
97
100
 
98
- // TODO: figure out how to test file and folder creation stuff,
99
- // especially when setup is excluded from the flow.
101
+ // // Make the folder to store all the files for this run of the tests
102
+ // scope.paths.artifacts = files.make_artifacts_folder();
103
+ // // Save its name in a file. `session_vars` can get it back later.
104
+ // // The value we are saving is used by at least run_cucumber.js.
100
105
 
101
- // Make the folder to store all the files for this run of the tests
102
- scope.paths.artifacts = files.make_artifacts_folder();
103
- // Save its name in a file. `session_vars` can get it back later.
104
- // The value we are saving is used by at least run_cucumber.js.
105
- session_vars.save_artifacts_path_name( scope.paths.artifacts );
106
+ // A little indirect, but the artifacts path should always have already been
107
+ // created by now, either by `run_cucumber.js` or the relevant test
108
+ scope.paths.artifacts = session_vars.get_artifacts_path_name();
109
+ log = new Log({ path: scope.paths.artifacts, context: `steps` });
110
+ scope.set_log({ log_obj: log });
106
111
 
107
- scope.paths.report = `${ scope.paths.artifacts }/report.txt`;
108
- scope.paths.failures = `${ scope.paths.artifacts }/${ log.unexpected_filename }`;
109
- scope.paths.debug_log = `${ scope.paths.artifacts }/${ log.debug_log_file }`;
110
- fs.writeFileSync( scope.paths.debug_log, "" );
112
+ scope.paths.debug_log = `${ scope.paths.artifacts }/${ log.debug_log_filename }`;
113
+ scope.paths.report_log = `${ scope.paths.artifacts }/${ log.report_log_filename }`;
114
+ scope.paths.report = `${ scope.paths.artifacts }/${ log.report_filename }`;
115
+ scope.paths.debug_of_unexpected = `${ scope.paths.artifacts }/${ log.temp_unexpected_debug_log_filename }`;
116
+ scope.paths.report_of_unexpected = `${ scope.paths.artifacts }/${ log.unexpected_filename }`;
111
117
 
112
118
  reports.create( scope );
113
119
  });
114
120
 
115
121
  Before(async (scenario) => {
116
122
 
117
- // Useful for those with a lot of tests
118
- // Is unable to print the first Scenario name for some reason
119
- console.log('');
120
- process.stdout.write(`Scenario: ${ scenario.pickle.name }: `);
123
+ // Start the running "progress bar" for the Scenario
124
+ log.stdout({}, `\nScenario: ${ scenario.pickle.name }: `);
121
125
 
122
126
  // Create browser, which can't be created in BeforeAll() where .driver doesn't exist for some reason
123
127
  if (!scope.browser) {
@@ -396,11 +400,9 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
396
400
  from_story_table: true,
397
401
  });
398
402
 
399
- log.debug({
400
- type: `info`,
401
- code: `ALK0061`,
402
- pre: `Rows, including special rows:, ${JSON.stringify( ensured_var_data )}`,
403
- });
403
+ log.debug({ code: `ALK0061`, context: `table`, },
404
+ `Rows, including special rows:`, ensured_var_data
405
+ );
404
406
 
405
407
  // THIS FUNCTION ACTUALLY MATTERS
406
408
  // Some errors happen in there to be closer to the relevant code. We'd have to try/catch anyway.
@@ -972,7 +974,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
972
974
  } // ends if !has_content
973
975
 
974
976
  if ( buttons_exist ) {
975
- process.stdout.write(`\x1b[36m${ 'v' }\x1b[0m`); // pressed button
977
+ log.stdout({}, `\x1b[36m${ 'v' }\x1b[0m`); // pressed button
976
978
  }
977
979
 
978
980
  resolve();
@@ -1263,11 +1265,22 @@ Then(/I fail to delete (\d) detected interview(?:s)? and get no error __internal
1263
1265
  //#####################################
1264
1266
  //#####################################
1265
1267
 
1268
+ AfterStep(async function({ result }) {
1269
+ // TODO: Abstract this
1270
+ if ( result.status === `PASSED` ) { log.stdout({ records_only: true }, `.`); }
1271
+ else if ( result.status === `FAILED` ) { log.stdout({ records_only: true }, `F`); }
1272
+ else if ( result.status === `UNDEFINED` ) { log.stdout({ records_only: true }, `U`); }
1273
+ else if ( result.status === `SKIPPED` ) { log.stdout({ records_only: true }, `-`); }
1274
+ else if ( result.status === `PENDING` ) { log.stdout({ records_only: true }, `P`); }
1275
+ });
1276
+
1266
1277
  After(async function(scenario) {
1267
1278
 
1268
1279
  // Log errors
1269
1280
  if ( scenario.result.message ) {
1270
- log.debug({ type: `info`, code: `ALK0091`, pre: `cucumber error: ${ scenario.result.message }` }); //, verbose: true });
1281
+ log.debug({ code: `ALK0091`, context: `scenario`, },
1282
+ scenario.result.message
1283
+ );
1271
1284
  }
1272
1285
 
1273
1286
  let changeable_test_status = scenario.result.status;
@@ -1297,11 +1310,9 @@ After(async function(scenario) {
1297
1310
  reports.saveFinalScenarioData( scope, { status: changeable_test_status });
1298
1311
 
1299
1312
  if (changeable_test_status !== `PASSED`) {
1300
- log.debug({
1301
- type: `error`,
1302
- code: `ALK0094`,
1303
- pre: `Non-passed status occurred while running test: ${changeable_test_status}`,
1304
- });
1313
+ log.debug({ code: `ALK0094`, level: `warn`, },
1314
+ `Non-passed status occurred while running test: ${ changeable_test_status }`
1315
+ );
1305
1316
 
1306
1317
  if ( scope.page ) {
1307
1318
 
@@ -1397,8 +1408,9 @@ After(async function(scenario) {
1397
1408
  + `Expected status: ${scope.expected_status}. `
1398
1409
  + `See the report for details.`;
1399
1410
  let code = `ALK0099`;
1400
- log.debug({ type: `error`, code: code, pre: msg, })
1401
- expected_status_err_msg = `🤕 ${ code } ERROR: ${ msg }`
1411
+ expected_status_err_msg = log.debug({ code: code, level: `error`, },
1412
+ msg
1413
+ );
1402
1414
  }
1403
1415
  }
1404
1416
  scope.expected_status = null; // reset for next Scenario
@@ -1418,17 +1430,25 @@ After(async function(scenario) {
1418
1430
  // Make sure that tests which ALKiln has calculated as failed
1419
1431
  // cause a cucumber error later. Just changing the status to `FAILED`
1420
1432
  // causes unhelpful cucumber errors. TODO: Add to internal docs.
1421
- log.debug({
1422
- type: `error`,
1423
- code: force_failure_code,
1424
- pre: force_failure_msg
1425
- });
1433
+ // TODO: Should we move this to where the error is thrown?
1434
+ log.debug({ code: force_failure_code, level: `error`, },
1435
+ force_failure_msg
1436
+ );
1426
1437
  // TODO: Look into setDefinitionFunctionWrapper to catch errors
1427
1438
  // of failing tests that should indeed be failing:
1428
1439
  // https://github.com/cucumber/cucumber-js/blob/main/docs/support_files/api_reference.md#setdefinitionfunctionwrapperwrapper
1429
1440
  need_to_force_failed_status = true;
1430
1441
  }
1431
1442
 
1443
+ // Not sure if this is necessary
1444
+ if ( changeable_test_status === 'PASSED' ) {
1445
+ log.stdout({ records_only: true }, `.` );
1446
+ } else if ( changeable_test_status === `SKIPPED` ) {
1447
+ log.stdout({ records_only: true }, `-` );
1448
+ } else {
1449
+ log.stdout({ records_only: true }, changeable_test_status[0].toUpperCase() );
1450
+ }
1451
+
1432
1452
  let signout_succeeded = true;
1433
1453
  let signout_error_msg = ``;
1434
1454
  // If there is a page open, then sign out and close it
@@ -1482,9 +1502,10 @@ After(async function(scenario) {
1482
1502
  // going to the wrong file). If it becomes a problem we can track it down later.
1483
1503
  if ( visible_keys.length > 0 ) {
1484
1504
  // Can an API key ever see an interview that it can't delete?
1485
- log.info({ type: `internal warning`, code: `ALK0197`,
1486
- pre: `The API key can see ${ visible_keys.length } interviews that were not deleted: ${ visible_keys.join( ', ' ) }`
1487
- });
1505
+ log.debug({ code: `ALK0197`, context: `internal`, level: `warn`, }
1506
+ `The API key can see ${ visible_keys.length } interviews that were not deleted:`,
1507
+ visible_keys
1508
+ );
1488
1509
  }
1489
1510
  } catch ( error ) {
1490
1511
  // Deleting interviews is nice, but not non-critical. Don't bother the
@@ -1494,7 +1515,9 @@ After(async function(scenario) {
1494
1515
  // interview. If it becomes a problem we can track it down later.
1495
1516
  // Consider add "internal" to docs: "If you see this, make an
1496
1517
  // issue in ALKiln with your debug/verbose log"
1497
- log.info({ type: `internal warning`, code: `ALK0198`, pre: `Error while cleaning up interviews.`, data: error });
1518
+ log.debug({ code: `ALK0198`, context: `internal`, level: `warn`, error },
1519
+ `Cleaning up interviews incomplete, but unimportant.`
1520
+ );
1498
1521
  }
1499
1522
  }
1500
1523
 
@@ -1513,11 +1536,16 @@ After(async function(scenario) {
1513
1536
  throw new Error( `🤕 ${ force_failure_code } unreachable ERROR: ${ force_failure_msg }` );
1514
1537
 
1515
1538
  } else if ( internal_errors.length > 0 ) {
1516
- log.info({ type: `internal error`, code: `ALK0203`, pre: `No need to force failed status and yet there are error messages. ${ errors.join( errors, '\n') }` });
1539
+ log.debug({ code: `ALK0203`, context: `internal`, level: `warn`, },
1540
+ `There was no need to force failed status and yet there are error messages.`,
1541
+ internal_errors
1542
+ );
1517
1543
  }
1518
1544
 
1519
1545
  // Find the race condition.
1520
- log.debug({ type: `cucumber`, code: `ALK0100`, verbose: true, pre: `Scenario AfterStep() message`, data: scenario.result.message });
1546
+ log.debug({ code: `ALK0100`, context: `scenario`, },
1547
+ `Scenario After() message:`, scenario.result.message
1548
+ );
1521
1549
 
1522
1550
  });
1523
1551