@suffolklitlab/alkiln 4.0.0-pre-api.7 → 4.0.0
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/CHANGELOG.md +63 -9
- package/README.md +10 -2
- package/action.yml +8 -6
- package/lib/docassemble/docassemble_api_interface.js +2 -1
- package/lib/docassemble/get_base_interview_url.js +19 -0
- package/lib/index.js +0 -2
- package/lib/scope.js +158 -87
- package/lib/steps.js +10 -10
- package/lib/utils/langs.js +2 -2
- package/lib/utils/session_vars.js +54 -80
- package/package.json +4 -5
- package/tests/features/establishing_steps.feature +2 -2
- package/tests/features/observation_steps.feature +1 -1
- package/tests/features/reports.feature +23 -55
- package/tests/features/story_table_formats.feature +1 -1
- package/tests/features/story_tables.feature +1 -1
- package/tests/unit_tests/getMatchingRows.test.js +28 -0
- package/tests/unit_tests/matches.fixtures.js +13 -1
- package/tests/unit_tests/session_vars.test.js +46 -0
- package/tests/unit_tests/tables.fixtures.js +12 -2
- package/lib/utils/env_vars.js +0 -177
- package/tests/unit_tests/env_vars.test.js +0 -53
package/CHANGELOG.md
CHANGED
|
@@ -38,12 +38,12 @@ Format:
|
|
|
38
38
|
### Security
|
|
39
39
|
-
|
|
40
40
|
-->
|
|
41
|
-
<!--
|
|
41
|
+
<!-- ## [Unreleased] -->
|
|
42
42
|
|
|
43
|
-
## [
|
|
43
|
+
## [4.0.0] - 2022-03-30
|
|
44
44
|
### Added
|
|
45
|
-
- Add
|
|
46
|
-
- Add
|
|
45
|
+
- BREAKING: Add package.json creation/overwriting to action.yml. Simplify package.json.
|
|
46
|
+
- BREAKING: Add action.yml that runs most of what users' workflows run now, along with notes for a new user workflow that will take less maintainance. See #420. We need to add documentation on how to write a workflow file as it currently is if they want to take back control. Setup interview has not yet been updated with this workflow.
|
|
47
47
|
- Step: `the text in the JSON variable "variable_with_text_value" should be`. Compare JSON variable with a text value to given text. See #470. Does not accept nested values, e.g. "child.name.first". Downloads all the JSON vars to a .json file in the "downloads" artifacts.
|
|
48
48
|
- Step to log the page's JSON variables and values in the report. Future goal: save to file. See #454.
|
|
49
49
|
- Step to log into the developer's docassemble server account using GitHub SECRETs. See #499.
|
|
@@ -51,12 +51,13 @@ Format:
|
|
|
51
51
|
- Add control of alkiln npm version to the action.yml
|
|
52
52
|
|
|
53
53
|
### Changed
|
|
54
|
-
-
|
|
55
|
-
- Update to
|
|
56
|
-
- Update
|
|
57
|
-
-
|
|
54
|
+
- BREAKING: NPM package is now suffolklitlab scoped package. Refer to @suffolklitlab/alkiln from now on.
|
|
55
|
+
- BREAKING: Update to v13 of puppeteer
|
|
56
|
+
- BREAKING: Update to v7 of cucumber
|
|
57
|
+
- BREAKING: Update action.yml node to v17
|
|
58
|
+
- BREAKING: Use alkiln v4 package.json we create during the test run in `action.yml` (as discussed in #489)
|
|
59
|
+
- BREAKING: Use API key to access da server, create projects, pull code, delete projects, and check for server restart.
|
|
58
60
|
- Remove and ignore package-lock.json so that our tests will behave more like our users' tests
|
|
59
|
-
- Use API key to access da server, create projects, pull code, delete projects, and check for server restart.
|
|
60
61
|
- Add warning in steps.js for name input with too many parts. Add test in reports.
|
|
61
62
|
- fix typo in the report
|
|
62
63
|
- Throw an error for uploading nonexistent file, changed to error instead of warning in reports.
|
|
@@ -67,10 +68,63 @@ Format:
|
|
|
67
68
|
- Peer dependencies and dev dependencies. Now `cucumber` is just a dependency. See https://github.com/SuffolkLITLab/ALKiln/issues/396 for discussion. Setup interview has not been updated to remove dependencies.
|
|
68
69
|
|
|
69
70
|
### Fixed
|
|
71
|
+
- Proxy var story table row matches for any page containing the given variable name when there's only one row for the relevant variable. #464.
|
|
70
72
|
- Fixed interview name not allowing url parameters. #449
|
|
71
73
|
- Freeze colors npm package to before bug in both package.json and action.yml
|
|
72
74
|
- Fix invalid project name allowed.
|
|
73
75
|
- fix typo in the report.
|
|
76
|
+
- session_vars branch path regex not removing start of branch path
|
|
77
|
+
|
|
78
|
+
## [3.2.1] - 2022-02-09
|
|
79
|
+
### Fixed
|
|
80
|
+
- Typo in report that was messing up copy/paste of test table
|
|
81
|
+
|
|
82
|
+
## [3.2.0] - 2022-02-09
|
|
83
|
+
### Added
|
|
84
|
+
- Add Step to log into server account using GitHub SECRETs (environment variables). See #499.
|
|
85
|
+
|
|
86
|
+
## [3.1.11] - 2022-02-02
|
|
87
|
+
### Changed
|
|
88
|
+
- Handle an Assembly Line name Step value that has too many names, including a warning for the developer. Include the first three names and the last name to try to catch suffixes.
|
|
89
|
+
|
|
90
|
+
## [3.0.11] - 2022-01-25
|
|
91
|
+
### Added
|
|
92
|
+
- Step: `the text in the JSON variable "variable_with_text_value" should be`. Compare JSON variable with a text value to given text. See #470. Does not accept nested values, e.g. "child.name.first". Downloads all the JSON vars to a .json file in the "downloads" artifacts.
|
|
93
|
+
|
|
94
|
+
## [3.0.10] - 2022-01-24
|
|
95
|
+
### Added
|
|
96
|
+
- Step to log the page's JSON variables and values in the report. Future goal: save to file. See #454.
|
|
97
|
+
|
|
98
|
+
## [3.0.9] - 2022-01-10
|
|
99
|
+
### Security
|
|
100
|
+
- Install the security fix in the action's package.json as well. Because dependents have to require cucumber itself, if they're not using our action (if they have their own package.json), they're going to have to implement this fix in their repo too. That's why our fix isn't working. We'll start the process of helping developers update. We also need to deprecate all previous versions of ALKiln. See #489.
|
|
101
|
+
|
|
102
|
+
## [3.0.8] - 2022-01-10
|
|
103
|
+
### Security
|
|
104
|
+
- Fully publish the security fix in v3.0.7
|
|
105
|
+
|
|
106
|
+
## [3.0.7] - 2022-01-10
|
|
107
|
+
### Security
|
|
108
|
+
- Pinned colors library to 1.4.0. See https://www.bleepingcomputer.com/news/security/dev-corrupts-npm-libs-colors-and-faker-breaking-thousands-of-apps/. We don't use faker, so we don't need to handle that.
|
|
109
|
+
|
|
110
|
+
## [3.0.6] - 2022-01-07
|
|
111
|
+
### Changed
|
|
112
|
+
- See #371
|
|
113
|
+
- Scenario heading tags are now on a second line
|
|
114
|
+
- Artifact filenames use dashes more and are created separately from Scenario headings
|
|
115
|
+
- Scenario ids are separated from filenames and Scenario headings
|
|
116
|
+
|
|
117
|
+
## [3.0.5] - 2022-01-06
|
|
118
|
+
### Added
|
|
119
|
+
- Add action.yml that runs most of what users' workflows run now, along with notes for a new user workflow that will take less maintainance. See #420. We need to add documentation on how to write a workflow file as it currently is if they want to take back control. Setup interview has not yet been updated with this workflow.
|
|
120
|
+
- Add package.json creation/overwriting to action.yml. Simplify package.json.
|
|
121
|
+
|
|
122
|
+
<!-- ## [3.0.1-peer-deps.1] - 2021-12-07 -->
|
|
123
|
+
### Removed
|
|
124
|
+
- Peer dependencies and dev dependencies. Now `cucumber` is just a dependency. See https://github.com/SuffolkLITLab/ALKiln/issues/396 for discussion. Setup interview has not been updated to remove dependencies.
|
|
125
|
+
|
|
126
|
+
### Fixed
|
|
127
|
+
- Fixed interview name not allowing url parameters. #449
|
|
74
128
|
|
|
75
129
|
## [3.0.4] - 2021-12-11
|
|
76
130
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
#
|
|
2
|
-
Integrated automated end-to-end testing with docassemble, puppeteer, and
|
|
1
|
+
# ALKiln
|
|
2
|
+
Integrated automated end-to-end testing with docassemble, puppeteer, cucumber, and chai.
|
|
3
|
+
|
|
4
|
+
Works well with https://github.com/suffolklitlab/docassemble-AssemblyLine but isn't
|
|
5
|
+
dependent on it.
|
|
6
|
+
|
|
7
|
+
Formerly named "docassemble-cucumber"
|
|
8
|
+
|
|
9
|
+
## Documentation
|
|
10
|
+
Read about how to use this framework at https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/.
|
|
3
11
|
|
|
4
12
|
## Integration tests
|
|
5
13
|
To setup for the integration tests, create the project on the server:
|
package/action.yml
CHANGED
|
@@ -32,6 +32,7 @@ inputs:
|
|
|
32
32
|
runs:
|
|
33
33
|
using: "composite"
|
|
34
34
|
steps:
|
|
35
|
+
|
|
35
36
|
# Set environment variables
|
|
36
37
|
- name: "ALKiln: Set environment variables"
|
|
37
38
|
# If extra languages were set manually, override the repository
|
|
@@ -54,15 +55,19 @@ runs:
|
|
|
54
55
|
echo "DOCASSEMBLE_DEVELOPER_API_KEY=${{ inputs.DOCASSEMBLE_DEVELOPER_API_KEY }}" >> $GITHUB_ENV
|
|
55
56
|
echo "ALKILN_VERSION=${{ inputs.ALKILN_VERSION }}" >> $GITHUB_ENV
|
|
56
57
|
shell: bash
|
|
58
|
+
- name: "ALKiln: confirm info"
|
|
59
|
+
run: echo -e "\nRepo is $REPO_URL\nBranch ref is $BRANCH_PATH\nManually added languages are $EXTRA_LANGUAGES\nAll other data is secret\n"
|
|
60
|
+
shell: bash
|
|
61
|
+
|
|
57
62
|
# This doesn't use an already established package-lock.json and that
|
|
58
63
|
# comes with some types of test breaking risks. Same problem as before.
|
|
59
|
-
- name:
|
|
64
|
+
- name: Temporarily overwrite or create package.json
|
|
60
65
|
run: |
|
|
61
66
|
cat << EOF > "package.json"
|
|
62
67
|
{
|
|
63
68
|
"scripts": {
|
|
64
69
|
"test": "npm run langs_setup && npm run cucumber -- $@",
|
|
65
|
-
"cucumber_base": "./node_modules/.bin/cucumber-js --require ./node_modules
|
|
70
|
+
"cucumber_base": "./node_modules/.bin/cucumber-js --require ./node_modules/@suffolklitlab/alkiln/lib/index.js",
|
|
66
71
|
"cucumber": "run_cucumber(){ npm run cucumber_base -- \"$@\" docassemble/*/data/sources/*.feature; }; run_cucumber",
|
|
67
72
|
"langs": "npm run langs_setup && ./node_modules/.bin/cucumber-js --require ./node_modules/.bin/alkiln-cucumber docassemble/*/data/sources/*_lang*.feature",
|
|
68
73
|
"langs_setup": "node_modules/.bin/alkiln-langs 'docassemble/*/data/sources/*.feature'",
|
|
@@ -76,11 +81,8 @@ runs:
|
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
shell: bash
|
|
79
|
-
- name: "ALKiln: confirm info"
|
|
80
|
-
run: echo -e "\nRepo is $REPO_URL\nBranch ref is $BRANCH_PATH\nManually added languages are $EXTRA_LANGUAGES\nAll other data is secret\n"
|
|
81
|
-
shell: bash
|
|
82
84
|
|
|
83
|
-
# Install node
|
|
85
|
+
# Install node
|
|
84
86
|
- name: "ALKiln: Install node packages"
|
|
85
87
|
uses: actions/setup-node@v1
|
|
86
88
|
with:
|
|
@@ -124,7 +124,8 @@ da_i.get_dev_id = async function ( dev_id_options ) {
|
|
|
124
124
|
|
|
125
125
|
// Default timeouts
|
|
126
126
|
let { timeout } = dev_id_options || { timeout: 75 * 1000 };
|
|
127
|
-
|
|
127
|
+
let response = await _da_REST.get_dev_id( timeout );
|
|
128
|
+
return response.data.id;
|
|
128
129
|
}; // Ends da_i.get_dev_id();
|
|
129
130
|
|
|
130
131
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
let session_vars = require(`../utils/session_vars`);
|
|
2
|
+
let docassemble_api_interface = require('./docassemble_api_interface');
|
|
3
|
+
|
|
4
|
+
let get_base_interview_url = async function () {
|
|
5
|
+
/** Return the part of the interview URL that comes before the filename.
|
|
6
|
+
* Depends on the project name, which may get changed.
|
|
7
|
+
* This also needs to ensure that variable values from previous tests aren't used.
|
|
8
|
+
* That's not completely possible, but our tests try to do this in several ways.
|
|
9
|
+
* This function uses `new_session=1` to try to ensure old session vars aren't used.
|
|
10
|
+
* When finishing a test, we go to the sign out page (which is supposed to help even
|
|
11
|
+
* when the user is not logged in.) We can't use `reset=2` because it caused an
|
|
12
|
+
* 'Unable to locate interview session' error. */
|
|
13
|
+
let server_url = session_vars.get_da_server_url();
|
|
14
|
+
let dev_id = await docassemble_api_interface.get_dev_id();
|
|
15
|
+
let project_name = session_vars.get_project_name();
|
|
16
|
+
return `${ server_url }/interview?new_session=1&i=docassemble.playground${ dev_id }${ project_name }%3A`;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
module.exports = get_base_interview_url;
|
package/lib/index.js
CHANGED
package/lib/scope.js
CHANGED
|
@@ -5,8 +5,8 @@ const fs = require('fs');
|
|
|
5
5
|
const safe_filename = require("sanitize-filename");
|
|
6
6
|
|
|
7
7
|
// Ours
|
|
8
|
-
const env_vars = require('./utils/env_vars');
|
|
9
8
|
const session_vars = require('./utils/session_vars');
|
|
9
|
+
const get_base_interview_url = require(`./docassemble/get_base_interview_url`);
|
|
10
10
|
const time = require('./utils/time');
|
|
11
11
|
|
|
12
12
|
|
|
@@ -25,13 +25,14 @@ let escape_regex = function( to_escape ) {
|
|
|
25
25
|
let proxies_regex = /(\bx\.)|(\bx\[)|(\[[ijklmn]\])/g;
|
|
26
26
|
|
|
27
27
|
module.exports = {
|
|
28
|
+
trigger_not_needed_flag: `ALKiln: no trigger variable needed`,
|
|
28
29
|
addToReport: async function( scope, { type, value }) {
|
|
29
30
|
/* Add an item to a specific list in a scenario's report.
|
|
30
31
|
* Create report if necessary.
|
|
31
32
|
*
|
|
32
33
|
* The report has a key for each scenario and each of those
|
|
33
34
|
* keys should correspond to a list containing the relevant data. */
|
|
34
|
-
if (
|
|
35
|
+
if ( session_vars.get_debug() ) { console.log(`Report:\ntype: ${ type }, value: ${ value }`) }
|
|
35
36
|
let scenario = await scope.makeSureReportPartsExist( scope );
|
|
36
37
|
scenario.lines.push({ type, value });
|
|
37
38
|
|
|
@@ -70,6 +71,10 @@ module.exports = {
|
|
|
70
71
|
let var_str = await scope.toTrimmedJSON( scope, { str: original.var });
|
|
71
72
|
let value_str = await scope.toTrimmedJSON( scope, { str: original.value });
|
|
72
73
|
let trigger_str = await scope.toTrimmedJSON( scope, { str: original.trigger });
|
|
74
|
+
if ( trigger_str === scope.trigger_not_needed_flag ) {
|
|
75
|
+
// This text was added by ALKiln itself. It's not part of the user's data
|
|
76
|
+
trigger_str = ``;
|
|
77
|
+
}
|
|
73
78
|
return `${ ' '.repeat(6) }| ${ var_str } | ${ value_str } | ${ trigger_str } |`;
|
|
74
79
|
}, // Ends scope.convertToOriginalStoryTableRows()
|
|
75
80
|
|
|
@@ -84,7 +89,7 @@ module.exports = {
|
|
|
84
89
|
/** Return printable heading based on Scenario name and tags.
|
|
85
90
|
* Allow characters from other languages.
|
|
86
91
|
*/
|
|
87
|
-
if (
|
|
92
|
+
if ( session_vars.get_debug() ) { console.log( scenario.pickle.name ); }
|
|
88
93
|
|
|
89
94
|
// Collect all tag names
|
|
90
95
|
let tag_names = [];
|
|
@@ -397,13 +402,14 @@ module.exports = {
|
|
|
397
402
|
|
|
398
403
|
// If there is no browser open, start a new one
|
|
399
404
|
if (!scope.browser) {
|
|
400
|
-
scope.browser = await scope.driver.launch({ headless: !
|
|
405
|
+
scope.browser = await scope.driver.launch({ headless: !session_vars.get_debug() });
|
|
401
406
|
}
|
|
402
407
|
if ( !scope.page ) { scope.page = await scope.browser.newPage(); }
|
|
403
408
|
|
|
404
409
|
if ( !file_name.includes('.yml') ) { file_name = `${ file_name }.yml` }
|
|
405
|
-
let
|
|
406
|
-
|
|
410
|
+
let base_url = await get_base_interview_url();
|
|
411
|
+
let interview_url = `${ base_url }${ file_name }`;
|
|
412
|
+
if ( session_vars.get_debug() ) { console.log( interview_url ); }
|
|
407
413
|
|
|
408
414
|
await scope.page.goto(interview_url, { waitUntil: 'domcontentloaded', timeout: scope.timeout })
|
|
409
415
|
|
|
@@ -481,7 +487,7 @@ module.exports = {
|
|
|
481
487
|
supported_table.push( result );
|
|
482
488
|
}
|
|
483
489
|
|
|
484
|
-
if (
|
|
490
|
+
if ( session_vars.get_debug() ) { console.log( 'scope.normalizedTable():\n', JSON.stringify( supported_table )); }
|
|
485
491
|
|
|
486
492
|
return supported_table;
|
|
487
493
|
}, // Ends scope.normalizeTable()
|
|
@@ -506,7 +512,7 @@ module.exports = {
|
|
|
506
512
|
let encoded_trigger_var = $( trigger_elem ).data( `variable` ); // This should come out to 'None' at the very least
|
|
507
513
|
if ( !encoded_trigger_var ) {
|
|
508
514
|
// TODO: Add documentation and then link to it in this message.
|
|
509
|
-
await scope.addToReport(scope, { type: `warning`, value: `You are missing an element containing the data for the page's trigger variable. Some fields, like loops with index variables, may not work correctly in these tests.
|
|
515
|
+
await scope.addToReport(scope, { type: `warning`, value: `You are missing an element containing the data for the page's trigger variable. Some fields, like loops with index variables, may not work correctly in these tests. See https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#a-missing-trigger-variable.` });
|
|
510
516
|
encoded_trigger_var = await scope.toBase64( scope, { utf8_str: `None` });
|
|
511
517
|
}
|
|
512
518
|
let trigger = await scope.fromBase64( scope, { base64_str: encoded_trigger_var });
|
|
@@ -598,7 +604,7 @@ module.exports = {
|
|
|
598
604
|
fields.push( field_data );
|
|
599
605
|
} // ends for every node
|
|
600
606
|
|
|
601
|
-
if (
|
|
607
|
+
if ( session_vars.get_debug() ) { console.log( `page fields:\n${ JSON.stringify( fields )}`); }
|
|
602
608
|
return fields;
|
|
603
609
|
}, // Ends scope.getAllFields()
|
|
604
610
|
|
|
@@ -828,6 +834,8 @@ module.exports = {
|
|
|
828
834
|
/* Given var_data, a list of objects with variable data for
|
|
829
835
|
* setting fields, and an object for a DOM field, returns list of the
|
|
830
836
|
* rows that match the DOM field. That list of story table rows may be empty.
|
|
837
|
+
*
|
|
838
|
+
* For notes on trigger var, see https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#trigger
|
|
831
839
|
*
|
|
832
840
|
* @param var_data {arr} - fields and the values they can be set to
|
|
833
841
|
* @param var_data[n].var {str} - name of variable to set
|
|
@@ -840,24 +848,26 @@ module.exports = {
|
|
|
840
848
|
* @param field[n].tag {str} - HTML tag of node
|
|
841
849
|
* @param field[n].type {str} - HTML type of node if it has one
|
|
842
850
|
* @param field[n].guesses {arr} - arr of objects that could
|
|
843
|
-
* possibly match a table row
|
|
851
|
+
* possibly match a table row's `var` column
|
|
844
852
|
* @param field[n].guesses[n].var {str} - name of variable
|
|
845
853
|
* @param field[n].guesses[n].value {str} - value of a choice
|
|
846
854
|
* @param <field[n].guesses[n].trigger> {str} - var name that triggered this page
|
|
847
855
|
*
|
|
848
856
|
* @returns [ var_data[n] ]
|
|
849
857
|
*/
|
|
850
|
-
let matches = [];
|
|
851
|
-
let uses_proxies = false;
|
|
852
858
|
|
|
853
|
-
|
|
859
|
+
let rows_that_match_field_names = [];
|
|
860
|
+
let uses_proxies = false; // See "proxies" notes further down
|
|
861
|
+
let trigger_might_be_needed = true;
|
|
862
|
+
|
|
863
|
+
// There are mutliple guesses because field names are sometimes encoded
|
|
854
864
|
for ( let guess of field.guesses ) {
|
|
855
|
-
for ( let
|
|
856
|
-
let
|
|
865
|
+
for ( let table_row of var_data ) {
|
|
866
|
+
let this_row_matches_this_field = false;
|
|
857
867
|
|
|
858
868
|
// If the dev uses single instead of double quotes, etc, allow that
|
|
859
|
-
let names_match = await scope.matchNeutralizedQuotes( scope, { value1: guess.var, value2:
|
|
860
|
-
let values_match = await scope.matchNeutralizedQuotes( scope, { value1: guess.value, value2:
|
|
869
|
+
let names_match = await scope.matchNeutralizedQuotes( scope, { value1: guess.var, value2: table_row.var });
|
|
870
|
+
let values_match = await scope.matchNeutralizedQuotes( scope, { value1: guess.value, value2: table_row.value });
|
|
861
871
|
|
|
862
872
|
// Check different things for different fields. An input box won't
|
|
863
873
|
// be expected to have the same value as the var you want to set while
|
|
@@ -865,91 +875,152 @@ module.exports = {
|
|
|
865
875
|
switch ( field.tag ) {
|
|
866
876
|
case 'button':
|
|
867
877
|
case 'select':
|
|
868
|
-
if ( names_match && values_match ) {
|
|
878
|
+
if ( names_match && values_match ) { this_row_matches_this_field = true; }
|
|
869
879
|
break;
|
|
870
880
|
|
|
871
881
|
case 'canvas':
|
|
872
882
|
case 'textarea':
|
|
873
883
|
case 'a':
|
|
874
|
-
if ( names_match ) {
|
|
884
|
+
if ( names_match ) { this_row_matches_this_field = true; }
|
|
875
885
|
break;
|
|
876
886
|
|
|
877
887
|
case 'input':
|
|
878
888
|
if ( field.type === 'radio' ) {
|
|
879
|
-
if ( names_match && values_match ) {
|
|
889
|
+
if ( names_match && values_match ) { this_row_matches_this_field = true; }
|
|
880
890
|
} else { // Some kind of text input
|
|
881
|
-
if ( names_match ) {
|
|
891
|
+
if ( names_match ) { this_row_matches_this_field = true; }
|
|
882
892
|
}
|
|
883
893
|
break;
|
|
884
894
|
}
|
|
885
895
|
|
|
886
|
-
if (
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
896
|
+
if ( this_row_matches_this_field ) {
|
|
897
|
+
rows_that_match_field_names.push( table_row );
|
|
898
|
+
// Check this until the value is set to `true` (if it ever is)
|
|
899
|
+
// If the field uses proxy variables - x, i, j, etc, flag it. See "proxies" note.
|
|
900
|
+
uses_proxies = uses_proxies || guess.var.match( proxies_regex );
|
|
901
|
+
|
|
902
|
+
if ( table_row.trigger === scope.trigger_not_needed_flag ) {
|
|
903
|
+
trigger_might_be_needed = false;
|
|
893
904
|
}
|
|
894
|
-
|
|
895
|
-
}
|
|
905
|
+
} // end if this_row_matches_this_field
|
|
896
906
|
|
|
897
907
|
} // ends for each table row
|
|
898
908
|
} // ends for each field row possibility
|
|
899
909
|
|
|
900
|
-
//
|
|
901
|
-
//
|
|
902
|
-
//
|
|
903
|
-
//
|
|
904
|
-
//
|
|
905
|
-
//
|
|
906
|
-
//
|
|
907
|
-
//
|
|
908
|
-
//
|
|
909
|
-
//
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
let
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
if (
|
|
933
|
-
//
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
910
|
+
// Proxies:
|
|
911
|
+
// When a dev has a loop with an index var or generic object, the
|
|
912
|
+
// same variable name will be used over and over, e.g.`users[i].name.first`.
|
|
913
|
+
// In a regular Step, there's no ambiguity about the value to set.
|
|
914
|
+
// In a dev's story table, though, there could be multiple rows that match
|
|
915
|
+
// a field named `users[i]`. For kiln to know which row to use on which
|
|
916
|
+
// page, we need the one unique HTML string that will appear on the page -
|
|
917
|
+
// the trigger var name. When a row with `users[i]` has a "trigger" column
|
|
918
|
+
// value that matches the page HTML's trigger var name, kiln will know to
|
|
919
|
+
// use the value from that row. We need to find the matching row.
|
|
920
|
+
let final_matching_rows = [];
|
|
921
|
+
if ( trigger_might_be_needed && uses_proxies && rows_that_match_field_names.length > 0 ) {
|
|
922
|
+
|
|
923
|
+
let rows_that_also_match_trigger_name = [];
|
|
924
|
+
let rows_without_a_trigger_name = [];
|
|
925
|
+
let rows_with_conflicting_trigger_names = [];
|
|
926
|
+
|
|
927
|
+
let page_trigger_name = field.trigger;
|
|
928
|
+
|
|
929
|
+
// field/page trigger name is needed but is missing.
|
|
930
|
+
if ( !page_trigger_name ) {
|
|
931
|
+
let page_trigger_warning = `A field on this page uses an index `
|
|
932
|
+
+ `variable or a generic object, but the page does not `
|
|
933
|
+
+ `contain the trigger variable's name in its HTML. See `
|
|
934
|
+
+ `https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#a-missing-trigger-variable.`;
|
|
935
|
+
await scope.addToReport(scope, { type: `warning`, value: page_trigger_warning });
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
for ( let row_that_matches_this_field of rows_that_match_field_names ) {
|
|
939
|
+
|
|
940
|
+
let table_row_trigger_name = row_that_matches_this_field.trigger;
|
|
941
|
+
|
|
942
|
+
if ( !table_row_trigger_name ) {
|
|
943
|
+
// var/row trigger name is (probably) needed but is missing. Caveat: If there's
|
|
944
|
+
// only one page in the proxy var loop and there's only one row, trigger name is
|
|
945
|
+
// not needed, but I'm not sure how we figure out all those things here and log
|
|
946
|
+
// a useful message.
|
|
947
|
+
|
|
948
|
+
// First, make the row data more readable to humans.
|
|
949
|
+
let row_as_story_table = await scope.convertToOriginalStoryTableRow( scope, { row_data: row_that_matches_this_field });
|
|
950
|
+
let row_trigger_warning = `This story table row needs a value in the "trigger" `
|
|
951
|
+
+ `column, but it is empty. The test might get unexpected results. See `
|
|
952
|
+
+ `https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#trigger.`
|
|
953
|
+
+ `\nThe row's data:\n${ row_as_story_table }`
|
|
954
|
+
+ `\nThis field's data:\n${ JSON.stringify( field )}`;
|
|
955
|
+
await scope.addToReport(scope, { type: `warning`, value: row_trigger_warning });
|
|
956
|
+
rows_without_a_trigger_name.push( row_that_matches_this_field );
|
|
957
|
+
} else {
|
|
958
|
+
let triggers_match = await scope.matchNeutralizedQuotes( scope, { value1: page_trigger_name, value2: table_row_trigger_name });
|
|
937
959
|
if ( triggers_match ) {
|
|
938
|
-
|
|
960
|
+
rows_that_also_match_trigger_name.push( row_that_matches_this_field );
|
|
961
|
+
} else {
|
|
962
|
+
rows_with_conflicting_trigger_names.push( row_that_matches_this_field );
|
|
939
963
|
}
|
|
940
964
|
}
|
|
965
|
+
} // ends for each row_that_matches_this_field
|
|
966
|
+
|
|
967
|
+
// Be very permissive, based on feedback. See https://github.com/SuffolkLITLab/ALKiln/issues/464#issuecomment-1005166616
|
|
968
|
+
if ( rows_that_also_match_trigger_name.length > 0 ) {
|
|
969
|
+
final_matching_rows = rows_that_also_match_trigger_name;
|
|
970
|
+
} else {
|
|
971
|
+
final_matching_rows = rows_without_a_trigger_name;
|
|
941
972
|
}
|
|
942
|
-
// Feedback for devs - we are missing the matching triggers we need
|
|
943
|
-
if ( matches.length === 0 ) { await scope.addToReport(scope, { type: `warning`, value: `A field on this page uses an index variable or a generic object. The test gave multiple matches for the story data for the field, but none match the trigger variable. Is the story table missing a row? For now, the test will have to ignore this field. The rows' data is\n${ JSON.stringify( original_matches )}.\nThe field's data is\n${ JSON.stringify( field )}` }); }
|
|
944
|
-
}
|
|
945
973
|
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
974
|
+
// Warn the user if all matching rows had trigger names that conflicted.
|
|
975
|
+
// Missing trigger names don't count as conflicting.
|
|
976
|
+
if ( final_matching_rows.length === 0 ) {
|
|
977
|
+
|
|
978
|
+
// First, make the row data more readable to humans.
|
|
979
|
+
let conflicts_as_story_table_rows = [];
|
|
980
|
+
for ( let row of rows_with_conflicting_trigger_names ) {
|
|
981
|
+
let table_str = await scope.convertToOriginalStoryTableRow( scope, { row_data: row });
|
|
982
|
+
conflicts_as_story_table_rows.push( table_str );
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
let all_trigger_names_conflict_msg = `Is this story table missing a row? `
|
|
986
|
+
+ `${ rows_that_match_field_names.length } story table row(s) matched a field name, `
|
|
987
|
+
+ `but none of their trigger variable name(s) matched the trigger variable `
|
|
988
|
+
+ `name in the screen's HTML. The test will have to ignore this field. See `
|
|
989
|
+
+ `https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#trigger.`
|
|
990
|
+
+ `\nThe data of row(s) with conflicting trigger name(s):\n${ conflicts_as_story_table_rows.join( `\n` )}`
|
|
991
|
+
+ `\nThis field's data:\n${ JSON.stringify( field )}`;
|
|
992
|
+
await scope.addToReport(scope, {
|
|
993
|
+
type: `warning`,
|
|
994
|
+
value: all_trigger_names_conflict_msg
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
} else {
|
|
999
|
+
// These should be used if NO trigger name was needed
|
|
1000
|
+
final_matching_rows = rows_that_match_field_names;
|
|
1001
|
+
} // ends if need trigger name to match
|
|
1002
|
+
|
|
1003
|
+
// Dev may have accidentally included a row more than once.
|
|
1004
|
+
if ( final_matching_rows.length > 1 ) {
|
|
1005
|
+
|
|
1006
|
+
// First, make the row data more readable to humans.
|
|
1007
|
+
let rows_as_story_table = [];
|
|
1008
|
+
for ( let row of final_matching_rows ) {
|
|
1009
|
+
let table_str = await scope.convertToOriginalStoryTableRow( scope, { row_data: row });
|
|
1010
|
+
rows_as_story_table.push( table_str );
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
let multiple_matches_msg = `Do you have a duplicate story table row? `
|
|
1014
|
+
+ `Multiple story table rows match this field's variable name. `
|
|
1015
|
+
+ `The test will use the last row that matched. See `
|
|
1016
|
+
+ `https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#trigger.`
|
|
1017
|
+
+ `\nThe matching rows:\n${ rows_as_story_table.join(`\n`) }`
|
|
1018
|
+
+ `\nThis field's data:\n${ JSON.stringify( field )}`;
|
|
1019
|
+
await scope.addToReport(scope, { type: `warning`, value: multiple_matches_msg });
|
|
949
1020
|
}
|
|
950
1021
|
|
|
951
|
-
if (
|
|
952
|
-
return
|
|
1022
|
+
if ( session_vars.get_debug() ) { console.log( `final_matching_rows:\n`, JSON.stringify( final_matching_rows )); }
|
|
1023
|
+
return final_matching_rows;
|
|
953
1024
|
}, // Ends scope.getMatchingRows()
|
|
954
1025
|
|
|
955
1026
|
|
|
@@ -963,7 +1034,6 @@ module.exports = {
|
|
|
963
1034
|
return val_2_str.match( val_1_regex );
|
|
964
1035
|
}, // Ends scope.matchNeutralizedQuotes()
|
|
965
1036
|
|
|
966
|
-
|
|
967
1037
|
setVariable: async function ( scope, { field, var_data }) {
|
|
968
1038
|
/* Returns true if the field was set.
|
|
969
1039
|
*
|
|
@@ -1004,7 +1074,7 @@ module.exports = {
|
|
|
1004
1074
|
// If field is disabled, don't set it yet
|
|
1005
1075
|
if ( disabled ) { return row_used; }
|
|
1006
1076
|
|
|
1007
|
-
if (
|
|
1077
|
+
if ( session_vars.get_debug() ) {
|
|
1008
1078
|
let name = await handle.evaluate(( elem ) => { // Not using this right now
|
|
1009
1079
|
try { elem.nextSibling.style.background = 'Khaki'; }
|
|
1010
1080
|
catch(err){ elem.style.background = 'Khaki'; }
|
|
@@ -1066,7 +1136,7 @@ module.exports = {
|
|
|
1066
1136
|
for ( let field_i = 0; field_i < fields.length; field_i++ ) {
|
|
1067
1137
|
let field = fields[ field_i ];
|
|
1068
1138
|
|
|
1069
|
-
if (
|
|
1139
|
+
if ( session_vars.get_debug() ) {
|
|
1070
1140
|
await field.evaluate( elem => { elem.style.background = `Khaki`; });
|
|
1071
1141
|
}
|
|
1072
1142
|
|
|
@@ -1130,7 +1200,7 @@ module.exports = {
|
|
|
1130
1200
|
|
|
1131
1201
|
let [label] = await handle.$x(`following-sibling::label`);
|
|
1132
1202
|
|
|
1133
|
-
if (
|
|
1203
|
+
if ( session_vars.get_debug() ) {
|
|
1134
1204
|
if ( set_to.toLowerCase() === 'false' ) { await label.evaluate( elem => { elem.style.background = 'tomato'; }); }
|
|
1135
1205
|
else { await label.evaluate( elem => { elem.style.background = 'teal'; }); }
|
|
1136
1206
|
let name = await label.evaluate( elem => { return elem.getAttribute('for'); });
|
|
@@ -1179,7 +1249,7 @@ module.exports = {
|
|
|
1179
1249
|
|
|
1180
1250
|
// Tests could be in one of two places. Figure out which directory they're in
|
|
1181
1251
|
// Makes an absolute path from the directory in which `npm run` was used plus our internal folders
|
|
1182
|
-
let dir_source = `${ process.cwd() }/docassemble/${
|
|
1252
|
+
let dir_source = `${ process.cwd() }/docassemble/${ session_vars.get_repo_url().match(/docassemble-(.*)/)[1] }/data/sources`;
|
|
1183
1253
|
let dir_features = `${ process.cwd() }/tests/features`;
|
|
1184
1254
|
let dir = null;
|
|
1185
1255
|
|
|
@@ -1334,7 +1404,7 @@ module.exports = {
|
|
|
1334
1404
|
* @param ensure_all_vars_used {bool} [false] = Default false. If true, errors if any
|
|
1335
1405
|
* object in var_data is not used during running of this function.
|
|
1336
1406
|
*/
|
|
1337
|
-
if (
|
|
1407
|
+
if ( session_vars.get_debug() ) { console.log( `~~~~~~~~~~~~~~ scope.setFields() ~~~~~~~~~~~~~~` ); }
|
|
1338
1408
|
|
|
1339
1409
|
// Only record the page id once for each page to which we navigate
|
|
1340
1410
|
let { id } = await scope.examinePageID( scope, 'none to match' );
|
|
@@ -1621,7 +1691,7 @@ module.exports = {
|
|
|
1621
1691
|
if ( possible_errors[ error_key ][0] ) {
|
|
1622
1692
|
// If you're debugging and you close the window early, the process will still
|
|
1623
1693
|
// take this long to exit after the tests finish. Not sure how to account for that.
|
|
1624
|
-
if (
|
|
1694
|
+
if ( session_vars.get_debug() ) { await scope.page.waitForTimeout( 60 * 1000 ); }
|
|
1625
1695
|
was_found = true;
|
|
1626
1696
|
error_handlers[ error_key ] = possible_errors[ error_key ][0];
|
|
1627
1697
|
}
|
|
@@ -1705,7 +1775,7 @@ module.exports = {
|
|
|
1705
1775
|
}, // Ends link_works()
|
|
1706
1776
|
|
|
1707
1777
|
set_regular_var: async ( scope, var_name, set_to ) => {
|
|
1708
|
-
/** Set
|
|
1778
|
+
/** Set a non-story table variable (with or without a choice associated with it) to a value. Set:
|
|
1709
1779
|
* - Buttons associated with variables
|
|
1710
1780
|
* - Dropdowns
|
|
1711
1781
|
* - Checkboxes (multiple choice checkboxes must be set to "true" or "false")
|
|
@@ -1713,11 +1783,12 @@ module.exports = {
|
|
|
1713
1783
|
* - Text inputs
|
|
1714
1784
|
* - Textareas
|
|
1715
1785
|
*/
|
|
1786
|
+
// Let kiln know not to test against trigger variable values, even for proxy vars
|
|
1716
1787
|
let var_data = await scope.normalizeTable( scope, {
|
|
1717
|
-
var_data: [{ var: var_name, value: set_to, trigger:
|
|
1788
|
+
var_data: [{ var: var_name, value: set_to, trigger: scope.trigger_not_needed_flag }],
|
|
1718
1789
|
});
|
|
1719
|
-
// Don't continue if the variable doesn't
|
|
1720
|
-
// cannot be
|
|
1790
|
+
// Don't continue if the variable doesn't cause navigation itself. Also,
|
|
1791
|
+
// if this variable cannot be set on this page, trigger an error
|
|
1721
1792
|
await scope.setFields( scope, { var_data, ensure_navigation: false, ensure_all_vars_used: true });
|
|
1722
1793
|
}, // Ends set_regular_var()
|
|
1723
1794
|
|