@suffolklitlab/alkiln 4.0.0-pre-api.8 → 4.1.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/lib/steps.js CHANGED
@@ -19,7 +19,7 @@ const { v4: uuidv4 } = require('uuid');
19
19
 
20
20
  // Ours
21
21
  const scope = require('./scope');
22
- const env_vars = require('./utils/env_vars');
22
+ const session_vars = require('./utils/session_vars');
23
23
 
24
24
  /* Of Note:
25
25
  - We're using `*=` because sometimes da text has funny characters in it that are hard to anticipate
@@ -33,7 +33,7 @@ require("dotenv").config(); // Where did this go?
33
33
  1. 'choice' to have a more specific way to access each item. For
34
34
  example, for a list collect or other things that have multiple
35
35
  things with the same text on the page.
36
- 1. Figure out how to test allowing felxibility for coder. For example:
36
+ 1. Figure out how to test allowing flexibility for coder. For example:
37
37
  there is placeholder text for the title of the form and if it's not
38
38
  defined, placeholder text should appear (though that behavior may
39
39
  bear discussion).
@@ -180,7 +180,7 @@ Given(/I start the interview at "([^"]+)"(?: in lang "([^"]+)")?/, {timeout: -1}
180
180
 
181
181
  // If response has a file in it that matches the file we are downloading
182
182
  if ( response._headers['content-disposition'] && (response._headers['content-disposition']).includes(`attachment;filename=${scope.toDownload}`) ) {
183
- // We're chosing avoid removing the event listener in case removal
183
+ // We're choosing to avoid removing the event listener in case removal
184
184
  // would prevent downloading other files. The listener should get
185
185
  // destroyed when the page closes anyway.
186
186
 
@@ -219,7 +219,7 @@ Given(/I start the interview at "([^"]+)"(?: in lang "([^"]+)")?/, {timeout: -1}
219
219
  await custom_lang_timeout;
220
220
 
221
221
 
222
- let custom_aferStep_timeout = new Promise(async ( resolve, reject ) => {
222
+ let custom_afterStep_timeout = new Promise(async ( resolve, reject ) => {
223
223
  let page_timeout = setTimeout(function() { reject(`\`afterStep()\` took too long.`); }, scope.timeout);
224
224
  // self-invoke an anonymous async function so we don't have to
225
225
  // abstract the details of resolving and rejecting.
@@ -230,8 +230,8 @@ Given(/I start the interview at "([^"]+)"(?: in lang "([^"]+)")?/, {timeout: -1}
230
230
  // prevent temporary hang at end of tests
231
231
  finally { clearTimeout( page_timeout ); }
232
232
  })();
233
- }); // ends custom_aferStep_timeout
234
- await custom_aferStep_timeout;
233
+ }); // ends custom_afterStep_timeout
234
+ await custom_afterStep_timeout;
235
235
  });
236
236
 
237
237
  Given(/I (?:sign|log) ?(?:in)?(?:on)?(?:to the server)? with(?: the email)? "([^"]+)" and(?: the password)? "([^"]+)"(?: SECRETs)?/i, {timeout: -1}, async ( email, password ) => {
@@ -252,7 +252,7 @@ Given(/I (?:sign|log) ?(?:in)?(?:on)?(?:to the server)? with(?: the email)? "([^
252
252
  );
253
253
  });
254
254
 
255
- // I am using a moble/pc
255
+ // I am using a mobile/pc
256
256
  When(/I am using an? (.*)/, async ( device ) => {
257
257
  // Let developer pick mobile device if they want to
258
258
  // TODO: Add tablet?
@@ -350,7 +350,7 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
350
350
  for ( let row of supported_table ) {
351
351
  row.report_str = `${ await scope.convertToOriginalStoryTableRow(scope, { row_data: row }) }`;
352
352
  }
353
- // Sort into aphabetical order, as is recommended for writing story tables
353
+ // Sort into alphabetical order, as is recommended for writing story tables
354
354
  let sorted_rows = supported_table.sort( function ( row1, row2 ) {
355
355
  if( row1.report_str < row2.report_str ) { return -1; }
356
356
  if( row1.report_str > row2.report_str ) { return 1; }
@@ -404,7 +404,7 @@ When(/I wait (\d*\.?\d+) seconds?/, { timeout: -1 }, async ( seconds ) => {
404
404
  * - Should this really reset the 'navigated' flag? Should the below
405
405
  * return succeed or fail? e.g:
406
406
  * I tap to continue; I wait 10 seconds; I arrive at the next page;
407
- * Maybe the 'navitaged' flag should only be reset just before trying to
407
+ * Maybe the 'navigated' flag should only be reset just before trying to
408
408
  * navigate (after pressing a button)
409
409
  */
410
410
  return wrapPromiseWithTimeout(
@@ -426,7 +426,7 @@ Then(/I take a screenshot ?(?:named "([^"]+)")?/, { timeout: -1 }, async ( name
426
426
  When("I do nothing", async () => { await scope.afterStep(scope); }); // √
427
427
 
428
428
  Then('the question id should be {string}', { timeout: -1 }, async ( question_id ) => {
429
- /* Looks for a santized version of the question id as it's written
429
+ /* Looks for a sanitized version of the question id as it's written
430
430
  * in the .yml. docassemble's way */
431
431
  let { question_has_id, id, id_matches } = await scope.examinePageID( scope, question_id );
432
432
 
@@ -449,7 +449,7 @@ Then('the question id should be {string}', { timeout: -1 }, async ( question_id
449
449
  });
450
450
 
451
451
  // Allow emphasis with capital letters
452
- Then(/I should see the phrase "([^"]+)"/i, async ( phrase ) => { // √
452
+ Then(/I ?(?:should)? see the phrase "([^"]+)"/i, async ( phrase ) => { // √
453
453
  /* In Chrome, this `innerText` gets only visible text */
454
454
  const bodyText = await scope.page.$eval('body', elem => elem.innerText);
455
455
 
@@ -567,7 +567,7 @@ Then('I arrive at the next page', async () => { // √
567
567
 
568
568
  // Link observations have the 'Escape' button link in mind
569
569
  Then('I should see the link {string}', async ( linkText ) => { // √
570
- /* Loosley match link visible text. Should probably deprecate this. */
570
+ /* Loosely match link visible text. Should probably deprecate this. */
571
571
  let [link] = await scope.page.$x(`//a[contains(text(), "${linkText}")]`);
572
572
 
573
573
  let msg = `Cannot find a link with the text "${ linkText }".`;
@@ -668,7 +668,7 @@ Then(/I get the(?: page's)?(?: JSON)? var(?:iable)?s and val(?:ue)?s/i, { timeou
668
668
  * to get the variable values on the page of the interview and add
669
669
  * them to the report.
670
670
  *
671
- * Varations:
671
+ * Variations:
672
672
  * I get the page's JSON variables and values
673
673
  * I get the json vars and vals
674
674
  * I get the page's vars and vals
@@ -717,7 +717,7 @@ When('I tap to continue', { timeout: -1 }, async () => {
717
717
  // --- VARIABLES-BASED STEPS ---
718
718
 
719
719
  When(/I set the var(?:iable)? "([^"]+)" to "([^"]+)"/, { timeout: -1 }, async ( var_name, set_to ) => {
720
- /* Set any variable (with or without a choice associated with it) to a value. Set:
720
+ /* Set a non-story table variable (with or without a choice associated with it) to a value. Set:
721
721
  * - Buttons associated with variables
722
722
  * - Dropdowns
723
723
  * - Checkboxes (multiple choice checkboxes must be set to "true" or "false")
@@ -725,7 +725,7 @@ When(/I set the var(?:iable)? "([^"]+)" to "([^"]+)"/, { timeout: -1 }, async (
725
725
  * - Text inputs
726
726
  * - Textareas
727
727
  *
728
- * Examples:
728
+ * Variations:
729
729
  * 1. I set the var "benefits['SSI']" to "false"
730
730
  * 1. I set the var "rent_interval" to "weekly"
731
731
  */
@@ -736,7 +736,7 @@ When(/I set the var(?:iable)? "([^"]+)" to "([^"]+)"/, { timeout: -1 }, async (
736
736
  });
737
737
 
738
738
  Then(/I upload "([^"]+)" to "([^"]+)"/, { timeout: -1 }, async ( filenames, var_name ) => {
739
- /* Uploads sibling file (docx, jpg, etc) to input var. Can actually use
739
+ /* Uploads file (docx, jpg, etc) in same folder to input var. Can actually use
740
740
  * 'I set the var "" to ""', but this is more explicit if it's desired. */
741
741
  return wrapPromiseWithTimeout(
742
742
  scope.steps.set_regular_var( scope, var_name, filenames ),
@@ -867,9 +867,9 @@ After(async function(scenario) {
867
867
  await scope.setReportScenarioStatus( scope, { status: scenario.result.status });
868
868
 
869
869
  if (scenario.result.status !== `PASSED`) {
870
- if ( env_vars.DEBUG ) {
870
+ if ( session_vars.get_debug() ) {
871
871
  console.log( `Non-passed status occurred while running test:`, scenario.result.status );
872
- // If there's a page visible, give us time to examine it
872
+ // If there's a page visible (locally), give us time to examine it
873
873
  if ( scope.page ) { await scope.page.waitForTimeout( 60 * 1000 ); }
874
874
  }
875
875
 
@@ -909,15 +909,15 @@ After(async function(scenario) {
909
909
  // Consider, instead, catching the reload error for a bad restart and just
910
910
  // re-trying at that point.
911
911
  try {
912
- await scope.page.goto(`${ env_vars.BASE_URL }/user/sign-out`, {waitUntil: `domcontentloaded`});
912
+ await scope.page.goto(`${ session_vars.get_da_server_url() }/user/sign-out`, {waitUntil: `domcontentloaded`});
913
913
  } catch ( err ) {
914
- if ( env_vars.DEBUG ) {
914
+ if ( session_vars.DEBUG ) {
915
915
  console.log( `Trying to reset for the next test a second time because the first time failed.` );
916
916
  }
917
917
 
918
918
  try {
919
- await scope.page.goto(`${ env_vars.BASE_URL }/user/sign-out`, {waitUntil: `domcontentloaded`});
920
-
919
+ await scope.page.goto(`${ session_vars.get_da_server_url() }/user/sign-out`, {waitUntil: `domcontentloaded`});
920
+
921
921
  } catch ( err ) {
922
922
  await scope.addToReport(scope, {
923
923
  type: `warning`,
@@ -4,7 +4,7 @@ let _path = require('path');
4
4
  let fs = require('fs');
5
5
  let glob = require('glob');
6
6
 
7
- let env_vars = require('./env_vars');
7
+ let session_vars = require('./session_vars');
8
8
 
9
9
  // Get the glob filepaths to find all the test files
10
10
  let dir_root = process.env.INIT_CWD; // Path to root of project
@@ -12,7 +12,7 @@ let test_glob = process.argv[2]; // String of glob (regex for filepaths)
12
12
  let filepaths = glob.sync( _path.resolve( dir_root, test_glob ) );
13
13
  let flag = '_lang_';
14
14
 
15
- let langs = env_vars.EXTRA_LANGUAGES;
15
+ let langs = session_vars.get_languages();
16
16
 
17
17
  // Generate new language files
18
18
  for ( let full_path of filepaths ) { // Handle old language files before creating new ones.
@@ -20,7 +20,7 @@ for ( let full_path of filepaths ) { // Handle old language files before creati
20
20
  // Don't apply this to the example test that this package always provides
21
21
  if ( full_path.includes( 'example_test.feature' )) { continue; }
22
22
 
23
- // Delete alredy existing non-default language files and skip the rest of this. For example,
23
+ // Delete already existing non-default language files and skip the rest of this. For example,
24
24
  // if 3 languages were tested previously and now only two are being tested.
25
25
  if ( full_path.includes( flag )) {
26
26
  fs.unlinkSync( full_path );
@@ -10,75 +10,28 @@ require('dotenv').config();
10
10
  /* This file provides user info variables. E.g. server address. They're either
11
11
  * `process.env` vars or vars that have been built from `process.env` vars.
12
12
  * It is meant to avoid duplication, make testing easier, and leave room for
13
- * future maninpulation. No other file should need to use `process.env`.
13
+ * future manipulation. No other file should need to use `process.env`.
14
14
  *
15
15
  * We have a bunch of `.get_foo()` functions partly because it makes the code
16
16
  * much easier to test.
17
17
  */
18
18
 
19
- const session_vars = {
20
- PLAYGROUND_ID: process.env.PLAYGROUND_ID, // Need to get rid of this in favor of API
21
- };
19
+ const session_vars = {};
22
20
 
23
21
  module.exports = session_vars;
24
22
 
25
- /**
26
- * Ensure all required environment variables exist. Add each missing variable
27
- * to an error that will be thrown at the end.
28
- *
29
- * @throws ReferenceError with a message for all missing variables.
30
- */
31
- session_vars.validateEnvironment = function() {
32
-
33
- // We are checking for BASE_URL, but we are referring it as SERVER_URL as that
34
- // is the name used in GitHub secrets, which is what most devs will be using.
35
-
36
- let errorMessage = '';
37
-
38
- // Since this code will most often be run in GitHub, I'm going to build a single error message that outlines all missing variables. Otherwise, a developer
39
- // will have to run their pipeline an incredible number of times to discover all the missing variables.
40
- if (!process.env.DOCASSEMBLE_DEVELOPER_API_KEY)
41
- {
42
- errorMessage += `\nThe DOCASSEMBLE_DEVELOPER_API_KEY GitHub secret must be defined. The DOCASSEMBLE_DEVELOPER_API_KEY is a docassemble API key you created for your server's testing account. It should have developer permissions.`
43
- }
44
- if (!process.env.PLAYGROUND_ID)
45
- {
46
- errorMessage += '\nThe PLAYGROUND_ID GitHub secret must be defined. The PLAYGROUND_ID is the ID of the Docassemble suite to run.'
47
- }
48
- if (!process.env.SERVER_URL && !process.env.BASE_URL)
49
- {
50
- errorMessage += `\nThe SERVER_URL GitHub secret must be defined. The SERVER_URL is the URL of the Docassemble Server where the tests should be run.`;
51
- }
52
- if (!process.env.REPO_URL)
53
- {
54
- errorMessage += `\nThe REPO_URL GitHub secret must be defined. The REPO_URL is the URL to the Git repo whose Actions are being run.`;
55
- }
56
- if (!process.env.BRANCH_PATH && !process.env.BRANCH_NAME)
57
- {
58
- errorMessage += `\nThe BRANCH_NAME environment variable must be defined. The BRANCH_NAME is the name of the repository branch that should be pulled into the docassemble project.`;
59
- }
60
-
61
- errorMessage = errorMessage.trim();
62
-
63
- if (errorMessage !== '') {
64
- throw new ReferenceError(errorMessage);
65
- }
66
- };
67
-
68
- session_vars.validateEnvironment();
23
+ session_vars.get_debug = function () { return process.env.DEBUG || null; };
24
+ session_vars.get_dev_api_key = function () { return process.env.DOCASSEMBLE_DEVELOPER_API_KEY || null; };
25
+ session_vars.get_repo_url = function () { return process.env.REPO_URL || null; };
69
26
 
70
- session_vars.get_dev_api_key = function () { return process.env.DOCASSEMBLE_DEVELOPER_API_KEY; };
71
27
  session_vars.get_da_server_url = function () {
72
- let url = process.env.SERVER_URL || process.env.BASE_URL;
73
- return url.replace(/\/$/g, ''); // Remove any trailing slash for safety
28
+ try {
29
+ let url = process.env.SERVER_URL || process.env.BASE_URL;
30
+ return url.replace(/\/$/g, ''); // Remove any trailing slash for safety
31
+ } catch ( error ) {
32
+ return null;
33
+ }
74
34
  };
75
- session_vars.get_debug = function () { return process.env.DEBUG; };
76
- session_vars.get_repo_url = function () { return process.env.REPO_URL; };
77
- // missing/moving functionality:
78
- // getBaseInterviewURL
79
- // Project name setting/getting
80
- // Playground id functionality needs to be transfered to api requests
81
-
82
35
 
83
36
  session_vars.get_branch_name = function () {
84
37
  /* Get the name of the repository's branch that is being tested */
@@ -102,29 +55,6 @@ session_vars.get_project_base_name = function () {
102
55
  return sanitized_name;
103
56
  };
104
57
 
105
- // // Notes: We were using `new_session=1` in the url args, but it wasn't
106
- // // always working. The idea was to save each session so we could look
107
- // // back on it if needed. Aside from the fact that you usually need to log in
108
- // // to make the sessions retrievable (though if you're using `interview_list()`
109
- // // you can get them anyway), it also has inconsistent behavior when
110
- // // `sessions are unique`` or `temporary session` or use `interview_list()`
111
- // // and/or respond immediately with a `command()`. Three ways to avoid this:
112
- // // 1. x Start a new browser instead of just a new page each time. Lengthens the tests.
113
- // // 1. √ Visit `/user/sign-out` (needed even if the user is not logged in)
114
- // // 1. x (seems to be causing a 'Unable to locate interview session' error
115
- // // instead) Use `reset=2` instead , which will not save the previous session at all. It
116
- // // Will not just restart any existing interview sessions, it will also
117
- // // clear out the browser session's memory of what other interviews the user has
118
- // // visited in the same browser session_vars.
119
- // session_vars.getBaseInterviewURL = function () {
120
- // /* Return the part of the interview URL that comes before the filename.
121
- // * Depends on the project name, which may have been changed. */
122
- // let new_session = `/interview?new_session=1&i=docassemble.playground`;
123
- // return `${ session_vars.BASE_URL }${ new_session }${ session_vars.PLAYGROUND_ID }${ session_vars.getProjectName() }%3A`;
124
- // };
125
- // // session_vars.BASE_INTERVIEW_URL = session_vars.getBaseInterviewURL();
126
-
127
-
128
58
  session_vars.get_languages = function () {
129
59
  /* Returns an array of strings, names of languages that should be tested. */
130
60
 
@@ -175,7 +105,7 @@ session_vars.get_project_name = function () {
175
105
 
176
106
  session_vars.delete_project_name = function () {
177
107
  /* Delete the file storing the name of the docassemble project */
178
- // Avoid race coditions possibly caused by checking for existence first
108
+ // Avoid race conditions possibly caused by checking for existence first
179
109
  // Delete file if possible
180
110
  try {
181
111
  fs.unlinkSync( project_name_file_path );
@@ -186,3 +116,47 @@ session_vars.delete_project_name = function () {
186
116
  return false;
187
117
  }
188
118
  };
119
+
120
+ /**
121
+ * Ensure all required environment variables exist. Add each missing variable
122
+ * to an error that will be thrown at the end.
123
+ *
124
+ * @throws ReferenceError with a message for all missing variables.
125
+ */
126
+ session_vars.validateEnvironment = function() {
127
+ /** Throw a useful error for the developer if any of the required env vars
128
+ * are missing. For most devs, these will be GitHub secrets. */
129
+
130
+ // Note: We are checking for BASE_URL, but we are referring it as SERVER_URL as that
131
+ // is the name used in GitHub secrets, which is what most devs will be using.
132
+
133
+ let errorMessage = '';
134
+
135
+ // Since this code will most often be run in GitHub, I'm going to build a single error message that outlines all missing variables. Otherwise, a developer
136
+ // will have to run their pipeline an incredible number of times to discover all the missing variables.
137
+ if ( !session_vars.get_dev_api_key() )
138
+ {
139
+ errorMessage += `\nThe DOCASSEMBLE_DEVELOPER_API_KEY GitHub secret must be defined. The DOCASSEMBLE_DEVELOPER_API_KEY is a docassemble API key you created for your server's testing account. It should have developer permissions.`
140
+ }
141
+ if ( !session_vars.get_da_server_url() )
142
+ {
143
+ errorMessage += `\nThe SERVER_URL GitHub secret must be defined. The SERVER_URL is the URL of the Docassemble Server where the tests should be run.`;
144
+ }
145
+ if ( !session_vars.get_repo_url() )
146
+ {
147
+ errorMessage += `\nThe REPO_URL GitHub secret must be defined. The REPO_URL is the URL to the Git repo whose Actions are being run.`;
148
+ }
149
+ // The branch name is always automatically defined in Github, it'll be missing only if you're running locally and missing an env var.
150
+ if ( !session_vars.get_branch_name() )
151
+ {
152
+ errorMessage += `\nThe BRANCH_NAME environment variable must be defined. The BRANCH_NAME is the name of the repository branch that should be pulled into the docassemble project.`;
153
+ }
154
+
155
+ errorMessage = errorMessage.trim();
156
+
157
+ if (errorMessage !== '') {
158
+ throw new ReferenceError(errorMessage);
159
+ }
160
+ };
161
+
162
+ session_vars.validateEnvironment();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suffolklitlab/alkiln",
3
- "version": "4.0.0-pre-api.8",
3
+ "version": "4.1.0",
4
4
  "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "git+https://github.com/SuffolkLITLab/alkiln.git"
20
+ "url": "git+https://github.com/SuffolkLITLab/ALKiln.git"
21
21
  },
22
22
  "keywords": [
23
23
  "docassemble",
@@ -30,9 +30,9 @@
30
30
  "author": "",
31
31
  "license": "MIT",
32
32
  "bugs": {
33
- "url": "https://github.com/SuffolkLITLab/alkiln/issues"
33
+ "url": "https://github.com/SuffolkLITLab/ALKiln/issues"
34
34
  },
35
- "homepage": "https://suffolklitlab.github.io/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing",
35
+ "homepage": "https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/",
36
36
  "dependencies": {
37
37
  "@cucumber/cucumber": "^7.3.2",
38
38
  "axios": "^0.24.0",
@@ -48,7 +48,6 @@
48
48
  "bin": {
49
49
  "alkiln-setup": "./lib/docassemble/setup.js",
50
50
  "alkiln-takedown": "./lib/docassemble/takedown.js",
51
- "alkiln-cucumber": "./lib/index.js",
52
51
  "alkiln-langs": "./lib/utils/langs.js"
53
52
  }
54
53
  }
@@ -4,13 +4,13 @@ Feature: Establishing steps
4
4
  Steps that establish things about the state of the test
5
5
  # In tag names, 'e' is for 'establishing'
6
6
 
7
- @slow @e1
7
+ @slow @e1 @wait
8
8
  Scenario: I am able to set a custom wait time that allows steps to run longer than the half minute default
9
9
  Given I start the interview at "all_tests"
10
10
  And the max seconds for each step in this scenario is 40
11
11
  And I wait 35 seconds
12
12
 
13
- @slow @e2
13
+ @slow @e2 @wait
14
14
  Scenario: I am able to set a custom wait time before an interview has been loaded
15
15
  Given the max seconds for each step in this scenario is 40
16
16
  And I start the interview at "all_tests"
@@ -96,7 +96,7 @@ Scenario: Test "Then I don’t continue" with an apostrophe
96
96
  Then I don’t continue
97
97
  And I will be told an answer is invalid
98
98
 
99
- @fast @o9
99
+ @fast @o9 @table
100
100
  Scenario: I can match JSON page var to str
101
101
  Given I start the interview at "all_tests.yml"
102
102
    And I get to "showifs" with this data:
@@ -138,7 +138,7 @@ Scenario: Fail with missing link with url
138
138
  And I start the interview at "all_tests"
139
139
  Then I should see the link to "http://missing-url.com"
140
140
 
141
- @slow @rf13 @error
141
+ @slow @rf13 @error @table
142
142
  Scenario: Fail with link with given text does not lead to correct url
143
143
  Given the final Scenario status should be "failed"
144
144
  Given the Scenario report should include:
@@ -166,7 +166,7 @@ Scenario: Fail with link with given text does not lead to correct url
166
166
  And I should see the link to "http://ecosia.org/"
167
167
  Then the "Link to external page" link leads to "http://wrong-url.com"
168
168
 
169
- @slow @rf14 @error
169
+ @slow @rf14 @error @table
170
170
  Scenario: Fail with link unexpectedly opens in same window
171
171
  Given the final Scenario status should be "failed"
172
172
  Given the Scenario report should include:
@@ -194,7 +194,7 @@ Scenario: Fail with link unexpectedly opens in same window
194
194
  And I should see the link to "http://ecosia.org/"
195
195
  Then the "Link to external page" link opens in the same window
196
196
 
197
- @fast @rf15 @error
197
+ @fast @rf15 @error @table
198
198
  Scenario: Fail with link unexpectedly opens in a new window
199
199
  Given the final Scenario status should be "failed"
200
200
  Given the Scenario report should include:
@@ -223,7 +223,7 @@ Scenario: Fail with link unexpectedly opens in a new window
223
223
  Then the "Link: reload the page" link opens in a new window
224
224
 
225
225
  ## Don't currently have a broken link to test and this Step is not officially supported
226
- #@fast @rf16 @error
226
+ #@fast @rf16 @error @table
227
227
  #Scenario: Fail with link leads to a broken page
228
228
  # Given the final Scenario status should be "failed"
229
229
  # Given the Scenario report should include:
@@ -321,7 +321,7 @@ Scenario: Fail with system error after Step using wrong file name as trigger
321
321
  # """
322
322
  # And I start the interview at "wrong_yaml_filename"
323
323
 
324
- @fast @rf22
324
+ @fast @rf22 @table
325
325
  Scenario: I can't match JSON page var to str
326
326
  Given the final Scenario status should be "failed"
327
327
  Given the Scenario report should include:
@@ -376,6 +376,16 @@ Scenario: Fail with 2 wrong signin secrets
376
376
  """
377
377
    Given I sign in with the email "WRONG_EMAIL_NAME" and the password "WRONG_PASSWORD_NAME"
378
378
 
379
+ @fast @rf25 @upload
380
+ Scenario: Fail with could not find files
381
+ Given the final Scenario status should be "failed"
382
+ Given the Scenario report should include:
383
+ """
384
+ Could not find "nonexistant1.pdf, nonexistant2.pdf"
385
+ """
386
+ Given I start the interview at "all_tests"
387
+ And I upload "nonexistant1.pdf, nonexistant2.pdf" to "upload_files_visible"
388
+
379
389
  # scope.js
380
390
  # I upload "___" to "___"
381
391
  # No need to test wrong var name
@@ -384,6 +394,7 @@ Scenario: Fail with 2 wrong signin secrets
384
394
  #
385
395
  # I sign
386
396
 
397
+
387
398
  # ===============================
388
399
  # Reports for Scenarios with warnings
389
400
  # ===============================
@@ -448,70 +459,27 @@ Scenario: Report still shows page id when I tap to continue without setting any
448
459
  # Next page (showifs ID SHOULD BE SHOWN IN REPORT)
449
460
  Then the question id should be "buttons yesnomaybe"
450
461
 
451
- @slow @rp2
462
+ @slow @rp2 @table
452
463
  Scenario: Report lists unused table rows
453
464
  Given the final Scenario status should be "passed"
454
465
  Given the Scenario report should include:
455
466
  """
456
-
457
- ---------------
458
- Scenario: Report lists unused table rows
459
- Tags: @reports @slow @rp2
460
- ---------------
461
- screen id: upload-files
462
- screen id: group-of-complex-fields
463
- | single_quote_dict['single_quote_key']['sq_two'] | true | |
464
- | double_quote_dict[\"double_quote_key\"]['dq_two'] | true | |
465
-
466
- Rows that got set:
467
- And I get to the question id "direct standard fields" with this data:
468
- | var | value | trigger |
469
- | double_quote_dict[\"double_quote_key\"]['dq_two'] | true | |
470
- | single_quote_dict['single_quote_key']['sq_two'] | true | |
471
467
  Unused rows:
472
468
  | extra_2 | extra 2 | |
473
469
  | extra_out_of_alphabetical_order | extra 1 | |
474
-
475
- screen id: direct-standard-fields
476
- | checkboxes_other['checkbox_other_opt_1'] | true | |
477
- | radio_yesno | False | false |
478
- | radio_other | radio_other_opt_3 | |
479
- | text_input | Regular text input field value | |
480
- | textarea | Multiline text\narea value | |
481
- | dropdown_test | dropdown_opt_2 | |
482
-
483
- Rows that got set:
484
- And I get to the question id "showifs" with this data:
485
- | var | value | trigger |
486
- | checkboxes_other['checkbox_other_opt_1'] | true | |
487
- | dropdown_test | dropdown_opt_2 | |
488
- | radio_other | radio_other_opt_3 | |
489
- | radio_yesno | False | false |
490
- | text_input | Regular text input field value | |
491
- | textarea | Multiline text\narea value | |
470
+ """
471
+ Given the Scenario report should include:
472
+ """
492
473
  Unused rows:
493
474
  | extra_3 | extra 3 | |
494
475
  | extra_4 | extra 4 | |
495
476
  | extra_5 | extra 5 | |
496
-
497
- screen id: showifs
498
- screen id: buttons-yesnomaybe
499
- | buttons_yesnomaybe | True | |
500
- screen id: buttons-other
501
- | buttons_other | button_2 | |
502
- screen id: button-continue
503
- | button_continue | True | |
504
-
505
- Rows that got set:
506
- And I get to the question id "screen features" with this data:
507
- | var | value | trigger |
508
- | button_continue | True | |
509
- | buttons_other | button_2 | |
510
- | buttons_yesnomaybe | True | |
477
+ """
478
+ Given the Scenario report should include:
479
+ """
511
480
  Unused rows:
512
481
  | extra_6 | extra 6 | |
513
482
  | extra_7 | extra 7 | |
514
-
515
483
  """
516
484
  And I start the interview at "all_tests"
517
485
    And I get to "direct standard fields" with this data:
@@ -1,4 +1,4 @@
1
- @story_tables_formats
1
+ @story_tables_formats @table
2
2
  Feature: Supported story table formats
3
3
 
4
4
  NOTE:
@@ -1,4 +1,4 @@
1
- @story_tables
1
+ @story_tables @table
2
2
  Feature: Story tables
3
3
 
4
4
  NOTE:
@@ -7,6 +7,8 @@ const fields = require('./fields.fixtures.js');
7
7
  const matches = require('./matches.fixtures.js');
8
8
  const scope = require('../../lib/scope.js');
9
9
  const getMatchingRows = scope.getMatchingRows;
10
+ // Match state var instantiated in steps.js. TODO: Add tests for such rows.
11
+ scope.row_needs_trigger_flag = `ALKiln: no trigger variable needed`;
10
12
 
11
13
  // TODO: Add tests for reports
12
14
 
@@ -188,6 +190,32 @@ describe(`For mulitple rows with the same proxies`, async function() {
188
190
  }
189
191
  });
190
192
 
193
+ // Missing table row for loop
194
+ describe(`For a second proxy loop page,`, async function() {
195
+ for ( let test_i = 0; test_i < fields.proxies_multi.length; test_i++ ) {
196
+ let field = fields.proxies_multi[ test_i ];
197
+ let curr_matches = matches.proxies_missing_loop[ test_i ];
198
+ let name = getSafeName( field );
199
+ it( `does not find any matches when table row for second loop page is missing`, async function() {
200
+ let result = await getMatchingRows( scope, { field, var_data: tables.proxies_missing_loop });
201
+ expect( result ).to.deep.equal( curr_matches );
202
+ });
203
+ }
204
+ });
205
+
206
+ // Missing trigger matches second loop
207
+ describe(`For a second proxy loop page,`, async function() {
208
+ for ( let test_i = 0; test_i < fields.proxies_multi.length; test_i++ ) {
209
+ let field = fields.proxies_multi[ test_i ];
210
+ let curr_matches = matches.proxies_missing_trigger[ test_i ];
211
+ let name = getSafeName( field );
212
+ it( `matches with a missing trigger row with the correct variable name`, async function() {
213
+ let result = await getMatchingRows( scope, { field, var_data: tables.proxies_missing_trigger });
214
+ expect( result ).to.deep.equal( curr_matches );
215
+ });
216
+ }
217
+ });
218
+
191
219
 
192
220
  // ============================
193
221
  // Signature
@@ -15,7 +15,7 @@ matches.standard = [
15
15
  [],
16
16
  [ { var: 'radio_other', value: 'radio_other_opt_2' } ],
17
17
  [],
18
- [ { var: 'text_input', value: 'Some one-line text' }, { var: 'text_input', value: 'Some conflicting text' } ],
18
+ [ { var: 'text_input', value: 'Some one-line text', "original": { var: 'text_input', value: 'Some one-line text' } }, { var: 'text_input', value: 'Some conflicting text', "original": { var: 'text_input', value: 'Some conflicting text' } } ],
19
19
  [ { var: 'textarea', value: 'Some\nmulti-line\ntext' } ],
20
20
  [ { 'var': 'date_input','value':'today-1' } ],
21
21
  [ { var: 'dropdown_test', value: 'dropdown_opt_2' } ],
@@ -111,6 +111,18 @@ matches.proxies_multi = [
111
111
  []
112
112
  ];
113
113
 
114
+ // Missing table row for second loop
115
+ matches.proxies_missing_loop = [
116
+ [],
117
+ []
118
+ ];
119
+
120
+ // Missing trigger matches second loop
121
+ matches.proxies_missing_trigger = [
122
+ [{ "trigger": "", "var": "x[i].name.first", "value": "Firstname", "original": { "trigger": "", "var": "x[i].name.first", "value": "Firstname" }}],
123
+ []
124
+ ];
125
+
114
126
 
115
127
  // ============================
116
128
  // Signature