@suffolklitlab/alkiln 5.17.1-fix-stop-on-error-3 → 5.17.2-deps

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.
@@ -36,6 +36,17 @@ jobs:
36
36
  && github.event.pull_request.head.repo.fork
37
37
  )
38
38
  runs-on: ubuntu-latest
39
+
40
+ env:
41
+ NORMAL_USER_EMAIL: "alkiln@example.com"
42
+ NORMAL_USER_PASSWORD: "User123^"
43
+ WRONG_EMAIL: "wrong_email@example.com"
44
+ WRONG_PASSWORD: "wrong_password"
45
+ INVALID_API_KEY: "invalidAPIkey"
46
+ EMPTY_STRING: ""
47
+ SECRET_VAR1: "secret-var1-value"
48
+ SECRET_VAR2: "secret-var2-value"
49
+ SECRET_FOR_MISSING_FIELD: "secret for missing field"
39
50
 
40
51
  steps:
41
52
  # Place the root directory in this branch to access relative paths to
@@ -51,20 +62,70 @@ jobs:
51
62
  - run: echo "ALKiln finished starting the isolated GitHub docassemble server"
52
63
  shell: bash
53
64
 
65
+ - name: "Add a low-level user and their API key (in an env var)"
66
+ env:
67
+ SERVER_URL: "${{ steps.github_server.outputs.SERVER_URL }}"
68
+ KEY: ${{ steps.github_server.outputs.DOCASSEMBLE_DEVELOPER_API_KEY }}
69
+ run: |
70
+ # Create user and their API key
71
+
72
+ # Install json processor
73
+ sudo apt-get install jq
74
+
75
+ # https://docassemble.org/docs/api.html#user_new
76
+ user_data=$(curl -X POST \
77
+ ${{ env.SERVER_URL }}/api/user/new \
78
+ -H 'Content-Type: application/json' \
79
+ -d '{
80
+ "key": "${{ env.KEY }}",
81
+ "username": "${{ env.NORMAL_USER_EMAIL }}",
82
+ "password": "${{ env.NORMAL_USER_PASSWORD }}"
83
+ }')
84
+ echo "User data:"
85
+ echo "$user_data"
86
+
87
+ user_id=$(echo "$user_data" | jq '.user_id' -r)
88
+
89
+ # https://docassemble.org/docs/api.html#api_user_user_id_api_post
90
+ new_key_with_quotes=$(curl -X POST \
91
+ ${{ env.SERVER_URL }}/api/user/"$user_id"/api \
92
+ -H 'Content-Type: application/json' \
93
+ -d '{
94
+ "key": "${{ env.KEY }}",
95
+ "name": "alkiln_key",
96
+ "method": "none"
97
+ }')
98
+ echo "new key (watch out, this contains actual quotation marks): $new_key_with_quotes"
99
+ no_end_quote="${new_key_with_quotes%\"}"
100
+ unquoted="${no_end_quote#\"}"
101
+ echo "unquoted: $unquoted"
102
+
103
+ echo "NORMAL_USER_API_KEY=$unquoted" >> "$GITHUB_ENV"
104
+
54
105
  ## Optional debugging to explore things like docker issues
55
106
  #- name: Docker debug tmate session
56
107
  # #if: ${{ failure() && github.event.inputs.enable_tmate == 'true' }}
57
108
  # uses: mxschmitt/action-tmate@v3
58
109
 
59
- - name: "ALKiln - Confirm docassemble packages get installed correctly"
110
+ - name: "Create other env vars for our own tests from server output"
111
+ run: |
112
+ echo "USER1_EMAIL=${{ steps.github_server.outputs.DA_ADMIN_EMAIL }}" >> $GITHUB_ENV
113
+ echo "USER1_PASSWORD=${{ steps.github_server.outputs.DA_ADMIN_PASSWORD }}" >> $GITHUB_ENV
114
+
115
+ - name: "ALKiln - Run if docassemble packages get installed correctly"
60
116
  if: ${{ success() }}
61
117
  id: alkiln
62
118
  uses: ./
119
+ env:
120
+ # Internal: docassemble changes sometimes break @a11y tests. That is
121
+ # a da problem, not ours. We will trigger these tests manually when we
122
+ # want to check up on this.
123
+ ALKILN_TAG_EXPRESSION: "${{ (github.event.inputs.tags && format('{0}', github.event.inputs.tags)) || '(not @error) and (not @temp_error)' }}"
63
124
  with:
64
125
  # Required inputs. See
65
126
  # https://assemblyline.suffolklitlab.org/docs/alkiln/writing/#sandbox-inputs
66
127
  SERVER_URL: "${{ steps.github_server.outputs.SERVER_URL }}"
67
128
  DOCASSEMBLE_DEVELOPER_API_KEY: "${{ steps.github_server.outputs.DOCASSEMBLE_DEVELOPER_API_KEY }}"
68
129
  INSTALL_METHOD: "server"
69
- ALKILN_TAG_EXPRESSION: "@e3" # One very simple test
70
- ALKILN_VERSION: "stop_on_error"
130
+ ALKILN_TAG_EXPRESSION: "${{ env.ALKILN_TAG_EXPRESSION }}"
131
+ # ALKIlN_VERSION:
@@ -159,7 +159,7 @@ jobs:
159
159
  #### Developer note: You can probably leave the rest out
160
160
  #### To learn more, see https://assemblyline.suffolklitlab.org/docs/alkiln/writing/#optional-inputs
161
161
  ALKILN_TAG_EXPRESSION: "${{ env.ALKILN_TAG_EXPRESSION }}"
162
- ALKILN_VERSION: "stop_on_error"
162
+ # ALKIlN_VERSION:
163
163
 
164
164
  #### Developer note: Example of making an issue when tests fail
165
165
  #### that includes the text of the failure output file
@@ -80,4 +80,4 @@ jobs:
80
80
  # want to check up on this.
81
81
  ALKILN_TAG_EXPRESSION: "${{ env.ALKILN_TAG_EXPRESSION }}"
82
82
  #### Developer note: You can probably leave this out
83
- ALKILN_VERSION: "stop_on_error"
83
+ # ALKIlN_VERSION:
package/CHANGELOG.md CHANGED
@@ -46,21 +46,29 @@ Format:
46
46
 
47
47
  ## [Unreleased]
48
48
 
49
+ ### Changed
50
+
51
+ - Updated many dependencies
52
+ - Updated nodejs version
53
+
54
+ ## [5.17.2] - 2026-10-29
55
+
49
56
  ### Added
50
57
 
51
- - Step "I continue" which taps the continue button expecting to navigate to a new page in the interview. "I continue" and "I can't continue" should together replace the behaviors of "I tap to continue".
52
- - Step "I can't continue" which taps the continue button expecting to fail to navigate. It passes when there are invalid answers or when a continue button is missing. It fails on other errors, like system errors (including Assembly Line custom errors) and timeout errors. Those types of errors should always fail across the whole framework. An author could use "I can't continue" to check that the interview correctly indicated one or more invalid answers. This replaces the practice of using "I tap to continue" twice.
58
+ - Step `I continue` which taps the continue button expecting to navigate to a new page in the interview. `I continue` and `I can't continue` should together replace the behaviors of `I tap to continue`.
59
+ - Step `I can't continue` which taps the continue button expecting to fail to navigate. It passes when there are invalid answers or when a continue button is missing. It fails on other errors, like system errors (including Assembly Line custom errors) and timeout errors. Those types of errors should always fail across the whole framework. An author could use `I can't continue` to check that the interview correctly indicated one or more invalid answers. This replaces the practice of using `I tap to continue` twice.
53
60
 
54
61
  ### Changed
55
62
 
56
- - Part 2 of detecting custom AL error screen (final part): Though `I tap to continue` allows invalid answers again, it throws on other errors, like system errors (including Assembly Line custom errors) and timeout errors. Those types of errors should always fail across the whole framework. It also throws if the continue button is missing.
63
+ - Part 2 of detecting custom AL error screen (final part): Though `I tap to continue` allows invalid answers again, it throws on other errors, like system errors (including Assembly Line custom errors) and timeout errors. Those types of errors should always fail across the whole framework. It also throws if the continue button is missing. See [#656](https://github.com/SuffolkLITLab/ALKiln/issues/656).
57
64
  - Extended error message when the test causes a docassemble system error page (like syntax errors, etc.) It now should contain some of the relevant contents of the page.
58
65
  - Tweaked other log messages.
59
66
 
60
67
  ### Fixed
61
68
 
62
- - Restores the documented behavior of `I tap to continue`. Specifically, the documentation describes using `I tap to continue` to potentially trigger invalid answers. To do this, these commits re-implement old behavior - `I tap to continue` never fails with invalid answers or missing continue-type buttons. Does add a small change - `I tap to continue` now throws on other errors, like system errors and timeout errors. Those types of errors should always fail across the whole framework. Note: A different behavior was proposed where an author could tap to continue once and be guaranteed no failures and then fail on a second tap to the same page. There are situations in which this would break potentially live tests. For example, an author could test multiple invalid answers in a row for the same field by cycling through. `I tap to continue` and `I should see the phrase`. The proposed change would break those tests.
69
+ - Restored the documented behavior of `I tap to continue`. Specifically, the documentation describes using `I tap to continue` to potentially trigger invalid answers. To do this, these commits re-implement old behavior - `I tap to continue` never fails with invalid answers or missing continue-type buttons. Does add a small change - `I tap to continue` now throws on other errors, like system errors and timeout errors. Those types of errors should always fail across the whole framework. Note: A different behavior was proposed where an author could tap to continue once and be guaranteed no failures and then fail on a second tap to the same page. There are situations in which this would break potentially live tests. For example, an author could test multiple invalid answers in a row for the same field by cycling through `I tap to continue` and `I should see the phrase`. The proposed change would break those tests. The `I tap to continue` Step does now log its deprecation, as well as copious warnings with invalid answers or ambiguous lack of navigation. See [#1125](https://github.com/SuffolkLITLab/ALKiln/issues/1125).
63
70
  - Ensured new file upload buttons don't warn about duplicates since they have the same selectors.
71
+ - Restored wait for file upload completion signals. See [#1125](https://github.com/SuffolkLITLab/ALKiln/issues/1125).
64
72
 
65
73
  ### Internal
66
74
 
@@ -69,6 +77,9 @@ Format:
69
77
  - Refactored getting different types of page errors (system error pages vs. user errors.)
70
78
  - Started refactoring out `scope.examinePageID()` which is currently doing too many things.
71
79
  - Added tests
80
+ - Fixed check for previous filenames on file uploads. See [#1125](https://github.com/SuffolkLITLab/ALKiln/issues/1125).
81
+ - Removed `shared_names` from global scope. See [#1125](https://github.com/SuffolkLITLab/ALKiln/issues/1125).
82
+ - Use latest AL docassemble image to run all tests instead of just one.
72
83
 
73
84
  ## [5.17.1] - 2026-10-13
74
85
 
package/action.yml CHANGED
@@ -113,7 +113,7 @@ runs:
113
113
  - name: "💡 ALK0025 INFO: Set up node"
114
114
  uses: actions/setup-node@v6
115
115
  with:
116
- node-version: "24.x"
116
+ node-version: "26.x"
117
117
  package-manager-cache: false
118
118
 
119
119
  - name: "💡 ALK0026 INFO: Install ALKiln directly from npm"
@@ -36,7 +36,7 @@ function build_config() {
36
36
 
37
37
  const config = {
38
38
  publishQuiet: true,
39
- require: `./lib/index.js`,
39
+ require: [ `./lib/index.js` ],
40
40
  format: [`progress`, `summary:${ SUMMARY_FILENAME }`],
41
41
  paths: feature_paths,
42
42
  retry: 1,
package/lib/scope.js CHANGED
@@ -2330,7 +2330,7 @@ module.exports = {
2330
2330
  await scope.afterStep(scope, { waitForShowIf: true });
2331
2331
 
2332
2332
  } else if ( type === `file` ) {
2333
- row_was_used = await scope.uploadFiles( scope, { handle, answer });
2333
+ row_was_used = await scope.upload_files( scope, { handle, answer });
2334
2334
  await scope.afterStep(scope, { waitForShowIf: true });
2335
2335
 
2336
2336
  } else if (type == `date`) {
@@ -2410,7 +2410,7 @@ module.exports = {
2410
2410
  return valid_date;
2411
2411
  }, // Ends scope.today_to_date()
2412
2412
 
2413
- findFiles: async function( scope, {to_find_names}) {
2413
+ find_files: async function( scope, {to_find_names}) {
2414
2414
  /** Returns a list of paths to the sources folder associated
2415
2415
  * with the given list of file names.
2416
2416
  *
@@ -2467,9 +2467,9 @@ module.exports = {
2467
2467
  return [];
2468
2468
  }
2469
2469
  return paths;
2470
- }, // Ends scope.findFiles()
2470
+ }, // Ends scope.find_files()
2471
2471
 
2472
- uploadFiles: async function ( scope, { handle, answer }) {
2472
+ upload_files: async function ( scope, { handle, answer }) {
2473
2473
  /* Try to upload one or more files to a file-type input field.
2474
2474
  * Does not submit, so no danger of timeout with server reload.
2475
2475
  *
@@ -2479,8 +2479,8 @@ module.exports = {
2479
2479
  // First make sure the list of files was defined. Don't remove spaces yet -
2480
2480
  // allow a typo where someone forgot to include a space after a comma.
2481
2481
  let to_find_names = answer.split(`,`);
2482
- let paths = await scope.findFiles(scope, {to_find_names});
2483
- if (paths.length == 0) {
2482
+ let paths = await scope.find_files(scope, {to_find_names});
2483
+ if ( paths.length == 0 ) {
2484
2484
  // The row wasn't used
2485
2485
  return false;
2486
2486
  }
@@ -2492,16 +2492,17 @@ module.exports = {
2492
2492
  // it's not completely necessary. If the gobal `setTimeout` fires then the
2493
2493
  // `evaluate` won't be far behind. They both use about the same timeout
2494
2494
  // duration.
2495
- let finish_uploading = handle.evaluate( async function detect_finished_upload(
2495
+ let finish_uploading = handle.evaluate( async function (
2496
2496
  elem, timeout, answer
2497
2497
  ) {
2498
2498
  return new Promise (( resolve, reject ) => {
2499
2499
  /**
2500
2500
  * Resolve when upload completes. Logging in here will log to the hidden
2501
2501
  * browser. Must instead pass information out with `resolve`.
2502
+ *
2503
+ * Warn about uploading the same file multiple times. It won't protect
2504
+ * against accidentally uploading same file to a different field.
2502
2505
  * */
2503
- // Warn about uploading the same file multiple times
2504
- // Won't protect against accidentally uploading same file to diff field
2505
2506
 
2506
2507
  // If elem.files, the author has previously set this field. Warn them.
2507
2508
  // Example prev_filenames: "C:\\fakepath\\name_of_file.png"
@@ -2521,13 +2522,13 @@ module.exports = {
2521
2522
 
2522
2523
  let result = {
2523
2524
  ok: false, prev_filenames, type: null,
2524
- code: `ALK0283`, level: `debug`, msgs: [], error: null,
2525
+ code: `ALK0283`, level: `info`, msgs: [], error: null,
2525
2526
  };
2526
2527
 
2527
- if ( prev_filenames ) {
2528
+ if (( prev_filenames?.length || 0) > 0 ) {
2528
2529
  result.level = `warn`; // log level
2529
2530
  result.type = `warning`; // report type
2530
- shared_names = shared_names_list.join(`", "`);
2531
+ let shared_names = shared_names_list.join(`", "`);
2531
2532
 
2532
2533
  if ( shared_names_list.length > 0 ) {
2533
2534
  if ( all_matched ) {
@@ -2545,9 +2546,10 @@ module.exports = {
2545
2546
 
2546
2547
  let timeout_id = null;
2547
2548
  let resolve_on_change = function() {
2549
+ // bootstrap-fileinput starts reading
2548
2550
  result.ok = true; // May contain warnings
2549
2551
  clearTimeout( timeout_id );
2550
- result.msgs.push(`Finished uploading these files: "${ answer }"`);
2552
+ result.msgs.push(`Started uploading these files: "${ answer }"`);
2551
2553
  resolve( result );
2552
2554
  };
2553
2555
 
@@ -2568,7 +2570,7 @@ module.exports = {
2568
2570
  }); // ends Promise
2569
2571
 
2570
2572
  }, scope.timeout - 100, answer ); // ends page.evaluate()
2571
-
2573
+
2572
2574
  await handle.uploadFile(...paths);
2573
2575
 
2574
2576
  // Wait for upload to finish. If already done, execution will continue
@@ -2589,9 +2591,31 @@ module.exports = {
2589
2591
  if ( level === `throw` ) { log_type = `throw`; }
2590
2592
  log[ log_type ]({ code, level, error, context: `file_upload`}, ...msgs );
2591
2593
 
2594
+ // LLM Claude (version?) description: While the bootstrap-fileinput is
2595
+ // reading, it disables the input, adds `is-locked` to its container
2596
+ // (lock()), and adds `file-thumb-loading` (_toggleLoading("show")). We'll
2597
+ // wait till these are gone. See GitHub #1125. @plocket tested these
2598
+ // classes out and said they seem legit.
2599
+ await scope.page.waitForFunction(
2600
+ ( el ) => {
2601
+ const container = el.closest(`.file-input`);
2602
+ return (
2603
+ !el.disabled
2604
+ && !( container?.classList?.contains?.(`is-locked`)
2605
+ || container?.classList?.contains?.(`file-thumb-loading`)
2606
+ ));
2607
+ },
2608
+ { timeout: scope.timeout - 100 },
2609
+ handle,
2610
+ );
2611
+
2612
+ log.debug({ code: `ALK0297`, context: `file_upload`, level: `info`, },
2613
+ `Finished uploading files`
2614
+ );
2615
+
2592
2616
  // Return whether successfully used row
2593
2617
  return ok;
2594
- }, // Ends scope.uploadFiles()
2618
+ }, // Ends scope.upload_files()
2595
2619
 
2596
2620
  setCheckbox: async function ( scope, { label, answer }) {
2597
2621
  // Depending on the current value/status of a checkbox and the desired
@@ -3222,12 +3246,12 @@ module.exports = {
3222
3246
  * further information
3223
3247
  * 4. (optional) Unspecified non-navigation catchall comes last
3224
3248
  *
3225
- * Unfortunately, our navigation detection game is weaksauce. Navigation
3226
- * detection thoughts: https://github.com/SuffolkLITLab/ALKiln/issues/229
3227
- *
3228
3249
  * See docs/decisions/017_post_navigation_custom_behavior_2026_10_26.md for
3229
3250
  * a deeper discussion.
3230
3251
  *
3252
+ * Unfortunately, our navigation detection game is weaksauce. Navigation
3253
+ * detection thoughts: https://github.com/SuffolkLITLab/ALKiln/issues/229
3254
+ *
3231
3255
  * @param {Object} scope - Tests state object
3232
3256
  * @param {Object} kwargs - Named arguments
3233
3257
  * @param {str} kwargs.url - Assumed to be the url of the initial page
@@ -3241,11 +3265,11 @@ module.exports = {
3241
3265
  * @returns {Object} kwrvs.error_data - Data for any errors found
3242
3266
  * @returns {bool} kwrvs.error_data.was_found - True if the check found any
3243
3267
  * errors
3244
- * @returns {bool} kwrvserror_data.ok - False if the check found any errors.
3268
+ * @returns {bool} kwrvs.error_data.ok - False if the check found any errors.
3245
3269
  * Discuss: Should we only use `ok` for internal errors?
3246
- * @returns {`user`|`system`} kwrvserror_data.type - "user" with invalid
3270
+ * @returns {`user`|`system`} kwrvs.error_data.type - "user" with invalid
3247
3271
  * answers, "system" with da or AL server errors
3248
- * @returns {[ElementHandle]} kwrvserror_data.error_handles - List of
3272
+ * @returns {[ElementHandle]} kwrvs.error_data.error_handles - List of
3249
3273
  * puppeteer element handles the check found. There can be multiple
3250
3274
  * invalid answer elements.
3251
3275
  * */
@@ -3374,7 +3398,7 @@ module.exports = {
3374
3398
  // still be a bit too long.
3375
3399
  // Also, if you're debugging and you close the window early, the process
3376
3400
  // will still take this long to exit after the tests finish. May fix
3377
- // in future, but it's just during debugging, so yolo!
3401
+ // in future, but it's just during debugging, so yolo for now!
3378
3402
  if ( session_vars.get_debug() ) {
3379
3403
  await time.waitForTimeout( ( scope.timeout - (1 * 1000)) );
3380
3404
  }
@@ -3809,7 +3833,7 @@ module.exports = {
3809
3833
  }, // Ends scope.steps.get_response_filename()
3810
3834
 
3811
3835
  compare_pdfs: async function (scope, {existing_pdf_path, new_pdf_path}) {
3812
- let existing_paths = await scope.findFiles(scope, {to_find_names: [existing_pdf_path]});
3836
+ let existing_paths = await scope.find_files(scope, {to_find_names: [existing_pdf_path]});
3813
3837
  if (existing_paths.length == 0) {
3814
3838
  // The row wasn't used
3815
3839
  let msg = `Could not find the existing PDF at ${ existing_pdf_path }`;
@@ -4203,7 +4227,7 @@ module.exports = {
4203
4227
  let handle = await scope.page.waitForSelector(`#dasigcanvas`);
4204
4228
  // We cannot be sure of knowing the name of the signer and so want to
4205
4229
  // indicate it's random, but we do want something unique
4206
- await scope.funnel_the_answer( scope, { field, handle, answer: `Random ${ faker.name.lastName() }` });
4230
+ await scope.funnel_the_answer( scope, { field, handle, answer: `Random ${ faker.person.lastName() }` });
4207
4231
  },
4208
4232
  input: async function (scope, { field }) {
4209
4233
  /** Handles various `type`s of input element fields, except:
@@ -4306,7 +4330,7 @@ module.exports = {
4306
4330
  return `${ Math.random() > .5 }`;
4307
4331
  },
4308
4332
  text: async function ( scope ) {
4309
- return faker.random.word();
4333
+ return faker.word.sample();
4310
4334
  },
4311
4335
  number: async function ( scope ) {
4312
4336
  let answer = faker.helpers.arrayElement( [`1`, `2`, `3`], 1 );
@@ -4332,7 +4356,7 @@ module.exports = {
4332
4356
  return `${ Math.random() * 100 }`;
4333
4357
  },
4334
4358
  email: async function ( scope ) {
4335
- return faker.internet.email( faker.random.word(), faker.random.word(), `example.com` );
4359
+ return faker.internet.email( faker.word.adjective(), `-`, faker.word.noun(), `example.com` );
4336
4360
  },
4337
4361
  password: async function ( scope ) {
4338
4362
  return faker.internet.password();
package/models.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "providers": {
3
+ "ollama": {
4
+ "api": "openai-completions",
5
+ "apiKey": "ollama",
6
+ "baseUrl": "http://host.docker.internal:8080/v1",
7
+ "models": [
8
+ {
9
+ "contextWindow": 131072,
10
+ "id": "muse-glimmer:latest",
11
+ "input": [
12
+ "text"
13
+ ],
14
+ "reasoning": true
15
+ }
16
+ ]
17
+ }
18
+ }
19
+ }