@suffolklitlab/alkiln 5.13.4 → 5.15.0-deps-2
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/github_server.yml +1 -1
- package/.github/workflows/playground.yml +2 -1
- package/.github/workflows/unit_tests.yml +1 -1
- package/CHANGELOG.md +40 -0
- package/action.yml +26 -10
- package/action_for_github_server/action.yml +31 -27
- package/docs/4_environments.md +2 -2
- package/docs/artifacts_folder_name.md +121 -71
- package/docs/constrained_random_tests.md +220 -289
- package/docs/decisions/move_runtime_config_to_sources_folder.md +3 -1
- package/docs/prioritize_constrained_random_tests.md +35 -76
- package/lib/docassemble/server_install.js +0 -2
- package/lib/docassemble/setup.js +1 -3
- package/lib/docassemble/takedown.js +2 -2
- package/lib/globals.js +10 -0
- package/lib/run_cucumber.js +40 -53
- package/lib/scope.js +44 -1
- package/lib/setup/TestGenerator.js +1757 -0
- package/lib/setup/artifacts.js +20 -0
- package/lib/setup/generate.js +42 -0
- package/lib/setup/get_Gherkin_AST.js +26 -0
- package/lib/setup/validate.js +89 -0
- package/lib/steps.js +101 -42
- package/lib/utils/Log.js +317 -165
- package/lib/utils/_alkiln-constrained_random_tests_old.js +1776 -0
- package/lib/utils/get_error_stacks.js +16 -0
- package/lib/utils/pdf_reader.js +9 -2
- package/lib/utils/reports.js +44 -22
- package/lib/utils/session_vars.js +77 -57
- package/lib/utils/set_sources_paths.js +98 -0
- package/npm-shrinkwrap.json +739 -924
- package/package.json +17 -10
- package/tests/log_codes/check_codes.sh +17 -13
- package/tests/log_codes/log_code_expected_instances.txt +58 -0
- package/tests/unit_tests/constrained_random_answers/invalid.fixtures.js +178 -0
- package/tests/unit_tests/constrained_random_answers/invalid.test.js +251 -0
- package/tests/unit_tests/constrained_random_answers/valid.fixtures.js +957 -0
- package/tests/unit_tests/constrained_random_answers/valid.test.js +563 -0
- package/tests/unit_tests/example_of_chai_test_lifecycle.js +78 -0
- package/tests/unit_tests/log.test.js +17 -41
- package/docs/seprate_file_generation_GitHub_action.md +0 -11
- package/lib/parse.js +0 -36
- package/lib/utils/get_sources_paths.js +0 -162
- package/lib/utils/parse.js +0 -1
- package/lib/utils/proccess_command_line_args.js +0 -267
- /package/docs/{expected_inernal_results.md → expected_internal_results.md} +0 -0
|
@@ -1,101 +1,60 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Prioritize constrained random answers tests over pure random answers tests
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Context and Problem Statement
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
We could develop constrained randomness first or pure randomness first. Is there a reason to prioritize one over another?
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
2. They are worried they haven't covered all eventualities
|
|
7
|
+
## Considered Options
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
- Purely random answers. They fill in fields without any guidance.
|
|
10
|
+
- Constrained random answers tests. They make the test author list all the variable names and all the choices of answers they want to allow for those variables.
|
|
11
|
+
- Hybrid approach. Allow an author to specify some values while letting other values stay completely random.
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
## Decision Outcome
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
Prioritize constrained random answers tests over pure random answers tests. Strangely, they seem to be the more flexible of the two options.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
- **Some fields might require very specific answers, like a real lawyer's bar number or a specific file name for uploading exhibits.**
|
|
18
|
-
- Narrow down the paths at some points, like always choosing that you have minor children
|
|
19
|
-
- **Even if the user doesn't know it, this is the most flexible option**
|
|
17
|
+
## Pros and Cons of the Options
|
|
20
18
|
|
|
21
|
-
|
|
19
|
+
The discussion about the general pros and cons of random answers tests will be in the `constrained_random_tests` doc for now.
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
### Purely random answers
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
Pros:
|
|
26
24
|
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
- Or should we require a string (e.g. ALK_NO_ANSWER) for fields that should not get random inputs?
|
|
30
|
-
- (most easy purely random input method to implement without changing current code?) Or specify randomness (e.g. ALK_RANDOM_ANSWER)?
|
|
31
|
-
- **(backwards compatible because we can become more flexible later) Require both of the above?**
|
|
25
|
+
- Very little work for test authors to write.
|
|
26
|
+
- Because they're so free-form, they may do a better job finding unexpected behavior that authors didn't think to test for.
|
|
32
27
|
|
|
33
|
-
|
|
28
|
+
Cons:
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
- Add Step to answer randomly for one page.
|
|
30
|
+
- Unless we can find better ways of generating purely random test files for the authors, copying a random answers test to keep permanently might be very challenging.
|
|
31
|
+
- Fields might get nonsensical answers. For example, a very large number for age or a very small number for income.
|
|
32
|
+
- Impossible to use for fields that need very specific answers, like a real lawyer's bar number or a specific file name for uploading exhibits.
|
|
39
33
|
|
|
40
|
-
|
|
34
|
+
### Constrained random answers
|
|
41
35
|
|
|
42
|
-
|
|
36
|
+
Pros:
|
|
43
37
|
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
- "ALKiln: max length 3" (of 5 checkbox answers)
|
|
48
|
-
- "ALKiln: name"
|
|
49
|
-
- "ALKiln: number" (and other types like "currency") (we can probably figure those out)
|
|
38
|
+
- Useful for fields that need very specific answers, like a real lawyer's bar number or a specific file name for uploading exhibits.
|
|
39
|
+
- An author can focus the tests' answers on important user stories. For example, they might want to focus some tests on users that have children while keeping other answers more random.
|
|
40
|
+
- An author might be able to make more efficient random tests. For example, a multiple choice question may have multiple answers that, behind the scenes, set the same value. Authors might do this to help users who are confused about their options give more accurate answers. A purely random test would pick that repeated value too often and waste testing time.
|
|
50
41
|
|
|
51
|
-
|
|
42
|
+
Cons:
|
|
52
43
|
|
|
53
|
-
|
|
44
|
+
- They take more time to write.
|
|
45
|
+
- They take more work to maintain.
|
|
54
46
|
|
|
55
|
-
|
|
47
|
+
<!-- TODO: Add to docs - each `possible_values` cell should contain 1 or more possible answers. -->
|
|
56
48
|
|
|
57
|
-
|
|
49
|
+
### Hybrid
|
|
58
50
|
|
|
59
|
-
|
|
51
|
+
In future, we believe we can figure out how to let users have partially constrained tests where unspecified fields get purely random answers while constrained fields choose from a set of answers. It would need to be its own Step.
|
|
60
52
|
|
|
61
|
-
|
|
62
|
-
"rerun" and then change the name at the end of the run.
|
|
63
|
-
retry the test somehow (if needed)...
|
|
64
|
-
present the test and results to the user
|
|
53
|
+
Pros:
|
|
65
54
|
|
|
66
|
-
|
|
55
|
+
- Offers the most flexibility and control.
|
|
67
56
|
|
|
68
|
-
|
|
69
|
-
Avoid repeating tests of users (which is much(?) harder)
|
|
57
|
+
Cons:
|
|
70
58
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Create a file with a certain # of Scenarios and run it
|
|
74
|
-
- Does cucumber decide the tests it'll run at runtime or before? I think the former
|
|
75
|
-
- Is it better to split tests up into separate files if there are a lot?
|
|
76
|
-
- Is it better or worse to help the developer feel the magnitude of the #
|
|
77
|
-
of tests they're running (using Example) [Answer: worse]
|
|
78
|
-
- How to handle separate constrained random tables?
|
|
79
|
-
|
|
80
|
-
## right now
|
|
81
|
-
[r1 opt 1, r1 opt 2, r1 opt 3, name input, date input, description input]
|
|
82
|
-
|
|
83
|
-
## need for random
|
|
84
|
-
|
|
85
|
-
detect r1 while looping through the fields
|
|
86
|
-
[r1 opt 1, r1 opt 2, r1 opt 3, name input, date input, description input]
|
|
87
|
-
Con: as we loop through, we don't know the number of options at each particular instance
|
|
88
|
-
|
|
89
|
-
or make grouped fields into groups. Basically group r1:
|
|
90
|
-
[[r1 opt 1, r1 opt 2, r1 opt 3], name input, date input, description input]
|
|
91
|
-
|
|
92
|
-
## why not generate a test that uses all the constraints and thus is fully random?
|
|
93
|
-
|
|
94
|
-
Because we'd have to parse docassembled *compiled* files, which is very hard
|
|
95
|
-
|
|
96
|
-
Alternative:
|
|
97
|
-
|
|
98
|
-
When the developer runs a test, they can use a Step to have it collect all possible fields that ALKiln sees during that test run.
|
|
99
|
-
Q: How effectively can we decode the fields? Or can we make a tool that will help a developer decode their fields? (Include an input that just decodes one encoded string (`atob()`))
|
|
100
|
-
Q: If the fields are multiple choice and the developer's test picks at least one choice from that group of fields, can we use that to properly decode all the names of the fields? Hesitant to do this because some values may be encoded differently from other values (I think).
|
|
101
|
-
Q: Is this more work than just refactoring how we group fields?
|
|
59
|
+
- Might be hard to avoid confusion about test behavior.
|
|
60
|
+
- Means developing both methods of testing. That's out of scope for one PR.
|
|
@@ -56,8 +56,6 @@ const server_install = async () => {
|
|
|
56
56
|
session_vars.validateEnvironment();
|
|
57
57
|
|
|
58
58
|
// 2. Prepare logs
|
|
59
|
-
// Local devs: This will be separate from your test paths
|
|
60
|
-
// unless you pass a `path` argument into the cucumber run command
|
|
61
59
|
const argv = require(`minimist`)(process.argv.slice(2));
|
|
62
60
|
const log = new Log({ path: argv.path, context, });
|
|
63
61
|
|
package/lib/docassemble/setup.js
CHANGED
|
@@ -54,7 +54,7 @@ const setup = async () => {
|
|
|
54
54
|
+ `dainstall this package on your server. See above for more details.`;
|
|
55
55
|
log.throw({ code: `ALK0047`, context,
|
|
56
56
|
before: `☝️ ☝️ ☝️ Error data ☝️ ☝️ ☝️\n`,
|
|
57
|
-
error: `Error while installing project` },
|
|
57
|
+
error: new Error(`Error while installing project on your docassemble server during GitHub+You™ tests`) },
|
|
58
58
|
install_err_msg
|
|
59
59
|
);
|
|
60
60
|
}
|
|
@@ -68,8 +68,6 @@ const setup = async () => {
|
|
|
68
68
|
session_vars.validateEnvironment();
|
|
69
69
|
|
|
70
70
|
// 2. Prepare logs
|
|
71
|
-
// Local devs: This will be separate from your test paths
|
|
72
|
-
// unless you pass a `path` argument into the cucumber run command
|
|
73
71
|
const argv = require(`minimist`)(process.argv.slice(2));
|
|
74
72
|
const log = new Log({ path: argv.path, context, });
|
|
75
73
|
|
|
@@ -40,8 +40,8 @@ const takedown = async () => {
|
|
|
40
40
|
session_vars.validateEnvironment();
|
|
41
41
|
|
|
42
42
|
// 2. Prepare logs
|
|
43
|
-
// Local devs: This will be separate from your test artifacts paths
|
|
44
|
-
//
|
|
43
|
+
// Local devs: This will be separate from your test artifacts paths unless you
|
|
44
|
+
// pass a `path` argument into the `cucumber` run command
|
|
45
45
|
const argv = require(`minimist`)( process.argv.slice(2) );
|
|
46
46
|
const log = new Log({ path: argv.path, context, });
|
|
47
47
|
|
package/lib/globals.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
let globals = {
|
|
2
|
+
// File name prefix ALKiln can use to identify random answer generator files
|
|
3
|
+
generators_prefix: `alkiln_random`,
|
|
4
|
+
generated_files_folder_name: `_alkiln_generated`,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
module.exports = globals;
|
|
8
|
+
|
|
9
|
+
// Discuss: Would this be clearer as a config file instead? If not, change name
|
|
10
|
+
// to `constants`?
|
package/lib/run_cucumber.js
CHANGED
|
@@ -1,21 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
|
+
const fast_glob = require(`fast-glob`);
|
|
4
5
|
const path = require(`path`);
|
|
5
6
|
const cucumber_api = require('@cucumber/cucumber/api');
|
|
6
7
|
|
|
8
|
+
const globals = require(`./globals`);
|
|
7
9
|
const Log = require('./utils/Log');
|
|
8
10
|
const session_vars = require('./utils/session_vars');
|
|
11
|
+
const set_sources_paths = require(`./utils/set_sources_paths`);
|
|
9
12
|
|
|
10
13
|
const SUMMARY_FILENAME = `cucumber-report.txt`;
|
|
11
14
|
|
|
12
15
|
|
|
13
|
-
function build_config(
|
|
16
|
+
function build_config() {
|
|
17
|
+
/** Use the command line arguments to build the cucumberjs config object.
|
|
18
|
+
*
|
|
19
|
+
* @param {string?} - Arguments passed to the command line.
|
|
20
|
+
*
|
|
21
|
+
* @returns {object} - cucumberjs config object
|
|
22
|
+
* */
|
|
14
23
|
// Turn all the paths to the sources folders into paths to the feature files
|
|
15
24
|
let sources_paths = session_vars.get_sources_paths();
|
|
16
25
|
let feature_paths = [];
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
|
|
27
|
+
// Get all the paths for the right feature files (for cucumber to use)
|
|
28
|
+
for ( let sources_path of sources_paths ) {
|
|
29
|
+
let glob = fast_glob.sync(
|
|
30
|
+
`${ sources_path }/**/*.feature`,
|
|
31
|
+
// Ignore "generator" feature files that we used to generate actual tests
|
|
32
|
+
{ ignore: [`${ sources_path }/${ globals.generators_prefix }*.feature`] }
|
|
33
|
+
);
|
|
34
|
+
feature_paths.push( ...glob );
|
|
19
35
|
}
|
|
20
36
|
|
|
21
37
|
const config = {
|
|
@@ -26,9 +42,10 @@ function build_config( command_args ) {
|
|
|
26
42
|
retry: 1,
|
|
27
43
|
}
|
|
28
44
|
|
|
45
|
+
const cli_args = require(`minimist`)( process.argv.slice(2) );
|
|
29
46
|
// Normalize tag expressions, if there are any
|
|
30
47
|
// Joining and splitting handles both tags surrounded with `""` or those without `""`
|
|
31
|
-
let tags =
|
|
48
|
+
let tags = cli_args[`_`].join(` `).split(` `);
|
|
32
49
|
// If there was no tag expression, `tags` is ['']. Remove that item.
|
|
33
50
|
tags = tags.filter(function(entry) { return entry !== ""; })
|
|
34
51
|
if ( tags.length > 0 ) {
|
|
@@ -56,46 +73,12 @@ function build_config( command_args ) {
|
|
|
56
73
|
|
|
57
74
|
async function main() {
|
|
58
75
|
/** Create the cucumber config, run the tests, create and
|
|
59
|
-
*
|
|
76
|
+
* manipulate the output. */
|
|
60
77
|
|
|
61
78
|
const environment = { cwd: process.cwd() };
|
|
62
79
|
log.info({ code: `ALK0049`, context: `pre_run environment` },
|
|
63
80
|
environment
|
|
64
81
|
);
|
|
65
|
-
|
|
66
|
-
// We'll use the user info to construct the possible paths to /sources
|
|
67
|
-
let id = session_vars.get_user_id();
|
|
68
|
-
// `project` is the name in the server docassemble "Project" page
|
|
69
|
-
let project = session_vars.get_user_project_name();
|
|
70
|
-
|
|
71
|
-
// process.argv is a list of strings of commands and args.
|
|
72
|
-
// If using `npm run cucumber` (as opposed to the bin commands),
|
|
73
|
-
// use `--` before `--sources=./foo`
|
|
74
|
-
const argv = require(`minimist`)( process.argv.slice(2), {
|
|
75
|
-
default: {
|
|
76
|
-
sources: [
|
|
77
|
-
`./docassemble/*/data/sources`, // In GitHub folder
|
|
78
|
-
`/usr/share/docassemble/files/playgroundsources/${id}/${project}/*.feature`, // From playground without S3
|
|
79
|
-
`/tmp/playgroundsources/${id}/${project}/*.feature` // From playground with S3
|
|
80
|
-
],
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// Make sure `sources` is always a list
|
|
85
|
-
let sources_list = argv.sources;
|
|
86
|
-
if ( typeof argv.sources === `string` ) {
|
|
87
|
-
sources_list = [ argv.sources ];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
let one_dir_exists = session_vars.set_sources_paths( sources_list );
|
|
91
|
-
if ( !one_dir_exists ) {
|
|
92
|
-
// If we don't throw, it'll be more confusing when no tests run
|
|
93
|
-
log.throw({ code: `ALK0050`, context: `pre_run`,
|
|
94
|
-
error: new ReferenceError(
|
|
95
|
-
`ALKiln can't find your "sources" folder(s). See above for details.`
|
|
96
|
-
),
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
82
|
|
|
100
83
|
const { runConfiguration } = await cucumber_api.loadConfiguration({
|
|
101
84
|
provided: build_config( argv ),
|
|
@@ -122,9 +105,9 @@ async function main() {
|
|
|
122
105
|
|
|
123
106
|
/** Write to files.
|
|
124
107
|
*
|
|
125
|
-
* Note:
|
|
126
|
-
* authors can focus on the info we give them, which they often miss
|
|
127
|
-
* the GitHub action.
|
|
108
|
+
* Note: Avoid adding to the purely report-focused files. Those are files
|
|
109
|
+
* where authors can focus on the info we give them, which they often miss
|
|
110
|
+
* in the GitHub action.
|
|
128
111
|
*/
|
|
129
112
|
|
|
130
113
|
if ( success ) {
|
|
@@ -134,7 +117,7 @@ async function main() {
|
|
|
134
117
|
} else {
|
|
135
118
|
// Don't want to throw error here, so don't pass error object
|
|
136
119
|
log.warn({ code: `ALK0055`, context: `post_run`, },
|
|
137
|
-
`The tests ran into something unexpected.
|
|
120
|
+
`The tests ran into something unexpected. See warnings and errors above.`
|
|
138
121
|
);
|
|
139
122
|
}
|
|
140
123
|
// Custom exit code. May overlap with other systems.
|
|
@@ -142,14 +125,12 @@ async function main() {
|
|
|
142
125
|
const exitCode = success ? 0 : 50;
|
|
143
126
|
process.exitCode = exitCode;
|
|
144
127
|
|
|
145
|
-
// This is hardcoded information from `provide_config` above.
|
|
146
|
-
// This code isn't used for anything other than detecting and removing color codes.
|
|
147
128
|
let summary_file_name = SUMMARY_FILENAME;
|
|
148
129
|
fs.readFile(summary_file_name, 'utf8', (read_error, text) => {
|
|
149
130
|
if ( read_error ) {
|
|
150
131
|
// Something else will probably show an error that's more relevant
|
|
151
132
|
log.warn({ code: `ALK0056`, context: `post_run error`, },
|
|
152
|
-
`Failed to read
|
|
133
|
+
`Failed to read the report of the 3rd party library, cucumberjs. There may be more information above or below.`, read_error
|
|
153
134
|
);
|
|
154
135
|
return;
|
|
155
136
|
}
|
|
@@ -184,7 +165,13 @@ async function main() {
|
|
|
184
165
|
try {
|
|
185
166
|
fs.readFileSync(`${ log.path }/${ log.unexpected_filename }`); // Does it have contents?
|
|
186
167
|
fs.appendFileSync(`${ log.path }/${ log.unexpected_filename }`, `\n\n\n\n\n` );
|
|
187
|
-
} catch ( ignore_err ) {
|
|
168
|
+
} catch ( ignore_err ) {
|
|
169
|
+
/* Missing "unexpected results" file is ok */
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// TODO: Is this meant to write to the debug file or is this debug info
|
|
173
|
+
// really supposed to be in the "unexpected results" file? Or are these
|
|
174
|
+
// comments old?
|
|
188
175
|
|
|
189
176
|
// Add the heading for debug content
|
|
190
177
|
fs.appendFileSync( `${ log.path }/${ log.unexpected_filename }`,
|
|
@@ -198,8 +185,8 @@ async function main() {
|
|
|
198
185
|
|
|
199
186
|
} catch ( writing_cucumber_error ) {
|
|
200
187
|
let code = `ALK0182`;
|
|
201
|
-
let context = `post_run
|
|
202
|
-
let msg = `Something probably went wrong earlier.
|
|
188
|
+
let context = `post_run`
|
|
189
|
+
let msg = `Something probably went wrong earlier. See warnings and errors above. Skipped writing to ${ log.path }/${ log.debug_log_filename }.`;
|
|
203
190
|
try {
|
|
204
191
|
log.warn({ code, context, }, msg, writing_cucumber_error );
|
|
205
192
|
} catch ( all_writing_error ) {
|
|
@@ -223,10 +210,10 @@ session_vars.validateEnvironment();
|
|
|
223
210
|
// If using `npm run cucumber` (as opposed to the bin commands),
|
|
224
211
|
// use `--` before `--sources=./foo`
|
|
225
212
|
const argv = require(`minimist`)( process.argv.slice(2) );
|
|
226
|
-
// The artifacts folder might be getting created for the first
|
|
227
|
-
// time right here. The tests themselves should avoid creating
|
|
228
|
-
// their own file.
|
|
229
213
|
const log = new Log({ path: argv.path, context: `run` });
|
|
230
214
|
|
|
231
|
-
// 3.
|
|
215
|
+
// 3. Prep the sources paths to find .feature files
|
|
216
|
+
let sources_paths = set_sources_paths({ log });
|
|
217
|
+
|
|
218
|
+
// 4. run
|
|
232
219
|
main();
|
package/lib/scope.js
CHANGED
|
@@ -888,7 +888,7 @@ module.exports = {
|
|
|
888
888
|
}, // Ends scope.normalizeTable()
|
|
889
889
|
|
|
890
890
|
getAllFields: async function ( scope, { html }) {
|
|
891
|
-
|
|
891
|
+
/** Given HTML string of a page, returns a list of all the fields in it
|
|
892
892
|
* as some kind of sophisticated data? With their selectors, for example.
|
|
893
893
|
* We will choose selectors that will continue to identify the node, eve
|
|
894
894
|
* after a hidden field is revealed, etc.
|
|
@@ -1019,12 +1019,15 @@ module.exports = {
|
|
|
1019
1019
|
// button, radio, checkbox yesno, and maybe others need the value of the `value` attribute
|
|
1020
1020
|
let values = [];
|
|
1021
1021
|
if ( node.name === `select` ) {
|
|
1022
|
+
// Get possible values
|
|
1022
1023
|
values = await scope.getOptionValues( scope, { $select: $node });
|
|
1023
1024
|
}
|
|
1024
1025
|
if ( values.length === 0 && node.attribs.value ) {
|
|
1025
1026
|
// TODO: Add links to docs about da YAML values that devs never fill in themselves and
|
|
1026
1027
|
// so don't know what they are. E.g. yesnomabye -> True/False/None.
|
|
1027
1028
|
// TODO: Text inputs and textareas must ignore the value, etc. How?
|
|
1029
|
+
|
|
1030
|
+
// Get the current value for not `select` fields
|
|
1028
1031
|
values.push( node.attribs.value );
|
|
1029
1032
|
}
|
|
1030
1033
|
|
|
@@ -1387,6 +1390,14 @@ module.exports = {
|
|
|
1387
1390
|
* @param <field[n].guesses[n].trigger> {str} - var name that triggered this page
|
|
1388
1391
|
*
|
|
1389
1392
|
* @returns [ var_data[n] ]
|
|
1393
|
+
* @returns [{
|
|
1394
|
+
* table_row: var_data[n],
|
|
1395
|
+
* field: {
|
|
1396
|
+
* selector: str,
|
|
1397
|
+
* guesses: [{ values: [str] }]
|
|
1398
|
+
* }
|
|
1399
|
+
* }]
|
|
1400
|
+
* What does it return when there's no match?
|
|
1390
1401
|
*/
|
|
1391
1402
|
|
|
1392
1403
|
let rows_that_match_field_names = [];
|
|
@@ -2118,6 +2129,38 @@ module.exports = {
|
|
|
2118
2129
|
// await scope.page.mouse.move(1, 1); // Too big? Wait a while before drawing?
|
|
2119
2130
|
await scope.page.mouse.up();
|
|
2120
2131
|
},
|
|
2132
|
+
|
|
2133
|
+
get_page_id: async function ( scope ) {
|
|
2134
|
+
/** Return the current page's docassemble `question` block ID or `null`
|
|
2135
|
+
*
|
|
2136
|
+
* Note: The ID is already sanitized by docassemble.
|
|
2137
|
+
*
|
|
2138
|
+
* @param {object} scope - Tests state object
|
|
2139
|
+
* @param {object} scope.page - puppeteerjs page object
|
|
2140
|
+
*
|
|
2141
|
+
* @returns {null | string}
|
|
2142
|
+
* */
|
|
2143
|
+
let question_id_match = await scope.page.$eval(`body`, function (elem) {
|
|
2144
|
+
return elem.className.match(/question-([^ ]+)/);
|
|
2145
|
+
});
|
|
2146
|
+
if ( question_id_match !== null ) {
|
|
2147
|
+
return question_id_match[ 1 ];
|
|
2148
|
+
}
|
|
2149
|
+
return null;
|
|
2150
|
+
},
|
|
2151
|
+
|
|
2152
|
+
to_docassemble_sanitized_id: function ( risky_id ) {
|
|
2153
|
+
/** Return a version of `risky_id` that's sanitized the same way docassemble
|
|
2154
|
+
* sanitizes its IDs to turn them into HTML class names in python:
|
|
2155
|
+
* re.sub(r'[^A-Za-z0-9]+', '-', interview_status.question.id.lower())
|
|
2156
|
+
* (reference needed)
|
|
2157
|
+
*
|
|
2158
|
+
* @param {string} risky_id - The un-sanitized id
|
|
2159
|
+
*
|
|
2160
|
+
* @returns {string} - Sanitized id
|
|
2161
|
+
* */
|
|
2162
|
+
return risky_id.toLowerCase().replace(/[^A-Za-z0-9]+/g, '-');
|
|
2163
|
+
},
|
|
2121
2164
|
|
|
2122
2165
|
examinePageID: async function ( scope, question_id = '' ) {
|
|
2123
2166
|
/* Looks for a sanitized version of the question id as it's written
|