@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/.github/workflows/{da_image_v_sha_7d13853.yml → al_da_image_latest.yml} +10 -5
- package/.github/workflows/github_server.yml +7 -3
- package/.github/workflows/playground.yml +2 -2
- package/CHANGELOG.md +54 -1
- package/docs/decisions/017_post_navigation_custom_behavior_2026_10_26.md +136 -0
- package/lib/docassemble/docassemble_api_interface.js +3 -0
- package/lib/scope.js +1199 -306
- package/lib/steps.js +150 -67
- 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 +20 -1
- 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/tests/unit_tests/navigation.test.js +72 -0
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
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
|
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
|
-
*
|
|
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)
|
|
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
|
-
|
|
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
|
|
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
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
-
|
|
824
|
-
|
|
825
|
-
|
|
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
|
-
|
|
828
|
-
|
|
829
|
-
|
|
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
|
-
|
|
1462
|
-
//
|
|
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 -
|
|
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
|
|
@@ -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
|
-
|
|
|
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"/>
|
|
@@ -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
|
+
});
|