@suffolklitlab/alkiln 5.16.1 → 5.17.0-fix-stop-on-error-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.
@@ -31,9 +31,6 @@ jobs:
31
31
  && github.event.pull_request.head.repo.fork
32
32
  )
33
33
  runs-on: ubuntu-latest
34
- strategy:
35
- matrix:
36
- node-version: [24.x]
37
34
 
38
35
  steps:
39
36
  # Place the root directory in this branch to access relative paths to
@@ -65,4 +62,4 @@ jobs:
65
62
  DOCASSEMBLE_DEVELOPER_API_KEY: "${{ steps.github_server.outputs.DOCASSEMBLE_DEVELOPER_API_KEY }}"
66
63
  INSTALL_METHOD: "server"
67
64
  ALKILN_TAG_EXPRESSION: "@e3" # One very simple test
68
- # ALKILN_VERSION:
65
+ ALKILN_VERSION: "stop_on_error"
@@ -36,9 +36,6 @@ jobs:
36
36
  && github.event.pull_request.head.repo.fork
37
37
  )
38
38
  runs-on: ubuntu-latest
39
- strategy:
40
- matrix:
41
- node-version: [24.x]
42
39
 
43
40
  #### Developer note: You can probably leave out these specific
44
41
  #### environment variables. You may not even need the `env`
@@ -160,7 +157,7 @@ jobs:
160
157
  #### Developer note: You can probably leave the rest out
161
158
  #### To learn more, see https://assemblyline.suffolklitlab.org/docs/alkiln/writing/#optional-inputs
162
159
  ALKILN_TAG_EXPRESSION: "${{ env.ALKILN_TAG_EXPRESSION }}"
163
- # ALKILN_VERSION:
160
+ ALKILN_VERSION: "stop_on_error"
164
161
 
165
162
  #### Developer note: Example of making an issue when tests fail
166
163
  #### that includes the text of the failure output file
@@ -27,9 +27,6 @@ jobs:
27
27
  && github.event.pull_request.head.repo.fork
28
28
  )
29
29
  runs-on: ubuntu-latest
30
- strategy:
31
- matrix:
32
- node-version: [24.x]
33
30
 
34
31
  env:
35
32
  #### Developer note: We recommend turning these into environment
@@ -83,4 +80,4 @@ jobs:
83
80
  # want to check up on this.
84
81
  ALKILN_TAG_EXPRESSION: "${{ env.ALKILN_TAG_EXPRESSION }}"
85
82
  #### Developer note: You can probably leave this out
86
- # ALKILN_VERSION:
83
+ ALKILN_VERSION: "stop_on_error"
package/CHANGELOG.md CHANGED
@@ -43,7 +43,35 @@ Format:
43
43
  -
44
44
  -->
45
45
 
46
- <!-- ## [Unreleased] -->
46
+ ## [Unreleased]
47
+
48
+ ### Changed
49
+
50
+ - Extended error message for reaching a docassemble system error page (like syntax errors, etc.) It now should contain the relevant contents of the page.
51
+
52
+ ### Fixed
53
+
54
+ - Fixed linear Steps not failing on error screens. See #656.
55
+
56
+ ### Internal
57
+
58
+ - Fixed confusing error message when tests expected to fail actually pass.
59
+
60
+ ## [5.17.0] - 2026-09-10
61
+
62
+ ### Changed
63
+
64
+ - 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.
65
+
66
+ ### Fixed
67
+
68
+ - Fixed GitHub+You action outdated docassemble cli version causing Scenario timeouts.
69
+ - Part 1 of detecting custom AL error screen: Story Table now catches `al_custom_error_action` error. Previously, Story Tables that ran into this screen would get stuck on the AL error page infinitely. Next steps: Linear Steps may need additional development. Currently linear Steps succeed even when they run into AL's error page. That bears additional discussion. See [issue #656](https://github.com/SuffolkLITLab/ALKiln/issues/656) and [PR #1116](https://github.com/SuffolkLITLab/ALKiln/pull/1116).
70
+
71
+ ### Internal
72
+
73
+ - Simplify workflow files by removing `node` env setup (leaving it to the actions)
74
+ - 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.
47
75
 
48
76
  ## [5.16.1] - 2026-06-01
49
77
 
package/action.yml CHANGED
@@ -14,7 +14,7 @@ inputs:
14
14
  ALKILN_VERSION:
15
15
  description: 'Version of ALKiln to get from npm.'
16
16
  required: false
17
- default: '5.15.4'
17
+ default: '^5.0.0'
18
18
  MAX_SECONDS_FOR_SETUP:
19
19
  description: "Amount of time to give the Project to upload your package's GitHub code."
20
20
  required: false
@@ -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,7 +111,7 @@ 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
@@ -155,7 +155,7 @@ runs:
155
155
 
156
156
  # Install package on playground
157
157
  # Don't rely on the environment's version of python
158
- - uses: actions/setup-python@v5
158
+ - uses: actions/setup-python@v6
159
159
  if: ${{ inputs.INSTALL_METHOD == 'playground' }}
160
160
  with:
161
161
  python-version: '3.10'
@@ -213,7 +213,7 @@ runs:
213
213
  # Upload artifacts that subscribers can download on the Actions summary page
214
214
  - name: "💡 ALK0033 INFO: Upload artifacts folder"
215
215
  if: ${{ always() }}
216
- uses: actions/upload-artifact@v4
216
+ uses: actions/upload-artifact@v7
217
217
  with:
218
218
  name: ${{ env.ARTIFACT_FOLDER }}
219
219
  path: ./alkiln-*
@@ -221,7 +221,7 @@ runs:
221
221
  # Download artifacts folder internally to create action output
222
222
  - name: "💡 ALK0183 INFO: Get uploaded artifacts folder"
223
223
  if: ${{ always() }}
224
- uses: actions/download-artifact@v4
224
+ uses: actions/download-artifact@v8
225
225
  with:
226
226
  name: ${{ env.ARTIFACT_FOLDER }}
227
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
@@ -255,9 +255,81 @@ module.exports = {
255
255
  }, // Ends afterStep
256
256
 
257
257
  tapElementAndNavigate: async function(scope, { elem, waitForTimeout=null }) {
258
- /** Tap a given element, detect navigation, and do appropriate related things. */
258
+ /**
259
+ * Tap a given element, detect navigation, and do appropriate related things.
260
+ *
261
+ * For other ideas for detecting navigation, see
262
+ * https://github.com/SuffolkLITLab/ALKiln/issues/229.
263
+ *
264
+ * TODO: Duplicates much of `waitUntilContinued`. Need to de-duplicate.
265
+ * Alternatively, continue other work on navigation detection with server
266
+ * responses.
267
+ *
268
+ * @param {int} obj.waitForTimeout - How long to wait for a timeout
269
+ * */
270
+ // await scope.throwAnySystemError( scope ); // error on current pg
271
+
272
+ // let prev_url = url;
273
+ // let curr_url = await scope.page.url();
274
+
275
+ // let timed_out = false; // Lazy timeout?
276
+ // let cont_timeout = setTimeout(function(){ timed_out = true }, scope.timeout);
277
+
278
+ // // Attempt to solve weird race condition issues where page has not properly continued
279
+ // while ( curr_url === prev_url && !page_result.element && !timed_out ) {
280
+ // await time.waitForTimeout( 50 );
281
+ // curr_url = await scope.page.url();
282
+ // page_result = await scope.throwAnySystemError( scope ); // error on new pg
283
+ // }
259
284
 
260
- let start_url = await scope.page.url(); // so we can later check for navigation
285
+ // // TODO: We sometimes log multiple error messages for an error because we're
286
+ // // logging at multiple levels. We should try to refactor to log just once
287
+ // // per error
288
+ // let timeout_err_message = `The test tried to continue to the next page, but it took longer than ${ (scope.timeout/1000)/60 } min. You can check the picture of this page unless a secret variable was used.`
289
+ // if ( timed_out ) { reports.addToReport(scope, { type: `error`, code: `ALK0146`, value: timeout_err_message }); }
290
+ // expect( timed_out, timeout_err_message ).to.be.false;
291
+
292
+ // clearTimeout( cont_timeout );
293
+
294
+ // return page_result;
295
+
296
+
297
+ // Invalid user answer will prevent navigation
298
+ // Shoot. How does this work for .setFields()...?
299
+ // Think. Checked for correct id. Possibly not. If did continue:
300
+ // Fill fields/press var of continue button or
301
+ // Press continue anyway (tap to continue or no fields to set)
302
+ // If on page with user error... id should have recognized it as valid
303
+ // destination or we shouldn't get stuck on the page. Which would make
304
+ // this work either way.
305
+ // That's fine. But. I wanted to change behavior so that a Story Table
306
+ // could set fields on the final page. If that were the case and _if_
307
+ // the field were a continue button, that would break our rule that
308
+ // Story Tables can contain extra fields without worry.
309
+ // Maybe that can be a flag - on final id page, so don't try to press
310
+ // continue button fields, even when it sets a variable in the table.
311
+ // Or any buttons? Hmm. I'll have to make an issue.
312
+ // TODO: Make an issue for the alternative Story Table behavior.
313
+ // But also. What if... well, under some circumstances the author will
314
+ // want to navigate to a page and get invalid values. But brain. Brain
315
+ // brain brain. But if the author navigates after expecting the page
316
+ // to have invalid values, then this should fail too.
317
+ // TODO: Write all this up in here, but better.
318
+ // But what if the author is navigating a different way, like with an
319
+ // action button. So, invalid answers, but then wants to navigate with
320
+ // an action button. That might indeed be a real problem. Right? Brain?
321
+ // You should be allowed to navigate away from any page.
322
+ // Ok, we really do need to split up system errors from user errors.
323
+ // Dangit.
324
+
325
+
326
+ // If on system error page, can't navigate.
327
+ // TODO: Discuss how to allow AL to test its custom error page buttons.
328
+ // Maybe avoiding a system error here would be sufficient.
329
+ await scope.throwAnySystemError( scope );
330
+
331
+ let start_url = await scope.page.url(); // for navigation check later
332
+ let curr_url = start_url;
261
333
 
262
334
  await scope.tapElementAndStayOnPage(scope, {
263
335
  elem,
@@ -267,29 +339,49 @@ module.exports = {
267
339
  ]
268
340
  });
269
341
 
270
- // For other ideas for detecting navigation, see
271
- // https://github.com/SuffolkLITLab/ALKiln/issues/229
272
- let end_url = await scope.page.url();
273
- let navigated = start_url !== end_url;
342
+ // Attempt to solve weird race condition issues where page has not properly
343
+ // continued. May exceed actual timeout
344
+ let timed_out = false;
345
+ let cont_timeout = setTimeout(
346
+ function(){ timed_out = true; }, ( scope.timeout - 10 )
347
+ );
348
+ while ( curr_url === start_url && !timed_out ) {
349
+ try {
350
+ await scope.throwAnySystemError( scope ); // quick exit on system error
351
+ } catch ( sys_err_msg ) {
352
+ clearTimeout( cont_timeout );
353
+ throw sys_err_msg;
354
+ }
355
+ await time.waitForTimeout( 50 );
356
+ curr_url = await scope.page.url();
357
+ }
358
+ clearTimeout( cont_timeout );
359
+
360
+ await scope.throwAnySystemError( scope ); // system error on new page
274
361
 
275
- // Might be able to handle this in `scope.afterStep`
276
- if ( navigated ) {
362
+ // TODO: We sometimes log multiple error messages for an error because we're
363
+ // logging at multiple levels. We should try to refactor to log just once
364
+ // per error
365
+ let timeout_err_message = `The test tried to continue to the next page, but it took longer than ${ (scope.timeout/1000)/60 } min. You can check the picture of this page unless a secret variable was used.`
366
+ if ( timed_out ) { reports.addToReport(scope, { type: `error`, code: `ALK0283`, value: timeout_err_message }); }
367
+ expect( timed_out, timeout_err_message ).to.be.false;
277
368
 
278
- // TODO: Can we remove this?
279
- // Save the id that comes next
280
- let { question_has_id, id, id_matches } = await scope.examinePageID( scope, 'none to match' );
369
+ // Might be able to handle this in `scope.afterStep`
370
+ if ( start_url !== curr_url ) {
281
371
 
372
+ scope.navigated = true;
282
373
  // If navigation, wait for 200ms to let things finish loading and moving
283
374
  await scope.afterStep(scope, {
284
375
  waitForTimeout: ( waitForTimeout || 200 ),
285
376
  navigated: true
286
377
  });
378
+
287
379
  } else {
288
- // Discuss: Fail here or later since the test didn't manage to navigate?
289
- await scope.afterStep(scope, {
290
- waitForTimeout: ( waitForTimeout || 0 ),
291
- navigated: false
292
- });
380
+
381
+ // TODO: #656, this with one other change may be all we need
382
+ let non_nav_msg = `Was unable to navigate away from page with the id "${ start_url }".`
383
+ reports.addToReport(scope, { type: `error`, code: `ALK0284`, value: non_nav_msg });
384
+ throw new Error( non_nav_msg );
293
385
  }
294
386
  }, // Ends scope.tapElementAndNavigate()
295
387
 
@@ -347,11 +439,14 @@ module.exports = {
347
439
  // This is meant to detect user-visible alerts/errors,
348
440
  // not breaking errors in the testing code.
349
441
  // No need for aborters since puppeteer has its own timeout
442
+ // TODO: Discuss why we need to detect invalid answers on a page where
443
+ // we don't plan to navigate.
350
444
  scope.page.waitForSelector('.alert-danger', { visible: true }),
351
445
  scope.page.waitForSelector('.da-has-error', { visible: true }),
352
446
  ]);
353
447
  } catch ( error ) {
354
448
  if ( error.name === `TimeoutError` ) {
449
+ // TODO: Discuss - this is a navigation timeout even though the tap shouldn't navigate
355
450
  let non_reload_report_msg = `After ALKiln tapped "${ tapperText }" it took too long to load the next page.`;
356
451
  await scope.handle_page_timeout_error( scope, { non_reload_report_data: {
357
452
  code: `ALK0103`,
@@ -608,6 +703,7 @@ module.exports = {
608
703
 
609
704
  checkForA11y: async function (scope ) {
610
705
  /** Return if there were any violations and the file containing aXe output with more details of the checks */
706
+ // Discuss: What problem was the page id solving? When was there a mismatch?
611
707
  let { id } = await scope.examinePageID( scope, 'none to match' );
612
708
  if ( scope.page_id !== id ) {
613
709
  reports.addToReport(scope, { type: `page_id info`, code: `ALK0108`, value: `${ id }`, });
@@ -643,7 +739,7 @@ module.exports = {
643
739
 
644
740
  tapSelectorToNavigate: async function ( scope, selector, waitForTimeout ) {
645
741
  let elem = await scope.page.$(`${ selector }`);
646
- let error_msg = `Couldn't find "${ selector }" on the page, is there a typo?`;
742
+ let error_msg = `Couldn't find "${ selector }" on the page. Is there a typo?`;
647
743
  if ( !elem ) {
648
744
  reports.addToReport(scope, { type: `error`, code: `ALK0111`, value: error_msg });
649
745
  }
@@ -2304,12 +2400,16 @@ module.exports = {
2304
2400
 
2305
2401
  // Press a continue button with a story table variable name if it exists
2306
2402
  let did_navigate = false;
2307
- let error = {};
2308
2403
  for ( let field of fields ) {
2309
2404
  // All other fields should have been set
2310
2405
  if ( field.tag === 'button' && field.type === 'submit' ) {
2311
2406
  let page_url = await scope.page.url();
2312
2407
 
2408
+ // TODO: Attempt avoiding `awaiting` and instead allow
2409
+ // waitUntilContinued to detect navigation.
2410
+ // Except `row_used` should be set before an error page. Maybe
2411
+ // `row_used` should always be set before all this, since we know the
2412
+ // field exists on the page (we know it's a submit button).
2313
2413
  let row_used = await scope.setVariable( scope, { field, var_data, });
2314
2414
  if ( row_used ) {
2315
2415
 
@@ -2334,10 +2434,7 @@ module.exports = {
2334
2434
  // Make sure no system or user error messages appear
2335
2435
  // Discuss moving this out of here. Having to pass in `id` just for
2336
2436
  // these messages smells bad. Con: Already a lot of code out there.
2337
- error = await scope.waitUntilContinued( scope, { url: page_url, id });
2338
- // if ( error.was_found ) {
2339
- // await scope.throwPageError( scope, { id: id });
2340
- // }
2437
+ await scope.waitUntilContinued( scope, { url: page_url });
2341
2438
 
2342
2439
  did_navigate = true;
2343
2440
  log.stdout({}, `\x1b[36m${ 'v' }\x1b[0m`); // pressed button successfully
@@ -2370,6 +2467,8 @@ module.exports = {
2370
2467
  if ( !did_navigate && ensure_navigation ) {
2371
2468
  // All fields on this page should be taken care of
2372
2469
  let page_url = await scope.page.url();
2470
+ // TODO: Attempt avoiding `awaiting` and instead allow
2471
+ // waitUntilContinued to detect navigation.
2373
2472
  await scope.continue( scope );
2374
2473
  // Have to catch errors right now or may get stuck in an infinite loop?
2375
2474
  // TODO: Don't catch errors - maybe the developer expected/wanted an error
@@ -2381,10 +2480,7 @@ module.exports = {
2381
2480
  // these messages smells bad. Con: Already a lot of code out there.
2382
2481
 
2383
2482
  // TODO: How do we let 'I set the var __ to __' also afterwards check for an invalidation message?
2384
- error = await scope.waitUntilContinued( scope, { url: page_url, id });
2385
- // if ( error.was_found ) {
2386
- // await scope.throwPageError( scope, { id: id });
2387
- // }
2483
+ await scope.waitUntilContinued( scope, { url: page_url });
2388
2484
 
2389
2485
  did_navigate = true;
2390
2486
  log.stdout({}, `\x1b[36m${ '>' }\x1b[0m`); // continued successfully
@@ -2400,7 +2496,7 @@ module.exports = {
2400
2496
  // TODO: Since we now continue only once no matter what, we can put
2401
2497
  // the error in the caller of the function, though we may want
2402
2498
  // to pass the symbol to print as well.
2403
- return error;
2499
+ return { ok: true };
2404
2500
  // TODO: Maybe we should return something else or something in addition about used vars
2405
2501
  }, // Ends scope.setFields()
2406
2502
 
@@ -2636,80 +2732,483 @@ module.exports = {
2636
2732
  }
2637
2733
  }, // Ends scope.has_interview_content()
2638
2734
 
2639
- waitUntilContinued: async function ( scope, { url = '', id = '' }) {
2640
- /** Given a url, attempt to continue until the page url changes or an error
2641
- is found. Return the error object no matter what. */
2642
- let prev_url = url;
2643
- let curr_url = await scope.page.url();
2644
- let error = await scope.checkForError( scope );
2735
+ waitUntilContinued: async function ( scope, { url = '' }) {
2736
+ /** Given a url, attempt to detect navigation until:
2737
+ *
2738
+ * - a page shows a system error or invalid user answers
2739
+ * - the page url changes
2740
+ * - the navigation times out
2741
+ *
2742
+ * Note: intended to run after an attempt to navigate.
2743
+ *
2744
+ * TODO: Duplicates much of `tapElementAndNavigate`. Need to de-duplicate.
2745
+ * Alternatively, continue other work on navigation detection with server
2746
+ * responses.
2747
+ *
2748
+ * If the navigation fails, throw an error.
2749
+ * */
2750
+
2751
+ // Discuss: Delay system err check? May allow AL to test custom error page.
2752
+ await scope.throwAnySystemError( scope ); // system error on current page
2753
+ await scope.throw_on_invalid_answers( scope ); // invalid user answers
2645
2754
 
2646
- let timedOut = false; // Lazy timeout?
2647
- let cont_timeout = setTimeout(function(){ timedOut = true }, scope.timeout);
2755
+ let start_url = url; // for navigation check later
2756
+ let curr_url = start_url;
2648
2757
 
2649
2758
  // Attempt to solve weird race condition issues where page has not properly continued
2650
- while ( curr_url === prev_url && !error.was_found && !timedOut ) {
2759
+ // May exceed actual timeout
2760
+ let timed_out = false;
2761
+ let cont_timeout = setTimeout(
2762
+ function(){ timed_out = true; }, ( scope.timeout - 10 )
2763
+ );
2764
+ while ( curr_url === start_url && !timed_out ) {
2765
+ try {
2766
+ await scope.throwAnySystemError( scope ); // system error on new page
2767
+ } catch ( sys_err_msg ) {
2768
+ clearTimeout( cont_timeout );
2769
+ throw sys_err_msg;
2770
+ }
2651
2771
  await time.waitForTimeout( 50 );
2652
2772
  curr_url = await scope.page.url();
2653
- error = await scope.checkForError( scope );
2654
2773
  }
2774
+ clearTimeout( cont_timeout );
2655
2775
 
2656
- let timeout_err_message = `The test tried to continue to the next page, but it took longer than ${ (scope.timeout/1000)/60 } min. Check the picture of this page. If a secret variable was used, there will be no picture.`
2657
- if ( timedOut ) { reports.addToReport(scope, { type: `error`, code: `ALK0146`, value: timeout_err_message }); }
2658
- expect( timedOut, timeout_err_message ).to.be.false;
2776
+ await scope.throwAnySystemError( scope ); // system error on new page
2777
+
2778
+ // TODO: We sometimes log multiple error messages for an error because we're
2779
+ // logging at multiple levels. We should try to refactor to log just once
2780
+ // per error
2781
+ let timeout_err_message = `The test tried to continue to the next page, but it took longer than ${ (scope.timeout/1000)/60 } min. You can check the picture of this page unless a secret variable was used.`
2782
+ if ( timed_out ) { reports.addToReport(scope, { type: `error`, code: `ALK0146`, value: timeout_err_message }); }
2783
+ expect( timed_out, timeout_err_message ).to.be.false;
2784
+
2785
+ // Note: Invalid answers on the new page would be ok, though they probably
2786
+ // won't exist yet anyway.
2787
+
2788
+ if ( start_url !== curr_url ) {
2789
+ scope.navigated = true;
2790
+ }
2791
+
2792
+ return true;
2659
2793
 
2660
- clearTimeout( cont_timeout );
2661
2794
 
2662
- return error;
2795
+ // let prev_url = url;
2796
+ // let curr_url = await scope.page.url();
2797
+ // let error_data = await scope.getPageError( scope ); // error on current pg
2798
+
2799
+ // let timed_out = false;
2800
+ // let cont_timeout = setTimeout(
2801
+ // function(){ timed_out = true }, ( scope.timeout - 10 )
2802
+ // );
2803
+
2804
+ // // Attempt to solve weird race condition issues where page has not properly continued
2805
+ // while ( curr_url === prev_url && !error_data.element && !timed_out ) {
2806
+ // await time.waitForTimeout( 50 );
2807
+ // curr_url = await scope.page.url();
2808
+ // error_data = await scope.getPageError( scope ); // error on new pg
2809
+ // }
2810
+
2811
+ // clearTimeout( cont_timeout );
2812
+
2813
+ // // TODO: Log only one error log per... error.
2814
+ // let timeout_err_message = `The test tried to continue to the next page, but it took longer than ${ (scope.timeout/1000)/60 } min. You can check the picture of this page unless a secret variable was used.`
2815
+ // if ( timed_out ) { reports.addToReport(scope, { type: `error`, code: `ALK0146`, value: timeout_err_message }); }
2816
+ // expect( timed_out, timeout_err_message ).to.be.false;
2817
+
2818
+ // return error_data;
2663
2819
  }, // Ends scope.waitUntilContinued()
2664
2820
 
2665
- checkForError: async function ( scope ) {
2666
- /* Returns whether any system or user error elements were found and, if so,
2667
- * returns those elements. The error elements are probably exclusive, but
2668
- * we're not sure. */
2669
-
2670
- // All results are lists to match `$x`
2671
- let possible_errors = {
2672
- complex_user_error: await scope.page.$$(`.alert-danger`),
2673
- // The below does not work if there's not a space between ':' and 'none'
2674
- simple_user_error: await scope.page.$$(`xpath/.//*[contains(@class, "da-has-error")][not(contains(@style,"display: none"))]`),
2675
- system_error: await scope.page.$$(`xpath/.//meta[@content="docassemble: Error"]` ),
2676
- };
2821
+ throwAnySystemError: async function ( scope ) {
2822
+ /**
2823
+ * TODO: Stack trace won't show user answers. Could they reveal other
2824
+ * sensitive information?
2825
+ * TODO: Add a state variable if any secret vars have been used thus far
2826
+ * and, if so, disallow pics, JSON, HTML, and page contents collecting.
2827
+ * In addition, allow author to disable all the above with a Step.
2828
+ *
2829
+ * @param {Object} scope - Tests state object
2830
+ * */
2831
+ let sys_err_msg = `Was unable to get a specific system error message, sorry.`;
2677
2832
 
2678
- let error_handlers = {};
2679
- let was_found = false;
2680
- for ( let error_key in possible_errors ) {
2681
- if ( possible_errors[ error_key ][0] ) {
2682
- // If there's a page visible (locally), give us as much time to examine it as timeout allows.
2683
- // Because of previous actions, this may still be a bit too long.
2684
- // Also, if you're debugging and you close the window early, the process will still
2685
- // take this long to exit after the tests finish. Not sure how to account for that.
2686
- if ( session_vars.get_debug() ) {
2687
- await time.waitForTimeout( ( scope.timeout - (1 * 1000)) );
2688
- }
2689
- was_found = true;
2690
- error_handlers[ error_key ] = possible_errors[ error_key ][0];
2691
- }
2692
- }
2833
+ let da_sys_err = await scope.page.$(`xpath/.//meta[@content=": Error"]|.//*[contains(@id, "damain")]//h1[@content="Error"]`);
2834
+ // `al_custom_error_action` in base64. This acts like a continue screen, but actually replaces system error pages.
2835
+ let al_custom_err = await scope.page.$(`.question-custom-error-action .daattributions #trigger[data-variable="YWxfY3VzdG9tX2Vycm9yX2FjdGlvbg"]`);
2836
+
2837
+ // if ( day_sys_err ) {
2838
+ // sys_err_msg = await scope.get_da_system_err( scope );
2839
+ // } else if ( al_custom_err ) {
2840
+ // sys_err_msg = await scope.get_al_system_err( scope );
2841
+ // }
2693
2842
 
2694
- return { was_found, error_handlers };
2695
- }, // Ends scope.checkForError()
2843
+ error_indicator = da_sys_err || al_custom_err;
2696
2844
 
2697
- throwPageError: async function ( scope, { error_handlers = {}, id = '' }) {
2698
- /* Throw a cucumber error with the error that appeared on the page. */
2699
- if ( error_handlers.system_error !== undefined ) {
2700
- let sys_err_message = `The test tried to continue to the next page after ${ id }, but there was a system error. Check the picture of this page.`
2701
- reports.addToReport(scope, { type: `error`, code: `ALK0147`, value: sys_err_message });
2702
- expect( was_system_error, sys_err_message ).to.be.false;
2845
+ if ( error_indicator ) {
2846
+ let sys_err_msg = await scope.get_system_error_page_sample(
2847
+ scope, { error_indicator }
2848
+ );
2703
2849
 
2704
- } else {
2705
- // If it wasn't a system error, it was a user error
2850
+ // TODO: Discuss adding some logs to just a Scenario's report as opposed
2851
+ // to full report.
2852
+ reports.addToReport( scope, { type: `error`, code: `ALK0147`, value: sys_err_msg });
2853
+ throw new Error( sys_err_msg );
2854
+ }
2855
+ },
2856
+
2857
+ get_invalid_answer_indicators: async function ( scope ) {
2858
+ /**
2859
+ * Return any elements associated with invalid user answers. Avoid throwing.
2860
+ * Invalid answers may be the author's goal.
2861
+ *
2862
+ * Note: May later want the actual text of the error messages.
2863
+ *
2864
+ * @param {Object} scope - Tests state object
2865
+ * */
2866
+ // TODO: While alerts come at the top of the page for validation errors,
2867
+ // an author can also use them as flavor text on the page. Ensure that
2868
+ // the latter doesn't trigger failure.
2869
+ let invalid_indicator1 = await scope.page.$$(`.alert-danger`);
2870
+ // ⚠️ 'display:' and 'none' MUST be separated by a space
2871
+ let invalid_indicator2 = await scope.page.$$(`xpath//*[contains(@class, "da-has-error")][not(contains(@style,"display: none"))]`);
2872
+ invalidity_indicators = [ ...invalid_indicator1, ...invalid_indicator2 ];
2873
+
2874
+ return invalidity_indicators;
2875
+ },
2876
+
2877
+ throw_on_invalid_answers: async function ( scope ) {
2878
+ /**
2879
+ * Throw an error if the user has given invalid answers.
2880
+ *
2881
+ * @param {Object} scope - Tests state object
2882
+ * */
2883
+ let invalidity_indicators = await scope.get_invalid_answer_indicators( scope );
2884
+ if ( invalidity_indicators.length > 0 ) {
2885
+ let { id } = await scope.examinePageID( scope, 'none to match' );
2706
2886
  let user_err_message = `The test tried to continue to the next page after ${ id }, but the user got an error. Check the picture of this page.`
2707
2887
  reports.addToReport(scope, { type: `error`, code: `ALK0148`, value: user_err_message });
2708
- expect( true, user_err_message ).to.be.false;
2888
+ expect( invalidity_indicators, user_err_message ).isEmpty([]);
2889
+ }
2890
+ },
2891
+
2892
+ // getPageError: async function ( scope ) {
2893
+ // /**
2894
+ // * Returns an object with a system/user error elem if they exist, As well
2895
+ // * as other data.
2896
+ // *
2897
+ // * TODO: Discuss how we can split system vs. user errors more effectively.
2898
+ // *
2899
+ // * @param {Object} scope - Tests state object
2900
+ // * */
2901
+
2902
+ // // let possible_errors = {
2903
+ // // // TODO: Ensure that intentional callouts don't trigger failure since they
2904
+ // // // too use .alert-danger I believe (bootstrap component)
2905
+ // // user_alert_error: await scope.page.$$(`.alert-danger`),
2906
+ // // // ⚠️ The below does not work if there's not a space between ':' and 'none'
2907
+ // // user_field_error: await scope.page.$$(`xpath/.//*[contains(@class, "da-has-error")][not(contains(@style,"display: none"))]`),
2908
+ // // // ⚠️ This xpath assumes this content stays in the metadata of the page
2909
+ // // system_error: await scope.page.$$(`xpath/.//meta[@content=": Error"]|.//*[contains(@id, "damain")]//h1[@content="Error"]` ),
2910
+ // // // Hardcoding the `al_custom_error_action` screen (that's what the base64 here is). It hides actual errors and looks like a continue screen, but isn't.
2911
+ // // al_custom_system_error: await scope.page.$$(`.question-custom-error-action .daattributions #trigger[data-variable="YWxfY3VzdG9tX2Vycm9yX2FjdGlvbg"]`),
2912
+ // // };
2913
+
2914
+ // let result = { ok: true, type: ``, element: null, msg: `` };
2915
+
2916
+ // let error_patterns = [
2917
+ // // TODO: While alerts come at the top of the page for validation errors,
2918
+ // // an author can also use them as flavor text on the page. Ensure that
2919
+ // // the latter doesn't trigger failure.
2920
+ // { type: `user_alert`, selector: `.alert-danger` },
2921
+ // // ⚠️ 'display:' and 'none' MUST have a space between them
2922
+ // { type: `user_field`, selector: `xpath//*[contains(@class, "da-has-error")][not(contains(@style,"display: none"))]` },
2923
+ // { type: `system_da`, selector: `xpath/.//meta[@content=": Error"]|.//*[contains(@id, "damain")]//h1[@content="Error"]` },
2924
+ // // `al_custom_error_action` in base64. This acts like a continue screen, but actually replaces system error pages.
2925
+ // { type: `system_al`, selector: `.question-custom-error-action .daattributions #trigger[data-variable="YWxfY3VzdG9tX2Vycm9yX2FjdGlvbg"]` },
2926
+ // ];
2927
+ // for ( let pattern of error_patterns ) {
2928
+ // let element = await scope.page.$( pattern.selector );
2929
+ // if ( element ) {
2930
+ // result.type = type: pattern.type;
2931
+ // result.element = element;
2932
+ // }
2933
+ // }
2934
+
2935
+ // if ( result.type.includes(`system`)) {
2936
+ // result.ok = false;
2937
+ // result.msg = await scope.get_system_error_page_sample(
2938
+ // scope, { error_element: result.element }
2939
+ // );
2940
+ // } else if ( result.type.includes(`user`) ) {
2941
+ // // Invalid user answers may be intentional and ok.
2942
+ // result.msg = `User gave invalid answer.`
2943
+ // }
2944
+
2945
+ // return result;
2946
+
2947
+
2948
+
2949
+
2950
+
2951
+
2952
+ // // let error = { type: `user_alert`, element: await scope.page.$$(`.alert-danger`) }
2953
+ // // if ( !error.element ) {
2954
+ // // error = { type: `user_field`, element: await scope.page.$$(`xpath/.//*[contains(@class, "da-has-error")][not(contains(@style,"display: none"))]`) }
2955
+ // // }
2956
+ // // if ( !error.element ) {
2957
+ // // error = { type: `system_da`, element: await scope.page.$$(`xpath/.//meta[@content=": Error"]|.//*[contains(@id, "damain")]//h1[@content="Error"]` ) }
2958
+ // // }
2959
+ // // if ( !error.element ) {
2960
+ // // error = { type: `system_al`, await scope.page.$$(`.question-custom-error-action .daattributions #trigger[data-variable="YWxfY3VzdG9tX2Vycm9yX2FjdGlvbg"]`) }
2961
+ // // }
2962
+
2963
+ // // let error_elems_by_type = {};
2964
+ // // let was_found = false;
2965
+ // // let type = null;
2966
+ // // for ( let error_key in possible_errors ) {
2967
+ // // error_elems_by_type[ error_key ] = possible_errors[ error_key ][0];
2968
+ // // if ( possible_errors[ error_key ][0] ) {
2969
+ // // // If there's a page visible (locally), give us as much time to examine it as timeout allows.
2970
+ // // // Because of previous actions, this may still be a bit too long.
2971
+ // // // Also, if you're debugging and you close the window early, the process will still
2972
+ // // // take this long to exit after the tests finish. Not sure how to account for that.
2973
+ // // if ( session_vars.get_debug() ) {
2974
+ // // await time.waitForTimeout( ( scope.timeout - (1 * 1000)) );
2975
+ // // }
2976
+ // // if ( error_key.includes(`user`) ) { type = `user`; }
2977
+ // // if ( error_key.includes(`system`) ) { type = `system`; }
2978
+ // // was_found = true;
2979
+ // // }
2980
+ // // }
2981
+
2982
+ // // return { was_found, type, error_elems_by_type };
2983
+ // }, // Ends scope.getPageError()
2984
+
2985
+ get_system_error_page_sample: async function ( scope ) {
2986
+ // get_da_system_err: async function ( scope ) {
2987
+ /**
2988
+ * Returns message for a docassemble system error page that includes a
2989
+ * sample of the page contents
2990
+ *
2991
+ * @param {Object} scope - Tests state object
2992
+ *
2993
+ * @returns {string}
2994
+ * */
2995
+ let { id } = await scope.examinePageID( scope, 'none to match' );
2996
+ let page_err_text = `Unable to get the text on this page. Check the picture of this page if possible.`
2997
+ try {
2998
+
2999
+ page_err_text = await scope.page.evaluate(() => {
3000
+ return (
3001
+ ( document.querySelector(`#YWxfZm9ybWF0dGVkX2Vycm9yX21lc3NhZ2U`)
3002
+ || document.querySelector(`.dabody`)
3003
+ ).textContent
3004
+ .split(`\n`)
3005
+ .slice( 0, 8 )
3006
+ .join(`\n`)
3007
+ .trim()
3008
+ + `\n...`
3009
+ );
3010
+ });
3011
+
3012
+ } catch ( pupp_err ) {
3013
+ log.debug({ code: `ALK0281`, context: `internal`, },
3014
+ `Skipped getting system error screen text with puppeteerjs:`, pupp_err,
3015
+ );
3016
+ }
3017
+
3018
+ return `Ran into an error page after page id "${ id }".
3019
+
3020
+ ${ page_err_text }`;
3021
+
3022
+ },
3023
+
3024
+ get_al_system_err: async function ( scope ) {
3025
+ /**
3026
+ * Returns message for a docassemble system error page that includes a
3027
+ * sample of the page contents
3028
+ *
3029
+ * @param {Object} scope - Tests state object
3030
+ *
3031
+ * @returns {string}
3032
+ * */
3033
+ let { id } = await scope.examinePageID( scope, 'none to match' );
3034
+ let page_err_text = `Unable to get the text on this page. Check the picture of this page if possible.`
3035
+ try {
3036
+
3037
+ page_err_text = await scope.page.evaluate(() => {
3038
+ return (
3039
+ // b64 for `al_formatted_error_message`, which seems most relevant
3040
+ document.querySelector(`#YWxfZm9ybWF0dGVkX2Vycm9yX21lc3NhZ2U`)
3041
+ .textContent
3042
+ .split(`\n`)
3043
+ .slice( 0, 10 )
3044
+ .join(`\n`)
3045
+ );
3046
+ });
3047
+
3048
+ } catch ( pupp_err ) {
3049
+ log.debug({ code: `ALK0`, context: `internal`, },
3050
+ `Skipped getting system error text on Assembly Line custom error screen with puppeteerjs:`, pupp_err,
3051
+ );
2709
3052
  }
2710
3053
 
2711
- return scope;
2712
- }, // Ends scope.throwPageError()
3054
+ return `Ran into an error page after page id "${ id }".
3055
+
3056
+ ${ page_err_text }`;
3057
+
3058
+ },
3059
+
3060
+ // warnOrThrowOnInvalidPage: async function ( scope, { page_result={} }) {
3061
+ // /**
3062
+ // * Given a page with invalid user input or a system error page, warn about
3063
+ // * it or throw an error.
3064
+ // *
3065
+ // * @param {Object} scope - Tests state object
3066
+ // * @param {Object} obj - Named arguments
3067
+ // * @param (Object) obj.page_result - Data from examining a docassemble page
3068
+ // * */
3069
+ // let error = page_result?.error;
3070
+ // if ( page_result && !page_result.ok ) {
3071
+ // if ( error.type.includes(`system`)) {
3072
+ // // Throw on a system error or an unhandled error of our own
3073
+ // reports.addToReport( scope, { type: `error`, code: `ALK0147`, value: error.msg });
3074
+ // throw new Error( error.msg );
3075
+ // } else {
3076
+ // reports.addToReport( scope, { type: `warn`, code: `ALK0282`, value: error.msg });
3077
+ // }
3078
+ // }
3079
+ // },
3080
+
3081
+ // checkForError: async function ( scope ) {
3082
+ // /* Returns whether any system or user error elements were found and, if so,
3083
+ // * returns those elements. The error elements are probably exclusive, but
3084
+ // * we're not sure. */
3085
+
3086
+ // // All results are lists to match `$x`
3087
+ // let possible_errors = {
3088
+ // // TODO: Ensure that intentional callouts don't trigger failure since they
3089
+ // // too use .alert-danger I believe (bootstrap component)
3090
+ // user_alert_error: await scope.page.$$(`.alert-danger`),
3091
+ // // ⚠️ The below does not work if there's not a space between ':' and 'none'
3092
+ // user_field_error: await scope.page.$$(`xpath/.//*[contains(@class, "da-has-error")][not(contains(@style,"display: none"))]`),
3093
+ // // ⚠️ This xpath assumes this content stays in the metadata of the page
3094
+ // system_error: await scope.page.$$(`xpath/.//meta[@content=": Error"]|.//*[contains(@id, "damain")]//h1[@content="Error"]` ),
3095
+ // // Hardcoding the `al_custom_error_action` screen (that's what the base64 here is). It hides actual errors and looks like a continue screen, but isn't.
3096
+ // al_custom_system_error: await scope.page.$$(`.question-custom-error-action .daattributions #trigger[data-variable="YWxfY3VzdG9tX2Vycm9yX2FjdGlvbg"]`),
3097
+ // };
3098
+
3099
+ // let error_elems_by_type = {};
3100
+ // let was_found = false;
3101
+ // let type = null;
3102
+ // for ( let error_key in possible_errors ) {
3103
+ // error_elems_by_type[ error_key ] = possible_errors[ error_key ][0];
3104
+ // if ( possible_errors[ error_key ][0] ) {
3105
+ // // If there's a page visible (locally), give us as much time to examine it as timeout allows.
3106
+ // // Because of previous actions, this may still be a bit too long.
3107
+ // // Also, if you're debugging and you close the window early, the process will still
3108
+ // // take this long to exit after the tests finish. Not sure how to account for that.
3109
+ // if ( session_vars.get_debug() ) {
3110
+ // await time.waitForTimeout( ( scope.timeout - (1 * 1000)) );
3111
+ // }
3112
+ // if ( error_key.includes(`user`) ) { type = `user`; }
3113
+ // if ( error_key.includes(`system`) ) { type = `system`; }
3114
+ // was_found = true;
3115
+ // }
3116
+ // }
3117
+
3118
+ // return { was_found, type, error_elems_by_type };
3119
+ // }, // Ends scope.checkForError()
3120
+
3121
+ // processPageError: async function ( scope, { error_elems_by_type = {}, id = `None` }) {
3122
+ // /**
3123
+ // * Throw a cucumber error with the error that appeared on the page.
3124
+ // *
3125
+ // * Discuss: Should we show the text on the page?
3126
+ // *
3127
+ // * @param {Object} scope - Tests state object
3128
+ // * @param {Object} obj - Named arguments
3129
+ // * @param {Object} obj.error_elems_by_type - A map of DOM Node | undefined
3130
+ // * by type of error
3131
+ // * @param {string} obj.id - The current page id
3132
+ // * */
3133
+ // let error_elems_by_type = error_data.error_elems_by_type
3134
+ // // TODO: #656, wasn't checking for this error specifically. May be almost all we need for that.
3135
+ // if ( error_elems_by_type.system_error !== undefined || error_elems_by_type.al_custom_system_error !== undefined ) {
3136
+ // return await scope.throwAnySystemError( scope, { error_elems_by_type, id });
3137
+ // }
3138
+
3139
+ // // If it wasn't a system error, it was a user error
3140
+ // // TODO: Explore whether this still allows an author to check for correctly
3141
+ // // triggering user input invalidation
3142
+ // let user_err_message = `The test tried to continue to the next page after ${ id }, but the user got an error. Check the picture of this page.`
3143
+ // reports.addToReport(scope, { type: `error`, code: `ALK0148`, value: user_err_message });
3144
+ // expect(
3145
+ // ( error_elems_by_type.user_alert_error == undefined && error_elems_by_type.user_field_error == undefined ),
3146
+ // user_err_message
3147
+ // ).to.be.true;
3148
+
3149
+ // return scope;
3150
+ // }, // Ends scope.processPageError()
3151
+
3152
+ // getPageError: async function( scope, { error_elems_by_type = {}, id = `None` }) {
3153
+ // /**
3154
+ // * Throw an error if the author's code caused a system error page to appear.
3155
+ // *
3156
+ // * @param {Object} scope - Tests state object
3157
+ // * @param {Object} obj - Named arguments
3158
+ // * @param {Object} obj.error_elems_by_type - A map of DOMNode | undefined
3159
+ // * by type of error
3160
+ // * @param {string} obj.id - The current page id
3161
+ // * */
3162
+ // // Get the relevant text on the page
3163
+ // let err_elem = error_elems_by_type.system_error || error_elems_by_type.al_custom_system_error;
3164
+ // let page_err_text = '';
3165
+ // if ( err_elem ) {
3166
+ // page_err_text = await get_page_error_text( scope, { error_elem = err_elem });
3167
+ // }
3168
+
3169
+ // // Add to the report
3170
+ // let sys_err = new Error(`The test tried to continue to the next page after ${ id }, but there was a system error.
3171
+
3172
+ // ${ page_err_text }`);
3173
+ // reports.addToReport(scope, { type: `error`, code: `ALK0147`, value: sys_err });
3174
+
3175
+ // // expect(
3176
+ // // ( error_elems_by_type.system_error == undefined && error_elems_by_type.al_custom_system_error == undefined ),
3177
+ // // sys_err
3178
+ // // ).to.be.true;
3179
+
3180
+ // return sys_err;
3181
+ // },
3182
+
3183
+ // get_page_error_text: async function( scope, { error_elem }) {
3184
+ // /**
3185
+ // * Given an element on an error page, return the relevant content.
3186
+ // *
3187
+ // * @param {object} scope - Tests state object
3188
+ // * @param {object} obj - Named arguments
3189
+ // * @param {Object} obj.error_elem - Element that indicated a system err
3190
+ // * */
3191
+ // let page_err_text = `Unable to get the text on this page. Check the picture of this page if possible.`
3192
+ // try {
3193
+
3194
+ // page_err_text = await page.evaluate(( err_elem ) => {
3195
+
3196
+ // let text_container = (
3197
+ // err_elem.closest(`.dabody`)
3198
+ // || err_elem.closest(`#dabody`)
3199
+ // ).querySelector(`* > .danavbarcontainer ~ *`);
3200
+ // return text_container.textContent;
3201
+
3202
+ // }, err_elems[0]);
3203
+
3204
+ // } catch ( pupp_err ) {
3205
+ // log.debug({ code: `ALK0281`, context: `internal`, },
3206
+ // `Skipped getting system error screen text with puppeteerjs:`, pupp_err,
3207
+ // );
3208
+ // }
3209
+
3210
+ // return page_err_text;
3211
+ // },
2713
3212
 
2714
3213
  take_a_screenshot: async ( scope,{ path }) => {
2715
3214
  /* Takes a jpeg screenshot. Avoids destroying signatures. */
@@ -2930,7 +3429,7 @@ module.exports = {
2930
3429
  /** Given a fetch response headers' Content-Disposition str, return the
2931
3430
  * filename of the response's file.
2932
3431
  *
2933
- * @return { string | null } - Name of fetched file
3432
+ * @returns { string | null } - Name of fetched file
2934
3433
  * */
2935
3434
  let filename = default_name;
2936
3435
  if ( disposition ) {
package/lib/steps.js CHANGED
@@ -400,7 +400,7 @@ When(/I? ?gets? to ?.*?(".*").*?with.*?(?:this data:)?/i, {timeout: -1}, async (
400
400
  // If we need to call recursively, signature function might look like:
401
401
  // func( scope, { resolve, reject, supported_table, page_timeout_id })
402
402
  // Maybe list of timeout and/or interval ids in state that can all be cleared at once?
403
- (async function() {
403
+ (async function setFieldsFromStoryTable() {
404
404
  try {
405
405
  let id = await scope.get_page_id( scope );
406
406
 
@@ -416,25 +416,28 @@ When(/I? ?gets? to ?.*?(".*").*?with.*?(?:this data:)?/i, {timeout: -1}, async (
416
416
  `Rows, including special rows:`, ensured_var_data
417
417
  );
418
418
 
419
- // THIS FUNCTION ACTUALLY MATTERS
420
- // Some errors happen in there to be closer to the relevant code. We'd have to try/catch anyway.
421
- let error = await scope.setFields(scope, { var_data: ensured_var_data, id, ensure_navigation: true });
419
+ let result = await scope.setFields( scope, {
420
+ var_data: ensured_var_data, id, ensure_navigation: true
421
+ });
422
+
423
+ resolve( result );
424
+
425
+ // } catch ( err ) {
426
+ // // // Discuss: This error message seems unnecessary cruft
427
+ // // let err_msg = `ALKiln ran into an error when it tried to answer fields on the page with the question id "${ id }". The text above might have more information.`
428
+ // // reports.addToReport( scope, { type: `error`, code: `ALK0062`, value: err_msg });
429
+
430
+ // // Catch a system error or an unhandled error
431
+ // // A system page error trace here may be more useful than in a deeper function
432
+ // reject( err );
422
433
 
423
- // Discuss: use Log class to throw this error?
424
- if ( error.was_found ) { await scope.throwPageError( scope, { id: id }); }
425
- resolve();
426
- } catch ( err ) {
427
- let err_msg = `ALKiln ran into an error when it tried to answer fields on the page with the question id "${ id }". The text above might have more information.`
428
- reports.addToReport( scope, { type: `error`, code: `ALK0062`, value: err_msg });
429
- // Throw error instead?
430
- reject( err );
431
434
  } finally {
432
435
  clearTimeout( page_timeout_id ); // prevent temporary hang at end of tests
433
436
  }
434
437
  })();
435
- }); // ends custom_timeout for setFields
438
+ }); // ends custom_timeout for filling in fields
436
439
 
437
- await custom_timeout;
440
+ let page_result = await custom_timeout;
438
441
 
439
442
  // Prep for next loop
440
443
  id = await scope.get_page_id( scope );
@@ -641,7 +644,7 @@ Then('I will be told an answer is invalid', async () => { // √
641
644
 
642
645
  let { was_found, error_handlers } = await scope.checkForError( scope );
643
646
 
644
- let no_error_msg = `No error message was found on the page`;
647
+ let no_error_msg = `The user should have been told their answer was invalid, but that text is missing on the page`;
645
648
  if ( !was_found ) { reports.addToReport(scope, { type: `error`, code: `ALK0076`, value: no_error_msg }); }
646
649
  expect( was_found, no_error_msg ).to.be.true;
647
650
 
@@ -653,6 +656,11 @@ Then('I will be told an answer is invalid', async () => { // √
653
656
  await scope.afterStep(scope);
654
657
  });
655
658
 
659
+ // ==============
660
+ // Welp, we have a diversion (da DOM changes)
661
+ // Resume here when ready
662
+ // ==============
663
+
656
664
  Then('I arrive at the next page', async () => { // √
657
665
  /* Tests for detection of url change from button or link tap.
658
666
  * Can other things trigger navigation? Re: other inputs things
@@ -820,6 +828,8 @@ Then(/the "([^"]+)" link opens a working page/, { timeout: -1 }, async ( linkTex
820
828
  );
821
829
  });
822
830
 
831
+ // #656, problem: tap to continue doesn't go through the full Story Table cycle
832
+ // and thus doesn't get any page errors detected.
823
833
  When(/I tap to continue/i, { timeout: -1 }, async () => {
824
834
  // Any selectors for this seem somewhat precarious
825
835
  // No data will cause an automatic continue when `ensure_navigation` is true
@@ -849,7 +859,7 @@ When(/I tap the "([^"]+)" element and wait (?:for )?([0-9]+) second(?:s)?/i, { t
849
859
  // Also, this is always supposed to navigate, and that handles its own waiting.
850
860
 
851
861
  // `page` timeout will deal with custom timeout
852
- await scope.tapSelectorToNavigate( scope, elemSelector, waitTime * 1000 )
862
+ await scope.tapSelectorToNavigate( scope, elemSelector, waitTime * 1000 );
853
863
  });
854
864
 
855
865
  When(/I check the page for (?:accessibility|a11y) issues/i, {timeout: -1}, async () => {
@@ -972,6 +982,7 @@ When(/I answer randomly for at most ([0-9]+) (?:page(?:s)?|screen(?:s)?)/, { tim
972
982
  buttons_exist = await scope.continue_exists( scope );
973
983
  if ( buttons_exist ) {
974
984
  await scope.continue( scope );
985
+ // TODO: Use warnOrThrowInvalidPage to error on error page
975
986
  } // ends if continue exists
976
987
  } // ends if buttons_exist
977
988
  if (!( await scope.is_on_server_still(scope))) {
@@ -1458,18 +1469,20 @@ After(async function(scenario) {
1458
1469
  // test), it failed. If is was supposed to pass and failed, it also
1459
1470
  // failed. E.g. a11y failures let the Scenario pass initially, but
1460
1471
  // should cause a failure here.
1461
- changeable_test_status = `FAILED`;
1462
- // Flag this failure to trigger an error later, so cucumber shows
1463
- // it as failed too.
1464
- expected_status_was_met = false;
1465
- // Keep it general. Users may see this message, e.g. for a11y tests.
1472
+
1473
+ // Keep this msg general. Users may see this message, e.g. for a11y tests.
1466
1474
  let msg = `Test ${ changeable_test_status }. `
1467
- + `Expected status: ${scope.expected_status}. `
1475
+ + `Expected status: ${ scope.expected_status }. `
1468
1476
  + `See the report for details.`;
1469
1477
  let code = `ALK0099`;
1470
1478
  expected_status_err_msg = log.debug({ code: code, level: `error`, },
1471
1479
  msg
1472
1480
  );
1481
+
1482
+ // Flag this failure to trigger an error later, so cucumber shows
1483
+ // it as failed too.
1484
+ changeable_test_status = `FAILED`;
1485
+ expected_status_was_met = false;
1473
1486
  }
1474
1487
  }
1475
1488
  scope.expected_status = null; // reset for next Scenario
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suffolklitlab/alkiln",
3
- "version": "5.16.1",
3
+ "version": "5.17.0-fix-stop-on-error-1",
4
4
  "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {