@suffolklitlab/alkiln 4.11.2 → 5.0.0-playground-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,7 +20,7 @@ jobs:
20
20
 
21
21
  strategy:
22
22
  matrix:
23
- node-version: [17.x]
23
+ node-version: [18.x]
24
24
 
25
25
  env:
26
26
  BASE_URL: ${{ secrets.SERVER_URL }}
@@ -34,6 +34,7 @@ jobs:
34
34
  SECRET_VAR1: secret-var1-value
35
35
  SECRET_VAR2: secret-var2-value
36
36
  SECRET_FOR_MISSING_FIELD: secret for missing field
37
+ _ORIGIN: github
37
38
 
38
39
  name: Run Puppeteer tests
39
40
  steps:
@@ -52,9 +53,9 @@ jobs:
52
53
  # For the testing repo, the `REPO_URL` is that of a different repo and is created in `env:`
53
54
  - run: echo "Repo address is $REPO_URL"
54
55
  - name: Checkout
55
- uses: actions/checkout@v2
56
+ uses: actions/checkout@v3
56
57
  - name: Setup environment
57
- uses: actions/setup-node@v1
58
+ uses: actions/setup-node@v3
58
59
  with:
59
60
  node-version: ${{ matrix.node-version }}
60
61
  - run: npm install
@@ -62,14 +63,14 @@ jobs:
62
63
  - run: npm run test -- ${{ github.event.inputs.tags && format('"--tags" "{0}"', github.event.inputs.tags) }}
63
64
  # Artifacts
64
65
  - name: upload unit tests artifacts folder
65
- uses: actions/upload-artifact@v2
66
+ uses: actions/upload-artifact@v3
66
67
  # This will upload even if a previous step has failed
67
68
  if: ${{ always() }}
68
69
  with:
69
70
  name: ${{ env.UNIT_TESTS_ARTIFACT_NAME }}
70
71
  path: ./_alkiln-*
71
72
  - name: upload integration tests artifacts folder
72
- uses: actions/upload-artifact@v2
73
+ uses: actions/upload-artifact@v3
73
74
  if: ${{ always() }}
74
75
  with:
75
76
  name: ${{ env.ARTIFACT_NAME }}
package/CHANGELOG.md CHANGED
@@ -38,15 +38,28 @@ Format:
38
38
  ### Security
39
39
  -
40
40
  -->
41
- <!-- ## [Unreleased] -->
41
+ ## [Unreleased]
42
+
43
+ ### Added
44
+ - A new script: `alkiln-run`, which acts like `npm run cucumber`, but can
45
+ be run in any directory, not just in an npm package.
46
+ - Additional environment variables and their validation to allow for tests that run on a developer's server/Playground instead of through GitHub. Also, other functionality for that purpose. [Issue #661](https://github.com/SuffolkLITLab/ALKiln/issues/661)
47
+ - Tests for new session_vars behavior and improve previous tests.
48
+
49
+ ### Changed
50
+ - upgraded cucumber v8.6.0
51
+ - using cucumber's JS API to run tests. For more details on how it works,
52
+ see [the cucumber-js docs](https://github.com/cucumber/cucumber-js/blob/main/docs/javascript_api.md).
53
+ - the github action no longer runs `npm run XYZ`; it directly calls scripts,
54
+ e.g. `alkiln-setup`, `alkiln-run`, `alkiln-takedown`
55
+ - don't print the ["publish this cucumber report" message](https://github.com/cucumber/cucumber-js/blob/main/docs/configuration.md#options)
56
+ - Adjusted validation of some environment variables to account for Playground vs. GitHub or local test runs. [Issue #661](https://github.com/SuffolkLITLab/ALKiln/issues/661)
42
57
 
43
- ## [4.11.2] - 2023-03-22
44
58
  ### Security
45
59
  - Pass docassemble API keys through HTTP headers instead of as parameters.
46
- - ACTION: all users should delete any uWSGI logs that show their API keys.
47
60
  - Parameters to certain HTTP requests are printed directly in docassemble's
48
61
  uWSGI log, leaking API keys to actors with log access on your docassemble
49
- server.
62
+ server
50
63
 
51
64
  ## [4.11.1] - 2023-03-21
52
65
  ### Changed
@@ -584,4 +597,4 @@ Format:
584
597
  - `example.feature` - an example test to get a project started
585
598
  - `github/workflows/run_form_tests.yml` - Automated github workflow to run tests when code is pushed and a manual trigger for testing without needing to push
586
599
  - `.gitignore` - ignores files that are generated by the tests and shouldn't be added to the repo
587
- - `package.json` - keep up-to-date version of this test repository and hold scripts that the automated tests run
600
+ - `package.json` - keep up-to-date version of this test repository and hold scripts that the automated tests run
package/action.yml CHANGED
@@ -61,6 +61,7 @@ runs:
61
61
  echo "DOCASSEMBLE_DEVELOPER_API_KEY=${{ inputs.DOCASSEMBLE_DEVELOPER_API_KEY }}" >> $GITHUB_ENV
62
62
  echo "ALKILN_VERSION=${{ inputs.ALKILN_VERSION }}" >> $GITHUB_ENV
63
63
  echo "MAX_SECONDS_FOR_SETUP=${{ inputs.MAX_SECONDS_FOR_SETUP }}" >> $GITHUB_ENV
64
+ echo "_ORIGIN=github" >> $GITHUB_ENV
64
65
  shell: bash
65
66
  - name: "ALKiln: confirm info"
66
67
  run: |
@@ -68,64 +69,36 @@ runs:
68
69
  echo -e "\nALKiln version is $ALKILN_VERSION\nRepo is $REPO_URL\nBranch ref is $BRANCH_PATH\nMAX_SECONDS_FOR_SETUP is $MAX_SECONDS_FOR_SETUP\nManually added languages are $EXTRA_LANGUAGES\n"
69
70
  shell: bash
70
71
 
71
- # This doesn't use an already established package-lock.json and that
72
- # comes with some types of test breaking risks. Same problem as before.
73
- - name: Temporarily overwrite or create package.json
74
- run: |
75
- cat << EOF > "package.json"
76
- {
77
- "scripts": {
78
- "test": "npm run langs_setup && npm run cucumber -- $@",
79
- "cucumber_base": "./node_modules/.bin/cucumber-js --require ./node_modules/@suffolklitlab/alkiln/lib/index.js",
80
- "cucumber": "run_cucumber(){ npm run cucumber_base -- \"$@\" docassemble/*/data/sources/*.feature --retry 1; }; run_cucumber",
81
- "langs": "npm run langs_setup && ./node_modules/.bin/cucumber-js --require ./node_modules/.bin/alkiln-cucumber docassemble/*/data/sources/*_lang*.feature",
82
- "langs_setup": "node_modules/.bin/alkiln-langs 'docassemble/*/data/sources/*.feature'",
83
- "setup": "node_modules/.bin/alkiln-setup",
84
- "takedown": "node_modules/.bin/alkiln-takedown"
85
- },
86
- "dependencies": {
87
- "colors": "1.4.0",
88
- "@cucumber/cucumber": "7.3.2",
89
- "@suffolklitlab/alkiln": "$ALKILN_VERSION"
90
- }
91
- }
92
- EOF
93
- # Keep in sync with the cucumber.js in root
94
- cat << EOF > "cucumber.js"
95
- module.exports = {
96
- default: "--require ./lib/index.js --format progress --format summary:cucumber-report.txt"
97
- }
98
- EOF
99
- shell: bash
100
-
101
72
  # Install node
102
73
  - name: "ALKiln: Install node packages"
103
- uses: actions/setup-node@v1
74
+ uses: actions/setup-node@v3
104
75
  with:
105
- node-version: "17"
106
- - run: npm install
76
+ node-version: "18"
77
+ - name: Install ALKiln directly from npm
78
+ run: |
79
+ npm install -g github:SuffolkLITLab/ALKiln#v5
107
80
  shell: bash
108
81
 
109
82
  # Run tests
110
83
  - name: "ALKiln: Create a Project and pull the package from GitHub"
111
- run: npm run setup
84
+ run: alkiln-setup
112
85
  shell: bash
113
86
  - name: "ALKiln: Run tests"
114
87
  if: ${{ success() }}
115
- run: npm run test -- ${{ github.event.inputs.tags && format('"--tags" "{0}"', github.event.inputs.tags) }}
88
+ run: alkiln-run ${{ github.event.inputs.tags && format('{0}', github.event.inputs.tags) }}
116
89
  shell: bash
117
90
  - run: echo -e "\n\n====ALKiln could not create a project on your server's testing account or pull your package into it. Check the messages above this line.\n\n"
118
91
  if: ${{ failure() }}
119
92
  shell: bash
120
93
  - name: "ALKiln: Delete project from docassemble test account"
121
94
  if: ${{ always() }}
122
- run: npm run takedown
95
+ run: alkiln-takedown
123
96
  shell: bash
124
97
 
125
98
  # Upload artifacts that subscribers can download on the Actions summary page
126
99
  - name: "ALKiln: Upload artifacts folder"
127
100
  if: ${{ always() }}
128
- uses: actions/upload-artifact@v2
101
+ uses: actions/upload-artifact@v3
129
102
  with:
130
103
  name: ${{ env.ARTIFACT_NAME }}
131
104
  path: ./alkiln-*
@@ -27,7 +27,8 @@ da.get_dev_id = async function ( timeout ) {
27
27
  try {
28
28
  return await axios.request( options );
29
29
  } catch (error) {
30
- throw {...error.toJSON(), data: error.response?.data};
30
+ let response = error.response || {data: null}
31
+ throw {...error.toJSON(), data: response.data};
31
32
  }
32
33
  }; // Ends da.get_dev_id()
33
34
 
@@ -48,7 +49,8 @@ da.create_project = async function ( project_name, timeout ) {
48
49
  try {
49
50
  return await axios.request( options );
50
51
  } catch (error) {
51
- throw {...error.toJSON(), data: error.response?.data};
52
+ let response = error.response || {data: null}
53
+ throw {...error.toJSON(), data: response.data};
52
54
  }
53
55
  }; // Ends da.create_project()
54
56
 
@@ -71,7 +73,8 @@ da.pull = async function ( timeout ) {
71
73
  try {
72
74
  return await axios.request( options );
73
75
  } catch (error) {
74
- throw {...error.toJSON(), data: error.response?.data};
76
+ let response = error.response || {data: null}
77
+ throw {...error.toJSON(), data: response.data};
75
78
  }
76
79
  }; // Ends da.pull()
77
80
 
@@ -92,7 +95,8 @@ da.has_task_finished = async function ( task_id, timeout ) {
92
95
  try {
93
96
  return await axios.request( options );
94
97
  } catch (error) {
95
- throw {...error.toJSON(), data: error.response?.data};
98
+ let response = error.response || {data: null}
99
+ throw {...error.toJSON(), data: response.data};
96
100
  }
97
101
  }; // Ends da.has_task_finished()
98
102
 
@@ -113,7 +117,8 @@ da.delete_project = async function ( timeout ) {
113
117
  try {
114
118
  return await axios.request( options );
115
119
  } catch (error) {
116
- throw {...error.toJSON(), data: error.response?.data};
120
+ let response = error.response || {data: null}
121
+ throw {...error.toJSON(), data: response.data};
117
122
  }
118
123
  }; // Ends da.delete_project()
119
124
 
@@ -142,7 +147,8 @@ da.__delete_projects_starting_with = async function ( base_name, starting_num=1,
142
147
 
143
148
  await axios.request( options );
144
149
  } catch ( error ) {
145
- console.log( {...error.toJSON(), data: error.response?.data});
150
+ let response = error.response || {data: null}
151
+ console.log( {...error.toJSON(), data: response.data});
146
152
  }
147
153
  name_incrementor++;
148
154
  }
@@ -122,9 +122,15 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
122
122
 
123
123
  // node -e 'require("./lib/docassemble/docassemble_api_interface.js").is_server_responding()'
124
124
  da_i.is_server_responding = async function ( dev_id_options ) {
125
- /** Use _da_REST.get_dev_id() to test if server is up.
125
+ /** For non-playground interviews, use _da_REST.get_dev_id() to
126
+ * return if server is up. Otherwise, return true.
126
127
  * See https://docassemble.org/docs/api.html#user. */
127
128
 
129
+ // Playground interview will itself timeout. No way to detect that.
130
+ if ( session_vars.get_origin() === 'playground' ) {
131
+ return true;
132
+ }
133
+
128
134
  // Default timeouts
129
135
  let { timeout } = dev_id_options || { timeout: DEFAULT_MAX_REQUEST_MS };
130
136
  try {
@@ -141,13 +147,28 @@ da_i.is_server_responding = async function ( dev_id_options ) {
141
147
 
142
148
  // node -e 'require("./lib/docassemble/docassemble_api_interface.js").get_dev_id()'
143
149
  da_i.get_dev_id = async function ( dev_id_options ) {
144
- /** For consistency, wrap _da_REST.get_dev_id().
145
- * See https://docassemble.org/docs/api.html#user. */
150
+ /** Get user id, either from the server or from env vars (for Playground runs).
151
+ * See https://docassemble.org/docs/api.html#user.
152
+ *
153
+ * @param dev_id_options {obj} Options for getting dev_id
154
+ * @param dev_id_options.timeout {int} Required if options is defined. Max MS
155
+ * to allow for server request.
156
+ */
146
157
 
147
- // Default timeouts
148
- let { timeout } = dev_id_options || { timeout: DEFAULT_MAX_REQUEST_MS };
149
- let response = await _da_REST.get_dev_id( timeout );
150
- return response.data.id;
158
+ let dev_id = null;
159
+
160
+ // For tests running in the dev Playground, use the appropriate env var
161
+ if ( session_vars.get_origin() === 'playground' ) {
162
+ dev_id = session_vars.get_user_id();
163
+
164
+ } else {
165
+ // Default timeouts
166
+ let { timeout } = dev_id_options || { timeout: DEFAULT_MAX_REQUEST_MS };
167
+ let response = await _da_REST.get_dev_id( timeout );
168
+ dev_id = response.data.id;
169
+ }
170
+
171
+ return dev_id;
151
172
  }; // Ends da_i.get_dev_id();
152
173
 
153
174
 
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const log = require('./utils/log');
5
+ const session_vars = require('./utils/session_vars');
6
+
7
+ const cucumber_api = require('@cucumber/cucumber/api');
8
+
9
+ (async function main() {
10
+ let argv = process.argv;
11
+
12
+ const environment = { cwd: process.cwd() };
13
+ console.log("alkiln-run: environment: %o", environment);
14
+ const provided_config = {
15
+ "publishQuiet": true,
16
+ "require": "./lib/index.js",
17
+ "format": ["progress", "summary:cucumber-report.txt"],
18
+ "paths": ["docassemble/*/data/sources/*.feature"],
19
+ "retry": 1,
20
+ }
21
+
22
+
23
+ // If they're running it from an interview on their server instead of from GitHub
24
+ if ( session_vars.get_origin() === `playground` ) {
25
+ provided_config.paths.push(`/usr/share/docassemble/files/playgroundsources/${session_vars.get_user_id()}/${session_vars.get_user_project_name()}/*.feature`);
26
+ // For S3
27
+ provided_config.paths.push(`/tmp/playgroundsources/${session_vars.get_user_id()}/${session_vars.get_user_project_name()}/*.feature`);
28
+ }
29
+
30
+ let tags = argv.slice(2);
31
+ if ( session_vars.get_origin() === `playground` ) {
32
+ tags = session_vars.get_tags();
33
+ }
34
+
35
+ if (tags.length > 0) {
36
+ // Cucumber expects tags in boolean expressions, like (@a0 or @a1) and @random.
37
+ // If we get a list of only tags, then "or" them all together
38
+ if (tags.every((val) => val.startsWith("@"))) {
39
+ provided_config.tags = tags.join(' or ');
40
+ }
41
+ // If we get a list of seemingly no tags, assume they meant to use tags
42
+ else if (tags.every((val) => !val.startsWith("@"))) {
43
+ provided_config.tags = tags.map((val) => "@" + val).join(' or ');
44
+ }
45
+ // If it's a mix of tags and no tags, it's probably a boolean expression!
46
+ else {
47
+ provided_config.tags = tags.join(' ');
48
+ }
49
+ }
50
+
51
+ const { runConfiguration } = await cucumber_api.loadConfiguration({
52
+ provided: provided_config
53
+ }, environment);
54
+ console.log("alkiln-run: runConfiguration: %o", runConfiguration);
55
+ var path = require('path');
56
+ var codeDir = path.dirname(require.main.filename);
57
+ const support = await cucumber_api.loadSupport({ support: {
58
+ requireModules: [`${ codeDir }/index.js`],
59
+ requirePaths: [ `${ codeDir }/index.js`],
60
+ importPaths: [],
61
+ }, sources: {paths: []}}, environment);
62
+
63
+ console.log("alkiln-run: working directory: %o", codeDir);
64
+
65
+ const { success } = await cucumber_api.runCucumber({...runConfiguration, support});
66
+ console.log("alkiln-run: success: %b", success);
67
+ const exitCode = success ? 0 : 1;
68
+ process.exitCode = exitCode;
69
+
70
+ // This is hardcoded information from `provide_config` above.
71
+ // This code isn't used for anything other than detecting and removing color codes.
72
+ let summary_file_name = 'cucumber-report.txt';
73
+ fs.readFile(summary_file_name, 'utf8', (err, data) => {
74
+ if (err) {
75
+ console.error(err);
76
+ return;
77
+ }
78
+
79
+ // Replace terminal color codes, if present
80
+ // https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797#colors--graphics-mode
81
+ data = data.replace(/\x1b\[[0-9][0-9]?[0-9]?m/g, '');
82
+
83
+ // Put it in the correct file in the correct folder
84
+ let artifacts_path = session_vars.get_artifacts_path_name();
85
+ fs.appendFileSync( `${ artifacts_path}/${ log.debug_log_file }`, data);
86
+ });
87
+ })();
package/lib/steps.js CHANGED
@@ -92,7 +92,7 @@ BeforeAll(async () => {
92
92
  // Make the folder to store all the files for this run of the tests
93
93
  scope.paths.artifacts = files.make_artifacts_folder();
94
94
  // Save its name in a file. `session_vars` can get it back later.
95
- // This is used by at least clean_reports.js.
95
+ // The value we are saving is used by at least run_cucumber.js.
96
96
  session_vars.save_artifacts_path_name( scope.paths.artifacts );
97
97
 
98
98
  scope.paths.report = `${ scope.paths.artifacts }/report.txt`;
@@ -103,7 +103,14 @@ BeforeAll(async () => {
103
103
  Before(async (scenario) => {
104
104
  // Create browser, which can't be created in BeforeAll() where .driver doesn't exist for some reason
105
105
  if (!scope.browser) {
106
- scope.browser = await scope.driver.launch({ headless: !session_vars.get_debug(), devtools: session_vars.get_debug() });
106
+ if (session_vars.get_origin() === 'playground') {
107
+ // Will only run in the Playground outside of a sandbox. TODO: There's a
108
+ // better way to do this, though it's more complicated. See comments in
109
+ // https://github.com/SuffolkLITLab/ALKiln/issues/661
110
+ scope.browser = await scope.driver.launch({ args: ['--no-sandbox'] });
111
+ } else {
112
+ scope.browser = await scope.driver.launch({ headless: !session_vars.get_debug(), devtools: session_vars.get_debug() });
113
+ }
107
114
  }
108
115
  // Clean up all previously existing pages
109
116
  for (const page of await scope.browser.pages()) {
@@ -23,9 +23,27 @@ module.exports = session_vars;
23
23
  // These default to null
24
24
  // TODO: How to isolate debug elsewhere so this file can use the logger?
25
25
  session_vars.get_debug = function () { return process.env.DEBUG || null; };
26
+ session_vars.is_dev_env = function () { return process.env.DEV || null; }
27
+
26
28
  session_vars.get_dev_api_key = function () { return process.env.DOCASSEMBLE_DEVELOPER_API_KEY || null; };
27
29
  session_vars.get_repo_url = function () { return process.env.REPO_URL || null; };
28
- session_vars.is_dev_env = function () { return process.env.DEV || null; }
30
+
31
+ session_vars.get_origin = function () {
32
+ /** The location from which the tests are being run. Possible
33
+ * valid return values: 'playground', 'github', or 'local'.
34
+ * Default is 'local' for ease of our own onboarding process.
35
+ */
36
+ return process.env._ORIGIN || 'local';
37
+ };
38
+ session_vars.get_user_project_name = function () { return process.env._PROJECT_NAME || null; };
39
+ session_vars.get_user_id = function () { return process.env._USER_ID || null; };
40
+ session_vars.get_tags = function () {
41
+ if ( process.env._TAGS ) {
42
+ return process.env._TAGS.split(` `);
43
+ } else {
44
+ return [];
45
+ }
46
+ };
29
47
 
30
48
  // More complex logic
31
49
  session_vars.get_server_reload_timeout = function () {
@@ -130,7 +148,10 @@ session_vars.save_project_name = function ( project_name ) {
130
148
  }; // Ends save_project_name()
131
149
 
132
150
  session_vars.get_project_name = function () {
133
- /* Get the name of the current docassemble Project */
151
+ /* Get the name of the current docassemble Project */
152
+ if ( session_vars.get_origin() === 'playground' ) {
153
+ return session_vars.get_user_project_name();
154
+ }
134
155
  let json = JSON.parse( fs.readFileSync( runtime_config_path ));
135
156
  let project_name = json[ project_name_key ] || null;
136
157
  if ( session_vars.get_debug() ) { console.log( `ALKiln debug: Project name from file is "${ project_name }".` ); }
@@ -174,7 +195,7 @@ session_vars.save_artifacts_path_name = function ( path_name ) {
174
195
  console.log( `ALKiln debug: Stored name of artifacts directory "${ path_name }" locally.` );
175
196
  }
176
197
  return file;
177
- }; // Ends get_project_name()
198
+ }; // Ends save_artifacts_path_name()
178
199
 
179
200
  session_vars.get_artifacts_path_name = function () {
180
201
  /* Get the name of the current artifacts path. */
@@ -187,48 +208,69 @@ session_vars.get_artifacts_path_name = function () {
187
208
  } catch ( error ) {
188
209
  return null;
189
210
  }
190
- }; // Ends get_project_name()
191
-
192
- /**
193
- * Ensure all required environment variables exist. Add each missing variable
194
- * to an error that will be thrown at the end.
195
- *
196
- * @throws ReferenceError with a message for all missing variables.
197
- */
198
- session_vars.validateEnvironment = function() {
199
- /** Throw a useful error for the developer if any of the required env vars
200
- * are missing. For most devs, these will be GitHub secrets. */
201
-
202
- // Note: We are checking for BASE_URL, but we are referring it as SERVER_URL as that
203
- // is the name used in GitHub secrets, which is what most devs will be using.
211
+ }; // Ends get_artifacts_path_name()
204
212
 
213
+ session_vars.validateEnvironment = function() {
214
+ /** Throw a useful error for the developer if any of the env vars
215
+ * required by absolutely all tests are missing. Gather all
216
+ * missing var names into one message to ease dev process.
217
+ *
218
+ * Rational for doing it all in here:
219
+ * 1. Easier to test
220
+ * 2. Will avoid piecemeal errors in separate locations
221
+ *
222
+ * TODO: Also validate format of variables?
223
+ *
224
+ * @throws ReferenceError with a message for all missing variables.
225
+ */
205
226
  let errorMessage = '';
206
227
 
207
- // 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
208
- // will have to run their pipeline an incredible number of times to discover all the missing variables.
209
- if ( !session_vars.get_dev_api_key() )
210
- {
211
- 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.`
212
- }
213
- if ( !session_vars.get_da_server_url() )
214
- {
228
+ // Env vars required for any tests
229
+ if ( !session_vars.get_da_server_url() ) {
215
230
  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.`;
216
231
  }
217
- if ( !session_vars.get_repo_url() )
218
- {
219
- errorMessage += `\nThe REPO_URL GitHub secret must be defined. The REPO_URL is the URL to the Git repo whose Actions are being run.`;
220
- }
221
- // The branch name is always automatically defined in Github, it'll be missing only if you're running locally and missing an env var.
222
- if ( !session_vars.get_branch_name() )
223
- {
224
- 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.`;
225
- }
232
+
233
+ let origin = session_vars.get_origin();
234
+ if ( origin !== 'github' && origin !== 'local' && origin !== 'playground' ) {
235
+ errorMessage += `\nThe _ORIGIN environment variable should be set to a valid value automatically. If you see this error, file an issue at https://github.com/suffolkLITLab/docassemble-ALKilnInThePlayground. It must be defined as either 'playground', 'github', or 'local', but its value was ${origin}`;
236
+
237
+ // If origin has one of the allowed values, we can do the rest of the validations
238
+ } else {
239
+
240
+ // Env vars required for github or local runs, as both create Projects
241
+ // in the Playground and pull the repo from GitHub
242
+ if ( origin === 'github' || origin === 'local' ) {
243
+ if ( !session_vars.get_dev_api_key() ) {
244
+ 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.`
245
+ }
246
+ // Github automatically gives this, but may be missing locally.
247
+ if ( !session_vars.get_repo_url() ) {
248
+ errorMessage += `\nThe REPO_URL environment variable must be defined. The REPO_URL is the URL to the GitHub repo whose Actions are being run.`;
249
+ }
250
+ // Github automatically gives this, but may be missing locally.
251
+ if ( !session_vars.get_branch_name() ) {
252
+ 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.`;
253
+ }
254
+ } // ends github and local env vars
255
+
256
+ // We're the ones ensuring these exist for Playground runs
257
+ if ( origin === 'playground' ) {
258
+ if ( !session_vars.get_user_project_name() ) {
259
+ errorMessage += `\nThe _PROJECT_NAME environment variable should automatically exist. If you see this error, file an issue at https://github.com/suffolkLITLab/docassemble-ALKilnInThePlayground. It is the name of the Project the developer chose to test.`
260
+ }
261
+ if ( !session_vars.get_user_id() ) {
262
+ errorMessage += `\nThe _USER_ID environment variable should automatically exist. If you see this error, file an issue at https://github.com/suffolkLITLab/docassemble-ALKilnInThePlayground. It is the user ID of the developer on the server.`
263
+ }
264
+ // Tags are optional
265
+ } // ends Playground env vars
266
+
267
+ } // ends validation based on origin
226
268
 
227
269
  errorMessage = errorMessage.trim();
228
270
 
229
271
  if (errorMessage !== '') {
230
- throw new ReferenceError(errorMessage);
272
+ throw new ReferenceError(`ALKiln error: ${errorMessage}`);
231
273
  }
232
274
  };
233
-
275
+
234
276
  session_vars.validateEnvironment();
package/package.json CHANGED
@@ -1,14 +1,12 @@
1
1
  {
2
2
  "name": "@suffolklitlab/alkiln",
3
- "version": "4.11.2",
3
+ "version": "5.0.0-playground-2",
4
4
  "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
7
7
  "test": "npm run unit && npm run langs_setup && npm run cucumber -- $@",
8
- "cucumber_base": "run_cucumber(){ ./node_modules/.bin/cucumber-js \"$@\"; status=$?; ./lib/utils/clean_reports.js; return $status; }; run_cucumber",
9
- "cucumber": "run_base(){ npm run cucumber_base -- \"$@\" docassemble/*/data/sources/*.feature --retry 1; }; run_base",
10
- "cucumber_external": "run_cucumber(){ tmp_path=`pwd`; ( new_path=\"$1\"; shift; cd \"$new_path\"; npm run --prefix \"${tmp_path}\" cucumber_base -- \"$@\" \"$new_path\"/docassemble/*/data/sources/*.feature;) }; run_cucumber",
11
- "langs": "npm run langs_setup && ./node_modules/.bin/cucumber-js --require ./lib/index.js docassemble/*/data/sources/*_lang*.feature",
8
+ "cucumber": "./lib/run_cucumber.js",
9
+ "langs": "npm run langs_setup && npm run cucumber",
12
10
  "langs_setup": "./lib/utils/langs.js 'docassemble/*/data/sources/*.feature'",
13
11
  "langs_local": "npm run langs_setup && npm run local",
14
12
  "local": "npm run setup && npm run cucumber; npm run takedown",
@@ -36,13 +34,13 @@
36
34
  "homepage": "https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/",
37
35
  "dependencies": {
38
36
  "@axe-core/puppeteer": "4.4.2",
39
- "@cucumber/cucumber": "7.3.2",
37
+ "@cucumber/cucumber": "8.6.0",
40
38
  "@faker-js/faker": "^7.3.0",
41
39
  "axios": "0.24.0",
42
40
  "chai": "4.2.0",
43
41
  "cheerio": "1.0.0-rc.10",
44
- "dotenv": "8.2.0",
45
42
  "deep-equal-in-any-order": "^2.0.0",
43
+ "dotenv": "8.2.0",
46
44
  "mocha": "9.2.0",
47
45
  "puppeteer": "13.1.3",
48
46
  "qs": "6.10.2",
@@ -52,6 +50,7 @@
52
50
  "bin": {
53
51
  "alkiln-setup": "./lib/docassemble/setup.js",
54
52
  "alkiln-takedown": "./lib/docassemble/takedown.js",
55
- "alkiln-langs": "./lib/utils/langs.js"
53
+ "alkiln-langs": "./lib/utils/langs.js",
54
+ "alkiln-run": "./lib/run_cucumber.js"
56
55
  }
57
56
  }
@@ -4,42 +4,135 @@ const expect = chai.expect;
4
4
  const session_vars = require("../../lib/utils/session_vars.js");
5
5
  const validateEnvironment = session_vars.validateEnvironment;
6
6
 
7
+ // Not going to totally support these any more.
8
+ delete process.env.BRANCH_PATH;
9
+ delete process.env.BASE_URL;
10
+
7
11
  describe('When the `session_vars` object uses', function () {
8
12
  describe('validateEnvironment, it', function() {
9
13
 
10
14
  /**
11
- * All required variables should be defined in .env. For reference, those variables are the following:
12
- *
13
- * DOCASSEMBLE_DEVELOPER_API_KEY
14
- * BASE_URL
15
- * REPO_URL
16
- */
17
- it("successfully completes when all required env vars exist", function() {
15
+ * All required variables should be defined in .env. Different
16
+ * env vars for different test runner locations/origins.
17
+ */
18
+ it("successfully completes when all required local env vars exist", function() {
19
+ process.env._ORIGIN = `local`;
20
+ process.env.SERVER_URL = `https://example.com`;
21
+ process.env.DOCASSEMBLE_DEVELOPER_API_KEY = `1`;
22
+ process.env.REPO_URL = `https://github.com/org/docassemble-repo`;
23
+ process.env.BRANCH_NAME = `a_branch`;
24
+
25
+ delete process.env._PROJECT_NAME;
26
+ delete process.env._USER_ID;
27
+
18
28
  session_vars.validateEnvironment();
19
29
  });
30
+ it("successfully completes when all required GitHub env vars exist", function() {
31
+ process.env._ORIGIN = `github`;
32
+ process.env.SERVER_URL = `https://example.com`;
33
+ process.env.DOCASSEMBLE_DEVELOPER_API_KEY = `1`;
34
+ process.env.REPO_URL = `https://github.com/org/docassemble-repo`;
35
+ process.env.BRANCH_NAME = `a_branch`;
36
+
37
+ delete process.env._PROJECT_NAME;
38
+ delete process.env._USER_ID;
39
+
40
+ session_vars.validateEnvironment();
41
+ });
42
+ it("successfully completes when all required Playground env vars exist", function() {
43
+ process.env._ORIGIN = `playground`;
44
+ process.env.SERVER_URL = `https://example.com`;
45
+ process.env._PROJECT_NAME = `TheProject`;
46
+ process.env._USER_ID = `1`;
20
47
 
21
- /**
22
- * This test has an ordering dependency with the test below. I'm not certain if I am getting lucky or if I need to explicitly declare the order.
23
- */
24
- it("throws a reference error when any var is missing", function() {
25
48
  delete process.env.DOCASSEMBLE_DEVELOPER_API_KEY;
49
+ delete process.env.REPO_URL;
50
+ delete process.env.BRANCH_NAME;
26
51
 
27
- expect(session_vars.validateEnvironment).to.throw(ReferenceError, /DOCASSEMBLE_DEVELOPER_API_KEY/);
52
+ session_vars.validateEnvironment();
28
53
  });
29
54
 
30
55
  /**
31
- * Delete all environment variables before validating the variables.
56
+ * Throwing errors
32
57
  */
33
- it("names all missing variables in the error, not just one", function() {
34
- delete process.env.DOCASSEMBLE_DEVELOPER_API_KEY;
35
- delete process.env.BASE_URL;
58
+ it("names all missing always-required variables in the error", function() {
59
+ process.env._ORIGIN = 'playground';
60
+ // Ensure it doesn't fail because of Playground required vars
61
+ process.env._PROJECT_NAME = `TheProject`;
62
+ process.env._USER_ID = `1`;
63
+
36
64
  delete process.env.SERVER_URL;
37
- delete process.env.REPO_URL;
38
65
 
39
66
  // This test just tests that the variable names show up in a fixed order.
40
67
  expect(session_vars.validateEnvironment).to.throw(
41
68
  ReferenceError,
42
- /DOCASSEMBLE_DEVELOPER_API_KEY.*\n.*SERVER_URL.*\n.*REPO_URL/
69
+ /SERVER_URL/
70
+ );
71
+ });
72
+
73
+ it("identifies when _ORIGIN has an invalid value", function() {
74
+ process.env._ORIGIN = 'invalid';
75
+ delete process.env.SERVER_URL;
76
+
77
+ // This test just tests that the variable names show up in a fixed order.
78
+ expect(session_vars.validateEnvironment).to.throw(
79
+ ReferenceError,
80
+ /_ORIGIN/
81
+ );
82
+ });
83
+
84
+ it("names all missing local variables in the error, not just one", function() {
85
+ // Ensure it doesn't fail because of always-required vars
86
+ process.env.SERVER_URL = `https://example.com`;
87
+ // Ensure it doesn't fail because of Playground required vars
88
+ process.env._PROJECT_NAME = `TheProject`;
89
+ process.env._USER_ID = `1`;
90
+
91
+ process.env._ORIGIN = `local`;
92
+ delete process.env.DOCASSEMBLE_DEVELOPER_API_KEY;
93
+ delete process.env.REPO_URL;
94
+ delete process.env.BRANCH_NAME;
95
+
96
+ // Matches message contents in any order
97
+ expect(session_vars.validateEnvironment).to.throw(
98
+ ReferenceError,
99
+ /(?=[\S\s]*REPO_URL)(?=[\S\s]*BRANCH_NAME)(?=[\S\s]*DOCASSEMBLE_DEVELOPER_API_KEY)/
100
+ );
101
+ });
102
+
103
+ it("names all missing GitHub variables in the error, not just one", function() {
104
+ // Ensure it doesn't fail because of always-required vars
105
+ process.env.SERVER_URL = `https://example.com`;
106
+ // Ensure it doesn't fail because of Playground required vars
107
+ process.env._PROJECT_NAME = `TheProject`;
108
+ process.env._USER_ID = `1`;
109
+
110
+ process.env._ORIGIN = `github`;
111
+ delete process.env.DOCASSEMBLE_DEVELOPER_API_KEY;
112
+ delete process.env.REPO_URL;
113
+ delete process.env.BRANCH_NAME;
114
+
115
+ expect(session_vars.validateEnvironment).to.throw(
116
+ ReferenceError,
117
+ /(?=[\S\s]*REPO_URL)(?=[\S\s]*BRANCH_NAME)(?=[\S\s]*DOCASSEMBLE_DEVELOPER_API_KEY)/
118
+ );
119
+ });
120
+
121
+ it("names all missing Playground variables in the error, not just one", function() {
122
+ // Ensure it doesn't fail because of always-required vars
123
+ process.env.SERVER_URL = `https://example.com`;
124
+ // Ensure it doesn't fail because of GitHub required vars
125
+ process.env.DOCASSEMBLE_DEVELOPER_API_KEY = `1`;
126
+ process.env.REPO_URL = `https://github.com/org/docassemble-repo`;
127
+ process.env.BRANCH_NAME = `a_branch`;
128
+
129
+ process.env._ORIGIN = `playground`;
130
+ delete process.env._PROJECT_NAME;
131
+ delete process.env._USER_ID;
132
+
133
+ expect(session_vars.validateEnvironment).to.throw(
134
+ ReferenceError,
135
+ /(?=[\S\s]*_PROJECT_NAME)(?=[\S\s]*_USER_ID)/
43
136
  );
44
137
  });
45
138
  });
package/cucumber.js DELETED
@@ -1,3 +0,0 @@
1
- module.exports = {
2
- default: "--require ./lib/index.js --format progress --format summary:cucumber-report.txt"
3
- }
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs');
4
- const log = require('./log');
5
- const session_vars = require('./session_vars');
6
-
7
- // This is hardcoded information from `cucumber.js`. Once we move to cucumber 8, we should
8
- // change that the cucumber.json and actually parse the info from it.
9
- // The file won't be used for anything other than detecting and removing color codes
10
- let summary_file_name = 'cucumber-report.txt';
11
- fs.readFile(summary_file_name, 'utf8', (err, data) => {
12
- if (err) {
13
- console.error(err);
14
- return;
15
- }
16
-
17
- // Replace terminal color codes, if present
18
- // https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797#colors--graphics-mode
19
- data = data.replace(/\x1b\[[0-9][0-9]?[0-9]?m/g, '');
20
-
21
- // Put it in the correct file in the correct folder
22
- let artifacts_path = session_vars.get_artifacts_path_name();
23
- fs.appendFile( `${ artifacts_path }/${ log.debug_log_file }`, data, err => {
24
- if (err) {
25
- console.error(err);
26
- return;
27
- }
28
- }); // Ends appendFileSync
29
- });