@suffolklitlab/alkiln 5.0.0-pre-9 → 5.0.0-pre-10
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 +4 -1
- package/docs/story_tables.md +89 -0
- package/lib/scope.js +169 -57
- package/lib/steps.js +7 -3
- package/package.json +1 -1
- package/tests/unit_tests/html.fixtures.js +0 -26
- package/tests/unit_tests/normalizeTable.test.js +2 -2
- package/tests/unit_tests/tables.fixtures.js +10 -244
- package/tests/unit_tests/ensureSpecialRows.test.js +0 -34
- package/tests/unit_tests/special_rows.fixtures.js +0 -101
package/CHANGELOG.md
CHANGED
|
@@ -45,7 +45,8 @@ Format:
|
|
|
45
45
|
be run in any directory, not just in an npm package.
|
|
46
46
|
- Additional environment variables and their validation to allow for tests that run on a developer's server/Playground instead of through GitHub. Also, other functionality for that purpose. [Issue #661](https://github.com/SuffolkLITLab/ALKiln/issues/661)
|
|
47
47
|
- Tests for new session_vars behavior and improve previous tests.
|
|
48
|
-
- Adds `npm-shrinkwrap.json`, so installs from npm will have fixed version dependencies
|
|
48
|
+
- Adds `npm-shrinkwrap.json`, so installs from npm will have fixed version dependencies.
|
|
49
|
+
- Allow author to specify loops with only `.target_number`. e.g. to leave out `.there_are_any`. See [issue #706](https://github.com/SuffolkLITLab/ALKiln/issues/706) and Story Table documentation in docs folder.
|
|
49
50
|
|
|
50
51
|
### Changed
|
|
51
52
|
- BREAKING: the github action no longer runs `npm run XYZ`; it directly calls scripts,
|
|
@@ -66,6 +67,8 @@ Format:
|
|
|
66
67
|
- Updated field decoding to handle new object field encoding. See [#711](https://github.com/SuffolkLITLab/ALKiln/issues/711)
|
|
67
68
|
- Allow multiple languages to be tested again. See [#713](https://github.com/SuffolkLITLab/ALKiln/issues/713).
|
|
68
69
|
- Fill in time fields correctly. See [#726](https://github.com/SuffolkLITLab/ALKiln/pull/726).
|
|
70
|
+
- Allow `.target_number` to be 0. See https://github.com/SuffolkLITLab/ALKiln/issues/706.
|
|
71
|
+
- Use the right number of loops for `.target_number`. See https://github.com/SuffolkLITLab/ALKiln/issues/706.
|
|
69
72
|
|
|
70
73
|
### Security
|
|
71
74
|
- Pass docassemble API keys through HTTP headers instead of as parameters.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Story Tables overview
|
|
2
|
+
|
|
3
|
+
The Story Table is a very flexible tool. It lets the author answer interview (form) questions in any order. They can make cosmetic changes to their interview code without having to change their tests. They can edit the order of their interview's pages, move fields from one page to another, and so on.
|
|
4
|
+
|
|
5
|
+
When the robot arrives on each interview page, it finds all the fields on the page and loops through the whole Story Table trying to match rows with fields.
|
|
6
|
+
|
|
7
|
+
Here's some [documentation on the use of Story Table rows](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/#story-tables).
|
|
8
|
+
|
|
9
|
+
## Tracking use of rows
|
|
10
|
+
|
|
11
|
+
Why do we track how many times each Story Table row was used to set a form's fields?
|
|
12
|
+
|
|
13
|
+
First, it lets us give the author useful information. We can show the author which rows of their story table were or weren't used. This can help the author tell that some crucial variables aren't being set as they expected. It also outputs a new Story Table for them. If they had a bunch of extra rows adding cruft, like from our [story table generator](https://plocket.github.io/al_story/), it gives the author a table with only the variables that are needed for the test.
|
|
14
|
+
|
|
15
|
+
Second, in some cases we need to actually throw an error if any row was left unused, so we have to keep track of that.
|
|
16
|
+
|
|
17
|
+
Third, looping questions that gather multiple items in a list are tricky when the interview answers need the `.there_is_another` attribute. We can't use that attribute. Instead, we handle all gathering with `target_number` rows. Next we'll discuss what problem the `.there_is_another` attribute creates and our approach to solving it.
|
|
18
|
+
|
|
19
|
+
## The loop problem
|
|
20
|
+
|
|
21
|
+
Since the Story Table loops through all its rows for each page, each row in the table needs to be unique. That is, you can only set one variable to one value. That's fine most of the time, but when gathering items in a list in a docassemble interview, there are a few ways to do it and one of them sets one variable over and over again - `.there_is_another`. This kind of loop can start with a question that sets `.there_are_any`.
|
|
22
|
+
|
|
23
|
+
For example, look for `.there_is_another` in the scenario below:
|
|
24
|
+
|
|
25
|
+
Page 1: Do you have any moms? (Set `moms.there_are_any` to `True`)
|
|
26
|
+
Page 2: What is your first mom's name? (Set `mom[0].name.first`)
|
|
27
|
+
Page 3: Do you have any other moms? (Set `moms.there_is_another` to `True`)
|
|
28
|
+
Page 4: What is your second mom's name? (Set `mom[1].name.first`)
|
|
29
|
+
Page 5: Do you have any other moms? (Set `moms.there_is_another` to `False`)
|
|
30
|
+
|
|
31
|
+
See that on page 3, `moms.there_is_another` is set to `True` and on page 5 it's set to `False`. We can't represent this in a Story Table.
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
| x.there_is_another | True | moms.there_is_another |
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The above will always say that we have another `mom`. If we allowed our users to do that, it would create an infinite loop. So we prevent our users from creating a `there_is_another` row that has a value of `True`. We still need to allow them to set `.there_is_another`, though, so what do we do?
|
|
38
|
+
|
|
39
|
+
## The loop solution
|
|
40
|
+
|
|
41
|
+
Well, there's another way to loop to gather items in docassemble - `target_number`. For example:
|
|
42
|
+
|
|
43
|
+
Page 1: How many moms do you have? (Set `moms.target_number` to `2`)
|
|
44
|
+
Page 2: What is your first mom's name? (Set `mom[0].name.first`)
|
|
45
|
+
Page 3: What is your second mom's name? (Set `mom[1].name.first`)
|
|
46
|
+
|
|
47
|
+
The target number row looks like this:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
| x.target_number | 2 | moms.target_number |
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
If authors use `.target_number`, that's no problem at all for our code. We can't require authors to use that method to ask their questions, though. That may make their form questions confusing to their end users sometimes. Fortunately, we _can_ require developers to use `.target_number`[^1] in their Story Tables. We can use a `.target_number` table value to calculate what to answer for `.there_are_any` and to know how many times to set `.there_is_another` to `'True'`.
|
|
54
|
+
|
|
55
|
+
## The loop architecture
|
|
56
|
+
|
|
57
|
+
First, the very basic flow for setting a variable. Note: whatever Step the dev uses to set a variable's value, we send it to this loop. It may be useful to follow along in the code here.
|
|
58
|
+
|
|
59
|
+
1. If this isn't a Story Table step, transform the given arguments into a Story Table structure - a list of row objects.
|
|
60
|
+
2. For each row object (see `scope.normalizeTable()`)
|
|
61
|
+
1. Create a new object with almost the same properties. It contains the original object in a property called `original` so the original can be used to print stuff for the user's report.
|
|
62
|
+
2. In this new object, substitute environment variables the author gave into actual values. They may have done this to keep a sensitive value (like a password) secret.
|
|
63
|
+
3. Add ways to accumulate the number of times the row is used.
|
|
64
|
+
3. For each page (see `scope.setFields()`)
|
|
65
|
+
1. Identify and store all the fields on the page and all the variables they set.
|
|
66
|
+
2. Shallowly clone the Story Table row list.
|
|
67
|
+
1. Create a new object almost the same as the old object
|
|
68
|
+
2. Print a warning to the user if they've created a `.there_is_another` row with a value of `'True'` and change that value to `'False'`.
|
|
69
|
+
3. If there is a `.target_number` row, use it to create new artificial rows for `.there_is_another` and `.there_are_any`.
|
|
70
|
+
4. This new list will be used to actually put answers into fields.
|
|
71
|
+
3. For each field on the page
|
|
72
|
+
1. Find which Story Table row, if any, matches that field.
|
|
73
|
+
2. Set the field to the given value.
|
|
74
|
+
3. Increment the row's accumulators.
|
|
75
|
+
1. Increment the relevant `.used_for['foo']` by 1.
|
|
76
|
+
2. Increment all relevant `.times_used`[^2] by 1.
|
|
77
|
+
4. Try to continue.
|
|
78
|
+
|
|
79
|
+
<!-- From feedback, I'm not sure how to clarify this part or if readers will need it
|
|
80
|
+
Why make artificial rows? Well, that way when we loop through to match a page's fields to the variables in the Story Table structure, we don't have to do anything special for these rows. If they're not needed, they won't be triggered.
|
|
81
|
+
|
|
82
|
+
Why not calculate the `.there_are_any` and `.there_is_another` values in the row-matching loop? That's another stylistic choice. There's more logic to the `.there_is_another` value than we've talked about here and that method seems like it would add a lot more cruft and complexity in that match-finding loop. We may eventually change our minds.
|
|
83
|
+
-->
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
[^1] Why use `target_number`? We count on our users being at least a bit familiar with docassemble variables and to have access to the docassemble docs, so the `target_number` variable is something they should be able to recognize or easily find information about. We decided to take advantage of that. Also, we chose to avoid 0 indexing so that the number starts at 1 and feels like human language, not computer language as our users are generally more human than computer. It's also the way docassemble does it.
|
|
88
|
+
|
|
89
|
+
[^2] It would seem like the `.times_used` property is redundant, but it has a different purpose that the `.used_for` properties. It lets us show the dev whether the `target_number` table row was used at all during the test to let us give appropriate feedback to the user. We could add up the `used_for` values, but for now we're avoiding doing that extra math and the extra refactoring. The choice has its pros and cons.
|
package/lib/scope.js
CHANGED
|
@@ -919,15 +919,21 @@ module.exports = {
|
|
|
919
919
|
}
|
|
920
920
|
|
|
921
921
|
let result = {
|
|
922
|
-
|
|
922
|
+
// NOTE: mutating frozen objects will fail silently, so don't mutate this attribute
|
|
923
|
+
original: Object.freeze(row),
|
|
924
|
+
artificial: false,
|
|
923
925
|
// Give fake data if needed to prevent some kind of hidden input field from being found
|
|
924
926
|
// See bug at https://github.com/plocket/docassemble-cucumber/issues/79
|
|
925
927
|
// May be able to remove this once converted to `getAllFields`
|
|
926
928
|
var: row.var || 'al_no_var_name',
|
|
927
929
|
value: actual_var_value,
|
|
928
930
|
trigger: row.trigger || '',
|
|
931
|
+
// This will track how many times total the row or its connected
|
|
932
|
+
// artificial rows are used. Artificial rows are only added for some rows.
|
|
929
933
|
times_used: 0,
|
|
934
|
+
used_for: {},
|
|
930
935
|
}
|
|
936
|
+
result.used_for[ result.var ] = 0;
|
|
931
937
|
|
|
932
938
|
supported_table.push( result );
|
|
933
939
|
}
|
|
@@ -2024,8 +2030,7 @@ module.exports = {
|
|
|
2024
2030
|
});
|
|
2025
2031
|
|
|
2026
2032
|
// Accumulate number of times this row was used. Esp important for `.target_number`
|
|
2027
|
-
|
|
2028
|
-
row_used.times_used += 1;
|
|
2033
|
+
await scope.increment_row_use( scope, { row: row_used });
|
|
2029
2034
|
|
|
2030
2035
|
process.stdout.write(`\x1b[36m${ '*' }\x1b[0m`); // assumes var was set if no error occurred
|
|
2031
2036
|
|
|
@@ -2050,8 +2055,7 @@ module.exports = {
|
|
|
2050
2055
|
});
|
|
2051
2056
|
|
|
2052
2057
|
// Esp important for `.target_number`
|
|
2053
|
-
|
|
2054
|
-
row_used.times_used += 1;
|
|
2058
|
+
await scope.increment_row_use( scope, { row: row_used });
|
|
2055
2059
|
|
|
2056
2060
|
// TODO: special continue/navigation button function that handles url and all?
|
|
2057
2061
|
|
|
@@ -2138,35 +2142,145 @@ module.exports = {
|
|
|
2138
2142
|
// TODO: Maybe we should return something else or something in addition about used vars
|
|
2139
2143
|
}, // Ends scope.setFields()
|
|
2140
2144
|
|
|
2141
|
-
|
|
2145
|
+
increment_row_use: async function ( scope, { row }) {
|
|
2146
|
+
/** Accumulate number of times this row was used. Esp important for `.target_number`.
|
|
2147
|
+
* Mutates `row` and its props. */
|
|
2148
|
+
|
|
2149
|
+
// Increment the number of times a row itself was used.
|
|
2150
|
+
row.times_used += 1;
|
|
2151
|
+
|
|
2152
|
+
// Increment the times a row was used for a particular variable.
|
|
2153
|
+
// TODO: check if we need this initial default assignment.
|
|
2154
|
+
if ( !row.used_for ) { row.used_for = {}; }
|
|
2155
|
+
// TODO: check if we need this initial default assignment.
|
|
2156
|
+
if ( row.used_for[ row.var ] ) {
|
|
2157
|
+
row.used_for[ row.var ] += 1;
|
|
2158
|
+
} else {
|
|
2159
|
+
row.used_for[ row.var ] = 1;
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
// If this is an artificial row, increment its source row as well. This may
|
|
2163
|
+
// be one of multiple artificial rows and the source row will need to be
|
|
2164
|
+
// incremented for each of its artificial rows.
|
|
2165
|
+
// Right now, this is only used for `target_number` rows.
|
|
2166
|
+
if ( row.source ) {
|
|
2167
|
+
// For each artificial row, increment the total number of times its
|
|
2168
|
+
// source row was used.
|
|
2169
|
+
row.source.times_used += 1;
|
|
2170
|
+
|
|
2171
|
+
// Indicate which of the artificial rows was incremented (this one).
|
|
2172
|
+
// TODO: check if we need this initial default assignment.
|
|
2173
|
+
if ( row.source.used_for[ row.var ] ) {
|
|
2174
|
+
row.source.used_for[ row.var ] += 1;
|
|
2175
|
+
} else {
|
|
2176
|
+
row.source.used_for[ row.var ] = 1;
|
|
2177
|
+
}
|
|
2178
|
+
} // ends if there's a source row
|
|
2179
|
+
|
|
2180
|
+
}, // Ends scope.increment_row_use()
|
|
2181
|
+
|
|
2182
|
+
|
|
2142
2183
|
ensureSpecialRows: async function ( scope, { var_data, from_story_table=true }) {
|
|
2143
2184
|
/* Given a list of variable data objects, add more objects or mutate
|
|
2144
|
-
*
|
|
2145
|
-
* - `.target_number`
|
|
2146
|
-
*
|
|
2185
|
+
* row objects under special circumstances:
|
|
2186
|
+
* - `.target_number` will create `.there_are_any` and `.there_is_another` rows.
|
|
2187
|
+
* If they're unused in the end, that's fine.
|
|
2188
|
+
* - `.there_is_another` with a value of True in a Story Table is invalid and
|
|
2189
|
+
* will be neutralized.
|
|
2190
|
+
*
|
|
2191
|
+
* To learn the bigger picture, see "docs/story_tables.md"
|
|
2147
2192
|
*/
|
|
2148
|
-
// Add special cases. Do not mutate `var_data` as a list, but
|
|
2149
|
-
// do allow mutation of items inside.
|
|
2150
2193
|
let enhanced_var_data = [...var_data];
|
|
2151
2194
|
for ( let var_datum of var_data ) {
|
|
2152
2195
|
|
|
2153
|
-
// Add a `.there_is_another` var if it might be needed.
|
|
2154
|
-
// If it's not used in the end, that's fine.
|
|
2155
|
-
// `.target_number` is sometimes a stand-in for `.there_is_another`.
|
|
2156
|
-
// That seems the clearest way to communicate to developers about what they
|
|
2157
|
-
// should write in the test. We will attempt to write more about
|
|
2158
|
-
// the rationale in the documentation at some point.
|
|
2159
2196
|
if ( from_story_table && var_datum.var.match( /\.target_number$/ )) {
|
|
2160
|
-
|
|
2161
|
-
|
|
2197
|
+
|
|
2198
|
+
// This is a row created by us, not by the user
|
|
2199
|
+
let artificial = true;
|
|
2200
|
+
|
|
2201
|
+
// Add `.there_are_any` value depending on `.target_number` value.
|
|
2202
|
+
// If the dev has also created a `.there_are_any` row, that will come
|
|
2203
|
+
// first in the table and will be overriden by this later artificial row.
|
|
2204
|
+
// TODO: Document this for devs as this behavior might confuse folks
|
|
2205
|
+
// who accidentally include both and have conflicting values
|
|
2206
|
+
// for each of them.
|
|
2207
|
+
let var_name_any = var_datum.var.replace( /\.target_number$/, `.there_are_any` );
|
|
2208
|
+
|
|
2209
|
+
let there_are_any = {
|
|
2210
|
+
artificial,
|
|
2211
|
+
// For now this is very nested. We may flatten in the future.
|
|
2212
|
+
...var_datum,
|
|
2213
|
+
// `source` is acting as the accumulator of the row use here
|
|
2214
|
+
source: var_datum,
|
|
2215
|
+
var: var_name_any,
|
|
2216
|
+
// devs are supposed to use the right `trigger` name, but they might not.
|
|
2217
|
+
// Of course, this too might be the wrong trigger name, but there's no way to
|
|
2218
|
+
// know that and it's unlikely.
|
|
2219
|
+
// TODO: Make sure this is documented.
|
|
2220
|
+
trigger: var_datum.trigger.replace( /\.target_number$/, `.there_are_any` ),
|
|
2221
|
+
used_for: {},
|
|
2222
|
+
};
|
|
2223
|
+
there_are_any.used_for[ var_name_any ] = 0;
|
|
2224
|
+
|
|
2225
|
+
// This creation function is run at the beginning of every page.
|
|
2226
|
+
// If this is the first page and the `target_number` source row
|
|
2227
|
+
// has just been created, it won't have this sub-accumulator yet,
|
|
2228
|
+
// so create them. Otherwise, don't override its current value.
|
|
2229
|
+
if ( var_datum.used_for[ var_name_any ] === undefined ) {
|
|
2230
|
+
var_datum.used_for[ var_name_any ] = 0;
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
// If `target_number` is 0, there are none of this item
|
|
2234
|
+
if ( parseInt(var_datum.value) <= 0 ) {
|
|
2235
|
+
there_are_any.value = `False`;
|
|
2236
|
+
// Otherwise, there's at least one item
|
|
2237
|
+
} else if ( parseInt(var_datum.value) > 0 ) {
|
|
2238
|
+
there_are_any.value = `True`;
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
// Now both `.target_number` and `.there_are_any` rows exist if needed
|
|
2242
|
+
enhanced_var_data.push( there_are_any );
|
|
2243
|
+
|
|
2244
|
+
// Add `.there_is_another` value depending on `.target_number` value.
|
|
2245
|
+
// If the dev has also created a `.there_is_another` row, that will come
|
|
2246
|
+
// first in the table and will be overriden by this later artificial row.
|
|
2247
|
+
// The dev will still get a warning at the start of each page that they've
|
|
2248
|
+
// included an invalid `.there_is_another` with a value of `True`.
|
|
2249
|
+
// This row might not be needed if `.target_number` is 0, but for now that
|
|
2250
|
+
// seems like it would add more logic to read.
|
|
2251
|
+
let var_name_another = var_datum.var.replace( /\.target_number$/, `.there_is_another` );
|
|
2252
|
+
|
|
2253
|
+
let there_is_another = {
|
|
2254
|
+
artificial,
|
|
2255
|
+
...var_datum,
|
|
2256
|
+
// `source` is acting as the accumulator of the row use here
|
|
2162
2257
|
source: var_datum,
|
|
2163
|
-
var:
|
|
2164
|
-
|
|
2165
|
-
//
|
|
2258
|
+
var: var_name_another,
|
|
2259
|
+
// devs are supposed to use the right `trigger` name, but they might not.
|
|
2260
|
+
// Of course, this too might be the wrong trigger name, but there's no way to
|
|
2261
|
+
// know that and it's unlikely.
|
|
2166
2262
|
trigger: var_datum.trigger.replace( /\.target_number$/, `.there_is_another` ),
|
|
2263
|
+
used_for: {},
|
|
2167
2264
|
};
|
|
2265
|
+
there_are_any.used_for[ var_name_another ] = 0;
|
|
2266
|
+
|
|
2267
|
+
// This creation function is run at the beginning of every
|
|
2268
|
+
// page. If this is the first page and the `target_number`
|
|
2269
|
+
// source row has just been created, it won't have these
|
|
2270
|
+
// sub-accumulators yet, so create them.
|
|
2271
|
+
// Otherwise, don't override their current values.
|
|
2272
|
+
if ( var_datum.used_for[ var_name_another ] === undefined ) {
|
|
2273
|
+
var_datum.used_for[ var_name_another ] = 0;
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
// Use those accumulators to get `True` or `False` for `there_is_another`
|
|
2277
|
+
there_is_another.value = await scope.getThereIsAnotherValue(
|
|
2278
|
+
scope,
|
|
2279
|
+
{ row: there_is_another, from_story_table }
|
|
2280
|
+
);
|
|
2168
2281
|
|
|
2169
|
-
|
|
2282
|
+
// Now both `.target_number` and `.there_is_another` rows exist if needed
|
|
2283
|
+
enhanced_var_data.push( there_is_another );
|
|
2170
2284
|
|
|
2171
2285
|
// Neutralize a `.there_is_another` row in a story table
|
|
2172
2286
|
} else if ( from_story_table && var_datum.var.match( /\.there_is_another$/ ) && var_datum.value !== `False`) {
|
|
@@ -2181,8 +2295,8 @@ module.exports = {
|
|
|
2181
2295
|
+ `that list. This test will now set this row's \`value\` to \`False\`. See `
|
|
2182
2296
|
+ `https://suffolklitlab.github.io/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing#there_is_another. `
|
|
2183
2297
|
+ `The row data is\n${ JSON.stringify( var_datum.original )}` });
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2298
|
+
} // ends if it's a `target_number` row
|
|
2299
|
+
} // ends for each row
|
|
2186
2300
|
|
|
2187
2301
|
return enhanced_var_data;
|
|
2188
2302
|
}, // Ends scope.ensureSpecialRows()
|
|
@@ -2190,23 +2304,16 @@ module.exports = {
|
|
|
2190
2304
|
getThereIsAnotherValue: async function ( scope, { row, from_story_table=true }) {
|
|
2191
2305
|
/* Given story row-like data as well as whether this is from a table row,
|
|
2192
2306
|
* return the best guess as to a safe value to set for this round. Give
|
|
2193
|
-
* the developer a warning if
|
|
2194
|
-
* it's an inappropriate value. Avoid mutation.
|
|
2195
|
-
*
|
|
2196
|
-
* WARNING: Remember to decrement the actual value later.
|
|
2307
|
+
* the developer a warning if it's an inappropriate value. Avoid mutation.
|
|
2197
2308
|
*
|
|
2198
|
-
*
|
|
2199
|
-
*
|
|
2200
|
-
* Sometimes this will be from `.there_is_another`. We think we've ensured
|
|
2201
|
-
* that it will only come from Steps and should be `True` or `False`.
|
|
2202
|
-
* This feels precarious, but I can't see another permutation.
|
|
2309
|
+
* Right now this argument should always be a `.target_number` number row from
|
|
2310
|
+
* Story Tables.
|
|
2203
2311
|
*
|
|
2204
|
-
*
|
|
2205
|
-
* normalization.
|
|
2312
|
+
* To learn the bigger picture, see the docs folder for info about Story Tables.
|
|
2206
2313
|
*/
|
|
2207
|
-
let value = row.value.toLowerCase();
|
|
2314
|
+
let value = row.source.value.toLowerCase();
|
|
2208
2315
|
let val_to_print = printable_var_value(row);
|
|
2209
|
-
let
|
|
2316
|
+
let desired_num = parseInt( value );
|
|
2210
2317
|
|
|
2211
2318
|
// Step stuff. Take into account a lot of possible values
|
|
2212
2319
|
if ( value.match( /(false|no|unchecked|uncheck|deselected|deselect)/i )) {
|
|
@@ -2214,35 +2321,36 @@ module.exports = {
|
|
|
2214
2321
|
return `False`;
|
|
2215
2322
|
|
|
2216
2323
|
} else if ( value.match( /(true|yes|checked|check|selected|select)/i )) {
|
|
2217
|
-
if ( from_story_table ) {
|
|
2324
|
+
if ( from_story_table && !row.artificial ) {
|
|
2218
2325
|
if ( row.var.match( /\.there_is_another$/ ) ) {
|
|
2219
|
-
// `.there_is_another` should never get in here, but just in case
|
|
2220
2326
|
await scope.addToReport( scope, { type: `warning`, value: `The attribute \`.there_is_another\` is invalid in story table tests. Replace it with \`.target_number\` in your \`var\` column. Set the \`value\` to the number of items in that list. This test will now set this row's \`value\` to \`False\`. See https://suffolklitlab.github.io/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing#there_is_another-loop. The row data is\n${ JSON.stringify( row.original )}` });
|
|
2221
2327
|
} else {
|
|
2222
2328
|
// If `target_number` is set to True instead of a number
|
|
2223
|
-
// Since secret vars can't be passed in a table, this should never be a secret var
|
|
2224
2329
|
await scope.addToReport( scope, { type: `warning`, value: `${ val_to_print } value is not a valid value for ${ row.var } here. This test will default to \`False\` to avoid problems.` });
|
|
2225
2330
|
}
|
|
2226
2331
|
return `False`;
|
|
2227
2332
|
} else { return `True`; }
|
|
2228
|
-
|
|
2229
|
-
// Story table
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
} else if ( row.times_used >= int_val - 1 ) { // Story table loop will end
|
|
2233
|
-
return `False`;
|
|
2234
|
-
|
|
2235
|
-
} else if ( row.source && row.source.times_used >= int_val - 1 ) { // Just in case use changes a tad
|
|
2236
|
-
return `False`;
|
|
2237
|
-
|
|
2238
|
-
} else if ( isNaN( parseInt( value )) ) { // Loop will end
|
|
2239
|
-
// I can't figure out how to trigger this warning either
|
|
2333
|
+
|
|
2334
|
+
} else if ( isNaN( desired_num )) { // Story table loop will end
|
|
2335
|
+
// I can't figure out how to trigger this warning, so maybe we're
|
|
2336
|
+
// already handling everything we need to
|
|
2240
2337
|
await scope.addToReport( scope, { type: `warning`, value: `${ val_to_print } value is not a valid value for ${ row.var } here. This test will default to \`False\` to avoid problems.` });
|
|
2241
2338
|
return `False`;
|
|
2242
2339
|
|
|
2243
|
-
|
|
2340
|
+
// Check that the number of elements in the list hits our desired number.
|
|
2341
|
+
// The first time `.there_is_another` is used, it's always the second item
|
|
2342
|
+
// the form is asking about (and so forth), so we have to add 1 to this check.
|
|
2343
|
+
|
|
2344
|
+
// Example behavior:
|
|
2345
|
+
// For 3 items, press 'yes' on there_is_another 2 times because
|
|
2346
|
+
// `.there_are_any` handles the first time.
|
|
2347
|
+
// target_number: 3
|
|
2348
|
+
// used_for[ <var_name>.target_number ]: 0 -> `True`, 1 -> `True`, 2 -> `False`
|
|
2349
|
+
} else if ( row.source && row.source.used_for[ row.var ] + 1 >= desired_num) { // Story table loop will end
|
|
2350
|
+
return `False`;
|
|
2351
|
+
} else {
|
|
2244
2352
|
return `True`;
|
|
2245
|
-
}
|
|
2353
|
+
} // ends act depending values
|
|
2246
2354
|
}, // Ends scope.getThereIsAnotherValue()
|
|
2247
2355
|
|
|
2248
2356
|
// throw_if_no_interview_content()?
|
|
@@ -2302,9 +2410,13 @@ module.exports = {
|
|
|
2302
2410
|
let was_found = false;
|
|
2303
2411
|
for ( let error_key in possible_errors ) {
|
|
2304
2412
|
if ( possible_errors[ error_key ][0] ) {
|
|
2305
|
-
// If
|
|
2413
|
+
// If there's a page visible (locally), give us as much time to examine it as timeout allows.
|
|
2414
|
+
// Because of previous actions, this may still be a bit too long.
|
|
2415
|
+
// Also, if you're debugging and you close the window early, the process will still
|
|
2306
2416
|
// take this long to exit after the tests finish. Not sure how to account for that.
|
|
2307
|
-
if ( session_vars.get_debug() ) {
|
|
2417
|
+
if ( session_vars.get_debug() ) {
|
|
2418
|
+
await scope.page.waitForTimeout( ( scope.timeout - (1 * 1000)) );
|
|
2419
|
+
}
|
|
2308
2420
|
was_found = true;
|
|
2309
2421
|
error_handlers[ error_key ] = possible_errors[ error_key ][0];
|
|
2310
2422
|
}
|
package/lib/steps.js
CHANGED
|
@@ -341,6 +341,11 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
|
|
|
341
341
|
var_data: supported_table,
|
|
342
342
|
from_story_table: true,
|
|
343
343
|
});
|
|
344
|
+
|
|
345
|
+
if ( session_vars.get_debug() ) {
|
|
346
|
+
console.log( `Rows, including special rows:`, JSON.stringify( ensured_var_data ));
|
|
347
|
+
}
|
|
348
|
+
|
|
344
349
|
// Some errors happen in there to be closer to the relevant code. We'd have to try/catch anyway.
|
|
345
350
|
let error = await scope.setFields(scope, { var_data: ensured_var_data, id, ensure_navigation: true });
|
|
346
351
|
if ( error.was_found ) { await scope.throwPageError( scope, { id: id }); }
|
|
@@ -1104,8 +1109,6 @@ After(async function(scenario) {
|
|
|
1104
1109
|
if (scenario.result.status !== `PASSED`) {
|
|
1105
1110
|
if ( session_vars.get_debug() ) {
|
|
1106
1111
|
console.log( `Non-passed status occurred while running test:`, scenario.result.status );
|
|
1107
|
-
// If there's a page visible (locally), give us time to examine it
|
|
1108
|
-
if ( scope.page ) { await scope.page.waitForTimeout( 60 * 1000 ); }
|
|
1109
1112
|
}
|
|
1110
1113
|
|
|
1111
1114
|
if ( scope.page ) {
|
|
@@ -1151,6 +1154,7 @@ After(async function(scenario) {
|
|
|
1151
1154
|
// If the status was as expected, pass this criteria
|
|
1152
1155
|
scenario.result.status = `PASSED`;
|
|
1153
1156
|
}
|
|
1157
|
+
|
|
1154
1158
|
if ( scope.expected_in_report && scope.expected_in_report.length > 0) {
|
|
1155
1159
|
let expected = scope.expected_in_report;
|
|
1156
1160
|
// Reset report values no matter what so they don't mess up future scenarios
|
|
@@ -1160,7 +1164,7 @@ After(async function(scenario) {
|
|
|
1160
1164
|
if ( all_were_included ) { scenario.result.status = `PASSED`; }
|
|
1161
1165
|
else { scenario.result.status = `FAILED`; }
|
|
1162
1166
|
}
|
|
1163
|
-
|
|
1167
|
+
|
|
1164
1168
|
if (scope.expected_not_in_report && scope.expected_not_in_report.length > 0) {
|
|
1165
1169
|
let not_expected = scope.expected_not_in_report;
|
|
1166
1170
|
// Reset report values no matter what so they don't mess up future scenarios
|
package/package.json
CHANGED
|
@@ -831,30 +831,4 @@ html.mixed_quotes = `
|
|
|
831
831
|
</div>`;
|
|
832
832
|
|
|
833
833
|
|
|
834
|
-
// // ============================
|
|
835
|
-
// // `.there_is_another`
|
|
836
|
-
// // ============================
|
|
837
|
-
// html.there_is_another = `
|
|
838
|
-
// <div id="daquestion" aria-labelledby="dapagetitle" role="main" class="tab-pane fade show active offset-xl-3 offset-lg-3 col-xl-6 col-lg-6 offset-md-2 col-md-8">
|
|
839
|
-
// <div data-variable="cHJveHlfbGlzdC50aGVyZV9pc19hbm90aGVy" id="trigger" aria-hidden="true" style="display: none;"></div>
|
|
840
|
-
// <form aria-labelledby="daMainQuestion" action="/interview?i=docassemble.playground12ALTestingTestsNewDOM%3Aall_tests.yml" id="daform" method="POST" novalidate="novalidate">
|
|
841
|
-
// <div class="da-page-header">
|
|
842
|
-
// <h1 class="h3" id="daMainQuestion">Is there another proxy var?</h1>
|
|
843
|
-
// <div class="daclear"></div>
|
|
844
|
-
// </div>
|
|
845
|
-
// <fieldset class="da-button-set da-field-yesno">
|
|
846
|
-
// <legend class="visually-hidden">Press one of the following buttons:</legend>
|
|
847
|
-
// <button class="btn btn-primary btn-da" name="eC50aGVyZV9pc19hbm90aGVy" type="submit" value="True">Yes</button>
|
|
848
|
-
// <button class="btn btn-secondary btn-da" name="eC50aGVyZV9pc19hbm90aGVy" type="submit" value="False">No</button>
|
|
849
|
-
// </fieldset>
|
|
850
|
-
// <input type="hidden" name="csrf_token" value="IjdmYTE5MzE3ZjY5OTE0Y2MzMGQwYzVkNjJmZDViZjcyMDM4MDZmMWIi.YZ5KUQ.2ObHJubASqN4YqSlhzwDhdZxaoQ">
|
|
851
|
-
// <input type="hidden" name="_event" value="WyJwcm94eV9saXN0LnRoZXJlX2lzX2Fub3RoZXIiXQ">
|
|
852
|
-
// <input type="hidden" name="_question_name" value="ID is there another generic">
|
|
853
|
-
// <input type="hidden" name="_tracker" value="19">
|
|
854
|
-
// <input type="hidden" name="_datatypes" value="eyJlQzUwYUdWeVpWOXBjMTloYm05MGFHVnkiOiAiYm9vbGVhbiJ9">
|
|
855
|
-
// <input type="hidden" name="_visible" value="">
|
|
856
|
-
// </form>
|
|
857
|
-
// </div>`;
|
|
858
|
-
|
|
859
|
-
|
|
860
834
|
module.exports = html;
|
|
@@ -17,6 +17,6 @@ it("normalizes a table with standard formatting correctly", async function() {
|
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
it("normalizes a table with missng `trigger` column correctly", async function() {
|
|
20
|
-
let result = await normalizeTable( scope, { var_data: tables.
|
|
21
|
-
expect( result ).to.deep.equal( tables.
|
|
20
|
+
let result = await normalizeTable( scope, { var_data: tables.missing_trigger });
|
|
21
|
+
expect( result ).to.deep.equal( tables.missing_trigger_to_normalized_formatting );
|
|
22
22
|
});
|
|
@@ -94,265 +94,39 @@ tables.buttons_event_action = [
|
|
|
94
94
|
tables.standard_formatting = [
|
|
95
95
|
{ "var": "direct_showifs", "value": "True", "trigger": "" },
|
|
96
96
|
{ "var": "button_continue", "value": "True", "trigger": "" },
|
|
97
|
-
{ "var": "buttons_other", "value": "button_2", "trigger": "" },
|
|
98
|
-
{ "var": "buttons_yesnomaybe", "value": "True", "trigger": "" },
|
|
99
|
-
{ "var": "checkboxes_other", "value": "checkbox_other_opt_1", "trigger": "" },
|
|
100
|
-
{ "var": "checkboxes_other", "value": "checkbox_other_opt_2", "trigger": "" },
|
|
101
|
-
{ "var": "checkboxes_other", "value": "checkbox_other_opt_3", "trigger": "" },
|
|
102
|
-
{ "var": "checkboxes_yesno", "value": "True", "trigger": "" },
|
|
103
|
-
{ "var": "direct_standard_fields", "value": "True", "trigger": "" },
|
|
104
|
-
{ "var": "dropdown_test", "value": "dropdown_opt_2", "trigger": "" },
|
|
105
|
-
{ "var": "radio_other", "value": "radio_other_opt_3", "trigger": "" },
|
|
106
|
-
{ "var": "radio_yesno", "value": "False", "trigger": "" },
|
|
107
|
-
{ "var": "screen_features", "value": "True", "trigger": "" },
|
|
108
|
-
{ "var": "showif_checkbox_yesno", "value": "False", "trigger": "" },
|
|
109
|
-
{ "var": "showif_checkboxes_other['showif_checkboxes_nota_1']", "value": "false", "trigger": "" },
|
|
110
|
-
{ "var": "showif_checkboxes_other['showif_checkboxes_nota_2']", "value": "true", "trigger": "" },
|
|
111
|
-
{ "var": "showif_dropdown", "value": "showif_dropdown_1", "trigger": "" },
|
|
112
|
-
{ "var": "showif_radio_other", "value": "showif_radio_multi_2", "trigger": "" },
|
|
113
|
-
{ "var": "showif_text_input", "value": "Show if text input value", "trigger": "" },
|
|
114
|
-
{ "var": "showif_textarea", "value": "Show if\nmultiline text\narea value", "trigger": "" },
|
|
115
|
-
{ "var": "showif_yesnoradio", "value": "True", "trigger": "" },
|
|
116
|
-
{ "var": "text_input", "value": "Regular text input field value", "trigger": "" },
|
|
117
|
-
{ "var": "textarea", "value": "Multiline text\narea value", "trigger": "" },
|
|
118
|
-
{ "var": "show_3", "value": "True", "trigger": "" },
|
|
119
|
-
{ "var": "show_2", "value": "True", "trigger": "" },
|
|
120
97
|
];
|
|
121
98
|
|
|
122
99
|
// Should not be changed at all
|
|
123
100
|
tables.standard_normalized_formatting = [
|
|
124
101
|
{
|
|
125
102
|
"original": { "var": "direct_showifs", "value": "True", "trigger": "" },
|
|
126
|
-
"var": "direct_showifs", "value": "True", "trigger": "", times_used: 0,
|
|
103
|
+
"var": "direct_showifs", "value": "True", "trigger": "", "times_used": 0,
|
|
104
|
+
"used_for": { "direct_showifs": 0 }, "artificial": false,
|
|
127
105
|
},
|
|
128
106
|
{
|
|
129
107
|
"original": { "var": "button_continue", "value": "True", "trigger": "" },
|
|
130
|
-
"var": "button_continue", "value": "True", "trigger": "", times_used: 0,
|
|
131
|
-
|
|
132
|
-
{
|
|
133
|
-
"original": { "var": "buttons_other", "value": "button_2", "trigger": "" },
|
|
134
|
-
"var": "buttons_other", "value": "button_2", "trigger": "", times_used: 0,
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
"original": { "var": "buttons_yesnomaybe", "value": "True", "trigger": "" },
|
|
138
|
-
"var": "buttons_yesnomaybe", "value": "True", "trigger": "", times_used: 0,
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
"original": { "var": "checkboxes_other", "value": "checkbox_other_opt_1", "trigger": "" },
|
|
142
|
-
"var": "checkboxes_other", "value": "checkbox_other_opt_1", "trigger": "", times_used: 0,
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
"original": { "var": "checkboxes_other", "value": "checkbox_other_opt_2", "trigger": "" },
|
|
146
|
-
"var": "checkboxes_other", "value": "checkbox_other_opt_2", "trigger": "", times_used: 0,
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
"original": { "var": "checkboxes_other", "value": "checkbox_other_opt_3", "trigger": "" },
|
|
150
|
-
"var": "checkboxes_other", "value": "checkbox_other_opt_3", "trigger": "", times_used: 0,
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
"original": { "var": "checkboxes_yesno", "value": "True", "trigger": "" },
|
|
154
|
-
"var": "checkboxes_yesno", "value": "True", "trigger": "", times_used: 0,
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
"original": { "var": "direct_standard_fields", "value": "True", "trigger": "" },
|
|
158
|
-
"var": "direct_standard_fields", "value": "True", "trigger": "", times_used: 0,
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
"original": { "var": "dropdown_test", "value": "dropdown_opt_2", "trigger": "" },
|
|
162
|
-
"var": "dropdown_test", "value": "dropdown_opt_2", "trigger": "", times_used: 0,
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
"original": { "var": "radio_other", "value": "radio_other_opt_3", "trigger": "" },
|
|
166
|
-
"var": "radio_other", "value": "radio_other_opt_3", "trigger": "", times_used: 0,
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
"original": { "var": "radio_yesno", "value": "False", "trigger": "" },
|
|
170
|
-
"var": "radio_yesno", "value": "False", "trigger": "", times_used: 0,
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
"original": { "var": "screen_features", "value": "True", "trigger": "" },
|
|
174
|
-
"var": "screen_features", "value": "True", "trigger": "", times_used: 0,
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
"original": { "var": "showif_checkbox_yesno", "value": "False", "trigger": "" },
|
|
178
|
-
"var": "showif_checkbox_yesno", "value": "False", "trigger": "", times_used: 0,
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
"original": { "var": "showif_checkboxes_other['showif_checkboxes_nota_1']", "value": "false", "trigger": "" },
|
|
182
|
-
"var": "showif_checkboxes_other['showif_checkboxes_nota_1']", "value": "false", "trigger": "", times_used: 0,
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
"original": { "var": "showif_checkboxes_other['showif_checkboxes_nota_2']", "value": "true", "trigger": "" },
|
|
186
|
-
"var": "showif_checkboxes_other['showif_checkboxes_nota_2']", "value": "true", "trigger": "", times_used: 0,
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
"original": { "var": "showif_dropdown", "value": "showif_dropdown_1", "trigger": "" },
|
|
190
|
-
"var": "showif_dropdown", "value": "showif_dropdown_1", "trigger": "", times_used: 0,
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
"original": { "var": "showif_radio_other", "value": "showif_radio_multi_2", "trigger": "" },
|
|
194
|
-
"var": "showif_radio_other", "value": "showif_radio_multi_2", "trigger": "", times_used: 0,
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
"original": { "var": "showif_text_input", "value": "Show if text input value", "trigger": "" },
|
|
198
|
-
"var": "showif_text_input", "value": "Show if text input value", "trigger": "", times_used: 0,
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
"original": { "var": "showif_textarea", "value": "Show if\nmultiline text\narea value", "trigger": "" },
|
|
202
|
-
"var": "showif_textarea", "value": "Show if\nmultiline text\narea value", "trigger": "", times_used: 0,
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
"original": { "var": "showif_yesnoradio", "value": "True", "trigger": "" },
|
|
206
|
-
"var": "showif_yesnoradio", "value": "True", "trigger": "", times_used: 0,
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
"original": { "var": "text_input", "value": "Regular text input field value", "trigger": "" },
|
|
210
|
-
"var": "text_input", "value": "Regular text input field value", "trigger": "", times_used: 0,
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
"original": { "var": "textarea", "value": "Multiline text\narea value", "trigger": "" },
|
|
214
|
-
"var": "textarea", "value": "Multiline text\narea value", "trigger": "", times_used: 0,
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
"original": { "var": "show_3", "value": "True", "trigger": "" },
|
|
218
|
-
"var": "show_3", "value": "True", "trigger": "", times_used: 0,
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
"original": { "var": "show_2", "value": "True", "trigger": "" },
|
|
222
|
-
"var": "show_2", "value": "True", "trigger": "", times_used: 0,
|
|
108
|
+
"var": "button_continue", "value": "True", "trigger": "", "times_used": 0,
|
|
109
|
+
"used_for": { "button_continue": 0 }, "artificial": false,
|
|
223
110
|
},
|
|
224
111
|
];
|
|
225
112
|
|
|
226
113
|
// Missing 'trigger' column
|
|
227
|
-
tables.
|
|
114
|
+
tables.missing_trigger = [
|
|
228
115
|
{ "var": "direct_showifs", "value": "True" },
|
|
229
116
|
{ "var": "button_continue", "value": "True" },
|
|
230
|
-
{ "var": "buttons_other", "value": "button_2" },
|
|
231
|
-
{ "var": "buttons_yesnomaybe", "value": "True" },
|
|
232
|
-
{ "var": "checkboxes_other", "value": "checkbox_other_opt_1" },
|
|
233
|
-
{ "var": "checkboxes_other", "value": "checkbox_other_opt_2" },
|
|
234
|
-
{ "var": "checkboxes_other", "value": "checkbox_other_opt_3" },
|
|
235
|
-
{ "var": "checkboxes_yesno", "value": "True" },
|
|
236
|
-
{ "var": "direct_standard_fields", "value": "True" },
|
|
237
|
-
{ "var": "dropdown_test", "value": "dropdown_opt_2" },
|
|
238
|
-
{ "var": "radio_other", "value": "radio_other_opt_3" },
|
|
239
|
-
{ "var": "radio_yesno", "value": "False" },
|
|
240
|
-
{ "var": "screen_features", "value": "True" },
|
|
241
|
-
{ "var": "showif_checkbox_yesno", "value": "False" },
|
|
242
|
-
{ "var": "showif_checkboxes_other['showif_checkboxes_nota_1']", "value": "false" },
|
|
243
|
-
{ "var": "showif_checkboxes_other['showif_checkboxes_nota_2']", "value": "true" },
|
|
244
|
-
{ "var": "showif_dropdown", "value": "showif_dropdown_1" },
|
|
245
|
-
{ "var": "showif_radio_other", "value": "showif_radio_multi_2" },
|
|
246
|
-
{ "var": "showif_text_input", "value": "Show if text input value" },
|
|
247
|
-
{ "var": "showif_textarea", "value": "Show if\nmultiline text\narea value" },
|
|
248
|
-
{ "var": "showif_yesnoradio", "value": "True" },
|
|
249
|
-
{ "var": "text_input", "value": "Regular text input field value" },
|
|
250
|
-
{ "var": "textarea", "value": "Multiline text\narea value" },
|
|
251
|
-
{ "var": "show_3", "value": "True" },
|
|
252
|
-
{ "var": "show_2", "value": "True" },
|
|
253
117
|
];
|
|
254
118
|
|
|
255
119
|
// 'trigger' should be added
|
|
256
|
-
tables.
|
|
120
|
+
tables.missing_trigger_to_normalized_formatting = [
|
|
257
121
|
{
|
|
258
122
|
"original": { "var": "direct_showifs", "value": "True", },
|
|
259
|
-
"var": "direct_showifs", "value": "True", "trigger": "", times_used: 0,
|
|
123
|
+
"var": "direct_showifs", "value": "True", "trigger": "", "times_used": 0,
|
|
124
|
+
"used_for": { "direct_showifs": 0 }, "artificial": false,
|
|
260
125
|
},
|
|
261
126
|
{
|
|
262
127
|
"original": { "var": "button_continue", "value": "True", },
|
|
263
|
-
"var": "button_continue", "value": "True", "trigger": "", times_used: 0,
|
|
264
|
-
|
|
265
|
-
{
|
|
266
|
-
"original": { "var": "buttons_other", "value": "button_2", },
|
|
267
|
-
"var": "buttons_other", "value": "button_2", "trigger": "", times_used: 0,
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
"original": { "var": "buttons_yesnomaybe", "value": "True", },
|
|
271
|
-
"var": "buttons_yesnomaybe", "value": "True", "trigger": "", times_used: 0,
|
|
272
|
-
},
|
|
273
|
-
{
|
|
274
|
-
"original": { "var": "checkboxes_other", "value": "checkbox_other_opt_1", },
|
|
275
|
-
"var": "checkboxes_other", "value": "checkbox_other_opt_1", "trigger": "", times_used: 0,
|
|
276
|
-
},
|
|
277
|
-
{
|
|
278
|
-
"original": { "var": "checkboxes_other", "value": "checkbox_other_opt_2", },
|
|
279
|
-
"var": "checkboxes_other", "value": "checkbox_other_opt_2", "trigger": "", times_used: 0,
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
"original": { "var": "checkboxes_other", "value": "checkbox_other_opt_3", },
|
|
283
|
-
"var": "checkboxes_other", "value": "checkbox_other_opt_3", "trigger": "", times_used: 0,
|
|
284
|
-
},
|
|
285
|
-
{
|
|
286
|
-
"original": { "var": "checkboxes_yesno", "value": "True", },
|
|
287
|
-
"var": "checkboxes_yesno", "value": "True", "trigger": "", times_used: 0,
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
"original": { "var": "direct_standard_fields", "value": "True", },
|
|
291
|
-
"var": "direct_standard_fields", "value": "True", "trigger": "", times_used: 0,
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
"original": { "var": "dropdown_test", "value": "dropdown_opt_2", },
|
|
295
|
-
"var": "dropdown_test", "value": "dropdown_opt_2", "trigger": "", times_used: 0,
|
|
296
|
-
},
|
|
297
|
-
{
|
|
298
|
-
"original": { "var": "radio_other", "value": "radio_other_opt_3", },
|
|
299
|
-
"var": "radio_other", "value": "radio_other_opt_3", "trigger": "", times_used: 0,
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
"original": { "var": "radio_yesno", "value": "False", },
|
|
303
|
-
"var": "radio_yesno", "value": "False", "trigger": "", times_used: 0,
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
"original": { "var": "screen_features", "value": "True", },
|
|
307
|
-
"var": "screen_features", "value": "True", "trigger": "", times_used: 0,
|
|
308
|
-
},
|
|
309
|
-
{
|
|
310
|
-
"original": { "var": "showif_checkbox_yesno", "value": "False", },
|
|
311
|
-
"var": "showif_checkbox_yesno", "value": "False", "trigger": "", times_used: 0,
|
|
312
|
-
},
|
|
313
|
-
{
|
|
314
|
-
"original": { "var": "showif_checkboxes_other['showif_checkboxes_nota_1']", "value": "false", },
|
|
315
|
-
"var": "showif_checkboxes_other['showif_checkboxes_nota_1']", "value": "false", "trigger": "", times_used: 0,
|
|
316
|
-
},
|
|
317
|
-
{
|
|
318
|
-
"original": { "var": "showif_checkboxes_other['showif_checkboxes_nota_2']", "value": "true", },
|
|
319
|
-
"var": "showif_checkboxes_other['showif_checkboxes_nota_2']", "value": "true", "trigger": "", times_used: 0,
|
|
320
|
-
},
|
|
321
|
-
{
|
|
322
|
-
"original": { "var": "showif_dropdown", "value": "showif_dropdown_1", },
|
|
323
|
-
"var": "showif_dropdown", "value": "showif_dropdown_1", "trigger": "", times_used: 0,
|
|
324
|
-
},
|
|
325
|
-
{
|
|
326
|
-
"original": { "var": "showif_radio_other", "value": "showif_radio_multi_2", },
|
|
327
|
-
"var": "showif_radio_other", "value": "showif_radio_multi_2", "trigger": "", times_used: 0,
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
"original": { "var": "showif_text_input", "value": "Show if text input value", },
|
|
331
|
-
"var": "showif_text_input", "value": "Show if text input value", "trigger": "", times_used: 0,
|
|
332
|
-
},
|
|
333
|
-
{
|
|
334
|
-
"original": { "var": "showif_textarea", "value": "Show if\nmultiline text\narea value", },
|
|
335
|
-
"var": "showif_textarea", "value": "Show if\nmultiline text\narea value", "trigger": "", times_used: 0,
|
|
336
|
-
},
|
|
337
|
-
{
|
|
338
|
-
"original": { "var": "showif_yesnoradio", "value": "True", },
|
|
339
|
-
"var": "showif_yesnoradio", "value": "True", "trigger": "", times_used: 0,
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
"original": { "var": "text_input", "value": "Regular text input field value", },
|
|
343
|
-
"var": "text_input", "value": "Regular text input field value", "trigger": "", times_used: 0,
|
|
344
|
-
},
|
|
345
|
-
{
|
|
346
|
-
"original": { "var": "textarea", "value": "Multiline text\narea value", },
|
|
347
|
-
"var": "textarea", "value": "Multiline text\narea value", "trigger": "", times_used: 0,
|
|
348
|
-
},
|
|
349
|
-
{
|
|
350
|
-
"original": { "var": "show_3", "value": "True", },
|
|
351
|
-
"var": "show_3", "value": "True", "trigger": "", times_used: 0,
|
|
352
|
-
},
|
|
353
|
-
{
|
|
354
|
-
"original": { "var": "show_2", "value": "True", },
|
|
355
|
-
"var": "show_2", "value": "True", "trigger": "", times_used: 0,
|
|
128
|
+
"var": "button_continue", "value": "True", "trigger": "", "times_used": 0,
|
|
129
|
+
"used_for": { "button_continue": 0 }, "artificial": false,
|
|
356
130
|
},
|
|
357
131
|
];
|
|
358
132
|
|
|
@@ -418,14 +192,6 @@ tables.object_dropdown = [
|
|
|
418
192
|
];
|
|
419
193
|
|
|
420
194
|
|
|
421
|
-
// ============================
|
|
422
|
-
// `.there_is_another`
|
|
423
|
-
// ============================
|
|
424
|
-
tables.there_is_another = [
|
|
425
|
-
{ "var": "x.there_is_another", "value": "True", "trigger": "proxy_list.there_is_another" }
|
|
426
|
-
];
|
|
427
|
-
|
|
428
|
-
|
|
429
195
|
// ============================
|
|
430
196
|
// mixed quotes
|
|
431
197
|
// ============================
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
const chai = require('chai');
|
|
2
|
-
const expect = chai.expect;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const special_rows = require('./special_rows.fixtures.js');
|
|
6
|
-
const scope = require('../../lib/scope.js');
|
|
7
|
-
const ensureSpecialRows = scope.ensureSpecialRows;
|
|
8
|
-
|
|
9
|
-
// TODO: Add tests for reports
|
|
10
|
-
|
|
11
|
-
// ============================
|
|
12
|
-
// `.target_number`
|
|
13
|
-
// ============================
|
|
14
|
-
describe("For a `.target_number` === 3 story table row", async function() {
|
|
15
|
-
it( `returns the right rows for the 1st \`.there_is_another\` question`, async function() {
|
|
16
|
-
let result = await ensureSpecialRows( scope, { var_data: special_rows.target_num_first_input, from_table: true });
|
|
17
|
-
expect( result ).to.deep.equal( special_rows.target_num_first_output );
|
|
18
|
-
});
|
|
19
|
-
it( `returns the right rows for the 2nd \`.there_is_another\` question`, async function() {
|
|
20
|
-
let result = await ensureSpecialRows( scope, { var_data: special_rows.target_num_half_way_input, from_table: true });
|
|
21
|
-
expect( result ).to.deep.equal( special_rows.target_num_half_way_output );
|
|
22
|
-
});
|
|
23
|
-
it( `returns the right rows for the 3rd \`.there_is_another\` question`, async function() {
|
|
24
|
-
let result = await ensureSpecialRows( scope, { var_data: special_rows.target_num_last_input, from_table: true });
|
|
25
|
-
expect( result ).to.deep.equal( special_rows.target_num_last_output );
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
describe(`For a \`.target_number\` trigger column`, async function() {
|
|
30
|
-
it( `it preserves the trigger`, async function() {
|
|
31
|
-
let result = await ensureSpecialRows( scope, { var_data: special_rows.target_number_trigger_input, from_table: true });
|
|
32
|
-
expect( result ).to.deep.equal( special_rows.target_number_trigger_output );
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
let special_rows = {};
|
|
2
|
-
|
|
3
|
-
// Includes `.there_is_another` and `.target_nubmer`.
|
|
4
|
-
// In the future, maybe some false positives/negatives.
|
|
5
|
-
|
|
6
|
-
// ==============================
|
|
7
|
-
// `.target_number`
|
|
8
|
-
// ==============================
|
|
9
|
-
// 3 `users`, 2 `parents`
|
|
10
|
-
|
|
11
|
-
// 1st press (Yes)
|
|
12
|
-
special_rows.target_num_first_input = [
|
|
13
|
-
{
|
|
14
|
-
"original": { "var": "x.target_number", "value": "2", "trigger": "parents.there_is_another" },
|
|
15
|
-
"var": "x.target_number", "value": "2", "trigger": "parents.there_is_another", times_used: 0,
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"original": { "var": "x.target_number", "value": "3", "trigger": "users.there_is_another" },
|
|
19
|
-
"var": "x.target_number", "value": "3", "trigger": "users.there_is_another", times_used: 0,
|
|
20
|
-
},
|
|
21
|
-
];
|
|
22
|
-
// Same as input plus`.there_is_another` rows
|
|
23
|
-
special_rows.target_num_first_output = [ ...special_rows.target_num_first_input ];
|
|
24
|
-
special_rows.target_num_first_output.push({
|
|
25
|
-
"artificial": true, "source": special_rows.target_num_first_input[0],
|
|
26
|
-
"var": "x.there_is_another", "value": "True", "trigger": "parents.there_is_another",
|
|
27
|
-
});
|
|
28
|
-
special_rows.target_num_first_output.push( {
|
|
29
|
-
"artificial": true, "source": special_rows.target_num_first_input[1],
|
|
30
|
-
"var": "x.there_is_another", "value": "True", "trigger": "users.there_is_another",
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// 2nd press (Yes and No)
|
|
34
|
-
special_rows.target_num_half_way_input = [
|
|
35
|
-
{
|
|
36
|
-
"original": { "var": "x.target_number", "value": "2", "trigger": "parents.there_is_another" },
|
|
37
|
-
"var": "x.target_number", "value": "2", "trigger": "parents.there_is_another", times_used: 1,
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"original": { "var": "x.target_number", "value": "3", "trigger": "users.there_is_another" },
|
|
41
|
-
"var": "x.target_number", "value": "3", "trigger": "users.there_is_another", times_used: 1,
|
|
42
|
-
},
|
|
43
|
-
];
|
|
44
|
-
// Same as input plus `.there_is_another` rows
|
|
45
|
-
special_rows.target_num_half_way_output = [ ...special_rows.target_num_half_way_input ];
|
|
46
|
-
special_rows.target_num_half_way_output.push({
|
|
47
|
-
"var": "x.there_is_another", "value": "False", "trigger": "parents.there_is_another",
|
|
48
|
-
"artificial": true, "source": special_rows.target_num_half_way_input[0],
|
|
49
|
-
});
|
|
50
|
-
special_rows.target_num_half_way_output.push( {
|
|
51
|
-
"var": "x.there_is_another", "value": "True", "trigger": "users.there_is_another",
|
|
52
|
-
"artificial": true, "source": special_rows.target_num_half_way_input[1],
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// 3rd press (No and No)
|
|
56
|
-
special_rows.target_num_last_input = [
|
|
57
|
-
{
|
|
58
|
-
"original": { "var": "x.target_number", "value": "2", "trigger": "parents.there_is_another" },
|
|
59
|
-
"var": "x.target_number", "value": "2", "trigger": "parents.there_is_another", times_used: 2,
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"original": { "var": "x.target_number", "value": "3", "trigger": "users.there_is_another" },
|
|
63
|
-
"var": "x.target_number", "value": "3", "trigger": "users.there_is_another", times_used: 2,
|
|
64
|
-
},
|
|
65
|
-
];
|
|
66
|
-
// Same as input plus `.there_is_another` rows
|
|
67
|
-
special_rows.target_num_last_output = [ ...special_rows.target_num_last_input ];
|
|
68
|
-
special_rows.target_num_last_output.push({
|
|
69
|
-
"var": "x.there_is_another", "value": "False", "trigger": "parents.there_is_another",
|
|
70
|
-
"artificial": true, "source": special_rows.target_num_last_input[0],
|
|
71
|
-
});
|
|
72
|
-
special_rows.target_num_last_output.push( {
|
|
73
|
-
"var": "x.there_is_another", "value": "False", "trigger": "users.there_is_another",
|
|
74
|
-
"artificial": true, "source": special_rows.target_num_last_input[1],
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// trigger uses `.target_number`
|
|
78
|
-
// devs are supposed to use the right `trigger` name, but they might not
|
|
79
|
-
special_rows.target_number_trigger_input = [
|
|
80
|
-
{
|
|
81
|
-
"original": { "var": "x.target_number", "value": "2", "trigger": "parents.target_number" },
|
|
82
|
-
"var": "x.target_number", "value": "2", "trigger": "parents.target_number", times_used: 0,
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
"original": { "var": "x.target_number", "value": "3", "trigger": "users.target_number" },
|
|
86
|
-
"var": "x.target_number", "value": "3", "trigger": "users.target_number", times_used: 0,
|
|
87
|
-
},
|
|
88
|
-
];
|
|
89
|
-
// Same as input plus`.there_is_another` rows
|
|
90
|
-
special_rows.target_number_trigger_output = [ ...special_rows.target_number_trigger_input ];
|
|
91
|
-
special_rows.target_number_trigger_output.push({
|
|
92
|
-
"artificial": true, "source": special_rows.target_number_trigger_input[0],
|
|
93
|
-
"var": "x.there_is_another", "value": "True", "trigger": "parents.there_is_another",
|
|
94
|
-
});
|
|
95
|
-
special_rows.target_number_trigger_output.push( {
|
|
96
|
-
"artificial": true, "source": special_rows.target_number_trigger_input[1],
|
|
97
|
-
"var": "x.there_is_another", "value": "True", "trigger": "users.there_is_another",
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
module.exports = special_rows;
|