@suffolklitlab/alkiln 5.17.0 → 5.17.1-detect-upload-1

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
@@ -383,7 +384,7 @@ When(/I? ?gets? to ?.*?(".*").*?with.*?(?:this data:)?/i, {timeout: -1}, async (
383
384
 
384
385
  // Set up the timeout that avoids infinite loops
385
386
  let on_page_timeout = async function() {
386
-
387
+ // Will throw
387
388
  let non_reload_report_msg = `The target id or ids were ${ target_ids }, but it took `
388
389
  + `too long to try to reach a valid id (over ${ scope.timeout/1000/60 } min). The `
389
390
  + `test got stuck on "${ id }".`
@@ -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,25 +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 = 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
424
+
425
+ // Story Table assumes we require navigation while other Steps don't.
426
+ // Throw here in case something sneaks through.
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 );
422
432
 
423
- // Discuss: use Log class to throw this error?
424
- if ( error.was_found ) { await scope.throwPageError( scope, { id: id }); }
425
- resolve();
426
433
  } catch ( err ) {
427
- 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.`
428
- reports.addToReport( scope, { type: `error`, code: `ALK0062`, value: err_msg });
429
- // Throw error instead?
430
- 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
+
431
441
  } finally {
432
442
  clearTimeout( page_timeout_id ); // prevent temporary hang at end of tests
433
443
  }
434
444
  })();
435
- }); // ends custom_timeout for setFields
445
+ }); // ends custom_timeout for filling in fields
436
446
 
437
- await custom_timeout;
447
+ let page_result = await custom_timeout;
438
448
 
439
449
  // Prep for next loop
440
450
  id = await scope.get_page_id( scope );
@@ -498,12 +508,9 @@ When(/I wait ?(?:for)? (\d*\.?\d+) seconds?/, { timeout: -1 }, async ( seconds )
498
508
  * - I wait 1 second
499
509
  * - I wait .5 seconds
500
510
  *
501
- * Questions:
502
- * - Should this really reset the 'navigated' flag? Should the below
503
- * return succeed or fail? e.g:
504
- * I tap to continue; I wait 10 seconds; I arrive at the next page;
505
- * Maybe the 'navigated' flag should only be reset just before trying to
506
- * navigate (after pressing a button)
511
+ * Discuss: Should this reset the 'navigated' flag? Maybe the 'navigated' flag
512
+ * should only be reset just before trying to navigate (after pressing a
513
+ * button.)
507
514
  */
508
515
  return wrapPromiseWithTimeout(
509
516
  scope.afterStep(scope, {waitForTimeout: (parseFloat( seconds ) * 1000)}),
@@ -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,41 +632,23 @@ 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
- /* Tests for detection of url change from button or link tap.
631
- * Can other things trigger navigation? Re: other inputs things
632
- * like 'Enter' acting like a click is a test for docassemble */
633
- let msg = `The page should have stopped the user from continuing, but the user was able to continue.`;
634
- if ( scope.navigated ) { reports.addToReport(scope, { type: `error`, code: `ALK0075`, value: msg }); }
635
- expect( scope.navigated, msg ).to.be.false;
636
-
637
- await scope.afterStep(scope);
638
- });
639
-
640
- Then('I will be told an answer is invalid', async () => { // √
641
-
642
- let { was_found, error_handlers } = await scope.checkForError( scope );
635
+ Then(/I will be told (?:an answer is)?(?:some )?(?:all )?(?:answers are)? invalid/i, async () => { // √
643
636
 
644
- let no_error_msg = `No error message was found on the page`;
645
- if ( !was_found ) { reports.addToReport(scope, { type: `error`, code: `ALK0076`, value: no_error_msg }); }
646
- expect( was_found, no_error_msg ).to.be.true;
637
+ let { ok, type } = await scope.checkForErrors( scope );
647
638
 
648
- let was_user_error = error_handlers.complex_user_error !== undefined || error_handlers.simple_user_error !== undefined;
649
- 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.`;
650
- if ( !was_user_error ) { reports.addToReport(scope, { type: `error`, code: `ALK0077`, value: not_user_error_msg }); }
651
- expect( was_user_error, not_user_error_msg ).to.be.true;
652
-
653
- await scope.afterStep(scope);
654
- });
639
+ let report_msg = null;
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
+ }
644
+ if ( type !== `user` ) {
645
+ report_msg = reports.addToReport(scope, { type: `error`, code: `ALK0077`,
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.`
647
+ });
648
+ throw Error( report_msg );
649
+ }
650
+ expect( type, report_msg ).to.equal( `user` );
655
651
 
656
- Then('I arrive at the next page', async () => { // √
657
- /* Tests for detection of url change from button or link tap.
658
- * Can other things trigger navigation? Re: other inputs things
659
- * like 'Enter' acting like a click is a test for docassemble */
660
- let msg = `User did not arrive at the next page.`
661
- if ( !scope.navigated ) { reports.addToReport(scope, { type: `error`, code: `ALK0078`, value: msg }); }
662
- expect( scope.navigated, msg ).to.be.true;
663
652
  await scope.afterStep(scope);
664
653
  });
665
654
 
@@ -820,13 +809,105 @@ Then(/the "([^"]+)" link opens a working page/, { timeout: -1 }, async ( linkTex
820
809
  );
821
810
  });
822
811
 
823
- When(/I tap to continue/i, { timeout: -1 }, async () => {
824
- // Any selectors for this seem somewhat precarious
825
- // 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()
826
836
 
827
- // `page` timeout will deal with custom timeout
828
- // check if the server is reloading
829
- 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);
830
911
  });
831
912
 
832
913
  When(/I tap the "([^"]+)" tab/i, { timeout: -1 }, async ( tabId ) => {
@@ -849,7 +930,7 @@ When(/I tap the "([^"]+)" element and wait (?:for )?([0-9]+) second(?:s)?/i, { t
849
930
  // Also, this is always supposed to navigate, and that handles its own waiting.
850
931
 
851
932
  // `page` timeout will deal with custom timeout
852
- await scope.tapSelectorToNavigate( scope, elemSelector, waitTime * 1000 )
933
+ await scope.tapSelectorToNavigate( scope, elemSelector, waitTime * 1000 );
853
934
  });
854
935
 
855
936
  When(/I check the page for (?:accessibility|a11y) issues/i, {timeout: -1}, async () => {
@@ -1458,18 +1539,20 @@ After(async function(scenario) {
1458
1539
  // test), it failed. If is was supposed to pass and failed, it also
1459
1540
  // failed. E.g. a11y failures let the Scenario pass initially, but
1460
1541
  // should cause a failure here.
1461
- changeable_test_status = `FAILED`;
1462
- // Flag this failure to trigger an error later, so cucumber shows
1463
- // it as failed too.
1464
- expected_status_was_met = false;
1465
- // 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.
1466
1544
  let msg = `Test ${ changeable_test_status }. `
1467
- + `Expected status: ${scope.expected_status}. `
1545
+ + `Expected status: ${ scope.expected_status }. `
1468
1546
  + `See the report for details.`;
1469
1547
  let code = `ALK0099`;
1470
1548
  expected_status_err_msg = log.debug({ code: code, level: `error`, },
1471
1549
  msg
1472
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;
1473
1556
  }
1474
1557
  }
1475
1558
  scope.expected_status = null; // reset for next Scenario
@@ -1603,7 +1686,7 @@ After(async function(scenario) {
1603
1686
 
1604
1687
  // Find the race condition.
1605
1688
  log.debug({ code: `ALK0100`, context: `scenario`, },
1606
- `Scenario After() message:`, scenario.result.message
1689
+ `%%%%%%%%%%%%%%%%%%`, `Scenario After() message:`, scenario.result.message
1607
1690
  );
1608
1691
 
1609
1692
  });
package/lib/utils/Log.js CHANGED
@@ -284,8 +284,9 @@ class Log {
284
284
  *
285
285
  * @param { Object } console_opts - Required. Metadata and functional flags
286
286
  * and data.
287
- * @param { "success"|"info"|"warn"|"error" } console_opts.level - Required.
288
- * The log level. Functional and metadata. Defined by the internal caller.
287
+ * @param { "success"|"info"|"warn"|"error"|"throw" } console_opts.level -
288
+ * Required. The log level. Functional and metadata. Defined by the
289
+ * internal caller.
289
290
  * @param { string } console_opts.before - Optional. Text to add before the
290
291
  * metadata of the log. Metadata. Default "".
291
292
  * @param { string } console_opts.icon - Required. Unicode char put at the
@@ -426,9 +427,9 @@ class Log {
426
427
  *
427
428
  * @param { Object } console_opts - Required. Metadata and functional flags
428
429
  * and data.
429
- * @param { "success"|"info"|"warn"|"error" } console_opts.level - Required.
430
- * The log level. Functional and metadata. Defined by the internal
431
- * caller.
430
+ * @param { "success"|"info"|"warn"|"error"|"throw" } console_opts.level -
431
+ * Required. The log level. Functional and metadata. Defined by the
432
+ * internal caller.
432
433
  * @param { string } console_opts.before - Optional. Text to add before the
433
434
  * metadata of the log. Metadata. Default "".
434
435
  * @param { string } console_opts.icon - Required. Unicode char put at the
@@ -459,6 +460,7 @@ class Log {
459
460
  *
460
461
  * @example
461
462
  * log.debug()
463
+ * // In debug_log.txt, not in the console:
462
464
  * // 🐛 ALK000d DEBUG [2024-08-16 00:38:49UTC]:
463
465
  * */
464
466
 
@@ -809,7 +811,7 @@ class Log {
809
811
  * @returns { string } Formatted metadata and logs.
810
812
  *
811
813
  * @example
812
- * log.warn({
814
+ * log.success({
813
815
  * before: "~~~",
814
816
  * code: "ALK00d3",
815
817
  * context: "demo",
@@ -843,7 +845,7 @@ class Log {
843
845
  * @returns { string } Formatted metadata and logs.
844
846
  *
845
847
  * @example
846
- * log.warn({
848
+ * log.info({
847
849
  * before: "~~~",
848
850
  * code: "ALK00d4",
849
851
  * context: "demo",
@@ -910,7 +912,7 @@ class Log {
910
912
  * @returns { string } Formatted metadata, logs, and, if given, the error.
911
913
  *
912
914
  * @example
913
- * log.warn({
915
+ * log.error({
914
916
  * before: "~~~",
915
917
  * code: "ALK00d5",
916
918
  * context: "demo",
@@ -90,6 +90,9 @@ session_vars.get_max_tests_per_template_scenario = function() {
90
90
  }
91
91
 
92
92
  session_vars.get_da_server_url = function () {
93
+ /**
94
+ * @returns { string } - the server url. Example: https://awesome_law.example
95
+ * */
93
96
  try {
94
97
  let url = process.env.SERVER_URL || process.env.BASE_URL;
95
98
  return url.replace(/\/$/g, ''); // Remove any trailing slash for safety
@@ -99,7 +102,10 @@ session_vars.get_da_server_url = function () {
99
102
  };
100
103
 
101
104
  session_vars.get_branch_name = function () {
102
- /* Get the name of the repository's branch that is being tested */
105
+ /**
106
+ * @returns {string} - the name of the branch that is being tested. Example:
107
+ * the_branch
108
+ * */
103
109
  let branch_name = null;
104
110
  if ( process.env.BRANCH_PATH ) {
105
111
  branch_name = process.env.BRANCH_PATH.replace( `refs/heads/`, '' );
@@ -110,13 +116,21 @@ session_vars.get_branch_name = function () {
110
116
  };
111
117
 
112
118
  session_vars.get_package_name = function () {
113
- /* Make an un-incremented project name based on the repo name and branch name. */
119
+ /**
120
+ * @returns {string} - an un-incremented package name based on the repo
121
+ * name. Example: ThePackage
122
+ *
123
+ * */
114
124
  let repo_url_without_trailing_slash = session_vars.get_repo_url().replace(/\/$/, '');
115
125
  let repo_name_parts = repo_url_without_trailing_slash.split('/');
116
126
  return repo_name_parts[ repo_name_parts.length - 1 ];
117
127
  };
118
128
 
119
129
  session_vars.get_project_base_name = function () {
130
+ /**
131
+ * @returns {string} - a valid docassemble Playground Project name, without
132
+ * any attempt to make it unique. Example: ALKilnTestThePackagethebranch
133
+ * */
120
134
  let name = `ALKilnTest${ session_vars.get_package_name() }${ session_vars.get_branch_name() }`;
121
135
  let sanitized_name = name.replace( /[^A-Za-z0-9]/g, '' );
122
136
  return sanitized_name;
@@ -139,12 +153,15 @@ session_vars.get_languages = function () {
139
153
 
140
154
  const project_name_key = `da_project_name`;
141
155
  session_vars.save_project_name = function ( project_name ) {
142
- /* Saves the name of the project to a json obj so deletion can use it later.
143
- * Built for local as well as remote testing. Assumes a kosher string.
144
- * We avoided a list because of concerns over getting out of date.
145
- * Should we try to prevent someone overwriting a previous project name?
146
- *
147
- * @param project_name { string } - purely alphanumeric string */
156
+ /**
157
+ * Saves the name of the project to a json obj so deletion can use it later.
158
+ * Built for local as well as remote testing. Assumes a kosher string.
159
+ * We avoided a list because of concerns over getting out of date.
160
+ * Should we try to prevent someone overwriting a previous project name?
161
+ *
162
+ * @param project_name { string } - purely alphanumeric string
163
+ * @returns {Object} - node fs file
164
+ * */
148
165
  const runtime_config_path = session_vars.get_runtime_config_path();
149
166
 
150
167
  // Warn local developers if file already exists
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suffolklitlab/alkiln",
3
- "version": "5.17.0",
3
+ "version": "5.17.1-detect-upload-1",
4
4
  "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -10,6 +10,7 @@ Reasons for deletion:
10
10
  0173 - redundant
11
11
  0181 - unknown
12
12
  0223 - changed Log behavior ("Only logs that throw should give a value to `error`.")
13
+ 0078 - removed unsupported Step "I arrive at the next page" and moved responsibility to Steps that navigate
13
14
 
14
15
  ALK0000:0
15
16
  ALK0001:1
@@ -89,7 +90,7 @@ ALK0074:1
89
90
  ALK0075:1
90
91
  ALK0076:1
91
92
  ALK0077:1
92
- ALK0078:1
93
+ ALK0078:0
93
94
  ALK0079:1
94
95
  ALK0080:1
95
96
  ALK0081:1
@@ -291,3 +292,21 @@ ALK0276:1
291
292
  ALK0277:1
292
293
  ALK0278:1
293
294
  ALK0279:1
295
+ ALK0280:1
296
+ ALK0281:1
297
+ ALK0282:1
298
+ ALK0283:1
299
+ ALK0284:1
300
+ ALK0285:1
301
+ ALK0286:1
302
+ ALK0287:1
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
@@ -573,7 +573,9 @@ Scenario: Keeps comment after table
573
573
  Given I start the interview at "file.yml"
574
574
  And ALKiln will get to "end" with:
575
575
  | var | possible_values |
576
- | user_choice | correct;; wrong |
576
+ | choice_1 | correct;; wrong |
577
+ | choice_2 | a;; b;; c |
578
+ | choice_3 | 1;; 2;; 3 |
577
579
  # Comment 1 keep and repeat
578
580
 
579
581
  # Comment 2 keep and repeat`;
@@ -3,6 +3,8 @@ let fields = {};
3
3
  /** The fixtures in here are used in at least 2 types of tests:
4
4
  * - getAllFields
5
5
  * - getMatchignRows
6
+ *
7
+ * ✏️ Fixtures are using HTML from docassemble versions <= 1.9.n
6
8
  * */
7
9
 
8
10
  // ============================
@@ -885,20 +887,22 @@ fields.proxy_substitution_i_is_1 = [
885
887
  // ============================
886
888
  // Signature
887
889
  // ============================
888
- // Signature page continue buttons are contained in a different DOM structure than regular ones
889
- // Also, there's an additional hidden continue button on the page with similar selectors
890
+ // Signature page continue buttons are contained in a different DOM structure
891
+ // than other pages. Also, there's an additional continue button on the page
892
+ // with similar selectors. One continue button is hidden on wide screens while
893
+ // the other is hidden on narrow screens.
890
894
  fields.signature = [
891
895
  {
892
- "selector": "#daquestion canvas",
896
+ "selector": "#daquestion canvas[id=\"dasigcanvas\"]",
893
897
  "tag": "canvas",
894
898
  "guesses": [
895
899
  {
896
- "var": "c2lnbmF0dXJlXzE",
897
- "value": ""
900
+ "value": "",
901
+ "var": "c2lnbmF0dXJlXzE"
898
902
  },
899
903
  {
900
- "var": "signature_1",
901
- "value": ""
904
+ "value": "",
905
+ "var": "signature_1"
902
906
  }
903
907
  ],
904
908
  "type": "",
@@ -1129,7 +1133,7 @@ fields.ajax = [
1129
1133
  "trigger": ""
1130
1134
  },
1131
1135
  {
1132
- "selector": "#daquestion input[id=\"ZmF2b3JpdGVfd29yZA\"][class=\"form-control da-ajax-combobox\"]",
1136
+ "selector": "#daquestion input[id=\"ZmF2b3JpdGVfd29yZA\"][class=\"form-control da-ajax-combobox\"][role=\"combobox\"]",
1133
1137
  "tag": "input",
1134
1138
  "guesses": [
1135
1139
  {
@@ -1160,6 +1164,13 @@ fields.ajax = [
1160
1164
  "type": "",
1161
1165
  "trigger": ""
1162
1166
  },
1167
+ {
1168
+ "selector": "#daquestion button[class=\"btn btn-outline-secondary dacomboboxtoggle\"]",
1169
+ "tag": "button",
1170
+ "guesses": [],
1171
+ "type": "button",
1172
+ "trigger": ""
1173
+ },
1163
1174
  {
1164
1175
  "selector": "#daquestion button[id=\"da-continue-button\"][class=\"btn btn-da btn-primary\"]",
1165
1176
  "tag": "button",
@@ -2,7 +2,7 @@ const chai = require('chai');
2
2
  const expect = chai.expect;
3
3
  const deepEqualInAnyOrder = require('deep-equal-in-any-order');
4
4
  chai.use(deepEqualInAnyOrder);
5
- // We need jest or something, right? To do fancy stuff.
5
+ // jest may let us store and update snapshots, but may discourage hand-checking
6
6
 
7
7
  const html = require('./html.fixtures.js');
8
8
  const fields = require('./fields.fixtures.js');
@@ -4,6 +4,8 @@
4
4
  * #trigger element is added into #daquestion, though it's not at
5
5
  * that location on the actual page. Otherwise, we'd have a lot more
6
6
  * html in here. In scope.js, it's found just using its id selector.
7
+ *
8
+ * ✏️ Fixtures are using HTML from docassemble versions <= 1.9.n
7
9
  */
8
10
 
9
11
  let html = {};
@@ -586,7 +588,7 @@ html.proxy_substitution_i_is_0 = `
586
588
  <legend class="visually-hidden">Press one of the following buttons:</legend>
587
589
  <button class="btn btn-da btn-primary" id="da-continue-button" type="submit">Continue</button>
588
590
  </fieldset>
589
- <input type="hidden" name="csrf_toke" value="ImNlMGRmOWY5NmQ4MGRjMjI0ZDQyNGFjMWRiYjdiODAzOTk2ZThhY2Ui.Zb7dOA.Fvil0jDP4mebaBoPmtM7sXv6wSQ"/>
591
+ <input type="hidden" name="csrf_token" value="ImNlMGRmOWY5NmQ4MGRjMjI0ZDQyNGFjMWRiYjdiODAzOTk2ZThhY2Ui.Zb7dOA.Fvil0jDP4mebaBoPmtM7sXv6wSQ"/>
590
592
  <input type="hidden" name="_event" value="WyJwcm94eV9saXN0WzBdLm5hbWUuZmlyc3QiXQ"/>
591
593
  <input type="hidden" name="_question_name" value="ID proxy vars"/>
592
594
  <input type="hidden" name="_tracker" value="3"/>
@@ -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
+ });