@suffolklitlab/alkiln 4.9.3 → 4.10.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/.github/workflows/test_tests.yml +3 -3
- package/CHANGELOG.md +15 -0
- package/action.yml +2 -2
- package/lib/scope.js +138 -91
- package/lib/steps.js +28 -0
- package/lib/utils/files.js +2 -2
- package/package.json +2 -1
- package/tests/features/interactive_steps.feature +32 -0
- package/tests/features/reports.feature +14 -3
- package/tests/unit_tests/fields.fixtures.js +79 -103
- package/tests/unit_tests/getAllFields.test.js +18 -13
- package/tests/unit_tests/getSafeScenarioFilename.test.js +2 -2
- package/tests/unit_tests/html.fixtures.js +0 -1
|
@@ -42,8 +42,8 @@ jobs:
|
|
|
42
42
|
- name: Set name of artifact folder with date and UTC time
|
|
43
43
|
# https://www.shell-tips.com/linux/how-to-format-date-and-time-in-linux-macos-and-bash/#how-to-format-a-date-in-bash
|
|
44
44
|
run: |
|
|
45
|
-
echo "ARTIFACT_NAME=
|
|
46
|
-
echo "UNIT_TESTS_ARTIFACT_NAME=_alkiln_tests_misc-$(date +'%Y-%m-%d at %
|
|
45
|
+
echo "ARTIFACT_NAME=alkiln-$(date +'%Y-%m-%d at %Hh%Mm%Ss' -u) UTC" >> $GITHUB_ENV
|
|
46
|
+
echo "UNIT_TESTS_ARTIFACT_NAME=_alkiln_tests_misc-$(date +'%Y-%m-%d at %Hh%Mm%Ss' -u) UTC" >> $GITHUB_ENV
|
|
47
47
|
- name: Set languages
|
|
48
48
|
run: echo "EXTRA_LANGUAGES=${{ github.event.inputs.extra_languages }}" >> $GITHUB_ENV
|
|
49
49
|
if: ${{ github.event.inputs.extra_languages != '' }}
|
|
@@ -71,7 +71,7 @@ jobs:
|
|
|
71
71
|
if: ${{ always() }}
|
|
72
72
|
with:
|
|
73
73
|
name: ${{ env.ARTIFACT_NAME }}
|
|
74
|
-
path: ./
|
|
74
|
+
path: ./alkiln-*
|
|
75
75
|
# Delete Playground Project and project name file
|
|
76
76
|
- name: Cleanup
|
|
77
77
|
if: ${{ always() }}
|
package/CHANGELOG.md
CHANGED
|
@@ -40,6 +40,21 @@ Format:
|
|
|
40
40
|
-->
|
|
41
41
|
<!-- ## [Unreleased] -->
|
|
42
42
|
|
|
43
|
+
## [4.10.0] - 2022-11-17
|
|
44
|
+
### Changed
|
|
45
|
+
- Don't mark `...there_in_another | False |` in story tables as invalid, as it's necessary for some
|
|
46
|
+
workflows (see https://github.com/SuffolkLITLab/ALKiln/pull/580 for a longer discussion).
|
|
47
|
+
- Explicitly **not** documented, as we don't want to encourage people to use it if it's not
|
|
48
|
+
necessary for their interviews.
|
|
49
|
+
- continuing between screens will no longer press button with the `btn-primary` class. This means
|
|
50
|
+
that it won't press "Exit", or "Restart" buttons, to avoid getting in an infinite loop.
|
|
51
|
+
- Shorten test report and artifact filenames, attempting to have fewer files whose whole paths are
|
|
52
|
+
longer than 260 characters. (See https://github.com/SuffolkLITLab/ALKiln/pull/626 for details).
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
- Corrects the month in the artifact folder timestamp; was printing things like `81` for September
|
|
56
|
+
instead of `09`, because of a `+` being interpreted as Javascript string concatenation and not math.
|
|
57
|
+
|
|
43
58
|
## [4.9.3] - 2022-09-07
|
|
44
59
|
### Fixed
|
|
45
60
|
- `retry` of failed tests not implemented for developers. See https://github.com/SuffolkLITLab/ALKiln/issues/601.
|
package/action.yml
CHANGED
|
@@ -43,7 +43,7 @@ runs:
|
|
|
43
43
|
# Human-readable date/time:
|
|
44
44
|
# https://www.shell-tips.com/linux/how-to-format-date-and-time-in-linux-macos-and-bash/#how-to-format-a-date-in-bash
|
|
45
45
|
run: |
|
|
46
|
-
echo "ARTIFACT_NAME=
|
|
46
|
+
echo "ARTIFACT_NAME=alkiln-$(date +'%Y-%m-%d at %Hh%Mm%Ss' -u) UTC" >> $GITHUB_ENV
|
|
47
47
|
if ${{ github.event.inputs.extra_languages != '' }}
|
|
48
48
|
then
|
|
49
49
|
echo "Manually set languages: ${{ github.event.inputs.extra_languages }}"
|
|
@@ -128,7 +128,7 @@ runs:
|
|
|
128
128
|
uses: actions/upload-artifact@v2
|
|
129
129
|
with:
|
|
130
130
|
name: ${{ env.ARTIFACT_NAME }}
|
|
131
|
-
path: ./
|
|
131
|
+
path: ./alkiln-*
|
|
132
132
|
|
|
133
133
|
# ##################################
|
|
134
134
|
# # A developer's workflow should look similar to the below
|
package/lib/scope.js
CHANGED
|
@@ -54,7 +54,33 @@ let is_secret_var = function(row) {
|
|
|
54
54
|
return original.flags !== undefined && original.flags.secret_name !== undefined;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
let fromBase64 = function(base64_str) {
|
|
58
|
+
/* Takes a base64 encoded string and returns it decoded.
|
|
59
|
+
* Note that this can take strings with invalid base 64 characters,
|
|
60
|
+
* but will change those characters to `/`s instead.
|
|
61
|
+
*/
|
|
62
|
+
let buffer = Buffer.from( base64_str, 'base64' );
|
|
63
|
+
return buffer.toString( `utf-8` );
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let toBase64 = function ( utf8_str, exclude_padding=true) {
|
|
67
|
+
/* Encoded a given string (@param utf8_str) in base64.
|
|
68
|
+
* @param exclude_padding {boolean} - if true, strips off `=` characters
|
|
69
|
+
* that are added as padding while base64 encoding (docassemble doesn't
|
|
70
|
+
* include those characters, and uses the UTF-7 base64 encoding
|
|
71
|
+
* (RFC-2152)). For more details, see
|
|
72
|
+
* https://github.com/nodejs/node/issues/6107#issuecomment-207177131
|
|
73
|
+
*/
|
|
74
|
+
if ( typeof( utf8_str ) !== `string` ) { // Prevent an error
|
|
75
|
+
return `Will+Never+Match+Existing+DOM+Node==`;
|
|
76
|
+
}
|
|
77
|
+
let buffer = Buffer.from( utf8_str, 'utf-8' );
|
|
78
|
+
let base64str = buffer.toString( `base64` )
|
|
79
|
+
return exclude_padding ? base64str.replace(/=/g, '') : base64str;
|
|
80
|
+
}
|
|
81
|
+
|
|
57
82
|
let proxies_regex = /(\bx\.)|(\bx\[)|(\[[ijklmn]\])/g;
|
|
83
|
+
let da_base64_regex = /^[A-Za-z0-9+/]+$/;
|
|
58
84
|
let misc_artifacts_dir = `_alkiln_test-misc_artifacts`;
|
|
59
85
|
|
|
60
86
|
module.exports = {
|
|
@@ -620,7 +646,7 @@ module.exports = {
|
|
|
620
646
|
* - Birthdate fields that will be showing up relatively soon
|
|
621
647
|
* - Time fields?
|
|
622
648
|
*/
|
|
623
|
-
let base64_var_name =
|
|
649
|
+
let base64_var_name = toBase64( var_name );
|
|
624
650
|
|
|
625
651
|
// Will catch some radio and checkbox inputs too, which will then be rejected
|
|
626
652
|
// Note: I think `showif`s with `code` don't use `data-saveas`
|
|
@@ -659,12 +685,14 @@ module.exports = {
|
|
|
659
685
|
return to_manipulate;
|
|
660
686
|
}, // Ends scope.setVar()
|
|
661
687
|
|
|
688
|
+
continue_button_selector: `fieldset.da-field-buttons button[type="submit"].btn-primary`,
|
|
689
|
+
signature_selector: `fieldset .dasigsave`,
|
|
662
690
|
continue: async function ( scope ) {
|
|
663
691
|
/* Presses whatever button it finds that might lead to the next page. */
|
|
664
692
|
// Any selectors I find seem somewhat precarious.
|
|
665
693
|
let elem = await Promise.race([
|
|
666
|
-
scope.page.waitForSelector(
|
|
667
|
-
scope.page.waitForSelector(
|
|
694
|
+
scope.page.waitForSelector(scope.continue_button_selector), // other pages (this is the most consistent way)
|
|
695
|
+
scope.page.waitForSelector(scope.signature_selector), // signature page
|
|
668
696
|
]);
|
|
669
697
|
await elem.evaluate( el => { return el.className });
|
|
670
698
|
// Waits for navigation or user error
|
|
@@ -675,15 +703,16 @@ module.exports = {
|
|
|
675
703
|
/** Return true if a button that will allow continuing appears
|
|
676
704
|
* on this page. Otherwise return false. */
|
|
677
705
|
|
|
678
|
-
//
|
|
706
|
+
// Will only find this with "Continue buttons":
|
|
707
|
+
// * An actual continue button that doesn't set a variable
|
|
708
|
+
// <button class="btn btn-da btn-primary" type="submit">Continue</button>
|
|
709
|
+
// * One that sets a variable:
|
|
710
|
+
// <button type="submit" class="btn btn-da btn-primary" name="Zm9v" value="True">Continue</button>
|
|
711
|
+
// It will not find event screen `buttons:`, like an exit button or a restart button
|
|
679
712
|
// <button type="submit" class="btn btn-da btn-danger" name="X211bHRpcGxlX2Nob2ljZQ" value="0">exit</button>
|
|
680
|
-
// An actual continue button that doesn't set a variable
|
|
681
|
-
// <button class="btn btn-da btn-primary" type="submit">Continue</button>
|
|
682
|
-
// One that sets a variable:
|
|
683
|
-
// <button type="submit" class="btn btn-da btn-primary" name="Zm9v" value="True">Continue</button>
|
|
684
713
|
// `buttons:` can be used in question blocks as choices
|
|
685
|
-
let regular = await scope.page.$(
|
|
686
|
-
let signature = await scope.page.$(
|
|
714
|
+
let regular = await scope.page.$(scope.continue_button_selector);
|
|
715
|
+
let signature = await scope.page.$(scope.signature_selector);
|
|
687
716
|
|
|
688
717
|
return regular !== null || signature !== null;
|
|
689
718
|
|
|
@@ -972,9 +1001,9 @@ module.exports = {
|
|
|
972
1001
|
if ( !encoded_trigger_var ) {
|
|
973
1002
|
// TODO: Add documentation and then link to it in this message.
|
|
974
1003
|
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.` });
|
|
975
|
-
encoded_trigger_var =
|
|
1004
|
+
encoded_trigger_var = toBase64( `None` );
|
|
976
1005
|
}
|
|
977
|
-
let trigger =
|
|
1006
|
+
let trigger = fromBase64( encoded_trigger_var );
|
|
978
1007
|
|
|
979
1008
|
|
|
980
1009
|
// Some `show if` fields have `names` like '_field_n' instead of their var name. Ex: _field_1
|
|
@@ -1120,7 +1149,18 @@ module.exports = {
|
|
|
1120
1149
|
// Inputs like textareas don't have values
|
|
1121
1150
|
if ( values.length > 0 ) {
|
|
1122
1151
|
for ( let value of values ){
|
|
1123
|
-
possible_table_rows.push({ var: var_name, value, });
|
|
1152
|
+
possible_table_rows.push({ var: var_name, value: value, });
|
|
1153
|
+
// Ignore blanks, and values that certainly aren't base64 encoded like True and False
|
|
1154
|
+
// Modified from https://stackoverflow.com/a/8571649/11416267
|
|
1155
|
+
// DA doesn't use `==` to pad base 64
|
|
1156
|
+
if (value !== 'None' && value !== 'True' && value !== 'False'
|
|
1157
|
+
&& value.match(da_base64_regex)) {
|
|
1158
|
+
let value_decoded = fromBase64(value);
|
|
1159
|
+
let values_decoded = scope.decodeFully( scope, {expr_str: value_decoded });
|
|
1160
|
+
for (let possible_val of values_decoded) {
|
|
1161
|
+
possible_table_rows.push({var: var_name, value: possible_val, });
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1124
1164
|
}
|
|
1125
1165
|
} else {
|
|
1126
1166
|
possible_table_rows.push({ var: var_name, value: '', });
|
|
@@ -1145,11 +1185,11 @@ module.exports = {
|
|
|
1145
1185
|
|
|
1146
1186
|
let field_like_names = {};
|
|
1147
1187
|
let field_like_names_encoded = $('input[name="_varnames"]')[0].attribs.value;
|
|
1148
|
-
let field_like_names_str =
|
|
1188
|
+
let field_like_names_str = fromBase64( field_like_names_encoded );
|
|
1149
1189
|
let field_like_names_json = JSON.parse( field_like_names_str );
|
|
1150
1190
|
for ( let field_like_key in field_like_names_json ) {
|
|
1151
|
-
let field_like_DOM_name =
|
|
1152
|
-
let field_like_var_name =
|
|
1191
|
+
let field_like_DOM_name = fromBase64( field_like_key );
|
|
1192
|
+
let field_like_var_name = fromBase64( field_like_names_json[ field_like_key ] );
|
|
1153
1193
|
field_like_names[ field_like_DOM_name ] = field_like_var_name;
|
|
1154
1194
|
}
|
|
1155
1195
|
|
|
@@ -1178,14 +1218,11 @@ module.exports = {
|
|
|
1178
1218
|
* @param encoded {str} - base64 encoded string of the node
|
|
1179
1219
|
* @param field_like_names {obj} - e.g. `{ _field_0: 'some_checkbox_var_name' }`
|
|
1180
1220
|
* @param nota {bool} - is the field a 'none of the above' field? */
|
|
1181
|
-
let var_names = [];
|
|
1182
|
-
let var_name = '';
|
|
1183
|
-
|
|
1184
1221
|
// E.g. continue buttons that don't set a variable
|
|
1185
|
-
if ( encoded === undefined ) { return
|
|
1222
|
+
if ( encoded === undefined ) { return []; }
|
|
1186
1223
|
|
|
1187
1224
|
// Names can be encoded multiple times, so start peeling the layers
|
|
1188
|
-
let layer_1_decoded =
|
|
1225
|
+
let layer_1_decoded = fromBase64( encoded );
|
|
1189
1226
|
// If this is of the format `_field_n`, we need to get the actual matching var name.
|
|
1190
1227
|
if ( field_like_names[ layer_1_decoded ] ) {
|
|
1191
1228
|
layer_1_decoded = field_like_names[ layer_1_decoded ];
|
|
@@ -1200,60 +1237,66 @@ module.exports = {
|
|
|
1200
1237
|
// TODO: Should we move this back into `getAllFields()`?
|
|
1201
1238
|
}
|
|
1202
1239
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1240
|
+
return scope.decodeFully(scope, { expr_str: layer_1_decoded, field_like_names, nota });
|
|
1241
|
+
}, // Ends scope.getPossibleVarNames()
|
|
1242
|
+
|
|
1243
|
+
decodeFully: function ( scope, { expr_str, field_like_names = {}, nota} ) {
|
|
1244
|
+
/*
|
|
1245
|
+
* Given a DA variable or value name, make sure it's fully decoded from base64.
|
|
1246
|
+
* @param variable_str {str} - a python variable string, potentially partially base64 encoded
|
|
1247
|
+
* Some examples:
|
|
1248
|
+
* - my_variable
|
|
1249
|
+
* - checkboxes_choices[B'Y2hlY2tib3hfb3RoZXJfb3B0XzI']
|
|
1250
|
+
* - foo[bar][B'Y2hlY2tib3hfb3RoZXJfb3B0XzI']
|
|
1251
|
+
* @param field_like_names {obj} - e.g. `{ _field_0: 'some_checkbox_var_name' }`
|
|
1252
|
+
* @param nota {bool} - is the field a 'none of the above' field?
|
|
1253
|
+
*/
|
|
1254
|
+
// Some fields have a key hidden inside them.
|
|
1255
|
+
// This gets the variable name before the final brackets, and the likely
|
|
1256
|
+
// still base64 encoded string inside the brackets
|
|
1257
|
+
let before_and_in_brackets = expr_str.match( /^(.*)\[[BR]'(.*)'\]/ );
|
|
1205
1258
|
|
|
1206
1259
|
// Of the format `foo[B'encoded']` or `foo[R'encoded']`
|
|
1260
|
+
// The B means internal to docassemble, the encoded portion is a string,
|
|
1261
|
+
// and the R means that the python `repr` function was called with a
|
|
1262
|
+
// non-string object
|
|
1207
1263
|
// See https://github.com/jhpyle/docassemble/blob/595ad0a08572c0188f9ab49b1a6af99a3621cd42/docassemble_base/docassemble/base/standardformatter.py#L2108
|
|
1208
1264
|
|
|
1209
|
-
// What about something like foo[R'encoded1'][B'encoded2']? Is that a
|
|
1210
|
-
// thing? Can they be index numbers? Or just `foo['encoded1']`? What
|
|
1211
|
-
// would their format be? The above? `foo[R'actual_choice'][B'encoded']`?
|
|
1212
|
-
// `foo['something'][B'encoded']`?
|
|
1213
|
-
|
|
1214
1265
|
// =====================
|
|
1215
1266
|
// Ex: checkboxes_choices[B'Y2hlY2tib3hfb3RoZXJfb3B0XzI']
|
|
1216
1267
|
// Ex: checkboxes_choices[R'Y2hlY2tib3hfb3RoZXJfb3B0XzI']
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
let key_or_name =
|
|
1268
|
+
let possible = [];
|
|
1269
|
+
if ( before_and_in_brackets ) {
|
|
1270
|
+
let key_or_name = before_and_in_brackets[1];
|
|
1220
1271
|
// If this is of the format `_field_n`, we need to get the actual matching var name.
|
|
1221
|
-
|
|
1222
|
-
var_name = field_like_names[ key_or_name ];
|
|
1223
|
-
} else {
|
|
1224
|
-
var_name = key_or_name;
|
|
1225
|
-
}
|
|
1272
|
+
let var_name = field_like_names [ key_or_name ] || key_or_name;
|
|
1226
1273
|
|
|
1227
|
-
|
|
1228
|
-
if ( nota ) { var_names.push( `${ var_name }['None']` ); }
|
|
1229
|
-
else {
|
|
1230
|
-
// Get the choice name. E.g. option_1, option_2
|
|
1231
|
-
let box_layer_1_decoded = await scope.fromBase64( scope, { base64_str: checkbox_match[2] });
|
|
1232
|
-
var_names.push( `${ var_name }['${ box_layer_1_decoded }']` );
|
|
1274
|
+
let dictionary_key = before_and_in_brackets[2];
|
|
1233
1275
|
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1276
|
+
// Our own marker for a 'none of the above' checkbox choice
|
|
1277
|
+
if ( nota ) {
|
|
1278
|
+
possible.push( `${ var_name }['None']` );
|
|
1279
|
+
} else {
|
|
1280
|
+
if (dictionary_key.match(da_base64_regex)) {
|
|
1281
|
+
// Get the choice name. E.g. option_1, option_2
|
|
1282
|
+
let dictionary_key_decoded = fromBase64( dictionary_key );
|
|
1283
|
+
possible.push( `${ var_name }['${ dictionary_key_decoded }']` );
|
|
1284
|
+
if (dictionary_key_decoded.match(da_base64_regex)) {
|
|
1285
|
+
try {
|
|
1286
|
+
// The part inside `[B'encoded']` could need one more decoding.
|
|
1287
|
+
possible.push(`${ var_name }['${ fromBase64( dictionary_key_decoded ) }']`);
|
|
1288
|
+
} catch ( err ) {} // It wasn't double encoded. Do nothing.
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1240
1291
|
}
|
|
1241
1292
|
|
|
1242
1293
|
// =====================
|
|
1243
|
-
//
|
|
1294
|
+
// If bracket regex didn't match, just use var name
|
|
1244
1295
|
} else {
|
|
1245
|
-
|
|
1246
|
-
var_names.push(`${ layer_1_decoded }`);
|
|
1247
|
-
// // Values are sometimes encoded multiple times (check this happens with a plain var name)
|
|
1248
|
-
// let layer_2_decoded = await scope.fromBase64( layer_1_decoded );
|
|
1249
|
-
// names.var_names.push( layer_2_decoded );
|
|
1296
|
+
possible.push(`${ expr_str }`);
|
|
1250
1297
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
// https://stackoverflow.com/a/23377268
|
|
1254
|
-
|
|
1255
|
-
return var_names;
|
|
1256
|
-
}, // Ends scope.getPossibleVarNames()
|
|
1298
|
+
return possible;
|
|
1299
|
+
},
|
|
1257
1300
|
|
|
1258
1301
|
getOptionValues: async function ( scope, { $select }) {
|
|
1259
1302
|
/* Returns all the option node values of the select node. */
|
|
@@ -1261,35 +1304,11 @@ module.exports = {
|
|
|
1261
1304
|
let option_nodes = $select.find( `option` );
|
|
1262
1305
|
for ( let opt of option_nodes ) {
|
|
1263
1306
|
options.push( opt.attribs.value );
|
|
1264
|
-
//
|
|
1265
|
-
let decoded = await scope.fromBase64( scope, { base64_str: opt.attribs.value });
|
|
1266
|
-
options.push( decoded );
|
|
1307
|
+
// Dropdowns created w/ objects are base64 encoded; but it's handled in getPossibleTableRowMatches
|
|
1267
1308
|
}
|
|
1268
|
-
// TODO: These may be encoded? Just 'decode' and append those vals too.
|
|
1269
|
-
// TODO: Keep those decoded values somewhere for setting the right
|
|
1270
|
-
// option in the DOM. Not sure how that can be put together sensically.
|
|
1271
1309
|
return options;
|
|
1272
1310
|
}, // Ends scope.getOptionValues()
|
|
1273
1311
|
|
|
1274
|
-
fromBase64: async function ( scope, { base64_str }) {
|
|
1275
|
-
let buffer = Buffer.from( base64_str, 'base64' );
|
|
1276
|
-
return buffer.toString( `utf-8` );
|
|
1277
|
-
},
|
|
1278
|
-
|
|
1279
|
-
toBase64: async function ( scope, { utf8_str }, exclude_padding=true) {
|
|
1280
|
-
if ( typeof( utf8_str ) !== `string` ) { // Prevent an error
|
|
1281
|
-
return `Will+Never+Match+Existing+DOM+Node==`;
|
|
1282
|
-
}
|
|
1283
|
-
// Sometimes, converting to base64 results in padding characters ('=')
|
|
1284
|
-
// being added to the end. Docassemble does not use these padding
|
|
1285
|
-
// characters everywhere, so we allow for stripping them using
|
|
1286
|
-
// the exclude_padding argument. For more details, see
|
|
1287
|
-
// https://github.com/nodejs/node/issues/6107#issuecomment-207177131
|
|
1288
|
-
let buffer = Buffer.from( utf8_str, 'utf-8' );
|
|
1289
|
-
let base64str = buffer.toString( `base64` )
|
|
1290
|
-
return exclude_padding ? base64str.replace(/=/g, '') : base64str;
|
|
1291
|
-
},
|
|
1292
|
-
|
|
1293
1312
|
|
|
1294
1313
|
// TODO: Maybe add `from_story_table` to the var data table (array...)
|
|
1295
1314
|
getMatchingRows: async function ( scope, { field, var_data }) {
|
|
@@ -1481,7 +1500,12 @@ module.exports = {
|
|
|
1481
1500
|
await scope.addToReport(scope, { type: `warning`, value: multiple_matches_msg });
|
|
1482
1501
|
}
|
|
1483
1502
|
|
|
1484
|
-
if ( session_vars.get_debug() ) {
|
|
1503
|
+
if ( session_vars.get_debug() ) {
|
|
1504
|
+
console.log( `final_matching_rows:\n`, JSON.stringify( final_matching_rows ));
|
|
1505
|
+
if ( final_matching_rows.length == 0) {
|
|
1506
|
+
console.log(` field:\n`, JSON.stringify( field ) );
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1485
1509
|
return final_matching_rows;
|
|
1486
1510
|
}, // Ends scope.getMatchingRows()
|
|
1487
1511
|
|
|
@@ -1685,8 +1709,6 @@ module.exports = {
|
|
|
1685
1709
|
},
|
|
1686
1710
|
select: async function ( scope, { handle, answer }) {
|
|
1687
1711
|
// Note: I don't remember what's really going on in here.
|
|
1688
|
-
// I will note that values of `select` elements are strings,
|
|
1689
|
-
// not variables, so we don't need to check for variable names.
|
|
1690
1712
|
|
|
1691
1713
|
// A dropdown's option value can be one of two things
|
|
1692
1714
|
// Try to find the element using the first value
|
|
@@ -1707,7 +1729,7 @@ module.exports = {
|
|
|
1707
1729
|
// If that literal value isn't on the page, it should be a base64 encoded value
|
|
1708
1730
|
// TODO: Can these be double encoded? Can they be encoded like objects?
|
|
1709
1731
|
} else {
|
|
1710
|
-
let base64_name =
|
|
1732
|
+
let base64_name = toBase64( answer );
|
|
1711
1733
|
await handle.select( base64_name );
|
|
1712
1734
|
}
|
|
1713
1735
|
|
|
@@ -2127,13 +2149,18 @@ module.exports = {
|
|
|
2127
2149
|
enhanced_var_data.push( special_row );
|
|
2128
2150
|
|
|
2129
2151
|
// Neutralize a `.there_is_another` row in a story table
|
|
2130
|
-
} else if ( from_story_table && var_datum.var.match( /\.there_is_another$/ ) ) {
|
|
2152
|
+
} else if ( from_story_table && var_datum.var.match( /\.there_is_another$/ ) && var_datum.value !== `False`) {
|
|
2131
2153
|
var_datum.invalid = true;
|
|
2132
2154
|
// Removing the row will just cause a different error that will take longer to fail and
|
|
2133
2155
|
// probably add a confusing message to the report, so mutating seems, sadly, more useful.
|
|
2134
2156
|
var_datum.value = `False`; // The original value is still preserved
|
|
2135
2157
|
// Warn the developer not to use `.there_is_another`.
|
|
2136
|
-
await scope.addToReport( scope, { type: `warning`,
|
|
2158
|
+
await scope.addToReport( scope, { type: `warning`,
|
|
2159
|
+
value: 'The attribute `.there_is_another` is invalid in story table tests. Replace it with '
|
|
2160
|
+
+ '`.target_number` in your `var` and `trigger` columns. Set the `value` to the number of items in '
|
|
2161
|
+
+ `that list. This test will now set this row's \`value\` to \`False\`. See `
|
|
2162
|
+
+ `https://suffolklitlab.github.io/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing#there_is_another. `
|
|
2163
|
+
+ `The row data is\n${ JSON.stringify( var_datum.original )}` });
|
|
2137
2164
|
}
|
|
2138
2165
|
}
|
|
2139
2166
|
|
|
@@ -2821,4 +2848,24 @@ module.exports = {
|
|
|
2821
2848
|
|
|
2822
2849
|
return all_are_included;
|
|
2823
2850
|
}, // Ends scope.reportIncludesAllExpected()
|
|
2851
|
+
|
|
2852
|
+
reportDoesNotInclude: async function ( scope, { not_expected=[] }) {
|
|
2853
|
+
/* Tests the behavior of this testing framework.
|
|
2854
|
+
* Test whether the expected values are not included in the test report.
|
|
2855
|
+
*
|
|
2856
|
+
* @param not_expected {array} Arr of strs that should not appear in the report.
|
|
2857
|
+
*/
|
|
2858
|
+
let current_report_obj = scope.report.get( scope.scenario_id );
|
|
2859
|
+
let scenario = [ scope.scenario_id, current_report_obj ]; // Mimic how loop works with `Map`
|
|
2860
|
+
let report = await scope.getPrintableScenario( scope, { scenario });
|
|
2861
|
+
let none_included = true;
|
|
2862
|
+
for ( let one_expectation of not_expected ) {
|
|
2863
|
+
if ( report.includes( one_expectation )) {
|
|
2864
|
+
none_included = false;
|
|
2865
|
+
expect( report ).not.to.contain( one_expectation );
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2869
|
+
return none_included;
|
|
2870
|
+
},
|
|
2824
2871
|
};
|
package/lib/steps.js
CHANGED
|
@@ -115,6 +115,7 @@ Before(async (scenario) => {
|
|
|
115
115
|
scope.scenario_id = uuidv4();
|
|
116
116
|
scope.expected_status = null;
|
|
117
117
|
scope.expected_in_report = null;
|
|
118
|
+
scope.expected_not_in_report = null;
|
|
118
119
|
scope.server_reload_promise = null;
|
|
119
120
|
|
|
120
121
|
await scope.addReportHeading(scope, {scenario});
|
|
@@ -1068,6 +1069,19 @@ Given(/the Scenario report should include/i, async ( report_text ) => {
|
|
|
1068
1069
|
|
|
1069
1070
|
});
|
|
1070
1071
|
|
|
1072
|
+
Given(/the Scenario report should not include/i, async ( report_text ) => {
|
|
1073
|
+
/* WARNING: If a Step will fail, this Step must come first.
|
|
1074
|
+
*
|
|
1075
|
+
* Prepare to test that the Scenario report does not include `report_text`.
|
|
1076
|
+
* The actual test has to be done at the end of the Scenario as some text
|
|
1077
|
+
* may be added to the report there. */
|
|
1078
|
+
if ( !scope.expected_not_in_report ) {
|
|
1079
|
+
scope.expected_not_in_report = [];
|
|
1080
|
+
}
|
|
1081
|
+
scope.expected_not_in_report.push( report_text );
|
|
1082
|
+
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1071
1085
|
Given(/the final Scenario status should be "(passed|failed)"/i, async ( expected_status ) => {
|
|
1072
1086
|
/* WARNING: If a Step will fail, this Step must come first.
|
|
1073
1087
|
*
|
|
@@ -1156,6 +1170,20 @@ After(async function(scenario) {
|
|
|
1156
1170
|
else { scenario.result.status = `FAILED`; }
|
|
1157
1171
|
}
|
|
1158
1172
|
|
|
1173
|
+
if (scope.expected_not_in_report && scope.expected_not_in_report.length > 0) {
|
|
1174
|
+
let not_expected = scope.expected_not_in_report;
|
|
1175
|
+
// Reset report values no matter what so they don't mess up future scenarios
|
|
1176
|
+
scope.expected_not_in_report = null;
|
|
1177
|
+
let all_were_not_there = await scope.reportDoesNotInclude( scope, { not_expected });
|
|
1178
|
+
|
|
1179
|
+
if ( all_were_not_there ) {
|
|
1180
|
+
scenario.result.status = `PASSED`;
|
|
1181
|
+
} else {
|
|
1182
|
+
scenario.result.status = `FAILED`;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1159
1187
|
// If there is a page open, then sign out and close it
|
|
1160
1188
|
if ( scope.page ) {
|
|
1161
1189
|
// Catch a possible reload error.
|
package/lib/utils/files.js
CHANGED
|
@@ -11,14 +11,14 @@ files.readable_date = function() {
|
|
|
11
11
|
*/
|
|
12
12
|
let date = new Date(); // now
|
|
13
13
|
let day = ("0" + date.getUTCDate()).slice(-2);
|
|
14
|
-
let month = ("0" + date.getUTCMonth() + 1).slice(-2);
|
|
14
|
+
let month = ("0" + (date.getUTCMonth() + 1)).slice(-2);
|
|
15
15
|
let year = date.getUTCFullYear();
|
|
16
16
|
let hours = ("0" + date.getUTCHours()).slice(-2);
|
|
17
17
|
let mins = ("0" + date.getUTCMinutes()).slice(-2);
|
|
18
18
|
let secs = ("0" + date.getUTCSeconds()).slice(-2);
|
|
19
19
|
let msecs = ("00" + date.getUTCMilliseconds()).slice(-3);
|
|
20
20
|
|
|
21
|
-
let fancy_str = `${ year }-${ month }-${ day } at ${ hours }
|
|
21
|
+
let fancy_str = `${ year }-${ month }-${ day } at ${ hours }h${ mins }m${ secs }s${ msecs }ms UTC`;
|
|
22
22
|
return fancy_str;
|
|
23
23
|
}; // Ends files.readable_date()
|
|
24
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suffolklitlab/alkiln",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
4
4
|
"description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"chai": "4.2.0",
|
|
43
43
|
"cheerio": "1.0.0-rc.10",
|
|
44
44
|
"dotenv": "8.2.0",
|
|
45
|
+
"deep-equal-in-any-order": "^2.0.0",
|
|
45
46
|
"mocha": "9.2.0",
|
|
46
47
|
"puppeteer": "13.1.3",
|
|
47
48
|
"qs": "6.10.2",
|
|
@@ -132,3 +132,35 @@ Scenario: tap element with an error
|
|
|
132
132
|
"""
|
|
133
133
|
Given I start the interview at "test_taps"
|
|
134
134
|
And I tap the "Tests-not_there-tab" tab
|
|
135
|
+
|
|
136
|
+
@i5 @decoding-test @object-multiselect
|
|
137
|
+
Scenario: Base64 encoded corner cases are decoded correctly
|
|
138
|
+
Given I start the interview at "test_decoding"
|
|
139
|
+
And I get to "end screen" with this data:
|
|
140
|
+
| var | value | trigger |
|
|
141
|
+
| object_multiselect_test['obj_opt_1'] | True | |
|
|
142
|
+
| object_multiselect_test['obj_opt_3'] | True | |
|
|
143
|
+
| multiselect_test['hi'] | True | |
|
|
144
|
+
| multiselect_test['dear'] | True | |
|
|
145
|
+
| object_radio_field | object_radio_choice_one | |
|
|
146
|
+
| object_checkboxes_test['obj_chkbx_opt_1'] | True | |
|
|
147
|
+
| object_dropdown | obj_opt_2 | |
|
|
148
|
+
| checkbox_dict['single_quote_key']['🁳🧴🥫🅍🗎🗷🌲🯵'] | True | |
|
|
149
|
+
|
|
150
|
+
# This test should fail because it's target id is never found, but that also means
|
|
151
|
+
# it has succeeded because it didn't try to keep looking for the id by pressing 'restart'
|
|
152
|
+
# to continue.
|
|
153
|
+
@fast @i6
|
|
154
|
+
Scenario: Fails as it doesn't try to 'continue' with a restart button
|
|
155
|
+
Given the final Scenario status should be "failed"
|
|
156
|
+
And the Scenario report should include:
|
|
157
|
+
"""
|
|
158
|
+
The test got stuck on "kickout-screen"
|
|
159
|
+
"""
|
|
160
|
+
Given the max seconds for each step in this scenario is 10
|
|
161
|
+
Given I start the interview at "test_kickout"
|
|
162
|
+
And I get to "doesnt exist" with this data:
|
|
163
|
+
| var | value | trigger |
|
|
164
|
+
| user_choice | wrong | |
|
|
165
|
+
|
|
166
|
+
|
|
@@ -6,13 +6,13 @@ Feature: Reports show the right things
|
|
|
6
6
|
# rp for report pass
|
|
7
7
|
|
|
8
8
|
# ===============================
|
|
9
|
-
#
|
|
9
|
+
# Reports for "failed" Scenarios
|
|
10
10
|
# ===============================
|
|
11
11
|
|
|
12
12
|
# ---- Exceptions in steps.js ----
|
|
13
13
|
|
|
14
14
|
@fast @rf1 @error
|
|
15
|
-
Scenario: Fail with
|
|
15
|
+
Scenario: Fail with missing language link
|
|
16
16
|
Given the final Scenario status should be "failed"
|
|
17
17
|
Given the Scenario report should include:
|
|
18
18
|
"""
|
|
@@ -106,7 +106,7 @@ Scenario: Fail with missing error message
|
|
|
106
106
|
|
|
107
107
|
# TODO: Check this with validation code failure too
|
|
108
108
|
@fast @rf10 @error
|
|
109
|
-
Scenario: Fail with was
|
|
109
|
+
Scenario: Fail with was unexpectedly not able to continue for invalid field input message
|
|
110
110
|
Given the final Scenario status should be "failed"
|
|
111
111
|
Given the Scenario report should include:
|
|
112
112
|
"""
|
|
@@ -544,3 +544,14 @@ Scenario: Sign in to server successfully
|
|
|
544
544
|
Signed in
|
|
545
545
|
"""
|
|
546
546
|
Given I sign in with the email "USER1_EMAIL" and the password "USER1_PASSWORD"
|
|
547
|
+
|
|
548
|
+
@rp4 @table
|
|
549
|
+
Scenario: Report doesn't complain about `there_is_another | False` in a table.
|
|
550
|
+
Given the Scenario report should not include:
|
|
551
|
+
"""
|
|
552
|
+
The attribute `.there_is_another` is invalid in story table tests
|
|
553
|
+
"""
|
|
554
|
+
Given I start the interview at "test_gather"
|
|
555
|
+
And I get to "end screen" with this data:
|
|
556
|
+
| var | value | trigger |
|
|
557
|
+
| users.there_is_another | False | users.there_is_another |
|
|
@@ -15,7 +15,7 @@ fields.standard = [
|
|
|
15
15
|
}
|
|
16
16
|
],
|
|
17
17
|
"type": "checkbox",
|
|
18
|
-
"trigger": "direct_standard_fields"
|
|
18
|
+
"trigger": "direct_standard_fields",
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"selector": "#daquestion input[name=\"Y2hlY2tib3hlc19vdGhlcltCJ1kyaGxZMnRpYjNoZmIzUm9aWEpmYjNCMFh6RSdd\"][value=\"True\"][id=\"Y2hlY2tib3hlc19vdGhlcg_0\"][class=\"dafield1 danon-nota-checkbox da-to-labelauty checkbox-icon labelauty da-active-invisible dafullwidth\"]",
|
|
@@ -25,13 +25,9 @@ fields.standard = [
|
|
|
25
25
|
"var": "checkboxes_other['checkbox_other_opt_1']",
|
|
26
26
|
"value": "True"
|
|
27
27
|
},
|
|
28
|
-
{
|
|
29
|
-
"var": "checkboxes_other['r\u0017���1��az����']",
|
|
30
|
-
"value": "True"
|
|
31
|
-
}
|
|
32
28
|
],
|
|
33
29
|
"type": "checkbox",
|
|
34
|
-
"trigger": "direct_standard_fields"
|
|
30
|
+
"trigger": "direct_standard_fields",
|
|
35
31
|
},
|
|
36
32
|
{
|
|
37
33
|
"selector": "#daquestion input[name=\"Y2hlY2tib3hlc19vdGhlcltCJ1kyaGxZMnRpYjNoZmIzUm9aWEpmYjNCMFh6SSdd\"][value=\"True\"][id=\"Y2hlY2tib3hlc19vdGhlcg_1\"][class=\"dafield1 danon-nota-checkbox da-to-labelauty checkbox-icon labelauty da-active-invisible dafullwidth\"]",
|
|
@@ -41,13 +37,9 @@ fields.standard = [
|
|
|
41
37
|
"var": "checkboxes_other['checkbox_other_opt_2']",
|
|
42
38
|
"value": "True"
|
|
43
39
|
},
|
|
44
|
-
{
|
|
45
|
-
"var": "checkboxes_other['r\u0017���1��az����']",
|
|
46
|
-
"value": "True"
|
|
47
|
-
}
|
|
48
40
|
],
|
|
49
41
|
"type": "checkbox",
|
|
50
|
-
"trigger": "direct_standard_fields"
|
|
42
|
+
"trigger": "direct_standard_fields",
|
|
51
43
|
},
|
|
52
44
|
{
|
|
53
45
|
"selector": "#daquestion input[name=\"Y2hlY2tib3hlc19vdGhlcltCJ1kyaGxZMnRpYjNoZmIzUm9aWEpmYjNCMFh6TSdd\"][value=\"True\"][id=\"Y2hlY2tib3hlc19vdGhlcg_2\"][class=\"dafield1 danon-nota-checkbox da-to-labelauty checkbox-icon labelauty da-active-invisible dafullwidth\"]",
|
|
@@ -57,13 +49,9 @@ fields.standard = [
|
|
|
57
49
|
"var": "checkboxes_other['checkbox_other_opt_3']",
|
|
58
50
|
"value": "True"
|
|
59
51
|
},
|
|
60
|
-
{
|
|
61
|
-
"var": "checkboxes_other['r\u0017���1��az����']",
|
|
62
|
-
"value": "True"
|
|
63
|
-
}
|
|
64
52
|
],
|
|
65
53
|
"type": "checkbox",
|
|
66
|
-
"trigger": "direct_standard_fields"
|
|
54
|
+
"trigger": "direct_standard_fields",
|
|
67
55
|
},
|
|
68
56
|
{
|
|
69
57
|
"selector": "#daquestion input[name=\"_ignore1\"][id=\"labelauty-294201\"][class=\"dafield1 danota-checkbox da-to-labelauty checkbox-icon labelauty da-active-invisible dafullwidth\"]",
|
|
@@ -75,7 +63,7 @@ fields.standard = [
|
|
|
75
63
|
}
|
|
76
64
|
],
|
|
77
65
|
"type": "checkbox",
|
|
78
|
-
"trigger": "direct_standard_fields"
|
|
66
|
+
"trigger": "direct_standard_fields",
|
|
79
67
|
},
|
|
80
68
|
{
|
|
81
69
|
"selector": "#daquestion input[name=\"cmFkaW9feWVzbm8\"][value=\"True\"][id=\"cmFkaW9feWVzbm8_0\"][class=\"da-to-labelauty labelauty da-active-invisible dafullwidth\"]",
|
|
@@ -87,7 +75,7 @@ fields.standard = [
|
|
|
87
75
|
}
|
|
88
76
|
],
|
|
89
77
|
"type": "radio",
|
|
90
|
-
"trigger": "direct_standard_fields"
|
|
78
|
+
"trigger": "direct_standard_fields",
|
|
91
79
|
},
|
|
92
80
|
{
|
|
93
81
|
"selector": "#daquestion input[name=\"cmFkaW9feWVzbm8\"][value=\"False\"][id=\"cmFkaW9feWVzbm8_1\"][class=\"da-to-labelauty labelauty da-active-invisible dafullwidth\"]",
|
|
@@ -99,7 +87,7 @@ fields.standard = [
|
|
|
99
87
|
}
|
|
100
88
|
],
|
|
101
89
|
"type": "radio",
|
|
102
|
-
"trigger": "direct_standard_fields"
|
|
90
|
+
"trigger": "direct_standard_fields",
|
|
103
91
|
},
|
|
104
92
|
{
|
|
105
93
|
"selector": "#daquestion input[name=\"cmFkaW9fb3RoZXI\"][value=\"radio_other_opt_1\"][id=\"cmFkaW9fb3RoZXI_0\"][class=\"da-to-labelauty labelauty da-active-invisible dafullwidth\"]",
|
|
@@ -111,7 +99,7 @@ fields.standard = [
|
|
|
111
99
|
}
|
|
112
100
|
],
|
|
113
101
|
"type": "radio",
|
|
114
|
-
"trigger": "direct_standard_fields"
|
|
102
|
+
"trigger": "direct_standard_fields",
|
|
115
103
|
},
|
|
116
104
|
{
|
|
117
105
|
"selector": "#daquestion input[name=\"cmFkaW9fb3RoZXI\"][value=\"radio_other_opt_2\"][id=\"cmFkaW9fb3RoZXI_1\"][class=\"da-to-labelauty labelauty da-active-invisible dafullwidth\"]",
|
|
@@ -123,7 +111,7 @@ fields.standard = [
|
|
|
123
111
|
}
|
|
124
112
|
],
|
|
125
113
|
"type": "radio",
|
|
126
|
-
"trigger": "direct_standard_fields"
|
|
114
|
+
"trigger": "direct_standard_fields",
|
|
127
115
|
},
|
|
128
116
|
{
|
|
129
117
|
"selector": "#daquestion input[name=\"cmFkaW9fb3RoZXI\"][value=\"radio_other_opt_3\"][id=\"cmFkaW9fb3RoZXI_2\"][class=\"da-to-labelauty labelauty da-active-invisible dafullwidth\"]",
|
|
@@ -135,7 +123,7 @@ fields.standard = [
|
|
|
135
123
|
}
|
|
136
124
|
],
|
|
137
125
|
"type": "radio",
|
|
138
|
-
"trigger": "direct_standard_fields"
|
|
126
|
+
"trigger": "direct_standard_fields",
|
|
139
127
|
},
|
|
140
128
|
{
|
|
141
129
|
"selector": "#daquestion input[name=\"dGV4dF9pbnB1dA\"][id=\"dGV4dF9pbnB1dA\"][class=\"form-control\"]",
|
|
@@ -147,7 +135,7 @@ fields.standard = [
|
|
|
147
135
|
}
|
|
148
136
|
],
|
|
149
137
|
"type": "text",
|
|
150
|
-
"trigger": "direct_standard_fields"
|
|
138
|
+
"trigger": "direct_standard_fields",
|
|
151
139
|
},
|
|
152
140
|
{
|
|
153
141
|
"selector": "#daquestion textarea[name=\"dGV4dGFyZWE\"][id=\"dGV4dGFyZWE\"][class=\"form-control\"]",
|
|
@@ -159,7 +147,7 @@ fields.standard = [
|
|
|
159
147
|
}
|
|
160
148
|
],
|
|
161
149
|
"type": "",
|
|
162
|
-
"trigger": "direct_standard_fields"
|
|
150
|
+
"trigger": "direct_standard_fields",
|
|
163
151
|
},
|
|
164
152
|
{
|
|
165
153
|
"selector": "#daquestion input[name=\"ZGF0ZV9pbnB1dA\"][id=\"ZGF0ZV9pbnB1dA\"][class=\"form-control\"]",
|
|
@@ -171,7 +159,7 @@ fields.standard = [
|
|
|
171
159
|
}
|
|
172
160
|
],
|
|
173
161
|
"type": "date",
|
|
174
|
-
"trigger": "direct_standard_fields"
|
|
162
|
+
"trigger": "direct_standard_fields",
|
|
175
163
|
},
|
|
176
164
|
{
|
|
177
165
|
"selector": "#daquestion select[name=\"ZHJvcGRvd25fdGVzdA\"][id=\"ZHJvcGRvd25fdGVzdA\"][class=\"form-select dasingleselect\"]",
|
|
@@ -181,37 +169,21 @@ fields.standard = [
|
|
|
181
169
|
"var": "dropdown_test",
|
|
182
170
|
"value": ""
|
|
183
171
|
},
|
|
184
|
-
{
|
|
185
|
-
"var": "dropdown_test",
|
|
186
|
-
"value": ""
|
|
187
|
-
},
|
|
188
172
|
{
|
|
189
173
|
"var": "dropdown_test",
|
|
190
174
|
"value": "dropdown_opt_1"
|
|
191
175
|
},
|
|
192
|
-
{
|
|
193
|
-
"var": "dropdown_test",
|
|
194
|
-
"value": "v�)v�'��m�"
|
|
195
|
-
},
|
|
196
176
|
{
|
|
197
177
|
"var": "dropdown_test",
|
|
198
178
|
"value": "dropdown_opt_2"
|
|
199
179
|
},
|
|
200
|
-
{
|
|
201
|
-
"var": "dropdown_test",
|
|
202
|
-
"value": "v�)v�'��m�"
|
|
203
|
-
},
|
|
204
180
|
{
|
|
205
181
|
"var": "dropdown_test",
|
|
206
182
|
"value": "dropdown_opt_3"
|
|
207
183
|
},
|
|
208
|
-
{
|
|
209
|
-
"var": "dropdown_test",
|
|
210
|
-
"value": "v�)v�'��m�"
|
|
211
|
-
}
|
|
212
184
|
],
|
|
213
185
|
"type": "",
|
|
214
|
-
"trigger": "direct_standard_fields"
|
|
186
|
+
"trigger": "direct_standard_fields",
|
|
215
187
|
},
|
|
216
188
|
{
|
|
217
189
|
"selector": "#daquestion button[name=\"ZGlyZWN0X3N0YW5kYXJkX2ZpZWxkcw\"][value=\"True\"][class=\"btn btn-da btn-primary\"]",
|
|
@@ -223,7 +195,7 @@ fields.standard = [
|
|
|
223
195
|
}
|
|
224
196
|
],
|
|
225
197
|
"type": "submit",
|
|
226
|
-
"trigger": "direct_standard_fields"
|
|
198
|
+
"trigger": "direct_standard_fields",
|
|
227
199
|
}
|
|
228
200
|
]; // ends fields.standard
|
|
229
201
|
|
|
@@ -276,10 +248,6 @@ fields.show_if = [
|
|
|
276
248
|
"var": "showif_checkboxes_other['showif_checkboxes_nota_1']",
|
|
277
249
|
"value": "True"
|
|
278
250
|
},
|
|
279
|
-
{
|
|
280
|
-
"var": "showif_checkboxes_other['�\u001a0��܅�$n�^��赯�']",
|
|
281
|
-
"value": "True"
|
|
282
|
-
}
|
|
283
251
|
],
|
|
284
252
|
"type": "checkbox",
|
|
285
253
|
"trigger": "direct_showifs"
|
|
@@ -292,10 +260,6 @@ fields.show_if = [
|
|
|
292
260
|
"var": "showif_checkboxes_other['showif_checkboxes_nota_2']",
|
|
293
261
|
"value": "True"
|
|
294
262
|
},
|
|
295
|
-
{
|
|
296
|
-
"var": "showif_checkboxes_other['�\u001a0��܅�$n�^��赯�']",
|
|
297
|
-
"value": "True"
|
|
298
|
-
}
|
|
299
263
|
],
|
|
300
264
|
"type": "checkbox",
|
|
301
265
|
"trigger": "direct_showifs"
|
|
@@ -308,10 +272,6 @@ fields.show_if = [
|
|
|
308
272
|
"var": "showif_checkboxes_other['showif_checkboxes_nota_3']",
|
|
309
273
|
"value": "True"
|
|
310
274
|
},
|
|
311
|
-
{
|
|
312
|
-
"var": "showif_checkboxes_other['�\u001a0��܅�$n�^��赯�']",
|
|
313
|
-
"value": "True"
|
|
314
|
-
}
|
|
315
275
|
],
|
|
316
276
|
"type": "checkbox",
|
|
317
277
|
"trigger": "direct_showifs"
|
|
@@ -420,42 +380,22 @@ fields.show_if = [
|
|
|
420
380
|
"var": "showif_dropdown",
|
|
421
381
|
"value": ""
|
|
422
382
|
},
|
|
423
|
-
{
|
|
424
|
-
"var": "showif_dropdown",
|
|
425
|
-
"value": ""
|
|
426
|
-
},
|
|
427
383
|
{
|
|
428
384
|
"var": "showif_dropdown",
|
|
429
385
|
"value": "showif_dropdown_1"
|
|
430
386
|
},
|
|
431
|
-
{
|
|
432
|
-
"var": "showif_dropdown",
|
|
433
|
-
"value": "�\u001a0��ݮ�]�\t�"
|
|
434
|
-
},
|
|
435
387
|
{
|
|
436
388
|
"var": "showif_dropdown",
|
|
437
389
|
"value": "showif_dropdown_2"
|
|
438
390
|
},
|
|
439
|
-
{
|
|
440
|
-
"var": "showif_dropdown",
|
|
441
|
-
"value": "�\u001a0��ݮ�]�\t�"
|
|
442
|
-
},
|
|
443
391
|
{
|
|
444
392
|
"var": "showif_dropdown",
|
|
445
393
|
"value": "showif_dropdown_3"
|
|
446
394
|
},
|
|
447
|
-
{
|
|
448
|
-
"var": "showif_dropdown",
|
|
449
|
-
"value": "�\u001a0��ݮ�]�\t�"
|
|
450
|
-
},
|
|
451
395
|
{
|
|
452
396
|
"var": "showif_dropdown",
|
|
453
397
|
"value": "showif_dropdown_4"
|
|
454
398
|
},
|
|
455
|
-
{
|
|
456
|
-
"var": "showif_dropdown",
|
|
457
|
-
"value": "�\u001a0��ݮ�]�\t�"
|
|
458
|
-
}
|
|
459
399
|
],
|
|
460
400
|
"type": "",
|
|
461
401
|
"trigger": "direct_showifs"
|
|
@@ -725,10 +665,6 @@ fields.object_dropdown = [
|
|
|
725
665
|
"var": "object_dropdown",
|
|
726
666
|
"value": ""
|
|
727
667
|
},
|
|
728
|
-
{
|
|
729
|
-
"var": "object_dropdown",
|
|
730
|
-
"value": ""
|
|
731
|
-
},
|
|
732
668
|
{
|
|
733
669
|
"var": "object_dropdown",
|
|
734
670
|
"value": "b2JqX29wdF8x"
|
|
@@ -766,6 +702,70 @@ fields.object_dropdown = [
|
|
|
766
702
|
}
|
|
767
703
|
];
|
|
768
704
|
|
|
705
|
+
// ============================
|
|
706
|
+
// multiselect created with objects
|
|
707
|
+
// ============================
|
|
708
|
+
// ```
|
|
709
|
+
// - Something: some_var
|
|
710
|
+
// datatype: object
|
|
711
|
+
// choices:
|
|
712
|
+
// - obj1
|
|
713
|
+
// - obj2
|
|
714
|
+
// ```
|
|
715
|
+
fields.object_multiselect = [
|
|
716
|
+
{
|
|
717
|
+
"selector": "#daquestion select[name=\"b2JqZWN0X211bHRpc2VsZWN0\"][id=\"b2JqZWN0X211bHRpc2VsZWN0\"][class=\"form-control damultiselect daobject\"]",
|
|
718
|
+
"tag": "select",
|
|
719
|
+
"guesses": [
|
|
720
|
+
{
|
|
721
|
+
"var": "object_multiselect['b2JqX29wdF8x']",
|
|
722
|
+
"value": "True"
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
"var": "object_multiselect['obj_opt_1']",
|
|
726
|
+
"value": "True"
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
"var": "object_multiselect['b2JqX29wdF8y']",
|
|
730
|
+
"value": "True"
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
"var": "object_multiselect['obj_opt_2']",
|
|
734
|
+
"value": "True"
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
"var": "object_multiselect['b2JqX29wdF8z']",
|
|
738
|
+
"value": "True"
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
"var": "object_multiselect['obj_opt_3']",
|
|
742
|
+
"value": "True"
|
|
743
|
+
},
|
|
744
|
+
],
|
|
745
|
+
"type": "",
|
|
746
|
+
"trigger": "object_multiselect"
|
|
747
|
+
},
|
|
748
|
+
{
|
|
749
|
+
"guesses": [
|
|
750
|
+
{
|
|
751
|
+
"value": "True",
|
|
752
|
+
"var": "object_multiselect.gathered"
|
|
753
|
+
}
|
|
754
|
+
],
|
|
755
|
+
"selector": "#daquestion input[name=\"b2JqZWN0X211bHRpc2VsZWN0LmdhdGhlcmVk\"][value=\"True\"]",
|
|
756
|
+
"tag": "input",
|
|
757
|
+
"trigger": "object_multiselect",
|
|
758
|
+
"type": "hidden"
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
"selector": "#daquestion button[class=\"btn btn-da btn-primary\"]",
|
|
762
|
+
"tag": "button",
|
|
763
|
+
"guesses": [],
|
|
764
|
+
"type": "submit",
|
|
765
|
+
"trigger": "object_multiselect"
|
|
766
|
+
},
|
|
767
|
+
];
|
|
768
|
+
|
|
769
769
|
|
|
770
770
|
// ============================
|
|
771
771
|
// mixed quotes
|
|
@@ -779,10 +779,6 @@ fields.mixed_quotes = [
|
|
|
779
779
|
"var": "single_quote_dict['single_quote_key']['sq_one']",
|
|
780
780
|
"value": "True"
|
|
781
781
|
},
|
|
782
|
-
{
|
|
783
|
-
"var": "single_quote_dict['single_quote_key']['���']",
|
|
784
|
-
"value": "True"
|
|
785
|
-
}
|
|
786
782
|
],
|
|
787
783
|
"type": "checkbox",
|
|
788
784
|
"trigger": "double_quote_dict['double_quote_key']"
|
|
@@ -795,10 +791,6 @@ fields.mixed_quotes = [
|
|
|
795
791
|
"var": "single_quote_dict['single_quote_key']['sq_two']",
|
|
796
792
|
"value": "True"
|
|
797
793
|
},
|
|
798
|
-
{
|
|
799
|
-
"var": "single_quote_dict['single_quote_key']['����']",
|
|
800
|
-
"value": "True"
|
|
801
|
-
}
|
|
802
794
|
],
|
|
803
795
|
"type": "checkbox",
|
|
804
796
|
"trigger": "double_quote_dict['double_quote_key']"
|
|
@@ -811,10 +803,6 @@ fields.mixed_quotes = [
|
|
|
811
803
|
"var": "single_quote_dict['single_quote_key']['sq_three']",
|
|
812
804
|
"value": "True"
|
|
813
805
|
},
|
|
814
|
-
{
|
|
815
|
-
"var": "single_quote_dict['single_quote_key']['��톷�']",
|
|
816
|
-
"value": "True"
|
|
817
|
-
}
|
|
818
806
|
],
|
|
819
807
|
"type": "checkbox",
|
|
820
808
|
"trigger": "double_quote_dict['double_quote_key']"
|
|
@@ -839,10 +827,6 @@ fields.mixed_quotes = [
|
|
|
839
827
|
"var": "double_quote_dict[\"double_quote_key\"]['dq_one']",
|
|
840
828
|
"value": "True"
|
|
841
829
|
},
|
|
842
|
-
{
|
|
843
|
-
"var": "double_quote_dict[\"double_quote_key\"]['v��']",
|
|
844
|
-
"value": "True"
|
|
845
|
-
}
|
|
846
830
|
],
|
|
847
831
|
"type": "checkbox",
|
|
848
832
|
"trigger": "double_quote_dict['double_quote_key']"
|
|
@@ -855,10 +839,6 @@ fields.mixed_quotes = [
|
|
|
855
839
|
"var": "double_quote_dict[\"double_quote_key\"]['dq_two']",
|
|
856
840
|
"value": "True"
|
|
857
841
|
},
|
|
858
|
-
{
|
|
859
|
-
"var": "double_quote_dict[\"double_quote_key\"]['v���']",
|
|
860
|
-
"value": "True"
|
|
861
|
-
}
|
|
862
842
|
],
|
|
863
843
|
"type": "checkbox",
|
|
864
844
|
"trigger": "double_quote_dict['double_quote_key']"
|
|
@@ -871,10 +851,6 @@ fields.mixed_quotes = [
|
|
|
871
851
|
"var": "double_quote_dict[\"double_quote_key\"]['dq_three']",
|
|
872
852
|
"value": "True"
|
|
873
853
|
},
|
|
874
|
-
{
|
|
875
|
-
"var": "double_quote_dict[\"double_quote_key\"]['v�톷�']",
|
|
876
|
-
"value": "True"
|
|
877
|
-
}
|
|
878
854
|
],
|
|
879
855
|
"type": "checkbox",
|
|
880
856
|
"trigger": "double_quote_dict['double_quote_key']"
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
const chai = require('chai');
|
|
2
2
|
const expect = chai.expect;
|
|
3
|
+
const deepEqualInAnyOrder = require('deep-equal-in-any-order');
|
|
4
|
+
chai.use(deepEqualInAnyOrder);
|
|
3
5
|
// We need jest or something, right? To do fancy stuff.
|
|
4
6
|
|
|
5
7
|
const html = require('./html.fixtures.js');
|
|
6
8
|
const fields = require('./fields.fixtures.js');
|
|
7
9
|
const scope = require('../../lib/scope.js');
|
|
8
10
|
const getAllFields = scope.getAllFields;
|
|
11
|
+
|
|
12
|
+
chai.config.truncateThreshold = 0
|
|
9
13
|
// TODO: Rearrange fixtures so related fixtures are next to each other? e.g.
|
|
10
14
|
// buttons_yesno.html, buttons_yesno.fields, buttons_yesno.table, buttons_yesno.matches
|
|
11
15
|
// May make more files both to make and to import, but may be more useful
|
|
@@ -29,7 +33,9 @@ const getAllFields = scope.getAllFields;
|
|
|
29
33
|
it(`creates the right data for standard fields`, async function() {
|
|
30
34
|
// 18 fields (03/15/21)
|
|
31
35
|
let result = await getAllFields( scope, { html: html.standard });
|
|
32
|
-
|
|
36
|
+
//console.log(`${ JSON.stringify(result) }`);
|
|
37
|
+
//console.log(`-----------------\n${ JSON.stringify(fields.standard) }`);
|
|
38
|
+
expect( result ).to.deep.equalInAnyOrder( fields.standard );
|
|
33
39
|
});
|
|
34
40
|
|
|
35
41
|
|
|
@@ -38,7 +44,7 @@ it(`creates the right data for standard fields`, async function() {
|
|
|
38
44
|
// ============================
|
|
39
45
|
it(`creates the right data for 'show if' fields`, async function() {
|
|
40
46
|
let result = await getAllFields( scope, { html: html.show_if });
|
|
41
|
-
expect( result ).to.deep.
|
|
47
|
+
expect( result ).to.deep.equalInAnyOrder( fields.show_if );
|
|
42
48
|
});
|
|
43
49
|
|
|
44
50
|
|
|
@@ -48,25 +54,25 @@ it(`creates the right data for 'show if' fields`, async function() {
|
|
|
48
54
|
it(`creates the right data for one continue button`, async function() {
|
|
49
55
|
// `continue button field:`
|
|
50
56
|
let result = await getAllFields( scope, { html: html.button_continue });
|
|
51
|
-
expect( result ).to.deep.
|
|
57
|
+
expect( result ).to.deep.equalInAnyOrder( fields.button_continue );
|
|
52
58
|
});
|
|
53
59
|
|
|
54
60
|
it(`creates the right data for yesnomaybe buttons`, async function() {
|
|
55
61
|
// `yesnomaybe:`
|
|
56
62
|
let result = await getAllFields( scope, { html: html.buttons_yesnomaybe });
|
|
57
|
-
expect( result ).to.deep.
|
|
63
|
+
expect( result ).to.deep.equalInAnyOrder( fields.buttons_yesnomaybe );
|
|
58
64
|
});
|
|
59
65
|
|
|
60
66
|
it(`creates the right data for other mutiple choice continue buttons`, async function() {
|
|
61
67
|
// `field:` and `buttons:`
|
|
62
68
|
let result = await getAllFields( scope, { html: html.buttons_other });
|
|
63
|
-
expect( result ).to.deep.
|
|
69
|
+
expect( result ).to.deep.equalInAnyOrder( fields.buttons_other );
|
|
64
70
|
});
|
|
65
71
|
|
|
66
72
|
it(`creates the right data for an event action button`, async function() {
|
|
67
73
|
// `field:` and `action buttons:`
|
|
68
74
|
let result = await getAllFields( scope, { html: html.buttons_event_action });
|
|
69
|
-
expect( result ).to.deep.
|
|
75
|
+
expect( result ).to.deep.equalInAnyOrder( fields.buttons_event_action );
|
|
70
76
|
});
|
|
71
77
|
|
|
72
78
|
|
|
@@ -76,7 +82,7 @@ it(`creates the right data for an event action button`, async function() {
|
|
|
76
82
|
// x[i].name.first
|
|
77
83
|
it(`creates the right data for a multi-proxy name (x[i])`, async function() {
|
|
78
84
|
let result = await getAllFields( scope, { html: html.proxies_xi });
|
|
79
|
-
expect( result ).to.deep.
|
|
85
|
+
expect( result ).to.deep.equalInAnyOrder( fields.proxies_xi );
|
|
80
86
|
});
|
|
81
87
|
|
|
82
88
|
// Multiple proxies by the same name are on the list (because of a loop)
|
|
@@ -84,7 +90,7 @@ it(`creates the right data for a multi-proxy name (x[i])`, async function() {
|
|
|
84
90
|
it(`creates the right data for a multi-proxy name (x[i]) again for consistency`, async function() {
|
|
85
91
|
// same data as `proxies_xi`, but have this here for consistency
|
|
86
92
|
let result = await getAllFields( scope, { html: html.proxies_multi });
|
|
87
|
-
expect( result ).to.deep.
|
|
93
|
+
expect( result ).to.deep.equalInAnyOrder( fields.proxies_multi );
|
|
88
94
|
});
|
|
89
95
|
|
|
90
96
|
|
|
@@ -94,7 +100,7 @@ it(`creates the right data for a multi-proxy name (x[i]) again for consistency`,
|
|
|
94
100
|
it(`creates the right data for a signature field`, async function() {
|
|
95
101
|
// `field:` and `action buttons:`
|
|
96
102
|
let result = await getAllFields( scope, { html: html.signature });
|
|
97
|
-
expect( result ).to.deep.
|
|
103
|
+
expect( result ).to.deep.equalInAnyOrder( fields.signature );
|
|
98
104
|
});
|
|
99
105
|
|
|
100
106
|
|
|
@@ -109,7 +115,7 @@ it(`creates the right data for a signature field`, async function() {
|
|
|
109
115
|
// ```
|
|
110
116
|
it(`creates the right data for a 'choices:' field`, async function() {
|
|
111
117
|
let result = await getAllFields( scope, { html: html.choices });
|
|
112
|
-
expect( result ).to.deep.
|
|
118
|
+
expect( result ).to.deep.equalInAnyOrder( fields.choices );
|
|
113
119
|
});
|
|
114
120
|
|
|
115
121
|
|
|
@@ -125,10 +131,9 @@ it(`creates the right data for a 'choices:' field`, async function() {
|
|
|
125
131
|
// ```
|
|
126
132
|
it(`creates the right data for a dropdown created with an object`, async function() {
|
|
127
133
|
let result = await getAllFields( scope, { html: html.object_dropdown });
|
|
128
|
-
expect( result ).to.deep.
|
|
134
|
+
expect( result ).to.deep.equalInAnyOrder(fields.object_dropdown );
|
|
129
135
|
});
|
|
130
136
|
|
|
131
|
-
|
|
132
137
|
// ============================
|
|
133
138
|
// DOM for mixed quotes test in getMatchingRows.test.js
|
|
134
139
|
// ============================
|
|
@@ -138,5 +143,5 @@ it(`creates the right data for a dropdown created with an object`, async functio
|
|
|
138
143
|
// following test is mostly useful for that second purpose.
|
|
139
144
|
it(`creates the right data for a question whose table will have mixed quotes`, async function() {
|
|
140
145
|
let result = await getAllFields( scope, { html: html.mixed_quotes });
|
|
141
|
-
expect( result ).to.deep.
|
|
146
|
+
expect( result ).to.deep.equalInAnyOrder( fields.mixed_quotes );
|
|
142
147
|
});
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
let test_filename = function ( expected_prefix, result ) {
|
|
11
11
|
/* Test a name to see if it's valid output with a language defined. */
|
|
12
|
-
let expected_name_regex_str = `^${ expected_prefix }-${ names.base_filename }-\.{
|
|
12
|
+
let expected_name_regex_str = `^${ expected_prefix }-${ names.base_filename }-\.{32}$`;
|
|
13
13
|
if ( expected_prefix == `` ) {
|
|
14
|
-
expected_name_regex_str = `^${ names.base_filename }-\.{
|
|
14
|
+
expected_name_regex_str = `^${ names.base_filename }-\.{32}$`;
|
|
15
15
|
}
|
|
16
16
|
let regex = new RegExp( expected_name_regex_str );
|
|
17
17
|
|
|
@@ -727,7 +727,6 @@ html.object_dropdown = `
|
|
|
727
727
|
</form>
|
|
728
728
|
</div>`;
|
|
729
729
|
|
|
730
|
-
|
|
731
730
|
html.mixed_quotes = `
|
|
732
731
|
<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">
|
|
733
732
|
<div data-variable="ZG91YmxlX3F1b3RlX2RpY3RbJ2RvdWJsZV9xdW90ZV9rZXknXQ" id="trigger" aria-hidden="true" style="display: none;"></div>
|