@suffolklitlab/alkiln 5.17.1 → 5.17.2

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
@@ -117,6 +117,7 @@ BeforeAll(async function() {
117
117
  Before(async (scenario) => {
118
118
 
119
119
  // Start the running "progress bar" for the Scenario
120
+ log.debug({ code: `ALK0289`, context: `before`, }, `%%%%%%%%%%%%%%%%%%`);
120
121
  log.stdout({}, `\nScenario: ${ scenario.pickle.name }: `);
121
122
 
122
123
  // Create browser, which can't be created in BeforeAll() where .driver doesn't exist for some reason
@@ -400,7 +401,7 @@ When(/I? ?gets? to ?.*?(".*").*?with.*?(?:this data:)?/i, {timeout: -1}, async (
400
401
  // If we need to call recursively, signature function might look like:
401
402
  // func( scope, { resolve, reject, supported_table, page_timeout_id })
402
403
  // Maybe list of timeout and/or interval ids in state that can all be cleared at once?
403
- (async function() {
404
+ let error_data = (async function() {
404
405
  try {
405
406
  let id = await scope.get_page_id( scope );
406
407
 
@@ -416,28 +417,34 @@ When(/I? ?gets? to ?.*?(".*").*?with.*?(?:this data:)?/i, {timeout: -1}, async (
416
417
  `Rows, including special rows:`, ensured_var_data
417
418
  );
418
419
 
419
- // THIS FUNCTION ACTUALLY MATTERS
420
- // Some errors happen in there to be closer to the relevant code. We'd have to try/catch anyway.
421
- let error_data = await scope.setFields(scope, { var_data: ensured_var_data, id, ensure_navigation: true });
420
+ let error_data = await scope.setFields(scope, {
421
+ var_data: ensured_var_data, id, ensure_navigation: true
422
+ });
423
+ clearTimeout( page_timeout_id ); // prevent temporary hang at end of tests
422
424
 
423
425
  // Story Table assumes we require navigation while other Steps don't.
424
426
  // Throw here in case something sneaks through.
425
- if ( error_data?.was_found ) { await scope.throw_system_error( scope, {
426
- error_handlers: error_data.handlers, id: id
427
- }); }
428
- resolve();
427
+ await scope.throw_on_system_error( scope, { id, error_data });
428
+ await scope.throw_on_invalid_answers( scope, { id, error_data });
429
+ await scope.throw_on_non_navigation_timeout( scope, { id, timed_out: false });
430
+ await scope.throw_on_non_navigation_catchall( scope, { id });
431
+ resolve( error_data );
432
+
429
433
  } catch ( err ) {
430
- let err_msg = `ALKiln ran into an error when it tried to answer fields on the page with the question id "${ id }". The text above might have more information.`
431
- reports.addToReport( scope, { type: `error`, code: `ALK0062`, value: err_msg });
432
- // Throw error instead?
433
- reject( err );
434
+ // Catching here allows us to clear the timeout if needed
435
+ clearTimeout( page_timeout_id ); // prevent temporary hang at end of tests
436
+ log.debug({
437
+ code: `ALK0062`, level: `error`, error: err, context: `story_table`,
438
+ }, `ALKiln ran into an error when it tried to answer fields on the page with the id "${ id }"` );
439
+ throw err;
440
+
434
441
  } finally {
435
442
  clearTimeout( page_timeout_id ); // prevent temporary hang at end of tests
436
443
  }
437
444
  })();
438
- }); // ends custom_timeout for setFields
445
+ }); // ends custom_timeout for filling in fields
439
446
 
440
- await custom_timeout;
447
+ let page_result = await custom_timeout;
441
448
 
442
449
  // Prep for next loop
443
450
  id = await scope.get_page_id( scope );
@@ -529,7 +536,7 @@ Then(/the (?:question|page|screen) id should be "([^"]+)"/i, { timeout: -1 }, as
529
536
  let { question_has_id, id, id_matches } = await scope.examinePageID( scope, question_id );
530
537
 
531
538
  // No question id exists
532
- let no_id_msg = `Did not find any question id.`;
539
+ let no_id_msg = `Did not find any question id`;
533
540
  if ( !question_has_id ) {
534
541
  reports.addToReport( scope, { type: `error`, code: `ALK0070`, value: no_id_msg });
535
542
  }
@@ -625,44 +632,20 @@ Then('an element should have the id {string}', async ( id ) => { // √
625
632
  // await scope.afterStep(scope);
626
633
  // });
627
634
 
628
- // This phrase is confusing. It sounds like it's going to at least try to continue.
629
- Then(/I (don't|can't|don’t|can’t|cannot|do not) continue/, async (unused) => { // √
630
- /** Press to continue. Succeeds when navigation then fails. */
631
-
632
- // Must catch "failure to navigate"-type error.
633
- // TODO: Explore using .continue + .checkForError (see "invalid answer" Step)
634
- try {
635
- let error_data = await scope.setFields( scope, { var_data: [], ensure_navigation: true });
636
- // TODO: Add test for excluding AL..075 msg when Step succeeds
637
- if ( scope.navigated ) {
638
- let report_msg = reports.addToReport(scope, { type: `error`, code: `ALK0075`,
639
- value: `The page should have stopped the user from continuing, but the user was able to continue.`
640
- });
641
- expect( scope.navigated, report_msg ).to.be.false;
642
- }
635
+ Then(/I will be told (?:an answer is)?(?:some )?(?:all )?(?:answers are)? invalid/i, async () => { // √
643
636
 
644
- } catch ( continue_attempt_error ) {
645
- // Discuss: Too deeply coupled? Alternatives?
646
- if ( !continue_attempt_error.message?.includes(`ALK0148`) ) {
647
- throw continue_attempt_error;
648
- }
649
- }
650
-
651
- await scope.afterStep(scope);
652
- });
653
-
654
- Then('I will be told an answer is invalid', async () => { // √
655
-
656
- let { ok, type, error_handlers } = await scope.checkForError( scope );
637
+ let { ok, type } = await scope.checkForErrors( scope );
657
638
 
658
639
  let report_msg = null;
659
- if ( ok ) { report_msg = reports.addToReport(scope, { type: `error`, code: `ALK0076`, value: `Sorry, you expected at least one invalid answer, but all answers were valid` }); }
660
- expect( ok, report_msg ).to.be.false;
661
-
640
+ if ( ok ) {
641
+ report_msg = reports.addToReport(scope, { type: `error`, code: `ALK0076`, value: `Sorry, you expected one or more invalid answers, but ALKiln only sees valid answers.` });
642
+ throw Error( report_msg );
643
+ }
662
644
  if ( type !== `user` ) {
663
645
  report_msg = reports.addToReport(scope, { type: `error`, code: `ALK0077`,
664
646
  value: `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.`
665
647
  });
648
+ throw Error( report_msg );
666
649
  }
667
650
  expect( type, report_msg ).to.equal( `user` );
668
651
 
@@ -826,13 +809,105 @@ Then(/the "([^"]+)" link opens a working page/, { timeout: -1 }, async ( linkTex
826
809
  );
827
810
  });
828
811
 
829
- When(/I tap to continue/i, { timeout: -1 }, async () => {
830
- // Any selectors for this seem somewhat precarious
831
- // No data will cause an automatic continue when `ensure_navigation` is true
812
+ Then(/I continue(?: to the next )?(?:page|screen)?/i, i_continue );
813
+ async function i_continue() {
814
+ /**
815
+ * Press to continue. If navigation fails, tests fail. This Step implicitly
816
+ * assumes the user is able to continue. Some reasons navigation can fail:
817
+ *
818
+ * - Invalid answers (client-side or `validation code`)
819
+ * - Missing continue button
820
+ * - System error
821
+ * - Timeout
822
+ * - Infinite loop page (unsatisfied `show if:`) (in future implementation)
823
+ * - Others?
824
+ *
825
+ * @example
826
+ * And I continue
827
+ * @example
828
+ * And I continue to the next page
829
+ * @example
830
+ * And I continue to the next screen
831
+ *
832
+ * */
833
+ let error_data = await scope.continue( scope );
834
+ await scope.afterStep(scope);
835
+ }; // Ends i_continue()
832
836
 
833
- // `page` timeout will deal with custom timeout
834
- // check if the server is reloading
835
- await scope.setFields( scope, { var_data: [], ensure_navigation: true });
837
+ Then(/I (don't|don’t|do not|can’t|can't|cannot|(?:am unable to)) continue/, async (_unused) => {
838
+ /**
839
+ * Press to continue. If navigation fails in the following ways, succeed:
840
+ *
841
+ * - Invalid answers (client-side or `validation code`)
842
+ * - Missing continue button
843
+ * - Can we think of any others?
844
+ *
845
+ * The following failures to navigate should still fail:
846
+ *
847
+ * - System error
848
+ * - Timeout
849
+ * - Infinite loop page (unsatisfied `show if:`) (in future implementation)
850
+ * - Others?
851
+ *
852
+ * @example
853
+ * And I can't continue
854
+ * @example
855
+ * And I can’t continue
856
+ * @example
857
+ * And I am unable to continue
858
+ * */ `I do have to try/catch. Right? No? Why would scope.navigated be not False?`
859
+ let error_data = await scope.continue(
860
+ scope, { post_nav_handlers: [
861
+ scope.throw_on_system_error,
862
+ scope.throw_on_non_navigation_timeout,
863
+ ]}
864
+ );
865
+ if ( scope.navigated ) {
866
+ let report_msg = reports.addToReport(scope, { type: `error`, code: `ALK0075`,
867
+ value: `The page should have stopped the user from continuing, but the user was able to continue.`
868
+ });
869
+ throw Error( report_msg );
870
+ }
871
+
872
+ await scope.afterStep(scope);
873
+ });
874
+
875
+ When(/I tap to continue/i, async () => {
876
+ /**
877
+ * This functionality supports legacy behavior. Deprecate this behavior and
878
+ * break it in v6. In v6 this Step will stop suppressing errors completely.
879
+ * The current "I can't continue" Step will fully take over this behavior.
880
+ * The current behavior:
881
+ *
882
+ * Press to continue. If navigation fails in the following ways, succeed:
883
+ *
884
+ * - Invalid answers (client-side or `validation code`)
885
+ * - Can we think of any others?
886
+ *
887
+ * The following failures to navigate should still fail:
888
+ *
889
+ * - Missing continue button
890
+ * - System error
891
+ * - Timeout
892
+ * - Infinite loop page (unsatisfied `show if:`) (in future implementation)
893
+ * - Others?
894
+ * */
895
+
896
+ await scope.continue(
897
+ scope, { post_nav_handlers: [
898
+ scope.throw_on_system_error, // Throw system errors if any first
899
+ // Author may not expect success with invalid answers. Warn them.
900
+ scope.warn_on_invalid_answers,
901
+ scope.throw_on_non_navigation_timeout,
902
+ scope.warn_on_non_navigation_catchall,
903
+ ]}
904
+ );
905
+
906
+ reports.addToReport( scope, { type: `warning`, code: `ALK0294`,
907
+ value: `You are using the old "I tap to continue" Step. We recommend you switch to a newer Step. Use "I continue" if you need to continue to the next page. Use "I can't continue" if you want to check for invalid answer messages or if there is no continue button.`
908
+ });
909
+
910
+ await scope.afterStep(scope);
836
911
  });
837
912
 
838
913
  When(/I tap the "([^"]+)" tab/i, { timeout: -1 }, async ( tabId ) => {
@@ -855,7 +930,7 @@ When(/I tap the "([^"]+)" element and wait (?:for )?([0-9]+) second(?:s)?/i, { t
855
930
  // Also, this is always supposed to navigate, and that handles its own waiting.
856
931
 
857
932
  // `page` timeout will deal with custom timeout
858
- await scope.tapSelectorToNavigate( scope, elemSelector, waitTime * 1000 )
933
+ await scope.tapSelectorToNavigate( scope, elemSelector, waitTime * 1000 );
859
934
  });
860
935
 
861
936
  When(/I check the page for (?:accessibility|a11y) issues/i, {timeout: -1}, async () => {
@@ -1464,18 +1539,20 @@ After(async function(scenario) {
1464
1539
  // test), it failed. If is was supposed to pass and failed, it also
1465
1540
  // failed. E.g. a11y failures let the Scenario pass initially, but
1466
1541
  // should cause a failure here.
1467
- changeable_test_status = `FAILED`;
1468
- // Flag this failure to trigger an error later, so cucumber shows
1469
- // it as failed too.
1470
- expected_status_was_met = false;
1471
- // Keep it general. Users may see this message, e.g. for a11y tests.
1542
+
1543
+ // Keep this msg general. Users may see this message, e.g. for a11y tests.
1472
1544
  let msg = `Test ${ changeable_test_status }. `
1473
- + `Expected status: ${scope.expected_status}. `
1545
+ + `Expected status: ${ scope.expected_status }. `
1474
1546
  + `See the report for details.`;
1475
1547
  let code = `ALK0099`;
1476
1548
  expected_status_err_msg = log.debug({ code: code, level: `error`, },
1477
1549
  msg
1478
1550
  );
1551
+
1552
+ // Flag this failure to trigger an error later, so cucumber shows
1553
+ // it as failed too.
1554
+ changeable_test_status = `FAILED`;
1555
+ expected_status_was_met = false;
1479
1556
  }
1480
1557
  }
1481
1558
  scope.expected_status = null; // reset for next Scenario
@@ -1609,7 +1686,7 @@ After(async function(scenario) {
1609
1686
 
1610
1687
  // Find the race condition.
1611
1688
  log.debug({ code: `ALK0100`, context: `scenario`, },
1612
- `Scenario After() message:`, scenario.result.message
1689
+ `%%%%%%%%%%%%%%%%%%`, `Scenario After() message:`, scenario.result.message
1613
1690
  );
1614
1691
 
1615
1692
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suffolklitlab/alkiln",
3
- "version": "5.17.1",
3
+ "version": "5.17.2",
4
4
  "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -160,7 +160,7 @@ ALK0144:1
160
160
  ALK0145:1
161
161
  ALK0146:1
162
162
  ALK0147:1
163
- ALK0148:2
163
+ ALK0148:1
164
164
  ALK0149:1
165
165
  ALK0150:1
166
166
  ALK0151:1
@@ -301,3 +301,12 @@ ALK0285:1
301
301
  ALK0286:1
302
302
  ALK0287:1
303
303
  ALK0288:1
304
+ ALK0289:1
305
+ ALK0290:1
306
+ ALK0291:1
307
+ ALK0292:1
308
+ ALK0293:1
309
+ ALK0294:1
310
+ ALK0295:1
311
+ ALK0296:1
312
+ ALK0297:1
@@ -0,0 +1,72 @@
1
+ const chai = require('chai');
2
+ const expect = chai.expect;
3
+ const fs = require(`fs`);
4
+
5
+ const scope = require(`../../lib/scope.js`);
6
+
7
+ scope.log_and_update_if_new_page_id = async function() {
8
+ /** Mock this `scope` function to avoid extraneous errors. */
9
+ return `mock-page-id`;
10
+ };
11
+
12
+ describe(`The deepest navigation logic`, function () {
13
+
14
+ it(`will handle a missing required system error handler by throwing ALK0295`, async function() {
15
+ let error = null
16
+ try {
17
+ await scope.wait_until_continued( scope, { post_nav_handlers: [
18
+ scope.throw_on_invalid_answers,
19
+ scope.throw_on_non_navigation_timeout,
20
+ ] });
21
+ } catch (err) {
22
+ error = err;
23
+ }
24
+ expect( error instanceof ReferenceError, `Not a ReferenceError` ).to.be.true;
25
+ expect( error.message.includes(`ALK0295`), `Missing log code ALK0295` ).to.be.true;
26
+ expect( error.message.includes(`ALK0296`), `Unwanted log code ALK0296 is present`).to.be.false;
27
+ });
28
+
29
+ it(`will handle a missing required navigation timeout handler by throwing ALK0296`, async function() {
30
+ let error = null
31
+ try {
32
+ await scope.wait_until_continued( scope, { post_nav_handlers: [
33
+ scope.throw_on_system_error,
34
+ scope.throw_on_invalid_answers,
35
+ ] });
36
+ } catch (err) {
37
+ error = err;
38
+ }
39
+ expect( error instanceof ReferenceError, `Not a ReferenceError` ).to.be.true;
40
+ expect( error.message.includes(`ALK0295`), `Unwanted log code ALK0295 is present`).to.be.false;
41
+ expect( error.message.includes(`ALK0296`), `Missing log code ALK0296` ).to.be.true;
42
+ });
43
+
44
+ it(`will handle the absence of both critical error throwers by throwing ALK0295 and ALK0296`, async function() {
45
+ let error = null
46
+ try {
47
+ await scope.wait_until_continued( scope, { post_nav_handlers: [
48
+ scope.throw_on_invalid_answers,
49
+ ] });
50
+ } catch (err) {
51
+ error = err;
52
+ }
53
+ expect( error instanceof ReferenceError, `Not a ReferenceError` ).to.be.true;
54
+ expect( error.message.includes(`ALK0295`), `Missing log code ALK0295` ).to.be.true;
55
+ expect( error.message.includes(`ALK0296`), `Missing log code ALK0296` ).to.be.true;
56
+ });
57
+
58
+ it(`will not throw ALK0295 or ALK0296 if all required handlers are present`, async function() {
59
+ let error = null
60
+ try {
61
+ await scope.wait_until_continued( scope, { post_nav_handlers: [
62
+ scope.throw_on_system_error,
63
+ scope.throw_on_non_navigation_timeout,
64
+ ] });
65
+ } catch (err) {
66
+ error = err;
67
+ }
68
+ expect( error.message.includes(`ALK0295`), `Unwanted log code ALK0295 is present` ).to.be.false;
69
+ expect( error.message.includes(`ALK0296`), `Unwanted log code ALK0296 is present` ).to.be.false;
70
+ });
71
+
72
+ });