@suffolklitlab/alkiln 5.17.0 → 5.17.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
@@ -383,7 +383,7 @@ When(/I? ?gets? to ?.*?(".*").*?with.*?(?:this data:)?/i, {timeout: -1}, async (
383
383
 
384
384
  // Set up the timeout that avoids infinite loops
385
385
  let on_page_timeout = async function() {
386
-
386
+ // Will throw
387
387
  let non_reload_report_msg = `The target id or ids were ${ target_ids }, but it took `
388
388
  + `too long to try to reach a valid id (over ${ scope.timeout/1000/60 } min). The `
389
389
  + `test got stuck on "${ id }".`
@@ -418,10 +418,13 @@ When(/I? ?gets? to ?.*?(".*").*?with.*?(?:this data:)?/i, {timeout: -1}, async (
418
418
 
419
419
  // THIS FUNCTION ACTUALLY MATTERS
420
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 });
421
+ let error_data = await scope.setFields(scope, { var_data: ensured_var_data, id, ensure_navigation: true });
422
422
 
423
- // Discuss: use Log class to throw this error?
424
- if ( error.was_found ) { await scope.throwPageError( scope, { id: id }); }
423
+ // Story Table assumes we require navigation while other Steps don't.
424
+ // 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
+ }); }
425
428
  resolve();
426
429
  } catch ( err ) {
427
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.`
@@ -498,12 +501,9 @@ When(/I wait ?(?:for)? (\d*\.?\d+) seconds?/, { timeout: -1 }, async ( seconds )
498
501
  * - I wait 1 second
499
502
  * - I wait .5 seconds
500
503
  *
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)
504
+ * Discuss: Should this reset the 'navigated' flag? Maybe the 'navigated' flag
505
+ * should only be reset just before trying to navigate (after pressing a
506
+ * button.)
507
507
  */
508
508
  return wrapPromiseWithTimeout(
509
509
  scope.afterStep(scope, {waitForTimeout: (parseFloat( seconds ) * 1000)}),
@@ -627,39 +627,45 @@ Then('an element should have the id {string}', async ( id ) => { // √
627
627
 
628
628
  // This phrase is confusing. It sounds like it's going to at least try to continue.
629
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;
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
+ }
643
+
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
+ }
636
650
 
637
651
  await scope.afterStep(scope);
638
652
  });
639
653
 
640
654
  Then('I will be told an answer is invalid', async () => { // √
641
655
 
642
- let { was_found, error_handlers } = await scope.checkForError( scope );
656
+ let { ok, type, error_handlers } = await scope.checkForError( scope );
643
657
 
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;
658
+ 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;
647
661
 
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
- });
662
+ if ( type !== `user` ) {
663
+ report_msg = reports.addToReport(scope, { type: `error`, code: `ALK0077`,
664
+ 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
+ });
666
+ }
667
+ expect( type, report_msg ).to.equal( `user` );
655
668
 
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
669
  await scope.afterStep(scope);
664
670
  });
665
671
 
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",
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
@@ -159,7 +160,7 @@ ALK0144:1
159
160
  ALK0145:1
160
161
  ALK0146:1
161
162
  ALK0147:1
162
- ALK0148:1
163
+ ALK0148:2
163
164
  ALK0149:1
164
165
  ALK0150:1
165
166
  ALK0151:1
@@ -291,3 +292,12 @@ 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
@@ -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"/>