@suffolklitlab/alkiln 5.16.0 → 5.16.1-always-html-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65,3 +65,4 @@ jobs:
65
65
  DOCASSEMBLE_DEVELOPER_API_KEY: "${{ steps.github_server.outputs.DOCASSEMBLE_DEVELOPER_API_KEY }}"
66
66
  INSTALL_METHOD: "server"
67
67
  ALKILN_TAG_EXPRESSION: "@e3" # One very simple test
68
+ # ALKILN_VERSION:
@@ -160,7 +160,7 @@ jobs:
160
160
  #### Developer note: You can probably leave the rest out
161
161
  #### To learn more, see https://assemblyline.suffolklitlab.org/docs/alkiln/writing/#optional-inputs
162
162
  ALKILN_TAG_EXPRESSION: "${{ env.ALKILN_TAG_EXPRESSION }}"
163
- # ALKILN_VERSION:
163
+ ALKILN_VERSION: "screenshot"
164
164
 
165
165
  #### Developer note: Example of making an issue when tests fail
166
166
  #### that includes the text of the failure output file
@@ -1,7 +1,6 @@
1
1
  name: Playground installation
2
2
 
3
3
  on:
4
- push:
5
4
  pull_request:
6
5
  workflow_dispatch:
7
6
  inputs:
@@ -9,6 +8,8 @@ on:
9
8
  required: False
10
9
  description: 'Optional. Use a "tag expression" specify which tagged tests to run (https://cucumber.io/docs/cucumber/api/#tag-expressions)'
11
10
  default: ''
11
+ schedule:
12
+ - cron: "0 1 * * SUN"
12
13
 
13
14
  jobs:
14
15
 
@@ -82,4 +83,4 @@ jobs:
82
83
  # want to check up on this.
83
84
  ALKILN_TAG_EXPRESSION: "${{ env.ALKILN_TAG_EXPRESSION }}"
84
85
  #### Developer note: You can probably leave this out
85
- # ALKILN_VERSION:
86
+ ALKILN_VERSION: "screenshot"
package/CHANGELOG.md CHANGED
@@ -43,6 +43,39 @@ Format:
43
43
  -
44
44
  -->
45
45
 
46
+ ## [Unreleased]
47
+
48
+ ### Changed
49
+
50
+ - On pages with sensitive answers, store the HTML of the page. The HTML excludes field values, so those sensitive answers will not be in the saved file. Still avoid taking a pic of the screen, which would reveal sensitive answers. NEVER USE REAL USERS' ANSWERS IN ALKILN TESTS. This HTML can still reveal information about a user's answers. For example, some answers will reveal new questions. That will change the code of the revealed fields and that code will be in the HTML.
51
+
52
+ - GitHub action release: Restored our GitHub action's default for ALKiln version to the latest version 5 again. Released first on GitHub actions. NPM release will come in time, but npm has no impact on GitHub action releases.
53
+
54
+ ### Fixed
55
+
56
+ - Fixed GitHub+You action outdated docassemble cli version causing Scenario timeouts.
57
+
58
+ ### Internal
59
+
60
+ - Break out the function in `Before()` to attempt better error tracing. Goal: try to repeat this for other functions in that file.
61
+
62
+ - Updated both of our actions' dependencies (the checkout, setup-node, setup-python, upload-artifacts, download-artifacts actions). Closes [#1095](https://github.com/suffolkLITLab/aLKiln/issues/1095). Once again, action related.
63
+
64
+ ## [5.16.1] - 2026-06-01
65
+
66
+ ### Fixed
67
+
68
+ - In action.yml, replaced `npm rebuild` with an explicit search for existing puppeteer chrome executables. There is no fallback if all of those are missing. Addresses [#1088](https://github.com/suffolkLITLab/aLKiln/issues/1088). We hope to address the core issue when we can.
69
+ - Restore custom artifacts path argument to allow environments like action.yml to make and reuse their own artifacts path value in environments where it's hard to pass around return values. Closes [#1055](https://github.com/suffolkLITLab/aLKiln/issues/1055).
70
+
71
+ ### Security
72
+
73
+ - Moved responsibility for security measures to ALKiln internal development by automating internal security measures with `.npmrc`. Closes [#1088](https://github.com/suffolkLITLab/aLKiln/issues/1088).
74
+
75
+ ### Internal
76
+
77
+ - Avoid testing on our dev server with every push, uses cron job instead.
78
+
46
79
  ## [5.16.0] - 2026-05-14
47
80
 
48
81
  ### Added
package/action.yml CHANGED
@@ -32,7 +32,7 @@ inputs:
32
32
  DOCASSEMBLECLI_VERSION:
33
33
  description: "The version of docassemblecli to install."
34
34
  required: false
35
- default: "0.0.17"
35
+ default: "0.0.25"
36
36
  ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO:
37
37
  description: "The maximum number of tests ALKiln will generate for every Scenario that can generate tests."
38
38
  required: false
@@ -59,7 +59,7 @@ runs:
59
59
  using: "composite"
60
60
  steps:
61
61
  # Place the root directory in this branch
62
- - uses: actions/checkout@v4
62
+ - uses: actions/checkout@v7
63
63
 
64
64
  # Set environment variables
65
65
  - name: "💡 ALK0023 INFO: Set environment variables"
@@ -111,18 +111,36 @@ runs:
111
111
 
112
112
  # Install ALKiln during GitHub workflow
113
113
  - name: "💡 ALK0025 INFO: Set up node"
114
- uses: actions/setup-node@v5
114
+ uses: actions/setup-node@v6
115
115
  with:
116
116
  node-version: "24.x"
117
117
  package-manager-cache: false
118
+
118
119
  - name: "💡 ALK0026 INFO: Install ALKiln directly from npm"
119
120
  shell: bash
120
121
  run: |
121
122
  log="💡 ALK0026 INFO: Install ALKiln directly from npm"
122
123
  echo -e "\n$log" >> "${{ env.DEBUG_PATH }}"
123
- npm install --ignore-scripts --min-release-age=7 -g "@suffolklitlab/alkiln@$ALKILN_VERSION"
124
- npm rebuild -g puppeteer --update-binary
124
+ npm install -g --ignore-scripts "@suffolklitlab/alkiln@$ALKILN_VERSION"
125
125
 
126
+ BROWSER="$(
127
+ command -v google-chrome-stable ||
128
+ command -v google-chrome ||
129
+ command -v chrome ||
130
+ command -v chromium-browser ||
131
+ command -v chromium ||
132
+ true
133
+ )"
134
+ if [[ -n "$BROWSER" ]]; then
135
+ log_result="💡 ALK0279 INFO: Found puppeteer browser executable at '$BROWSER'"
136
+ echo -e "\n$log_result" >> "${{ env.DEBUG_PATH }}" && echo -e "$log_result"
137
+ echo "PUPPETEER_EXECUTABLE_PATH=$BROWSER" >> "$GITHUB_ENV"
138
+ else
139
+ log_result="🤕 ALK0280 INFO: Unable to find puppeteer browser executable"
140
+ echo -e "\n$log_result" >> "${{ env.DEBUG_PATH }}" && echo -e "$log_result"
141
+ # Our project uses bash error codes in the 50's. This may change.
142
+ exit 51
143
+ fi
126
144
 
127
145
  # Files prep
128
146
  - shell: bash
@@ -137,7 +155,7 @@ runs:
137
155
 
138
156
  # Install package on playground
139
157
  # Don't rely on the environment's version of python
140
- - uses: actions/setup-python@v5
158
+ - uses: actions/setup-python@v6
141
159
  if: ${{ inputs.INSTALL_METHOD == 'playground' }}
142
160
  with:
143
161
  python-version: '3.10'
@@ -195,7 +213,7 @@ runs:
195
213
  # Upload artifacts that subscribers can download on the Actions summary page
196
214
  - name: "💡 ALK0033 INFO: Upload artifacts folder"
197
215
  if: ${{ always() }}
198
- uses: actions/upload-artifact@v4
216
+ uses: actions/upload-artifact@v7
199
217
  with:
200
218
  name: ${{ env.ARTIFACT_FOLDER }}
201
219
  path: ./alkiln-*
@@ -203,7 +221,7 @@ runs:
203
221
  # Download artifacts folder internally to create action output
204
222
  - name: "💡 ALK0183 INFO: Get uploaded artifacts folder"
205
223
  if: ${{ always() }}
206
- uses: actions/download-artifact@v4
224
+ uses: actions/download-artifact@v8
207
225
  with:
208
226
  name: ${{ env.ARTIFACT_FOLDER }}
209
227
 
@@ -49,7 +49,7 @@ runs:
49
49
  using: "composite"
50
50
  steps:
51
51
  # Place the root directory in this branch
52
- - uses: actions/checkout@v4
52
+ - uses: actions/checkout@v7
53
53
 
54
54
  - shell: bash
55
55
  run: |
@@ -253,7 +253,7 @@ runs:
253
253
  done
254
254
 
255
255
  # Don't rely on the environment's version of python
256
- - uses: actions/setup-python@v5
256
+ - uses: actions/setup-python@v6
257
257
  with:
258
258
  python-version: '3.10'
259
259
  # No output
@@ -285,7 +285,7 @@ runs:
285
285
  echo "$(docker output docassemble_container)" >> "${{ env.DEBUG_LOGS_PATH }}"
286
286
  - name: "💡 ALK0020 INFO: Upload artifacts folder"
287
287
  if: ${{ always() && env.OUTPUT_DOCKER == 'true' }}
288
- uses: actions/upload-artifact@v4
288
+ uses: actions/upload-artifact@v7
289
289
  with:
290
290
  name: ${{ env.GITHUB_SERVER_ARTIFACT_NAME }}
291
291
  path: ${{ env.DEBUG_LOGS_PATH }}
package/lib/scope.js CHANGED
@@ -2711,8 +2711,23 @@ module.exports = {
2711
2711
  return scope;
2712
2712
  }, // Ends scope.throwPageError()
2713
2713
 
2714
- take_a_screenshot: async ( scope,{ path }) => {
2715
- /* Takes a jpeg screenshot. Avoids destroying signatures. */
2714
+ take_a_screenshot: async ( scope, {
2715
+ path, disable_pic=false, disable_html=false
2716
+ }) => {
2717
+ /**
2718
+ * Saves a jpeg screenshot and page HTML to `path`. Avoids destroying
2719
+ * signatures.
2720
+ *
2721
+ * @param {obj} scope - State and internal functions
2722
+ * @param {obj} obj - Named arguments
2723
+ * @param {str} obj.path - name of path where to save the files
2724
+ * @param {bool} [obj.disable_pic=false] - Optional. Whether to disable a
2725
+ * puppeteer screenshot
2726
+ * @param {bool} [obj.disable_html=false] - Optional. Whether to disable
2727
+ * saving the HTML of the page
2728
+ *
2729
+ * @returns undefined
2730
+ * */
2716
2731
 
2717
2732
  let fullPage = true;
2718
2733
  let signature_elem = await scope.page.$(scope.signature_selector);
@@ -2721,28 +2736,32 @@ module.exports = {
2721
2736
  fullPage = false;
2722
2737
  }
2723
2738
 
2724
- await scope.page.screenshot({
2725
- path: path,
2726
- type: 'jpeg',
2727
- fullPage: fullPage,
2728
- });
2729
-
2730
- let html_path = path;
2731
- if (path.endsWith(".jpg")) {
2732
- html_path = html_path.substring(0, html_path.length - 4) + ".html";
2733
- } else {
2734
- html_path = html_path + ".html";
2739
+ if ( !disable_pic ){
2740
+ await scope.page.screenshot({
2741
+ path: path,
2742
+ type: 'jpeg',
2743
+ fullPage: fullPage,
2744
+ });
2735
2745
  }
2736
2746
 
2737
- // Also save the HTML of the page
2738
- await scope.page.content().then(content => {
2739
- let server_url = session_vars.get_da_server_url();
2740
- content = content.replaceAll(/"(\/static\/.*\.css\?v=[^"]+")/g, server_url + "$1");
2741
- content = content.replaceAll(/"(\/static\/.*\.js\?v=[^"]+")/g, server_url + "$1");
2742
- content = content.replaceAll(/"(\/packagestatic\/.*\.css\?v=[^"]+")/g, server_url + "$1");
2743
- content = content.replaceAll(/"(\/packagestatic\/.*\.js\?v=[^"]+")/g, server_url + "$1");
2744
- fs.writeFileSync(html_path, content)
2745
- });
2747
+ if ( !disable_html ) {
2748
+ let html_path = path;
2749
+ if (path.endsWith(".jpg")) {
2750
+ html_path = html_path.substring(0, html_path.length - 4) + ".html";
2751
+ } else {
2752
+ html_path = html_path + ".html";
2753
+ }
2754
+
2755
+ // Also save the HTML of the page
2756
+ await scope.page.content().then(content => {
2757
+ let server_url = session_vars.get_da_server_url();
2758
+ content = content.replaceAll(/"(\/static\/.*\.css\?v=[^"]+")/g, server_url + "$1");
2759
+ content = content.replaceAll(/"(\/static\/.*\.js\?v=[^"]+")/g, server_url + "$1");
2760
+ content = content.replaceAll(/"(\/packagestatic\/.*\.css\?v=[^"]+")/g, server_url + "$1");
2761
+ content = content.replaceAll(/"(\/packagestatic\/.*\.js\?v=[^"]+")/g, server_url + "$1");
2762
+ fs.writeFileSync(html_path, content)
2763
+ });
2764
+ }
2746
2765
  }, // Ends scope.take_a_screenshot()
2747
2766
 
2748
2767
 
@@ -11,7 +11,12 @@ const Log = require(`../utils/Log.js`);
11
11
 
12
12
  session_vars.delete_artifacts_path_name();
13
13
 
14
- const artifacts_path = files.make_artifacts_folder();
14
+ // process.argv is a list of strings of commands and args.
15
+ // If using `npm run artifacts` (as opposed to the bin commands),
16
+ // use `--` before, for example, `--sources=./foo`
17
+ // Envs like action.yml reuse their own .path when return vals are hard to get
18
+ const forced_path = require(`minimist`)( process.argv.slice(2) )?.path;
19
+ const artifacts_path = files.make_artifacts_folder( forced_path );
15
20
  session_vars.save_artifacts_path_name( artifacts_path );
16
21
 
17
22
  const log = new Log({ path: artifacts_path, context: `artifacts` });
package/lib/steps.js CHANGED
@@ -114,8 +114,9 @@ BeforeAll(async function() {
114
114
  reports.create( scope );
115
115
  });
116
116
 
117
- Before(async (scenario) => {
118
-
117
+ Before( beforeScenario );
118
+ async function beforeScenario( scenario ) {
119
+ console.log('👁️ 111 start beforeScenario');
119
120
  // Start the running "progress bar" for the Scenario
120
121
  log.stdout({}, `\nScenario: ${ scenario.pickle.name }: `);
121
122
 
@@ -125,15 +126,20 @@ Before(async (scenario) => {
125
126
  // Will only run in the Playground outside of a sandbox. TODO: There's a
126
127
  // better way to do this, though it's more complicated. See comments in
127
128
  // https://github.com/SuffolkLITLab/ALKiln/issues/661
129
+ console.log('👁️ 112 no browser, await new local browser');
128
130
  scope.browser = await scope.driver.launch({ args: ['--no-sandbox'] });
129
131
  } else {
132
+ console.log('👁️ 113 no browser, await new remote browser');
130
133
  scope.browser = await scope.driver.launch({ headless: !session_vars.get_debug(), devtools: session_vars.get_debug() });
131
134
  }
132
135
  }
136
+ console.log('👁️ 114 we have ensured browser exists, awaiting pages');
133
137
  // Clean up all previously existing pages
134
138
  for (const page of await scope.browser.pages()) {
139
+ console.log('👁️ 115 awaiting closing page');
135
140
  await page.close();
136
141
  }
142
+ console.log('👁️ 116 awaiting opening a new page');
137
143
  // Make a new page
138
144
  scope.page = await scope.browser.newPage()
139
145
 
@@ -147,8 +153,10 @@ Before(async (scenario) => {
147
153
  scope.server_reload_promise = null;
148
154
 
149
155
  reports.addReportHeading(scope, {scenario});
156
+ console.log('👁️ 117 awaiting getting safe scenario name');
150
157
  // Make folder for this Scenario in the all-tests artifacts folder
151
158
  scope.base_filename = await scope.getSafeScenarioBaseFilename(scope, {scenario});
159
+ console.log('👁️ 118 got safe scenario name');
152
160
  // Add a date for uniqueness in case dev has accidentally copied a Scenario description
153
161
  let date = Date.now();
154
162
  scope.paths.scenario = `${ scope.paths.artifacts }/${ scope.base_filename }-${ date }`;
@@ -171,7 +179,7 @@ Before(async (scenario) => {
171
179
 
172
180
  // Reset default timeout
173
181
  scope.timeout = default_timeout;
174
- });
182
+ }
175
183
 
176
184
  // Add a check for an error page before each step? After each step?
177
185
 
@@ -1379,25 +1387,34 @@ After(async function(scenario) {
1379
1387
  reports.addToReport(scope, {
1380
1388
  type: `row info`,
1381
1389
  code: `ALK0095`,
1382
- value: `For security, ALKiln will avoid creating a picture of the page for this error. It's possible a secret is being used on this screen.`
1390
+ // Discuss: Might people use secret variables to prevent any
1391
+ // information about a screen from getting out? For example, they have
1392
+ // proprietary info in the HTML itself.
1393
+ value: `For security, ALKiln will avoid creating a picture of the page for this error. It's possible a secret is being used on this screen. ALKiln will save the HTML, which omits the values in the fields.`
1383
1394
  });
1384
- } else {
1385
1395
 
1386
- // Save/download a picture of the screen that's showing during the unexpected status
1387
- // Save one copy in the outer-most artifact folder
1388
- let scenario_filename = await scope.getSafeScenarioFilename( scope, { prefix: `error_on` });
1389
- let path_outer = `${ scope.paths.artifacts }/${ scenario_filename }.jpg`;
1390
- await scope.take_a_screenshot( scope, { path: path_outer });
1391
-
1392
- // Save another copy in the artifact's Scenario folder
1393
- let screenshot_name = `error_on`;
1394
- let { id } = await scope.examinePageID( scope, 'none to match' );
1395
- let short_id = `${ id }`.substring(0, 20);
1396
- screenshot_name += `-${ short_id }`;
1397
- let path_scenario = `${ scope.paths.scenario }/${ screenshot_name }.jpg`;
1398
- await scope.take_a_screenshot( scope, { path: path_scenario });
1399
-
1400
- } // ends if scope.disable_error_screenshot
1396
+ // Save/download a picture of the screen that's showing during the unexpected status
1397
+ // Save one copy in the outer-most artifact folder
1398
+ let scenario_filename = await scope.getSafeScenarioFilename( scope, { prefix: `error_on` });
1399
+ let path_outer = `${ scope.paths.artifacts }/${ scenario_filename }.jpg`;
1400
+ await scope.take_a_screenshot( scope, {
1401
+ path: path_outer,
1402
+ disable_pic: scope.disable_error_screenshot,
1403
+ disable_html: false,
1404
+ });
1405
+
1406
+ // Save another copy in the artifact's Scenario folder
1407
+ let screenshot_name = `error_on`;
1408
+ let { id } = await scope.examinePageID( scope, 'none to match' );
1409
+ let short_id = `${ id }`.substring(0, 20);
1410
+ screenshot_name += `-${ short_id }`;
1411
+ let path_scenario = `${ scope.paths.scenario }/${ screenshot_name }.jpg`;
1412
+ await scope.take_a_screenshot( scope, {
1413
+ path: path_scenario,
1414
+ disable_pic: scope.disable_error_screenshot,
1415
+ disable_html: false,
1416
+ });
1417
+
1401
1418
  } // ends if scope.page exists
1402
1419
 
1403
1420
  // This has to come after the security message or security message doesn't print. Not sure why.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suffolklitlab/alkiln",
3
- "version": "5.16.0",
3
+ "version": "5.16.1-always-html-1",
4
4
  "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -288,4 +288,6 @@ ALK0273:1
288
288
  ALK0274:1
289
289
  ALK0275:1
290
290
  ALK0276:1
291
- ALK0277:1
291
+ ALK0277:1
292
+ ALK0278:1
293
+ ALK0279:1