@suffolklitlab/alkiln 5.8.1 → 5.9.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
@@ -106,6 +106,12 @@ BeforeAll(async () => {
106
106
  });
107
107
 
108
108
  Before(async (scenario) => {
109
+
110
+ // Useful for those with a lot of tests
111
+ // Is unable to print the first Scenario name for some reason
112
+ console.log('');
113
+ process.stdout.write(`Scenario: ${ scenario.pickle.name }: `);
114
+
109
115
  // Create browser, which can't be created in BeforeAll() where .driver doesn't exist for some reason
110
116
  if (!scope.browser) {
111
117
  if (session_vars.get_origin() === 'playground') {
@@ -297,7 +303,9 @@ Given(/the max(?:imum)? sec(?:ond)?s for each step (?:in this scenario )?is (\d+
297
303
  // Story
298
304
  //#####################################
299
305
  //#####################################
300
-
306
+ // TODO: Consider more permissive sentences:
307
+ // with(?: this data| these answers)?:?
308
+ // with(?:.*):?
301
309
  When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)" with this data:?/, {timeout: -1}, async (target_id, raw_var_data) => {
302
310
  /* NO CUCUMBER TIMEOUT. Must handle timeout ourselves:
303
311
  * https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/timeouts.md#disable-timeouts
@@ -341,6 +349,7 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
341
349
  try {
342
350
  let { question_has_id, id, id_matches } = await scope.examinePageID( scope );
343
351
 
352
+ // THIS FUNCTION ACTUALLY MATTERS
344
353
  // Add special rows. Basically, do weirder things.
345
354
  // TODO: Remove from_story_table - no longer needed if only called from in here
346
355
  let ensured_var_data = await scope.ensureSpecialRows( scope, {
@@ -352,8 +361,10 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
352
361
  console.log( `Rows, including special rows:`, JSON.stringify( ensured_var_data ));
353
362
  }
354
363
 
364
+ // THIS FUNCTION ACTUALLY MATTERS
355
365
  // Some errors happen in there to be closer to the relevant code. We'd have to try/catch anyway.
356
366
  let error = await scope.setFields(scope, { var_data: ensured_var_data, id, ensure_navigation: true });
367
+
357
368
  if ( error.was_found ) { await scope.throwPageError( scope, { id: id }); }
358
369
  resolve();
359
370
  } catch ( err ) {
@@ -415,6 +426,123 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
415
426
  reports.addToReport(scope, { type: `note`, value: `` });
416
427
  });
417
428
 
429
+ // When(/^foo id "([^"]+)"/, {timeout: -1}, async (target_id, raw_var_data) => {
430
+ // /* NO CUCUMBER TIMEOUT. Must handle timeout ourselves:
431
+ // * https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/timeouts.md#disable-timeouts
432
+ // * Iterate in here to keep timeout reasonable */
433
+
434
+ // // Note: Why do we not just stop when we've used all the vars in the table or
435
+ // // reach the last var? Vars are not required to be given in sequence and pages that
436
+ // // have non-button fields don't usually have a var assigned to the 'continue' button.
437
+ // // Alternative: require vars even for 'continue' pages. Then in loop require at least one var
438
+ // // to be found on a page before hitting 'continue'. That requires detecting navigation because
439
+ // // pages with fields will not have a var for the continue button, so there will
440
+ // // still have to be some automatic continuing.
441
+ // // It also seems harsh, but more reliable.
442
+ // // Ask developers what they'd like.
443
+
444
+
445
+ // let supported_table = await scope.normalizeTable( scope, { raw_var_data });
446
+
447
+ // let { question_has_id, id, id_matches } = await scope.examinePageID( scope, target_id );
448
+ // // Until we reach the target page, hit an error, or time out (take too long)
449
+ // while ( !id_matches ) {
450
+ // let custom_timeout = new Promise(function( resolve, reject ) {
451
+
452
+ // // Set up the timeout that avoids infinite loops
453
+ // let on_page_timeout = async function() {
454
+
455
+ // let non_reload_report_msg = `The target id was "${ target_id }", but it took `
456
+ // + `too long to try to reach it (over ${ scope.timeout/1000/60 } min). The `
457
+ // + `test got stuck on "${ id }".`
458
+ // let error = non_reload_report_msg;
459
+ // await scope.handle_page_timeout_error( scope, { non_reload_report_msg, error });
460
+ // }; // Ends on_page_timeout()
461
+ // let page_timeout_id = setTimeout(on_page_timeout, scope.timeout);
462
+
463
+ // // self-invoke an anonymous async function so we don't have to
464
+ // // abstract the details of resolving and rejecting.
465
+ // // If we need to call recursively, signature function might look like:
466
+ // // func( scope, { resolve, reject, supported_table, page_timeout_id })
467
+ // // Maybe list of timeout and/or interval ids in state that can all be cleared at once?
468
+ // (async function() {
469
+ // try {
470
+ // let { question_has_id, id, id_matches } = await scope.examinePageID( scope );
471
+
472
+ // // Add special rows. Basically, do weirder things.
473
+ // // TODO: Remove from_story_table - no longer needed if only called from in here
474
+ // let ensured_var_data = await scope.ensureSpecialRows( scope, {
475
+ // var_data: supported_table,
476
+ // from_story_table: true,
477
+ // });
478
+
479
+ // if ( session_vars.get_debug() ) {
480
+ // console.log( `Rows, including special rows:`, JSON.stringify( ensured_var_data ));
481
+ // }
482
+
483
+ // // Some errors happen in there to be closer to the relevant code. We'd have to try/catch anyway.
484
+ // let error = await scope.setFields(scope, { var_data: ensured_var_data, id, ensure_navigation: true });
485
+ // if ( error.was_found ) { await scope.throwPageError( scope, { id: id }); }
486
+ // resolve();
487
+ // } catch ( err ) {
488
+ // let err_msg = `Error occurred when tried to answer fields on question "${ id }".`
489
+ // reports.addToReport( scope, { type: `error`, value: err_msg });
490
+ // // Throw error instead?
491
+ // reject( err );
492
+ // } finally {
493
+ // clearTimeout( page_timeout_id ); // prevent temporary hang at end of tests
494
+ // }
495
+ // })();
496
+ // }); // ends custom_timeout for setFields
497
+
498
+ // await custom_timeout;
499
+
500
+ // // Prep for next loop
501
+ // ({ question_has_id, id, id_matches } = await scope.examinePageID( scope, target_id ));
502
+
503
+ // } // ends while !id_matches
504
+ // // Can anything unsatisfactory get through here?
505
+
506
+ // // Add report strings from supported table, not an altered one
507
+ // for ( let row of supported_table ) {
508
+ // row.report_str = `${ reports.convertToOriginalStoryTableRow(scope, { row_data: row }) }`;
509
+ // }
510
+ // // Sort into alphabetical order, as is recommended for writing story tables
511
+ // let sorted_rows = supported_table.sort( function ( row1, row2 ) {
512
+ // if( row1.report_str < row2.report_str ) { return -1; }
513
+ // if( row1.report_str > row2.report_str ) { return 1; }
514
+ // return 0;
515
+ // });
516
+
517
+ // // Add full used table to report so it can be copied if needed.
518
+ // // TODO: Should we/How do we get this in the final scenario report instead of here?
519
+ // reports.addToReport( scope, { type: `note`, value: `\n${ ' '.repeat(2) }Rows that got set:` });
520
+ // reports.addToReport( scope, { type: `step`, value: `${ ' '.repeat(4) }And I get to the question id "${ target_id }" with this data:` });
521
+ // reports.addToReport(scope, { type: `row`, value: `${ ' '.repeat(6) }| var | value | trigger |`, });
522
+ // let at_least_one_row_was_NOT_used = false;
523
+ // for ( let row of sorted_rows ) {
524
+ // if ( row.times_used > 0 ) {
525
+ // reports.addToReport(scope, { type: `row`, value: row.report_str, });
526
+ // } else {
527
+ // at_least_one_row_was_NOT_used = true;
528
+ // }
529
+ // }
530
+
531
+ // // Add unused rows if needed
532
+ // if ( at_least_one_row_was_NOT_used ) {
533
+ // reports.addToReport( scope, { type: `note`, value: `${ ' '.repeat(2) }Unused rows:` });
534
+ // for ( let row of sorted_rows ) {
535
+ // if ( row.times_used === 0 ) {
536
+ // reports.addToReport(scope, { type: `row`, value: row.report_str, });
537
+ // }
538
+ // }
539
+ // } else {
540
+ // reports.addToReport(scope, { type: `note`, value: `${ ' '.repeat(2) }All rows were used` });
541
+ // }
542
+
543
+ // reports.addToReport(scope, { type: `note`, value: `` });
544
+ // });
545
+
418
546
  //#####################################
419
547
  //#####################################
420
548
  // Passive/Observational
@@ -1122,8 +1250,8 @@ Given(/the Scenario report should not include/i, async ( report_text ) => {
1122
1250
 
1123
1251
  });
1124
1252
 
1125
- Given(/the final Scenario status should be "(passed|failed)"/i, async ( expected_status ) => {
1126
- /* WARNING: If a Step will fail, this Step must come first.
1253
+ Given(/the final Scenario status should be "([^"]+)"/i, async ( expected_status ) => {
1254
+ /** WARNING: If a Step will fail, this Step must come first.
1127
1255
  *
1128
1256
  * Prepare to test that the Scenario completes with the correct status. */
1129
1257
  scope.expected_status = expected_status.toUpperCase();
@@ -1138,6 +1266,12 @@ Given(/the final Scenario status should be "(passed|failed)"/i, async ( expected
1138
1266
 
1139
1267
  After(async function(scenario) {
1140
1268
 
1269
+ // Log errors
1270
+ if ( scenario.result.message ) {
1271
+ log.debug({ type: `error`, pre: scenario.result.message });
1272
+ // log.debug({ type: `error`, pre: scenario.result.message, verbose: true });
1273
+ }
1274
+
1141
1275
  let changeable_test_status = scenario.result.status;
1142
1276
  // The accessibility failures don't happen til the end, so the rest of the test can continue
1143
1277
  if ( scope.check_all_for_a11y && !scope.passed_all_for_a11y) {
@@ -1199,39 +1333,46 @@ After(async function(scenario) {
1199
1333
 
1200
1334
  } // ends if not passed
1201
1335
 
1202
- // Internal tests of this framework
1203
- if ( scope.expected_status ) {
1204
- let expected = scope.expected_status;
1205
- scope.expected_status = null; // reset for next Scenario
1206
- expect( changeable_test_status ).to.equal( expected );
1207
- // If the status was as expected, pass this criteria
1208
- changeable_test_status = `PASSED`;
1209
- }
1210
-
1336
+ // Add report text to Scenario folder after everything has been added to the report
1337
+ let current_report_obj = scope.report.get( scope.scenario_id );
1338
+ let scenario_report_obj = [ scope.scenario_id, current_report_obj ]; // Mimic how loop works with `Map`
1339
+ let report = reports.getPrintableScenario( scenario_report_obj );
1340
+ // Save the report as in the Scenario folder
1341
+ fs.writeFileSync( scope.paths.scenario_report, report );
1342
+
1343
+ // ---------------- Check internal test results of this framework ----------------
1344
+
1211
1345
  if ( scope.expected_in_report && scope.expected_in_report.length > 0) {
1212
- let expected = scope.expected_in_report;
1346
+ let report_includes_all_expected_strings = await scope.reportIncludesAllExpected(
1347
+ scope,
1348
+ { expected: scope.expected_in_report }
1349
+ );
1350
+ if ( !report_includes_all_expected_strings ) { changeable_test_status = `FAILED`; }
1213
1351
  // Reset report values no matter what so they don't mess up future scenarios
1214
1352
  scope.expected_in_report = null;
1215
- let all_were_included = await scope.reportIncludesAllExpected( scope, { expected });
1216
-
1217
- if ( all_were_included ) { changeable_test_status = `PASSED`; }
1218
- else { changeable_test_status = `FAILED`; }
1219
1353
  }
1220
1354
 
1221
1355
  if (scope.expected_not_in_report && scope.expected_not_in_report.length > 0) {
1222
- let not_expected = scope.expected_not_in_report;
1356
+ let all_prohibited_strings_were_absent = await scope.reportDoesNotInclude(
1357
+ scope,
1358
+ { not_expected: scope.expected_not_in_report }
1359
+ );
1360
+ if ( !all_prohibited_strings_were_absent ) { changeable_test_status = `FAILED`;}
1223
1361
  // Reset report values no matter what so they don't mess up future scenarios
1224
1362
  scope.expected_not_in_report = null;
1225
- let all_were_not_there = await scope.reportDoesNotInclude( scope, { not_expected });
1226
-
1227
- if ( all_were_not_there ) {
1228
- changeable_test_status = `PASSED`;
1229
- } else {
1230
- changeable_test_status = `FAILED`;
1231
- }
1363
+ }
1232
1364
 
1365
+ // The default is 'passed', which won't need to get in here
1366
+ if ( scope.expected_status ) {
1367
+ // When internal tests should fail (to test our failure conditions), passing _is_ failing
1368
+ expect( changeable_test_status ).to.equal( scope.expected_status );
1369
+ // If the status was as expected, pass this Scenario
1370
+ changeable_test_status = `PASSED`;
1371
+ scope.expected_status = null; // reset for next Scenario
1233
1372
  }
1234
1373
 
1374
+ // ---------------- Ends check internal test results of this framework ----------------
1375
+
1235
1376
  // If there is a page open, then sign out and close it
1236
1377
  if ( scope.page ) {
1237
1378
  // Catch a possible reload error.
@@ -1259,23 +1400,19 @@ After(async function(scenario) {
1259
1400
 
1260
1401
  } // ends if scope.page
1261
1402
 
1262
- // Add report text to Scenario folder after everything has been added to the report
1263
- let current_report_obj = scope.report.get( scope.scenario_id );
1264
- let scenario_report_obj = [ scope.scenario_id, current_report_obj ]; // Mimic how loop works with `Map`
1265
- let report = reports.getPrintableScenario( scenario_report_obj );
1266
- // Save the report as in the Scenario folder
1267
- fs.writeFileSync( scope.paths.scenario_report, report );
1268
-
1269
1403
  if ( changeable_test_status === `FAILED` ) {
1270
1404
  // If the result object status wasn't failure and we still want
1271
1405
  // the tests to fail, as with a11y tests, we can't mess with the
1272
1406
  // cucumber result.status object because it will cause unwanted
1273
1407
  // errors. We have to throw an error here instead.
1274
1408
  if ( scenario.result.status !== `FAILED` ) {
1275
- throw new Error(`The test failed. See the report for more information.`);
1409
+ throw new Error(`The test failed. See the report above for more information.`);
1276
1410
  }
1277
1411
  } else {
1278
- // Other types of errors should be ok
1412
+ // Some internal test failures should be ok
1413
+ // TODO: Look into setDefinitionFunctionWrapper to catch errors
1414
+ // of failing tests that should indeed be failing:
1415
+ // https://github.com/cucumber/cucumber-js/blob/main/docs/support_files/api_reference.md#setdefinitionfunctionwrapperwrapper
1279
1416
  scenario.result.status = changeable_test_status;
1280
1417
  }
1281
1418
  });
package/lib/utils/log.js CHANGED
@@ -40,14 +40,25 @@ log.error = function ({ type='', pre='', data='', post='' }) {
40
40
  if ( post ) { console.error( post, `\n` ); }
41
41
  };
42
42
 
43
- log.debug = function ({ type='', pre='', data='', post='' }) {
43
+ log.debug = function ({ type='', pre='', data='', post='', verbose=false }) {
44
44
  /* If debugging is turned on, log a message that includes `ALKiln` and `debug` */
45
- if ( session_vars.get_debug() ) {
45
+ if ( session_vars.get_debug() || verbose ) {
46
46
  let start = create_msg_start([ type, 'debug' ], pre );
47
+ let full = `\n${ start }`;
47
48
  // Log them each on a separate line
48
- console.log( `ALKiln ${ type } debug: ${ pre }` );
49
- if ( data ) { console.log( data ); }
50
- if ( post ) { console.log( post ); }
49
+ console.log( `\n${ start }` );
50
+ if ( data ) {
51
+ full += `\n${ data }`;
52
+ console.log( data );
53
+ }
54
+ if ( post ) {
55
+ full += `\n${ post }`;
56
+ console.log( post );
57
+ }
58
+ // TODO: Make a verbose log instead of adding to the debug one and always add to it
59
+ if ( verbose ) {
60
+ log.add_to_debug_log( full );
61
+ }
51
62
  }
52
63
  };
53
64
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suffolklitlab/alkiln",
3
- "version": "5.8.1",
3
+ "version": "5.9.0",
4
4
  "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {