@suffolklitlab/alkiln 5.11.2 → 5.12.0-delete2

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.
@@ -23,8 +23,8 @@ on:
23
23
  jobs:
24
24
 
25
25
  github-server-tests:
26
- #### Developer note: If under `on` (above) you have both `push` and
27
- #### `pull_request`, this avoids running double tests when someone
26
+ #### Developer note: If you have both `push` and `pull_request` in the
27
+ #### `on` section (above), this avoids running double tests when someone
28
28
  #### on the team makes a pull request
29
29
  # 1: Trigger job on push or dispatch from this repository
30
30
  # 2: Trigger job on pull request from a fork
@@ -71,10 +71,10 @@ jobs:
71
71
  shell: bash
72
72
 
73
73
  #### Developer note: You can probably leave this out
74
- # Optional debugging to explore things like docker issues
75
- - name: Docker debug tmate session
76
- if: ${{ github.event.inputs.enable_tmate }}
77
- uses: mxschmitt/action-tmate@v3
74
+ # # Optional debugging to explore things like docker issues
75
+ # - name: Docker debug tmate session
76
+ # if: ${{ failure() && github.event.inputs.enable_tmate == 'true' }}
77
+ # uses: mxschmitt/action-tmate@v3
78
78
 
79
79
  #### Developer note: You can probably leave this out
80
80
  - name: Run ALKiln - Env vars for our own tests from server output
@@ -93,25 +93,49 @@ jobs:
93
93
 
94
94
  - name: "Run ALKiln tests"
95
95
  if: ${{ success() }}
96
+ id: alkiln
96
97
  #### Developer note: you'll need to replace `uses: ./`
97
98
  #### with the path to ALKiln’s repository name and branch name. For example,
98
99
  #### for version 5, use the branch name `v5`: `uses: suffolkLITLab/ALKiln@v5`
99
100
  uses: ./
100
- #### Developer note: You can probably leave this out
101
+ #### Developer note: You can probably leave out this `env` section
101
102
  env:
102
- ALKILN_TAG_EXPRESSION: "${{ (github.event.inputs.tags && format('{0}', github.event.inputs.tags)) || 'not @a11y' }}"
103
+ # Internal: docassemble changes sometimes break @a11y tests. That is
104
+ # a da problem, not ours. We will trigger these tests manually when we
105
+ # want to check up on this.
106
+ ALKILN_TAG_EXPRESSION: "${{ (github.event.inputs.tags && format('{0}', github.event.inputs.tags)) || '(not @error) and (not @temp_error)' }}"
103
107
  with:
104
108
  #### Developer note: Required inputs. See
105
109
  #### https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/writing/#sandbox-inputs
106
110
  SERVER_URL: "${{ steps.github_server.outputs.SERVER_URL }}"
107
111
  DOCASSEMBLE_DEVELOPER_API_KEY: "${{ steps.github_server.outputs.DOCASSEMBLE_DEVELOPER_API_KEY }}"
108
112
  INSTALL_METHOD: "server"
109
- #### Developer note: See https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/writing/#optional-inputs
110
- #### You can probably leave this out
111
- # Internal: docassemble changes sometimes break @a11y tests. That is
112
- # a da problem, not ours. We will trigger these tests manually when we
113
- # want to check up on this.
114
- ALKILN_TAG_EXPRESSION: $ALKILN_TAG_EXPRESSION
115
- #### Developer note: You can probably leave this out
116
- # ALKILN_VERSION: url
113
+ #### Developer note: You can probably leave the rest out
114
+ #### To learn more, see https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/writing/#optional-inputs
115
+ ALKILN_TAG_EXPRESSION: "${{ env.ALKILN_TAG_EXPRESSION }}"
116
+ ALKILN_VERSION: delete
117
+
118
+ #### Developer note: Example of making an issue when tests fail
119
+ #### that includes the text of the failure output file
120
+ #### See https://cli.github.com/manual/gh_issue_create for more details
121
+ - shell: bash
122
+ if: ${{ failure() }}
123
+ env:
124
+ GH_TOKEN: ${{ github.token }}
125
+ PATH_TO_REPORT_FILE: "${{ steps.alkiln.outputs.PATH_TO_REPORT_FILE }}"
126
+ PATH_TO_UNEXPECTED_RESULTS_FILE: "${{ steps.alkiln.outputs.PATH_TO_UNEXPECTED_RESULTS_FILE }}"
127
+ PATH_TO_DEBUG_LOG_FILE: "${{ steps.alkiln.outputs.PATH_TO_DEBUG_LOG_FILE }}"
128
+ run: |
129
+ # Make an issue with file contents or 'no file' message as part of the body
130
+
131
+ if [ -f "${{ env.PATH_TO_UNEXPECTED_RESULTS_FILE }}" ]; then
132
+ UNEXPECTED_2=$(cat "${{ env.PATH_TO_UNEXPECTED_RESULTS_FILE }}")
133
+ else
134
+ UNEXPECTED_2="unexpected_results.txt does not exist. Something other than the interview tests may have failed."
135
+ fi
136
+
137
+ gh issue create --body "An ALKiln test run failed. See the action at ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}.
138
+
139
+ ### Results:
117
140
 
141
+ $UNEXPECTED_2" --repo "$GITHUB_REPOSITORY" --label "testing" --title "ALKiln tests failed"
@@ -13,8 +13,8 @@ on:
13
13
  jobs:
14
14
 
15
15
  puppeteer-tests:
16
- #### Developer note: If under `on` (above) you have both `push` and
17
- #### `pull_request`, this avoids running double tests when someone
16
+ #### Developer note: If you have both `push` and `pull_request` in the
17
+ #### `on` section (above), this avoids running double tests when someone
18
18
  #### editing the repository makes a pull request
19
19
  # 1: Trigger job on push or dispatch from this repository
20
20
  # 2: Trigger job on pull request from a fork
@@ -55,18 +55,13 @@ jobs:
55
55
  # Place the root directory in this branch to access relative paths to action files
56
56
  - uses: actions/checkout@v4
57
57
 
58
- - name: Pause between triggers
59
- if: (github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork)
60
- shell: bash
61
- run: sleep 60
62
-
63
58
  #### Developer note: you'll need to replace `uses: ./`
64
59
  #### with the path to ALKiln’s repository name and branch name. For example,
65
60
  #### for version 5, use the branch name `v5`: `uses: suffolkLITLab/ALKiln@v5`
66
61
  - uses: ./
67
62
  #### Developer note: You can probably leave this out
68
63
  env:
69
- ALKILN_TAG_EXPRESSION: "${{ (github.event.inputs.tags && format('{0}', github.event.inputs.tags)) || 'not @a11y' }}"
64
+ ALKILN_TAG_EXPRESSION: "${{ (github.event.inputs.tags && format('{0}', github.event.inputs.tags)) || '(not @error) and (not @temp_error)' }}"
70
65
  with:
71
66
  #### Developer note: Required ALKiln inputs. You can read more at
72
67
  #### https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/writing/#githubNyou-inputs
@@ -77,6 +72,6 @@ jobs:
77
72
  # Internal: docassemble changes sometimes break @a11y tests. That is
78
73
  # a da problem, not ours. We will trigger these tests manually when we
79
74
  # want to check up on this.
80
- ALKILN_TAG_EXPRESSION: $ALKILN_TAG_EXPRESSION
75
+ ALKILN_TAG_EXPRESSION: "${{ env.ALKILN_TAG_EXPRESSION }}"
81
76
  #### Developer note: You can probably leave this out
82
- # ALKILN_VERSION: url
77
+ ALKILN_VERSION: delete
package/CHANGELOG.md CHANGED
@@ -43,7 +43,45 @@ Format:
43
43
  -
44
44
  -->
45
45
 
46
- <!-- ## [Unreleased] -->
46
+ ## [Unreleased]
47
+
48
+ ### Changed
49
+
50
+ - Update links in logs to new Suffolk docs pages
51
+
52
+ ### Fixed
53
+
54
+ - Now delete interviews upon interview completion for signed in users
55
+
56
+ ### Internal
57
+
58
+ - Test deleting interviews upon interview completion for signed in users
59
+ - Added @temp_error to neutralize tests that are getting undesired errors right now, but which we anticipate being fixed soon. We want to ignore them for a short while. For example, an upstream change temporarily breaking some tests.
60
+
61
+ ## [5.12.0] - 2024-06-06
62
+
63
+ ### Added
64
+
65
+ - New file with just failure results, including cucumber results when possible. It accumulates Scenario info as the tests run. Even if a test run stops early, there will still be some results.
66
+ - New output from ALKiln action - `PATH_TO_ARTIFACTS`. The value is `""` or the path to the folder that contains the output of the test run, if the test run actually happened. Sometimes this folder doesn't get created. For example, if there was a typo in one or more `.feature` file. For example, `"alkiln-2024-06-03 at 02h29m44sUTC"`.
67
+ - New outputs from the ALKiln action - `PATH_TO_REPORT_FILE`, `PATH_TO_UNEXPECTED_RESULTS_FILE`, and `PATH_TO_DEBUG_LOG_FILE`. For example, `"alkiln-2024-06-03 at 02h29m44sUTC/report.txt"`. Authors can choose to use these as they wish. For example, they can put the contents of these files in issues they create from their workflow. Closes [#701](https://github.com/SuffolkLITLab/ALKiln/issues/701).
68
+ - Added cucumber output to report.txt.
69
+ - Added more explanation for the logs that appear when test runs error in a way that prevents debug_log.txt and such files from being created. We will watch for feedback about the error data itself. It may still be too confusing.
70
+
71
+ ### Changed
72
+
73
+ - report.txt accumulates over time instead of being created all at once at the end. Even if a test run stops early, there will still be some results.
74
+
75
+ ### Fixed
76
+
77
+ - Tag expression not being parsed correctly. Surrounded `ALKILN_TAG_EXPRESSION` with `"`.
78
+
79
+ ### Internal
80
+
81
+ - Download ALKiln's artifacts back into the running job.
82
+ - Removed our own workflow's pause between triggers.
83
+ - Improved our use of ALKIN_TAG_EXPRESSION env var.
84
+ - Exclude tests that we are aware fail. We will test those manually.
47
85
 
48
86
  ## [5.11.2] - 2024-05-17
49
87
 
package/README.md CHANGED
@@ -12,7 +12,7 @@ https://github.com/18F/open-source-guide/blob/18f-pages/pages/making-readmes-rea
12
12
 
13
13
  ## Use ALKiln
14
14
 
15
- Read about how to use this framework in [Assembly Line's documentation](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/).
15
+ Read about how to use [this framework in Assembly Line's documentation](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/intro).
16
16
 
17
17
  If you have questions or need to report a bug, [start a new issue](https://github.com/SuffolkLITLab/ALKiln/issues/new).
18
18
 
package/action.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  name: "ALKiln: Automated docassemble tests"
2
- description: "Automatically test any docassemble interview in a any branch on your GitHub repository whenever you commit or push. See https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing."
2
+ description: "Automatically test any docassemble interview in a any branch on your GitHub repository whenever you commit or push. See https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/setup/#types."
3
3
 
4
4
  inputs:
5
5
  SERVER_URL:
@@ -34,6 +34,20 @@ inputs:
34
34
  required: false
35
35
  default: "0.0.17"
36
36
 
37
+ outputs:
38
+ PATH_TO_ARTIFACTS:
39
+ description: 'An empty string ("") or the path to the artifacts folder that contains the test run output. Remember to check for the existence of the folder before using it.'
40
+ value: ${{ steps.alkiln_outputs.outputs.PATH_TO_ARTIFACTS }}
41
+ PATH_TO_REPORT_FILE:
42
+ description: 'An empty string ("") or the path to report.txt. Remember to check for the existence of the file before using it.'
43
+ value: ${{ steps.alkiln_outputs.outputs.PATH_TO_REPORT_FILE }}
44
+ PATH_TO_UNEXPECTED_RESULTS_FILE:
45
+ description: 'An empty string ("") or the path to unexpected_results.txt. Remember to check for the existence of the file before using it.'
46
+ value: ${{ steps.alkiln_outputs.outputs.PATH_TO_UNEXPECTED_RESULTS_FILE }}
47
+ PATH_TO_DEBUG_LOG_FILE:
48
+ description: 'An empty string ("") or the path to debug_log.txt. Remember to check for the existence of the file before using it.'
49
+ value: ${{ steps.alkiln_outputs.outputs.PATH_TO_DEBUG_LOG_FILE }}
50
+
37
51
  runs:
38
52
  using: "composite"
39
53
  steps:
@@ -102,6 +116,7 @@ runs:
102
116
  if: ${{ inputs.INSTALL_METHOD == 'playground' && failure() }}
103
117
  shell: bash
104
118
  run: |
119
+ # 😞 ALK0028 ERROR: Unable to create a Project
105
120
  echo -e "\n\n====\n😞 ALK0028 ERROR: Unable to create a Project on your server's testing account or pull your package into it. Check the messages above this line.\n\n"
106
121
 
107
122
  # Server installations - find folders and save session vars
@@ -121,7 +136,7 @@ runs:
121
136
  env:
122
137
  ALKILN_TAG_EXPRESSION: ${{ inputs.ALKILN_TAG_EXPRESSION || github.event.inputs.tags && format('{0}', github.event.inputs.tags) }}
123
138
  shell: bash
124
- run: alkiln-run $ALKILN_TAG_EXPRESSION
139
+ run: alkiln-run "$ALKILN_TAG_EXPRESSION"
125
140
 
126
141
  # on playground, delete project
127
142
  - name: "💡 ALK0032 INFO: Delete the Project from the Playground"
@@ -137,6 +152,64 @@ runs:
137
152
  name: ${{ env.ARTIFACT_NAME }}
138
153
  path: ./alkiln-*
139
154
 
155
+ # Download artifacts folder internally to create action output
156
+ - name: "💡 ALK0183 INFO: Get uploaded artifacts folder"
157
+ if: ${{ always() }}
158
+ uses: actions/download-artifact@v4
159
+ with:
160
+ name: ${{ env.ARTIFACT_NAME }}
161
+
162
+ - name: "💡 ALK0184 INFO: Prepare ALKiln output file paths"
163
+ if: ${{ always() }}
164
+ id: alkiln_outputs
165
+ shell: bash
166
+ run: |
167
+ # 💡 ALK0184 INFO: Prepare ALKiln output file paths
168
+
169
+ FOLDER=$(ls -d */ | grep -E '^alkiln-*' || true)
170
+
171
+ echo "Test results artifacts folder is $FOLDER"
172
+
173
+ if [[ "$FOLDER" = "" ]]; then
174
+ echo "Artifacts folder is missing. The path is an empty string: '$FOLDER'"
175
+
176
+ else
177
+ REPORT_PATH="${FOLDER}report.txt"
178
+ UNEXPECTED_RESULTS_PATH="${FOLDER}unexpected_results.txt"
179
+ DEBUG_LOG_PATH="${FOLDER}debug_log.txt"
180
+
181
+ if [ -f "$REPORT_PATH" ]; then
182
+ echo "$REPORT_PATH exists"
183
+ else
184
+ echo "$REPORT_PATH is missing"
185
+ REPORT_PATH=""
186
+ fi
187
+
188
+ if [ -f "$UNEXPECTED_RESULTS_PATH" ]; then
189
+ echo "$UNEXPECTED_RESULTS_PATH exists"
190
+ else
191
+ echo "$UNEXPECTED_RESULTS_PATH is missing"
192
+ UNEXPECTED_RESULTS_PATH=""
193
+ fi
194
+
195
+ if [ -f "$DEBUG_LOG_PATH" ]; then
196
+ echo "$DEBUG_LOG_PATH exists"
197
+ else
198
+ echo "$DEBUG_LOG_PATH is missing"
199
+ DEBUG_LOG_PATH=""
200
+ fi
201
+
202
+ fi
203
+
204
+ echo "PATH_TO_ARTIFACTS=$FOLDER" >> "$GITHUB_OUTPUT"
205
+ echo "PATH_TO_REPORT_FILE=$REPORT_PATH" >> "$GITHUB_OUTPUT"
206
+ echo "PATH_TO_UNEXPECTED_RESULTS_FILE=$UNEXPECTED_RESULTS_PATH" >> "$GITHUB_OUTPUT"
207
+ echo "PATH_TO_DEBUG_LOG_FILE=$DEBUG_LOG_PATH" >> "$GITHUB_OUTPUT"
208
+
209
+ # Avoid creating output from text in files
210
+ # Cannot properly create outputs with multi-line text. Collapses onto one line.
211
+
140
212
  - shell: bash
213
+ if: ${{ always() }}
141
214
  run: |
142
215
  echo "💡 ALK0034 INFO: ALkiln finished running tests"
@@ -109,12 +109,14 @@ runs:
109
109
  if: ${{ env.OUTPUT_DOCKER == 'false' }}
110
110
  shell: bash
111
111
  run: |
112
- echo '===\n💡 ALK0007 INFO: ALKiln will now hide docker output to avoid possibly showing details about your config. If you want to get output to see more of what is going on, use "with:" and set the input "SHOW_DOCKER_OUTPUT" to "true". This output will only be visible to those who can already see the action output, like people with admin or write permissions.\n==='
112
+ # 💡 ALK0007 INFO: ALKiln will now hide docker output
113
+ echo -e '===\n💡 ALK0007 INFO: ALKiln will now hide docker output to avoid possibly showing details about your config. If you want to get output to see more of what is going on, use "with:" and set the input "SHOW_DOCKER_OUTPUT" to "true". This output will only be visible to those who can already see the action output, like people with admin or write permissions.\n==='
113
114
  - name: "💡 ALK0008 INFO: ALKiln will show docker output"
114
115
  if: ${{ env.OUTPUT_DOCKER == 'true' }}
115
116
  shell: bash
116
117
  run: |
117
- echo '===\n💡 ALK0008 INFO: ALKiln will show docker output. ALKiln cannot control this output. If you want to prevent output, use "with:" and set the input "SHOW_DOCKER_OUTPUT" to "false". This output will only be visible to those who can already see the action output, like people with admin or write permissions.\n==='
118
+ # 💡 ALK0008 INFO: ALKiln will show docker output
119
+ echo -e '===\n💡 ALK0008 INFO: ALKiln will show docker output. ALKiln cannot control this output. If you want to prevent output, use "with:" and set the input "SHOW_DOCKER_OUTPUT" to "false". This output will only be visible to those who can already see the action output, like people with admin or write permissions.\n==='
118
120
 
119
121
  # TODO: Reduce these to 1 block by putting ` > /dev/null 2>&1` into an env var
120
122
  # No docker output
@@ -156,6 +158,7 @@ runs:
156
158
  if: ${{ env.OUTPUT_DOCKER == 'false' }}
157
159
  shell: bash
158
160
  run: |
161
+ # 💡 ALK0011 INFO: The following messages are not docker output
159
162
  echo "💡 ALK0011 INFO: The following messages are not docker output, just ALKiln waiting for the server to start"
160
163
 
161
164
  - name: "💡 ALK0012 INFO: Waiting for server to start"
@@ -245,4 +248,3 @@ runs:
245
248
  if: ${{ failure() }}
246
249
  run: |
247
250
  echo "🤕 ALK0022 ERROR: Something went wrong. See above for more details."
248
-
@@ -4,7 +4,7 @@ The Story Table is a very flexible tool. It lets the author answer interview (fo
4
4
 
5
5
  When the robot arrives on each interview page, it finds all the fields on the page and loops through the whole Story Table trying to match rows with fields.
6
6
 
7
- Here's some [documentation on the use of Story Table rows](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/#story-tables).
7
+ Here's some [documentation on the use of Story Table rows](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln/writing#story-table).
8
8
 
9
9
  ## Tracking use of rows
10
10
 
@@ -31,7 +31,7 @@ da.get_dev_id = async function ( timeout ) {
31
31
  }; // Ends da.get_dev_id()
32
32
 
33
33
 
34
- // node -e 'require("./lib/docassemble/docassemble_api_REST.js").delete_project( "create1" )'
34
+ // node -e 'require("./lib/docassemble/docassemble_api_REST.js").delete_project()'
35
35
  da.delete_project = async function ( timeout ) {
36
36
  /* Delete a project on the user's da server. */
37
37
  let options = {
@@ -53,6 +53,51 @@ da.delete_project = async function ( timeout ) {
53
53
  }; // Ends da.delete_project()
54
54
 
55
55
 
56
+ // node -e 'require("./lib/docassemble/docassemble_api_REST.js").delete_interview(...)'
57
+ da.delete_interview = async function ( timeout, { session = null, api_key = null }) {
58
+ /* Delete an interview with the given session id. */
59
+ let options = {
60
+ method: 'DELETE',
61
+ headers: { 'X-API-Key': api_key },
62
+ // Can't use `api/session` that uses the API key to identify the user's
63
+ // interview sessions. The test may have signed in with an account
64
+ // that's different than the ALKiln dev account that sends this request.
65
+ url: `${ session_vars.get_da_server_url() }/api/user/interviews`,
66
+ params: { session },
67
+ timeout: timeout,
68
+ };
69
+
70
+ try {
71
+ return await axios.request( options );
72
+ } catch (error) {
73
+ let response = error.response || {data: null}
74
+ throw {...error.toJSON(), data: response.data};
75
+ }
76
+ }; // Ends da.delete_interview()
77
+
78
+ // node -e 'require("./lib/docassemble/docassemble_api_REST.js").get_interview(...)'
79
+ da.get_interview = async function ( timeout, { session = null, api_key = null }) {
80
+ /* Get a specific interview. */
81
+ let options = {
82
+ method: 'GET',
83
+ headers: { 'X-API-Key': api_key },
84
+ // Can't use `/api/user/interviews` that uses the API key to identify the user's
85
+ // interview sessions. The test may have signed in with an account
86
+ // that's different than the ALKiln dev account that sends this request.
87
+ url: `${ session_vars.get_da_server_url() }/api/user/interviews`,
88
+ params: { session },
89
+ timeout: timeout,
90
+ };
91
+
92
+ try {
93
+ return await axios.request( options );
94
+ } catch ( error ) {
95
+ let response = error.response || { data: null }
96
+ throw { ...error.toJSON(), data: response.data };
97
+ }
98
+ }; // Ends da.get_interview()
99
+
100
+
56
101
  // node -e 'require("./lib/docassemble/docassemble_api_REST.js").__delete_projects_starting_with( "testingdocassembleALAutomatedTestingTestsmain", 1, 1 )'
57
102
  da.__delete_projects_starting_with = async function ( base_name, starting_num=1, ending_num ) {
58
103
  /** FOR DEVELOPMENT ONLY. Deletes a set of Projects that start with a shared
@@ -15,6 +15,9 @@ const time = require('../utils/time');
15
15
  // to specific functions, which can run in a very small
16
16
  // window.
17
17
 
18
+ // TODO: Move get_dev_id() and such into here instead of in
19
+ // docassemble_REST - args should be the same vals as the request expects
20
+
18
21
  /** Notes:
19
22
  * - Checking for a busy server is vulnerable to race conditions, but
20
23
  * there's nothing we can do about that.
@@ -180,7 +183,7 @@ da_i.delete_project = async function ( delete_options ) {
180
183
  * for a timeout? */
181
184
 
182
185
  // Defaults
183
- let { timeout } = delete_options || { timeout: DEFAULT_MAX_REQUEST_MS };
186
+ let { timeout } = delete_options || { timeout: DEFAULT_MAX_REQUEST_MS };
184
187
 
185
188
  let project_name = session_vars.get_project_name()
186
189
  log.info({ type: `da api`, code: `ALK0170`, pre: `Trying to delete Project ${ project_name }` });
@@ -196,3 +199,129 @@ da_i.delete_project = async function ( delete_options ) {
196
199
  }
197
200
 
198
201
  }; // Ends da_i.delete_project()
202
+
203
+ // node -e 'require("./lib/docassemble/docassemble_api_interface.js").quietly_try_to_delete_interviews(...)'
204
+ da_i.quietly_try_to_delete_interviews = async function ({ interviews = {}, api_keys = [], options = {} }) {
205
+ /** Try to delete the given interviews. Fail silently. Example failure: the test
206
+ * did not log in any users and the testing account's role prevents
207
+ * access to other user's interviews. Do we need to watch for a timeout? */
208
+
209
+ // Defaults
210
+ let { timeout } = options || { timeout: DEFAULT_MAX_REQUEST_MS };
211
+
212
+ let responses = [];
213
+ for ( let id in interviews ) {
214
+ let interview = interviews[ id ];
215
+ if ( interview && interview.uid ) {
216
+ let sub_responses = await da_i.quietly_delete_one_interview( timeout, {
217
+ interview,
218
+ api_keys
219
+ });
220
+ responses.push( sub_responses );
221
+ } else {
222
+ log.debug({ type: `da api info`, code: `ALK0191`, pre: `There is no interview to delete.` });
223
+ }
224
+ }
225
+ return responses;
226
+
227
+ }; // Ends da_i.quietly_try_to_delete_interviews()
228
+
229
+ // node -e 'require("./lib/docassemble/docassemble_api_interface.js").quietly_delete_one_interview(...)'
230
+ da_i.quietly_delete_one_interview = async function ( timeout, { interview = {}, api_keys = [] }) {
231
+ /** Try to delete an interview using all available API keys. Fail silently.
232
+ * Example failure: the test did not log in any users and the testing
233
+ * account's role prevents access to other user's interviews. */
234
+ api_keys = normalize_api_keys( api_keys );
235
+ let responses = [];
236
+ for ( let api_key of api_keys ) {
237
+ try {
238
+ log.debug({ type: `da api info`, code: `ALK0188`,
239
+ pre: `Trying to delete the interview with the id ${ interview.uid } and an API key.`
240
+ });
241
+
242
+ // Actual work:
243
+ let response = await _da_REST.delete_interview( timeout, {
244
+ session: interview.uid,
245
+ api_key
246
+ });
247
+ responses.push( response );
248
+
249
+ log.debug({ type: `da api info`, code: `ALK0189`,
250
+ pre: `No error when trying to delete interview ${ interview.uid } at ${ interview.i }.`
251
+ });
252
+ } catch ( error ) {
253
+ error.interview = interview;
254
+ let msg = `Error when deleting the interview. See https://docassemble.org/docs/api.html#session_delete.`
255
+ log.debug({ type: `da api info`, code: `ALK0190`, pre: msg, data: error });
256
+ }
257
+ } // ends for api keys
258
+ return responses;
259
+ }; // Ends da_i.quietly_delete_one_interview()
260
+
261
+ // node -e 'require("./lib/docassemble/docassemble_api_interface.js").get_interviews(...)'
262
+ da_i.get_interviews = async function ({ interviews = {}, api_keys = [], options = {} }) {
263
+ /** Return an object of interviews that exist on the server
264
+ * that match the given interviews. Keys are the session ids. */
265
+
266
+ api_keys = normalize_api_keys( api_keys );
267
+
268
+ let fetched_interviews = {};
269
+ if ( interviews ) {
270
+ for ( let id in interviews ) {
271
+ let interview = interviews[ id ];
272
+ let fetched = await da_i.get_interview({ interview, api_keys, options });
273
+ if ( fetched && fetched.session ) {
274
+ fetched_interviews[ fetched.session ] = fetched;
275
+ }
276
+ }
277
+ } // ends if interviews
278
+
279
+ return fetched_interviews;
280
+ }; // Ends da_i.get_interviews()
281
+
282
+ // node -e 'require("./lib/docassemble/docassemble_api_interface.js").get_interview(...)'
283
+ da_i.get_interview = async function ({ interview = {}, api_keys = [], options = {} }) {
284
+ /** Try to get an interview from the server that matches the given interview data. */
285
+
286
+ // Defaults
287
+ let { timeout } = options || { timeout: DEFAULT_MAX_REQUEST_MS };
288
+ api_keys = normalize_api_keys( api_keys );
289
+
290
+ if ( interview && interview.uid ) {
291
+ for ( let api_key of api_keys ) {
292
+ try {
293
+ log.debug({ type: `da api info`, code: `ALK0192`, pre: `Trying to get the interview with the id ${ interview.uid } and an API key.` });
294
+
295
+ // Actual work:
296
+ let response = await _da_REST.get_interview( timeout, {
297
+ session: interview.uid,
298
+ api_key
299
+ });
300
+
301
+ if ( response && response.data && response.data.items && response.data.items[0] && response.data.items[0].session ) {
302
+ log.debug({ type: `da api info`, code: `ALK0193`, pre: `Status ${ response.status }. Got ${ response.data.items[0].session }.`});
303
+ return response.data.items[0];
304
+ } else {
305
+ log.debug({ type: `da api info`, code: `ALK0194`, pre: `Status ${ response.status }. Cannot find ${ interview.uid }`});
306
+ }
307
+ } catch ( error ) {
308
+ error.interview = interview;
309
+ let msg = `${ error.status || `No error.status.` } ${ error.statusText || error.data || `No error.statusText.` } Ran into an error when getting the interview ${ interview.uid }. See https://docassemble.org/docs/api.html#interviews.`
310
+ log.debug({ type: `da api info`, code: `ALK0195`, pre: msg, data: error });
311
+ }
312
+ } // ends for api_keys
313
+ } else {
314
+ log.debug({ type: `da api info`, code: `ALK0196`, pre: `There is no interview to get.` });
315
+ }
316
+
317
+ return null;
318
+
319
+ }; // Ends da_i.get_interview()
320
+
321
+ let normalize_api_keys = function ( api_keys = [] ) {
322
+ /** Ensure the dev API key is in the api_keys list. */
323
+ let dev_key = session_vars.get_dev_api_key();
324
+ if ( !api_keys ) { api_keys = [ dev_key ]; }
325
+ if ( !api_keys.includes( dev_key ) ) { api_keys.unshift( dev_key ); }
326
+ return api_keys;
327
+ };
@@ -3,9 +3,12 @@
3
3
  const fs = require('fs');
4
4
  const cucumber_api = require('@cucumber/cucumber/api');
5
5
 
6
- const log = require('./utils/log');
6
+ const log = require('./utils/log'); // These always log in the console
7
7
  const session_vars = require('./utils/session_vars');
8
8
 
9
+ const SUMMARY_FILENAME = `cucumber-report.txt`;
10
+
11
+
9
12
  function build_config( command_args ) {
10
13
  // Turn all the paths to the sources folders into paths to the feature files
11
14
  let sources_paths = session_vars.get_sources_paths();
@@ -17,14 +20,17 @@ function build_config( command_args ) {
17
20
  const config = {
18
21
  publishQuiet: true,
19
22
  require: `./lib/index.js`,
20
- format: [`progress`, `summary:cucumber-report.txt`],
23
+ format: [`progress`, `summary:${ SUMMARY_FILENAME }`],
21
24
  paths: feature_paths,
22
25
  retry: 1,
23
26
  }
24
27
 
25
28
  // Normalize tag expressions, if there are any
26
- let tags = command_args[`_`];
27
- if (tags.length > 0) {
29
+ // Joining and splitting handles both tags surrounded with `""` or those without `""`
30
+ let tags = command_args[`_`].join(` `).split(` `);
31
+ // If there was no tag expression, `tags` is ['']. Remove that item.
32
+ tags = tags.filter(function(entry) { return entry !== ""; })
33
+ if ( tags.length > 0 ) {
28
34
  let joined_tags = '';
29
35
  // Cucumber expects tags in boolean expressions, like (@a0 or @a1) and @random.
30
36
  // If we get a list of only tags, then "or" them all together
@@ -105,19 +111,22 @@ function build_config( command_args ) {
105
111
 
106
112
  const { success } = await cucumber_api.runCucumber({...runConfiguration, support});
107
113
  if ( success ) {
108
- log.info({ type: `run`, code: `ALK0054`, pre: `Cucumber executed correctly` });
114
+ log.success({ type: `run`, code: `ALK0054`, pre: `Tests succeeded!` });
109
115
  } else {
110
- log.error({ type: `run`, code: `ALK0055`, pre: `Cucumber ran into an error` });
116
+ log.error({ type: `run`, code: `ALK0055`, pre: `The test run ran into something unexpected. Look above for more details.` });
111
117
  }
112
- const exitCode = success ? 0 : 1;
118
+ // Custom exit code. May overlap with other systems.
119
+ // Best info I've found via stack overflow: https://tldp.org/LDP/abs/html/exitcodes.html
120
+ const exitCode = success ? 0 : 50;
113
121
  process.exitCode = exitCode;
114
122
 
115
123
  // This is hardcoded information from `provide_config` above.
116
124
  // This code isn't used for anything other than detecting and removing color codes.
117
- let summary_file_name = 'cucumber-report.txt';
125
+ let summary_file_name = SUMMARY_FILENAME;
118
126
  fs.readFile(summary_file_name, 'utf8', (err, text) => {
119
127
  if (err) {
120
- log.error({ type: `run`, code: `ALK0056`, pre: `cucumber report`, data: err });
128
+ // Something else will probably show an error that's more relevant
129
+ log.info({ type: `run`, code: `ALK0056`, pre: `Failed to read cucumber report`, data: err });
121
130
  return;
122
131
  }
123
132
 
@@ -127,6 +136,18 @@ function build_config( command_args ) {
127
136
 
128
137
  // Put it in the correct file in the correct folder
129
138
  let artifacts_path = session_vars.get_artifacts_path_name();
130
- fs.appendFileSync( `${ artifacts_path }/${ log.debug_log_file }`, `\n\n${text}`);
139
+ try {
140
+ fs.appendFileSync( `${ artifacts_path }/${ log.debug_log_file }`, `\n\n${text}`);
141
+ } catch ( error ) {
142
+ log.info({ type: `run`, code: `ALK0182`, pre: `Something probably went wrong earlier. Look above for more details, but here is more information just in case: ALKiln was unable to write to ${ artifacts_path }/${ log.debug_log_file }.`, data: error });
143
+ }
144
+ try {
145
+ // Add to a unexpected results file when tests themselves fail
146
+ if ( exitCode === 50 ) {
147
+ fs.appendFileSync( `${ artifacts_path }/${ log.unexpected_filename }`, `\n\n${ text }` );
148
+ }
149
+ } catch ( error ) {
150
+ log.info({ type: `run`, code: `ALK0181`, pre: `Something probably went wrong earlier. Look above for more details, but here is more information just in case: ALKiln was unable to write to ${ artifacts_path }/${ log.unexpected_filename }.`, data: error });
151
+ }
131
152
  });
132
153
  })();