@suffolklitlab/alkiln 4.7.0 → 4.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -1
- package/lib/scope.js +61 -9
- package/lib/steps.js +89 -24
- package/lib/utils/files.js +1 -0
- package/package.json +1 -1
- package/tests/features/interactive_steps.feature +0 -5
- package/tests/features/random_input.feature +18 -2
- package/tests/features/story_table_formats.feature +49 -1
package/CHANGELOG.md
CHANGED
|
@@ -40,12 +40,25 @@ Format:
|
|
|
40
40
|
-->
|
|
41
41
|
<!-- ## [Unreleased] -->
|
|
42
42
|
|
|
43
|
+
## [4.7.1] - 2022-08-07
|
|
44
|
+
### Changed
|
|
45
|
+
- Ensure unique folder names for random input screenshots in case developer uses multiple random steps in one Scenario.
|
|
46
|
+
- Ensure random input detects unexpected interview errors.
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
- Fix failure when signature page is the first screen.
|
|
50
|
+
- Internal tests failing even when failure status is as expected.
|
|
51
|
+
- Double printing in report of scenario failure, one of them even when Scenario was possibly non-failure, like skipped.
|
|
52
|
+
|
|
43
53
|
## [4.7.0] - 2022-07-27
|
|
44
54
|
### Added
|
|
45
|
-
- Generate random input to fill out a simple form automatically
|
|
55
|
+
- Generate random input to fill out a simple form automatically. Does not yet ensure that random screenshot folder names are unique, or that error screens are detected as errors. 4.7.1 implements those.
|
|
46
56
|
|
|
47
57
|
### Changed
|
|
48
58
|
- Add page id to the "Missing Variable or variables on page" error report
|
|
59
|
+
- Allows Story Tables to not include a header row, as long as they have 2 or 3 columns.
|
|
60
|
+
This isn't the suggested way at the moment, since including headers is more readable,
|
|
61
|
+
but can prevent confusing and unnecessary errors.
|
|
49
62
|
|
|
50
63
|
## [4.6.2] - 2022-06-28
|
|
51
64
|
### Changed
|
package/lib/scope.js
CHANGED
|
@@ -191,6 +191,7 @@ module.exports = {
|
|
|
191
191
|
let report = ``;
|
|
192
192
|
for ( let line of data.lines ) {
|
|
193
193
|
if ( line.type === `page_id` ) { report += `screen id: `; }
|
|
194
|
+
else if ( line.type === `outcome` ) { report += `\n`; }
|
|
194
195
|
else if ( line.type === `warning` ) { report += `WARNING: `; }
|
|
195
196
|
else if ( line.type === `error` ) { report += `\nERROR: ` }
|
|
196
197
|
report += `${ line.value }\n`;
|
|
@@ -356,7 +357,7 @@ module.exports = {
|
|
|
356
357
|
// This check might never be needed now as we try to match fields on the page
|
|
357
358
|
// first in user-triggered cases - users used to be able to access this almost
|
|
358
359
|
// directly, but they can't anymore. Our functions should know to only pass good things.
|
|
359
|
-
let msg = `Cannot find
|
|
360
|
+
let msg = `Cannot find the element to tap.`;
|
|
360
361
|
if ( !elem ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
|
|
361
362
|
|
|
362
363
|
expect( elem, msg ).to.exist;
|
|
@@ -617,6 +618,9 @@ module.exports = {
|
|
|
617
618
|
let result = { error: ``, elem: null };
|
|
618
619
|
|
|
619
620
|
let winner = await Promise.race([
|
|
621
|
+
// Signature page
|
|
622
|
+
scope.page.waitForSelector( `#dasigpage`, { timeout: scope.timeout }),
|
|
623
|
+
// Any other valid page
|
|
620
624
|
scope.page.waitForSelector( `#daMainQuestion`, { timeout: scope.timeout }),
|
|
621
625
|
scope.page.waitForSelector( `#da-retry`, { timeout: scope.timeout }),
|
|
622
626
|
]);
|
|
@@ -626,10 +630,14 @@ module.exports = {
|
|
|
626
630
|
let error_id_elem = await scope.page.$( `#da-retry` );
|
|
627
631
|
if ( error_id_elem ) {
|
|
628
632
|
let error_elem = await scope.page.$( `blockquote` );
|
|
629
|
-
|
|
630
|
-
|
|
633
|
+
if (error_elem !== null) {
|
|
634
|
+
let error_handle = await error_elem.getProperty( `textContent` );
|
|
635
|
+
let system_error_text = await error_handle.jsonValue();
|
|
631
636
|
|
|
632
|
-
|
|
637
|
+
result.error = system_error_text;
|
|
638
|
+
} else {
|
|
639
|
+
result.error = "Docassemble ran into an error on the page, but ALKiln does not know what the error is. If a screenshot was taken, check the screenshot";
|
|
640
|
+
}
|
|
633
641
|
}
|
|
634
642
|
|
|
635
643
|
return result;
|
|
@@ -650,9 +658,40 @@ module.exports = {
|
|
|
650
658
|
}
|
|
651
659
|
}, // Ends scope.setLanguage()
|
|
652
660
|
|
|
653
|
-
normalizeTable: async function ( scope, { var_data,
|
|
654
|
-
/* Return data derived from cucumber variable-setting data
|
|
655
|
-
*
|
|
661
|
+
normalizeTable: async function ( scope, { var_data=null, raw_var_data=null }) {
|
|
662
|
+
/* Return data derived from cucumber variable-setting data, from either an object format
|
|
663
|
+
* (var_data) or an array format (raw_var_data). `var_data` only is used when calling
|
|
664
|
+
* normalizeTable internally.
|
|
665
|
+
*/
|
|
666
|
+
|
|
667
|
+
if (!var_data && ! raw_var_data) {
|
|
668
|
+
let no_data_msg = `ALKiln Internal error: normalizeTable called w/o var_data or raw_var_data:` +
|
|
669
|
+
` open an issue at https://github.com/SuffolkLITLab/ALKiln/issues/new if you see this!`;
|
|
670
|
+
await scope.addToReport( scope, { type: `error`, value: no_data_msg });
|
|
671
|
+
throw Error(no_data_msg);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Always use the raw var data if present; it has the full first row
|
|
675
|
+
if (raw_var_data) {
|
|
676
|
+
var_data = raw_var_data.hashes();
|
|
677
|
+
if (!('var' in var_data[0]) || !('value' in var_data[0])) {
|
|
678
|
+
let raw_array = raw_var_data.raw();
|
|
679
|
+
var_data = [];
|
|
680
|
+
for (let row_raw of raw_array) {
|
|
681
|
+
if (row_raw.length < 2) {
|
|
682
|
+
let bad_table_msg = `Your Story Table definition needs to be changed. The best practice`
|
|
683
|
+
+ ` is to make your table 3 columns wide with a first row in this format: "| var | value | trigger |".`
|
|
684
|
+
+ ` The first row of your table is actually "${ raw_array[0] }"`;
|
|
685
|
+
await scope.addToReport( scope, { type: `error`, value: bad_table_msg})
|
|
686
|
+
throw Error(bad_table_msg);
|
|
687
|
+
} else if (row_raw.length == 2) {
|
|
688
|
+
var_data.push({var: row_raw[0], value: row_raw[1]});
|
|
689
|
+
} else {
|
|
690
|
+
var_data.push({var: row_raw[0], value: row_raw[1], trigger: row_raw[2]});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
656
695
|
|
|
657
696
|
// Support tables with no 'trigger' column
|
|
658
697
|
let supported_table = [];
|
|
@@ -1934,6 +1973,20 @@ module.exports = {
|
|
|
1934
1973
|
}
|
|
1935
1974
|
}, // Ends scope.getThereIsAnotherValue()
|
|
1936
1975
|
|
|
1976
|
+
// throw_if_no_interview_content()?
|
|
1977
|
+
has_interview_content: async function ( scope ) {
|
|
1978
|
+
/** Return false if ther's no element indicating a da interview question,
|
|
1979
|
+
* like a signature page, other fields, or interview text. Assumes that page has already
|
|
1980
|
+
* finished navigating. */
|
|
1981
|
+
let elem_da_question = await scope.page.$( `#daMainQuestion` );
|
|
1982
|
+
let elem_da_signature = await scope.page.$( `#dasigpage` );
|
|
1983
|
+
if ( elem_da_question === null && elem_da_signature === null ) {
|
|
1984
|
+
return false;
|
|
1985
|
+
} else {
|
|
1986
|
+
return true;
|
|
1987
|
+
}
|
|
1988
|
+
}, // Ends scope.has_interview_content()
|
|
1989
|
+
|
|
1937
1990
|
waitUntilContinued: async function ( scope, { url = '', id = '' }) {
|
|
1938
1991
|
/** Given a url, attempt to continue until the page url changes or an error
|
|
1939
1992
|
is found. Return the error object no matter what. */
|
|
@@ -2279,7 +2332,6 @@ module.exports = {
|
|
|
2279
2332
|
|
|
2280
2333
|
// For every field on the page
|
|
2281
2334
|
for ( let field of fields ) {
|
|
2282
|
-
|
|
2283
2335
|
// fields[n].tag is HTML tag of node, as a string
|
|
2284
2336
|
|
|
2285
2337
|
// If the field is a button, save it in a list of buttons that we'll deal with later
|
|
@@ -2406,7 +2458,7 @@ module.exports = {
|
|
|
2406
2458
|
if ( type === `file` ) {
|
|
2407
2459
|
await scope.addToReport( scope, {
|
|
2408
2460
|
type: `warning`,
|
|
2409
|
-
value: `Sorry, ALKiln's random
|
|
2461
|
+
value: `Sorry, ALKiln's random input tests cannot yet handle "${ type }" fields.`
|
|
2410
2462
|
});
|
|
2411
2463
|
} else {
|
|
2412
2464
|
let answer = await scope.get_random_input_for[ type ]( scope );
|
package/lib/steps.js
CHANGED
|
@@ -334,7 +334,8 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
|
|
|
334
334
|
// It also seems harsh, but more reliable.
|
|
335
335
|
// Ask developers what they'd like.
|
|
336
336
|
|
|
337
|
-
|
|
337
|
+
|
|
338
|
+
let supported_table = await scope.normalizeTable( scope, { raw_var_data });
|
|
338
339
|
|
|
339
340
|
let { question_has_id, id, id_matches } = await scope.examinePageID( scope, target_id );
|
|
340
341
|
// Until we reach the final page or hit an error
|
|
@@ -641,7 +642,7 @@ Then(/the "([^"]+)" link opens in (a new window|the same window)/, async (linkTe
|
|
|
641
642
|
let [link] = await scope.page.$x(`//a[contains(text(), "${linkText}")]`);
|
|
642
643
|
|
|
643
644
|
let prop_obj = await link.getProperty('target');
|
|
644
|
-
let target = await
|
|
645
|
+
let target = await prop_obj.jsonValue();
|
|
645
646
|
|
|
646
647
|
let should_open_a_new_window = which_window === 'a new window';
|
|
647
648
|
let opens_a_new_window = target === '_blank';
|
|
@@ -790,35 +791,61 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
790
791
|
* I answer randomly for at most 10 pages
|
|
791
792
|
* I answer randomly for at most 101 screens
|
|
792
793
|
*/
|
|
793
|
-
|
|
794
|
+
// Give each screenshots folder a time-stamped name in case the dev gets clever
|
|
795
|
+
// and use the same Scenario to run multiple randomized tests.
|
|
796
|
+
let timestamp = files.readable_date();
|
|
797
|
+
let random_input_path = `${ scope.paths.scenario }/random_answers_screenshots-${ timestamp }`
|
|
798
|
+
scope.paths.random_screenshots = random_input_path;
|
|
794
799
|
fs.mkdirSync( scope.paths.random_screenshots );
|
|
800
|
+
await scope.addToReport( scope, {
|
|
801
|
+
type: 'row',
|
|
802
|
+
value: `Testing interview with random input (in ${ random_input_path })`
|
|
803
|
+
});
|
|
795
804
|
|
|
796
805
|
// Force the dev to prevent infinite loops
|
|
797
806
|
let max_pages = parseInt( max_pages_str );
|
|
798
807
|
let curr_page_num = 0;
|
|
799
808
|
|
|
800
|
-
// While a continue button is on the current page, fill out
|
|
809
|
+
// While a continue-type button is on the current page, fill out
|
|
801
810
|
// the fields and then try to continue
|
|
802
|
-
// let continue_exists = true;
|
|
803
|
-
// while ( continue_exists && curr_page_num < max_pages ) {
|
|
804
811
|
let buttons_exist = true;
|
|
805
812
|
while ( buttons_exist && curr_page_num < max_pages ) {
|
|
806
813
|
|
|
807
814
|
curr_page_num = curr_page_num + 1;
|
|
808
815
|
let { id } = await scope.examinePageID( scope, `no target id` );
|
|
809
816
|
|
|
817
|
+
let no_content_err = `The interview seemed to run into an error page`
|
|
818
|
+
+ ` after the page with the question id "${ id }".`
|
|
819
|
+
+ ` See the screenshot in "${ random_input_path }".`;
|
|
820
|
+
|
|
821
|
+
let has_thrown = false;
|
|
810
822
|
// Ensure no infinite loop, but each page has a full timeout to run
|
|
811
823
|
let custom_timeout = new Promise(async function( resolve, reject ) {
|
|
812
824
|
|
|
813
825
|
// Set up timeout to stop infinite loops
|
|
814
826
|
let page_timeout_id = setTimeout(async function() {
|
|
815
827
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
throw( timeout_message );
|
|
828
|
+
// Make sure to only throw once
|
|
829
|
+
if ( has_thrown ) { return; }
|
|
830
|
+
else { has_thrown = true; }
|
|
820
831
|
|
|
821
|
-
|
|
832
|
+
let has_content = await scope.has_interview_content( scope );
|
|
833
|
+
|
|
834
|
+
if ( !has_content ) {
|
|
835
|
+
await scope.addToReport( scope, {
|
|
836
|
+
type: `error`,
|
|
837
|
+
value: no_content_err
|
|
838
|
+
});
|
|
839
|
+
reject( no_content_err );
|
|
840
|
+
|
|
841
|
+
} else {
|
|
842
|
+
let timeout_message = `The page with an id of "${ id }" took too long`
|
|
843
|
+
+ ` to fill with random answers (over ${ scope.timeout/1000/60 } min).`
|
|
844
|
+
await scope.addToReport( scope, { type: `error`, value: timeout_message });
|
|
845
|
+
reject( timeout_message );
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
}, scope.timeout );
|
|
822
849
|
|
|
823
850
|
// self-invoke an anonymous async function so we don't have to
|
|
824
851
|
// abstract the details of resolving and rejecting.
|
|
@@ -830,7 +857,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
830
857
|
|
|
831
858
|
// Take a screenshot of the page before continuing or ending
|
|
832
859
|
await scope.page.screenshot({
|
|
833
|
-
path: `${ scope.paths.random_screenshots }
|
|
860
|
+
path: `${ scope.paths.random_screenshots }/random-${ Date.now() }.jpg`,
|
|
834
861
|
type: `jpeg`,
|
|
835
862
|
fullPage: true
|
|
836
863
|
});
|
|
@@ -848,7 +875,23 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
848
875
|
if ( buttons_exist ) {
|
|
849
876
|
await scope.continue( scope );
|
|
850
877
|
} // ends if continue exists
|
|
851
|
-
}
|
|
878
|
+
} // ends if buttons_exist
|
|
879
|
+
|
|
880
|
+
let has_content = await scope.has_interview_content( scope );
|
|
881
|
+
if ( !has_content ) {
|
|
882
|
+
|
|
883
|
+
// Throw an error if there's no interview content
|
|
884
|
+
// Make sure to only throw once
|
|
885
|
+
if ( !has_thrown ) {
|
|
886
|
+
has_thrown = true;
|
|
887
|
+
await scope.addToReport( scope, {
|
|
888
|
+
type: `error`,
|
|
889
|
+
value: no_content_err
|
|
890
|
+
});
|
|
891
|
+
reject( no_content_err );
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
} // ends if !has_content
|
|
852
895
|
|
|
853
896
|
if ( buttons_exist ) {
|
|
854
897
|
process.stdout.write(`\x1b[36m${ 'v' }\x1b[0m`); // pressed button
|
|
@@ -856,11 +899,28 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
|
|
|
856
899
|
|
|
857
900
|
resolve();
|
|
858
901
|
|
|
859
|
-
} catch (
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
902
|
+
} catch ( error ) {
|
|
903
|
+
|
|
904
|
+
// Make sure to only throw once
|
|
905
|
+
if ( has_thrown ) { return; }
|
|
906
|
+
else { has_thrown = true; }
|
|
907
|
+
|
|
908
|
+
// Throw a specific error if there's no interview content
|
|
909
|
+
let has_content = await scope.has_interview_content( scope );
|
|
910
|
+
if ( !has_content ) {
|
|
911
|
+
await scope.addToReport( scope, {
|
|
912
|
+
type: `error`,
|
|
913
|
+
value: no_content_err
|
|
914
|
+
});
|
|
915
|
+
reject( no_content_err );
|
|
916
|
+
|
|
917
|
+
// Otherwise throw a generic error
|
|
918
|
+
} else {
|
|
919
|
+
let err_msg = `The page with the question id "${ id }" errored when trying to fill`
|
|
920
|
+
+ ` it with random answers: ${ error.name }`
|
|
921
|
+
await scope.addToReport( scope, { type: `error`, value: err_msg });
|
|
922
|
+
reject( error );
|
|
923
|
+
} // ends if !has_content
|
|
864
924
|
|
|
865
925
|
} finally {
|
|
866
926
|
// prevent temporary hang at end of tests
|
|
@@ -1051,9 +1111,9 @@ After(async function(scenario) {
|
|
|
1051
1111
|
if ( scope.page ) { await scope.page.waitForTimeout( 60 * 1000 ); }
|
|
1052
1112
|
}
|
|
1053
1113
|
|
|
1054
|
-
await scope.addToReport(scope, { type: `outcome`, value: `**-- Scenario Failed --**` });
|
|
1055
1114
|
if ( scope.page ) {
|
|
1056
|
-
|
|
1115
|
+
|
|
1116
|
+
if ( !!scope.disable_error_screenshot ) {
|
|
1057
1117
|
scope.addToReport(scope, {
|
|
1058
1118
|
type: `row`,
|
|
1059
1119
|
value: `For security, ALKiln will not create a screenshot for this error. It's possible a secret is being used on this screen.`
|
|
@@ -1079,11 +1139,12 @@ After(async function(scenario) {
|
|
|
1079
1139
|
} // ends if scope.disable_error_screenshot
|
|
1080
1140
|
} // ends if scope.page exists
|
|
1081
1141
|
|
|
1082
|
-
|
|
1142
|
+
// This has to come after the security message or security message doesn't print. Not sure why.
|
|
1143
|
+
if (scenario.result.status === `FAILED`) {
|
|
1144
|
+
await scope.addToReport(scope, { type: `outcome`, value: `**-- Scenario Failed --**` });
|
|
1145
|
+
}
|
|
1083
1146
|
|
|
1084
|
-
|
|
1085
|
-
await scope.addToReport(scope, { type: `outcome`, value: `**-- Scenario Failed --**` });
|
|
1086
|
-
}
|
|
1147
|
+
} // ends if not passed
|
|
1087
1148
|
|
|
1088
1149
|
let original_scenario_status = scenario.result.status;
|
|
1089
1150
|
|
|
@@ -1092,13 +1153,17 @@ After(async function(scenario) {
|
|
|
1092
1153
|
let expected = scope.expected_status;
|
|
1093
1154
|
scope.expected_status = null; // reset for next Scenario
|
|
1094
1155
|
expect( scenario.result.status ).to.equal( expected );
|
|
1156
|
+
// If the status was as expected, pass this criteria
|
|
1157
|
+
scenario.result.status = `PASSED`;
|
|
1095
1158
|
}
|
|
1096
1159
|
if ( scope.expected_in_report && scope.expected_in_report.length > 0) {
|
|
1097
1160
|
let expected = scope.expected_in_report;
|
|
1098
1161
|
// Reset report values no matter what so they don't mess up future scenarios
|
|
1099
1162
|
scope.expected_in_report = null;
|
|
1100
1163
|
let all_were_included = await scope.reportIncludesAllExpected( scope, { expected });
|
|
1164
|
+
|
|
1101
1165
|
if ( all_were_included ) { scenario.result.status = `PASSED`; }
|
|
1166
|
+
else { scenario.result.status = `FAILED`; }
|
|
1102
1167
|
}
|
|
1103
1168
|
|
|
1104
1169
|
// If there is a page open, then sign out and close it
|
package/lib/utils/files.js
CHANGED
|
@@ -8,6 +8,7 @@ files.readable_date = function() {
|
|
|
8
8
|
* match the workflow artifact date format. */
|
|
9
9
|
let date = new Date(); // now
|
|
10
10
|
let str = date.toLocaleString('en-GB', { timeZone: 'UTC' });
|
|
11
|
+
// To properly get the year/month/day format, look at https://stackoverflow.com/a/48729396/14144258
|
|
11
12
|
let fancy_str = str.replace(/\//g, '-').replace(',', ' at').replace(':', 'hrs ').replace(':', 'mins ') + 'secs UTC';
|
|
12
13
|
return fancy_str;
|
|
13
14
|
}; // Ends files.readable_date()
|
package/package.json
CHANGED
|
@@ -132,8 +132,3 @@ 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 @random
|
|
137
|
-
Scenario: I answer randomly till the end
|
|
138
|
-
Given I start the interview at "all_tests"
|
|
139
|
-
And I answer randomly for at most 50 pages
|
|
@@ -3,13 +3,29 @@ Feature: Assembly Line package-specific Steps
|
|
|
3
3
|
|
|
4
4
|
# In tag names, 'ri' is for 'random input'
|
|
5
5
|
|
|
6
|
-
@
|
|
6
|
+
@fast @ri1 @random
|
|
7
7
|
Scenario: I fill in random input till the end
|
|
8
8
|
Given I start the interview at "test_random_input"
|
|
9
9
|
And I answer randomly for at most 20 pages
|
|
10
10
|
Then the question id should be "end"
|
|
11
11
|
|
|
12
|
-
@
|
|
12
|
+
@fast @ri2 @random
|
|
13
13
|
Scenario: I fill in random input for 1 page
|
|
14
14
|
Given I start the interview at "test_random_input"
|
|
15
15
|
And I answer randomly for at most 1 page
|
|
16
|
+
|
|
17
|
+
# Should create two unique folders for random step screenshots which
|
|
18
|
+
# must be checked manually
|
|
19
|
+
@fast @ri3 @random
|
|
20
|
+
Scenario: I answer randomly till the end twice
|
|
21
|
+
Given I start the interview at "test_random_input"
|
|
22
|
+
And I answer randomly for at most 50 pages
|
|
23
|
+
Given I start the interview at "test_random_input"
|
|
24
|
+
And I answer randomly for at most 50 pages
|
|
25
|
+
|
|
26
|
+
@fast @ri4 @random @error
|
|
27
|
+
Scenario: Fail with error page from random input
|
|
28
|
+
Given the final Scenario status should be "failed"
|
|
29
|
+
Given I start the interview at "test_missing_var_error_screen"
|
|
30
|
+
And the max seconds for each step in this scenario is 10
|
|
31
|
+
And I answer randomly for at most 3 pages
|
|
@@ -3,7 +3,6 @@ Feature: Supported story table formats
|
|
|
3
3
|
|
|
4
4
|
NOTE:
|
|
5
5
|
Unsupported formats:
|
|
6
|
-
- Arrays (see https://github.com/plocket/docassemble-cucumber/issues/257)
|
|
7
6
|
- Format 1 and 2 tables (permanent) (see https://github.com/plocket/docassemble-cucumber/issues/264)
|
|
8
7
|
|
|
9
8
|
NOTE:
|
|
@@ -75,3 +74,52 @@ Scenario: Table MISSING trigger column
|
|
|
75
74
|
| single_quote_dict['single_quote_key']['sq_two'] | true |
|
|
76
75
|
| text_input | Regular text input field value |
|
|
77
76
|
| textarea | Multiline text\narea value |
|
|
77
|
+
|
|
78
|
+
@fast @stf5
|
|
79
|
+
Scenario: Table has no header row, has trigger column
|
|
80
|
+
Given I start the interview at "all_tests"
|
|
81
|
+
And I get to "showifs" with this data:
|
|
82
|
+
| double_quote_dict["double_quote_key"]['dq_two'] | true | |
|
|
83
|
+
| checkboxes_other['checkbox_other_opt_1'] | true | |
|
|
84
|
+
| checkboxes_other['checkbox_other_opt_2'] | true | |
|
|
85
|
+
| checkboxes_other['checkbox_other_opt_3'] | false | |
|
|
86
|
+
| checkboxes_yesno | True | |
|
|
87
|
+
| dropdown_test | dropdown_opt_2 | |
|
|
88
|
+
| radio_other | radio_other_opt_3 | |
|
|
89
|
+
| radio_yesno | False | |
|
|
90
|
+
| single_quote_dict['single_quote_key']['sq_two'] | true | |
|
|
91
|
+
| text_input | Regular text input field value | |
|
|
92
|
+
| textarea | Multiline text\narea value | |
|
|
93
|
+
|
|
94
|
+
@fast @stf6
|
|
95
|
+
Scenario: Table has no header row, MISSING trigger column
|
|
96
|
+
Given I start the interview at "all_tests"
|
|
97
|
+
And I get to "showifs" with this data:
|
|
98
|
+
| double_quote_dict["double_quote_key"]['dq_two'] | true |
|
|
99
|
+
| checkboxes_other['checkbox_other_opt_2'] | true |
|
|
100
|
+
| checkboxes_yesno | True |
|
|
101
|
+
| dropdown_test | dropdown_opt_2 |
|
|
102
|
+
| radio_other | radio_other_opt_3 |
|
|
103
|
+
| radio_yesno | False |
|
|
104
|
+
| single_quote_dict['single_quote_key']['sq_two'] | true |
|
|
105
|
+
| text_input | Regular text input field value |
|
|
106
|
+
| textarea | Multiline text\narea value |
|
|
107
|
+
|
|
108
|
+
@fast @stf7
|
|
109
|
+
Scenario: Fails when table has no header row and rows have only one column
|
|
110
|
+
Given the final Scenario status should be "failed"
|
|
111
|
+
And the Scenario report should include:
|
|
112
|
+
"""
|
|
113
|
+
Your Story Table definition needs to be changed.
|
|
114
|
+
"""
|
|
115
|
+
Given I start the interview at "all_tests"
|
|
116
|
+
And I get to "showifs" with this data:
|
|
117
|
+
| double_quote_dict["double_quote_key"]['dq_two'] |
|
|
118
|
+
| checkboxes_other['checkbox_other_opt_2'] |
|
|
119
|
+
| checkboxes_yesno |
|
|
120
|
+
| dropdown_test |
|
|
121
|
+
| radio_other |
|
|
122
|
+
| radio_yesno |
|
|
123
|
+
| single_quote_dict['single_quote_key']['sq_two'] |
|
|
124
|
+
| text_input |
|
|
125
|
+
| textarea |
|