@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/.github/workflows/{da_image_v_sha_7d13853.yml → al_da_image_latest.yml} +8 -3
- package/.github/workflows/github_server.yml +5 -3
- package/.github/workflows/playground.yml +2 -2
- package/CHANGELOG.md +22 -1
- package/lib/docassemble/docassemble_api_interface.js +3 -0
- package/lib/scope.js +627 -152
- package/lib/steps.js +40 -34
- package/lib/utils/Log.js +10 -8
- package/lib/utils/session_vars.js +25 -8
- package/package.json +1 -1
- package/tests/log_codes/log_code_expected_instances.txt +12 -2
- package/tests/unit_tests/constrained_random_answers/valid.fixtures.js +3 -1
- package/tests/unit_tests/fields.fixtures.js +19 -8
- package/tests/unit_tests/getAllFields.test.js +1 -1
- package/tests/unit_tests/html.fixtures.js +3 -1
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
|
|
421
|
+
let error_data = await scope.setFields(scope, { var_data: ensured_var_data, id, ensure_navigation: true });
|
|
422
422
|
|
|
423
|
-
//
|
|
424
|
-
|
|
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
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
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
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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 {
|
|
656
|
+
let { ok, type, error_handlers } = await scope.checkForError( scope );
|
|
643
657
|
|
|
644
|
-
let
|
|
645
|
-
if (
|
|
646
|
-
expect(
|
|
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
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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 -
|
|
288
|
-
* The log level. Functional and metadata. Defined by the
|
|
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 -
|
|
430
|
-
* The log level. Functional and metadata. Defined by the
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
@@ -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:
|
|
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:
|
|
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
|
-
|
|
|
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
|
|
889
|
-
// Also, there's an additional
|
|
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
|
-
"
|
|
897
|
-
"
|
|
900
|
+
"value": "",
|
|
901
|
+
"var": "c2lnbmF0dXJlXzE"
|
|
898
902
|
},
|
|
899
903
|
{
|
|
900
|
-
"
|
|
901
|
-
"
|
|
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
|
-
//
|
|
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="
|
|
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"/>
|