@suffolklitlab/alkiln 5.13.3 → 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 (37) hide show
  1. package/.github/workflows/github_server.yml +7 -7
  2. package/.github/workflows/playground.yml +1 -1
  3. package/CHANGELOG.md +24 -0
  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 +320 -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 -1
  12. package/docs/decisions/reports_and_logs_2024_08_11.md +161 -0
  13. package/docs/kinds_of_errors.md +26 -0
  14. package/lib/docassemble/docassemble_api_interface.js +77 -51
  15. package/lib/docassemble/server_install.js +32 -9
  16. package/lib/docassemble/setup.js +40 -25
  17. package/lib/docassemble/takedown.js +33 -10
  18. package/lib/parse.js +36 -0
  19. package/lib/run_cucumber.js +115 -36
  20. package/lib/scope.js +60 -75
  21. package/lib/steps.js +70 -42
  22. package/lib/utils/Log.js +982 -0
  23. package/lib/utils/files.js +18 -11
  24. package/lib/utils/get_sources_paths.js +162 -0
  25. package/lib/utils/parse.js +1 -0
  26. package/lib/utils/proccess_command_line_args.js +267 -0
  27. package/lib/utils/reports.js +64 -17
  28. package/lib/utils/session_vars.js +34 -21
  29. package/lib/utils/time.js +44 -0
  30. package/package.json +1 -1
  31. package/tests/log_codes/check_codes.sh +2 -1
  32. package/tests/log_codes/log_code_expected_instances.txt +33 -9
  33. package/tests/unit_tests/getAllFields.test.js +5 -0
  34. package/tests/unit_tests/getMatchingRows.test.js +5 -0
  35. package/tests/unit_tests/log.test.js +581 -0
  36. package/tests/unit_tests/normalizeTable.test.js +5 -0
  37. package/lib/utils/log.js +0 -96
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