@suffolklitlab/alkiln 3.0.9 → 4.0.0-pre-api.4

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.
package/lib/steps.js CHANGED
@@ -1,10 +1,23 @@
1
- const { When, Then, And, Given, After, Before, BeforeAll, AfterAll, setDefaultTimeout } = require('cucumber');
1
+ const {
2
+ When,
3
+ Then,
4
+ And,
5
+ Given,
6
+ After,
7
+ Before,
8
+ BeforeAll,
9
+ AfterAll,
10
+ setDefaultTimeout //,
11
+ // wrapPromiseWithTimeout // cucumber v8
12
+ } = require('@cucumber/cucumber');
13
+ const wrapPromiseWithTimeout = require('./cucumber_8_shim');
2
14
  const fs = require('fs');
3
15
  const path = require('path');
4
16
  const { expect } = require('chai');
5
17
  const puppeteer = require('puppeteer');
6
18
  const { v4: uuidv4 } = require('uuid');
7
19
 
20
+ // Ours
8
21
  const scope = require('./scope');
9
22
  const env_vars = require('./utils/env_vars');
10
23
 
@@ -57,6 +70,7 @@ BeforeAll(async () => {
57
70
  scope.timeout = 30 * 1000;
58
71
  scope.showif_timeout = 600;
59
72
  scope.report = new Map();
73
+ fs.writeFileSync('log.txt', "");
60
74
  });
61
75
 
62
76
  Before(async (scenario) => {
@@ -76,7 +90,6 @@ Before(async (scenario) => {
76
90
 
77
91
  // Reset default timeout
78
92
  scope.timeout = 30 * 1000;
79
- setDefaultTimeout( 30 * 1000 );
80
93
  });
81
94
 
82
95
  // Add a check for an error page before each step? After each step?
@@ -167,8 +180,9 @@ Given(/I start the interview at "([^"]+)"(?: in lang "([^"]+)")?/, {timeout: -1}
167
180
 
168
181
  // If response has a file in it that matches the file we are downloading
169
182
  if ( response._headers['content-disposition'] && (response._headers['content-disposition']).includes(`attachment;filename=${scope.toDownload}`) ) {
170
- // Remove the event listener since we might not be closing the browser
171
- scope.page.removeEventListener('response', arguments.callee);
183
+ // We're chosing avoid removing the event listener in case removal
184
+ // would prevent downloading other files. The listener should get
185
+ // destroyed when the page closes anyway.
172
186
 
173
187
  // Watch event on download folder or file
174
188
  await fs.watchFile( docs_dir, function (curr, prev) {
@@ -220,6 +234,24 @@ Given(/I start the interview at "([^"]+)"(?: in lang "([^"]+)")?/, {timeout: -1}
220
234
  await custom_aferStep_timeout;
221
235
  });
222
236
 
237
+ Given(/I (?:sign|log) ?(?:in)?(?:on)?(?:to the server)? with(?: the email)? "([^"]+)" and(?: the password)? "([^"]+)"(?: SECRETs)?/i, {timeout: -1}, async ( email, password ) => {
238
+ /** Uses the names of environment variables (most often GitHub SECRETs) to
239
+ * log into an account on the user's server. Must be SECRETs to stay secure.
240
+ *
241
+ * Variations:
242
+ * I sign into the server with the email "USER1_EMAIL" and the password "USER1_PASSWORD" SECRETs
243
+ * I sign on with the email "USER1_EMAIL" and the password "USER1_PASSWORD" SECRETs
244
+ * I log in with "USER1_EMAIL" and "USER1_PASSWORD"
245
+ */
246
+ return wrapPromiseWithTimeout(
247
+ scope.steps.sign_in( scope, {
248
+ email_secret_name: email,
249
+ password_secret_name: password
250
+ }),
251
+ scope.timeout
252
+ );
253
+ });
254
+
223
255
  // I am using a moble/pc
224
256
  When(/I am using an? (.*)/, async ( device ) => {
225
257
  // Let developer pick mobile device if they want to
@@ -240,7 +272,9 @@ When(/I am using an? (.*)/, async ( device ) => {
240
272
  Given(/the max(?:imum)? sec(?:ond)?s for each step (?:in this scenario )?is (\d+)/i, async ( seconds ) => {
241
273
  /* At any time, set a custom timeout for the scenario. */
242
274
  scope.timeout = parseInt( seconds ) * 1000;
243
- setDefaultTimeout( scope.timeout );
275
+
276
+ // Cucumber timeouts are made custom for each step that needs it
277
+
244
278
  // If there is no page yet, we've ensured custom timeout will still be set
245
279
  // appropriately in the interview loading step
246
280
  if ( scope.page ) { await scope.page.setDefaultTimeout( scope.timeout ); }
@@ -326,7 +360,7 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
326
360
  // Add full used table to report so it can be copied if needed.
327
361
  // TODO: Should we/How do we get this in the final scenario report instead of here?
328
362
  await scope.addToReport( scope, { type: `note`, value: `\n${ ' '.repeat(2) }Rows that got set:` });
329
- await scope.addToReport( scope, { type: `step`, value: `${ ' '.repeat(4) }And I get the question id "${ target_id }" with this data:` });
363
+ await scope.addToReport( scope, { type: `step`, value: `${ ' '.repeat(4) }And I get to the question id "${ target_id }" with this data:` });
330
364
  await scope.addToReport(scope, { type: `row`, value: `${ ' '.repeat(6) }| var | value | trigger |`, });
331
365
  let at_least_one_row_was_NOT_used = false;
332
366
  for ( let row of sorted_rows ) {
@@ -358,41 +392,57 @@ When(/^(?:the user gets)?(?:I get)? to ?(?:the)?(?: ?question)?(?: id)? "([^"]+)
358
392
  //#####################################
359
393
  //#####################################
360
394
 
361
- // Need to see if it's possible to remove the need for this on most occasions
362
- // I wait 1 second
363
- // I wait .5 seconds
364
- When(/I wait (\d*\.?\d+) seconds?/, async (seconds) => { // √
365
- // Should this reset the 'navigated' flag?
366
- // Shoudl any observational things reset the 'navigated' flag?
367
- // Maybe the 'navitaged' flag should only be reset just before trying to
368
- // navigate (after pressing a button)
369
- await scope.afterStep(scope, {waitForTimeout: (parseFloat(seconds) * 1000)});
395
+ When(/I wait (\d*\.?\d+) seconds?/, { timeout: -1 }, async ( seconds ) => {
396
+ /** Wait for a specified amount of time. Can help with some race conditions.
397
+ * We're working on reducing race conditions, but it'll never be perfect.
398
+ *
399
+ * Variations:
400
+ * - I wait 1 second
401
+ * - I wait .5 seconds
402
+ *
403
+ * Questions:
404
+ * - Should this really reset the 'navigated' flag? Should the below
405
+ * return succeed or fail? e.g:
406
+ * I tap to continue; I wait 10 seconds; I arrive at the next page;
407
+ * Maybe the 'navitaged' flag should only be reset just before trying to
408
+ * navigate (after pressing a button)
409
+ */
410
+ return wrapPromiseWithTimeout(
411
+ scope.afterStep(scope, {waitForTimeout: (parseFloat( seconds ) * 1000)}),
412
+ scope.timeout
413
+ );
370
414
  });
371
415
 
372
- Then(/I take a screenshot ?(?:named "([^"]+)")?/, async ( name ) => {
373
- /* Save/download a screenshot. `name` does not have to be unique as
374
- * the filename will be made unique. */
375
- name = name || '';
376
- let safe_path = await scope.getSafeScenarioFilename( scope, { prefix: `screenshot_${ name }` });
377
- await scope.page.screenshot({ path: `./${ safe_path }.jpg`, type: 'jpeg', fullPage: true });
378
- await scope.afterStep(scope);
416
+ Then(/I take a screenshot ?(?:named "([^"]+)")?/, { timeout: -1 }, async ( name ) => {
417
+ /* Download and save a screenshot. `name` does not have to be
418
+ * unique as the filename will be made unique. */
419
+ return wrapPromiseWithTimeout(
420
+ scope.steps.screenshot( scope, name ),
421
+ scope.timeout
422
+ );
379
423
  });
380
424
 
381
425
  /* Here to make writing tests more comfortable. */
382
426
  When("I do nothing", async () => { await scope.afterStep(scope); }); // √
383
427
 
384
- Then('the question id should be {string}', async (question_id) => { // √
428
+ Then('the question id should be {string}', { timeout: -1 }, async ( question_id ) => {
385
429
  /* Looks for a santized version of the question id as it's written
386
430
  * in the .yml. docassemble's way */
387
431
  let { question_has_id, id, id_matches } = await scope.examinePageID( scope, question_id );
388
432
 
389
- // Trying to make helpful error messages
433
+ // No question id exists
390
434
  let no_id_msg = `Did not find any question id.`;
391
- if ( !question_has_id ) { await scope.addToReport( scope, { type: `error`, value: no_id_msg }); }
435
+ if ( !question_has_id ) {
436
+ await scope.addToReport( scope, { type: `error`, value: no_id_msg });
437
+ }
392
438
  expect( question_has_id , no_id_msg ).to.be.true;
393
439
 
394
- let no_match_msg = `The question id was supposed to be "${ question_id }", but it's actually "${ id }".`;
395
- if ( !id_matches ) { await scope.addToReport( scope, { type: `error`, value: no_match_msg }); }
440
+ // Wrong question id
441
+ let no_match_msg = `The question id was supposed to be "${ question_id }"`
442
+ + `, but it's actually "${ id }".`;
443
+ if ( !id_matches ) {
444
+ await scope.addToReport( scope, { type: `error`, value: no_match_msg });
445
+ }
396
446
  expect( id_matches, no_match_msg ).to.be.true;
397
447
 
398
448
  await scope.afterStep(scope);
@@ -477,7 +527,7 @@ Then('an element should have the id {string}', async ( id ) => { // √
477
527
  // await scope.afterStep(scope);
478
528
  // });
479
529
 
480
- // This is phrase is confusing. It sounds like it's going to at least try to continue.
530
+ // This phrase is confusing. It sounds like it's going to at least try to continue.
481
531
  Then(/I (don't|can't|don’t|can’t|cannot|do not) continue/, async (unused) => { // √
482
532
  /* Tests for detection of url change from button or link tap.
483
533
  * Can other things trigger navigation? Re: other inputs things
@@ -573,6 +623,63 @@ Then(/the "([^"]+)" link opens in (a new window|the same window)/, async (linkTe
573
623
  await scope.afterStep(scope);
574
624
  });
575
625
 
626
+ Then(/(?:the )?text(?: in)?(?: the)?(?: JSON)?(?: var)?(?:iable)?(?: )?"([^"]+)" should be/i, async (var_name, expected_value) => {
627
+ /* Check that the text string of the page's JSON var value matches
628
+ * the given text string. Does not currently accept nested values
629
+ * like "foo.bar". The word "text" is required.
630
+ *
631
+ * The format for this step is something like this:
632
+ * ```
633
+ * Then the text in the JSON variable "var_name" should be
634
+ * """
635
+ * A single or
636
+ * multiline string
637
+ * """
638
+ * ```
639
+ *
640
+ * Sentence combinations that will work:
641
+ * `the text in the JSON variable "variable_with_text_value" should be`
642
+ * `the text in the JSON var "variable_with_text_value" should be`
643
+ * `the text in "variable_with_text_value" should be`
644
+ * `text var "variable_with_text_value" should be`
645
+ */
646
+
647
+ // Get JSON variable value
648
+ let { filename, json } = await scope.savePageJSONData( scope );
649
+ let actual_value = json.variables[ var_name ];
650
+
651
+ // Add warning to report if value is not as expected
652
+ if ( actual_value !== expected_value ) {
653
+ await scope.addToReport( scope, {
654
+ type: `error`,
655
+ value: `The variable "${ var_name }" was not equal to the expected `
656
+ + `value on the "${ scope.page_id }" screen. Check the downloads `
657
+ + `file ${ filename } to see the page's JSON variables.`
658
+ });
659
+ }
660
+
661
+ expect( actual_value ).to.equal( expected_value );
662
+
663
+ });
664
+
665
+
666
+ Then(/I get the(?: page's)?(?: JSON)? var(?:iable)?s and val(?:ue)?s/i, { timeout: -1 }, async () => {
667
+ /** Uses https://docassemble.org/docs/functions.html#js_get_interview_variables
668
+ * to get the variable values on the page of the interview and add
669
+ * them to the report.
670
+ *
671
+ * Varations:
672
+ * I get the page's JSON variables and values
673
+ * I get the json vars and vals
674
+ * I get the page's vars and vals
675
+ * I get the vars and vals
676
+ */
677
+ return await wrapPromiseWithTimeout(
678
+ scope.steps.add_json_vars_to_report( scope ),
679
+ scope.timeout
680
+ );
681
+ });
682
+
576
683
  //#####################################
577
684
  //#####################################
578
685
  // Actions
@@ -585,27 +692,20 @@ Then(/the "([^"]+)" link opens in (a new window|the same window)/, async (linkTe
585
692
 
586
693
  // Consider people wanting to use this for an in-interview page
587
694
  // Also consider 'the link url "..." should open...'?
588
- Then(/the "([^"]+)" link opens a working page/, async (linkText) => { // √
589
- let [link] = await scope.page.$x(`//a[contains(text(), "${linkText}")]`);
590
- let prop_obj = await link.getProperty('href');
591
- let actual_url = await prop_obj.jsonValue();
592
-
593
- let linkPage = await scope.browser.newPage();
594
- let response = await linkPage.goto(actual_url, {waitUntil: 'domcontentloaded'});
595
-
596
- let msg = `The "${ linkText }" link is broken.`;
597
- if ( !response.ok() ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
598
- expect( response.ok(), msg ).to.be.true;
599
-
600
- linkPage.close()
601
-
602
- await scope.afterStep(scope);
695
+ Then(/the "([^"]+)" link opens a working page/, { timeout: -1 }, async ( linkText ) => {
696
+ return wrapPromiseWithTimeout(
697
+ scope.steps.link_works( scope, linkText ),
698
+ scope.timeout
699
+ );
603
700
  });
604
701
 
605
- When('I tap to continue', async () => {
702
+ When('I tap to continue', { timeout: -1 }, async () => {
606
703
  // Any selectors for this seem somewhat precarious
607
704
  // No data will cause an automatic continue when `ensure_navigation` is true
608
- await scope.setFields( scope, { var_data: [], ensure_navigation: true });
705
+ return wrapPromiseWithTimeout(
706
+ scope.setFields( scope, { var_data: [], ensure_navigation: true }),
707
+ scope.timeout
708
+ );
609
709
  });
610
710
 
611
711
 
@@ -616,7 +716,7 @@ When('I tap to continue', async () => {
616
716
  // -------------------------------------
617
717
  // --- VARIABLES-BASED STEPS ---
618
718
 
619
- When(/I set the var(?:iable)? "([^"]+)" to "([^"]+)"/, async ( var_name, set_to ) => {
719
+ When(/I set the var(?:iable)? "([^"]+)" to "([^"]+)"/, { timeout: -1 }, async ( var_name, set_to ) => {
620
720
  /* Set any variable (with or without a choice associated with it) to a value. Set:
621
721
  * - Buttons associated with variables
622
722
  * - Dropdowns
@@ -629,60 +729,51 @@ When(/I set the var(?:iable)? "([^"]+)" to "([^"]+)"/, async ( var_name, set_to
629
729
  * 1. I set the var "benefits['SSI']" to "false"
630
730
  * 1. I set the var "rent_interval" to "weekly"
631
731
  */
632
- let var_data = await scope.normalizeTable( scope, {
633
- var_data: [{ var: var_name, value: set_to, trigger: '' }],
634
- });
635
- // Don't continue if the variable doesn't navigate and if this variable cannot be set
636
- // on this page, trigger an error
637
- await scope.setFields( scope, { var_data, ensure_navigation: false, ensure_all_vars_used: true });
732
+ return wrapPromiseWithTimeout(
733
+ scope.steps.set_regular_var( scope, var_name, set_to ),
734
+ scope.timeout
735
+ );
638
736
  });
639
737
 
640
- Then(/I upload "([^"]+)" to "([^"]+)"/, async ( filenames, var_name ) => {
738
+ Then(/I upload "([^"]+)" to "([^"]+)"/, { timeout: -1 }, async ( filenames, var_name ) => {
641
739
  /* Uploads sibling file (docx, jpg, etc) to input var. Can actually use
642
740
  * 'I set the var "" to ""', but this is more explicit if it's desired. */
643
- let var_data = await scope.normalizeTable( scope, {
644
- var_data: [{ var: var_name, value: filenames, trigger: '' }],
645
- });
646
- // Don't continue if the variable doesn't navigate and if this variable cannot be set
647
- // on this page, trigger an error
648
- await scope.setFields( scope, { var_data, ensure_navigation: false, ensure_all_vars_used: true });
741
+ return wrapPromiseWithTimeout(
742
+ scope.steps.set_regular_var( scope, var_name, filenames ),
743
+ scope.timeout
744
+ );
649
745
  });
650
746
 
651
747
 
652
748
  // -------------------------------------
653
749
  // --- OTHER UI STEPS ---
654
750
 
655
- When('I tap the defined text link {string}', async (phrase) => { // hold
656
- /* Not sure what 'defined' means here. Maybe for terms? */
657
- const [link] = await scope.page.$x(`//a[contains(text(), "${phrase}")]`);
658
-
659
- let msg = `The term "${ phrase }" seems to be missing.`;
660
- if ( !link ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
661
- expect( link, msg ).to.exist;
662
-
663
- await link.evaluate( elem => { return elem.click(); });
664
-
665
- await scope.afterStep(scope, {waitForShowIf: true});
751
+ When('I tap the defined text link {string}', { timeout: -1 }, async ( phrase ) => {
752
+ /** Tap a link that doesn't navigate. Depends on the language of the text. */
753
+ return wrapPromiseWithTimeout(
754
+ scope.steps.tap_term( scope, phrase ),
755
+ scope.timeout
756
+ );
666
757
  });
667
758
 
668
- Then('I sign', async () => {
669
- await scope.sign( scope );
670
- await scope.afterStep(scope);
759
+ Then('I sign', { timeout: -1 }, async () => {
760
+ return wrapPromiseWithTimeout(
761
+ scope.steps.sign( scope ),
762
+ scope.timeout
763
+ );
671
764
  });
672
765
 
673
- When(/I download "([^"]+)"$/, async (filename) => {
766
+ When(/I download "([^"]+)"$/, { timeout: -1 }, async ( filename ) => {
674
767
  /* Taps the link that leads to the given filename to trigger downloading.
675
- * and waits till the file has been downloaded before allowing the tests to continue.
676
- WARNING: Cannot download the same file twice in a single scenario. */
677
- let [elem] = await scope.page.$x(`//a[contains(@href, "${ filename }")]`);
678
-
679
- let msg = `"${ filename }" seems to be missing. Cannot find a link to that document.`;
680
- if ( !elem ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
681
- expect( elem, msg ).to.exist;
682
-
683
- scope.toDownload = filename;
684
- await elem.evaluate( elem => { return elem.click(); });
685
- await scope.detectDownloadComplete( scope );
768
+ * and waits till the file has been downloaded before allowing the tests to continue.
769
+ * WARNING: Cannot download the same file twice in a single scenario.
770
+ * WARNING: Must not contain any characters that are totally unique to that
771
+ * page load. Just stick to the name of the file.
772
+ */
773
+ return wrapPromiseWithTimeout(
774
+ scope.steps.download( scope, filename ),
775
+ scope.timeout
776
+ );
686
777
  });
687
778
 
688
779
  //#####################################
@@ -691,7 +782,7 @@ When(/I download "([^"]+)"$/, async (filename) => {
691
782
  //#####################################
692
783
  //#####################################
693
784
 
694
- When(/I set the name of ?(?:the var(?:iable)?)? "([^"]+)" to "([^"]+)"/, async (var_base, name_str) => {
785
+ When(/I set the name of ?(?:the var(?:iable)?)? "([^"]+)" to "([^"]+)"/, { timeout: -1 }, async (var_base, name_str) => {
695
786
  /* Given a variable name and a space-separated string, fills the
696
787
  * appropriate fields on an Assembly Line name question.
697
788
  * It handles any of:
@@ -708,28 +799,14 @@ When(/I set the name of ?(?:the var(?:iable)?)? "([^"]+)" to "([^"]+)"/, async (
708
799
  * 1. I set the name of the var "defendant" to "Dalia Day Defo Jr"
709
800
  * 1. I set the name of the variable "defendant" to "Dalia Day Defo Jr"
710
801
  */
711
- let name_patterns = [
712
- [ `text` ],
713
- [ `first`, `last` ],
714
- [ `first`, `middle`, `last` ],
715
- [ `first`, `middle`, `last`, `suffix` ],
716
- ];
717
-
718
- let name_parts = name_str.split(' ');
719
- let desired_pattern = name_patterns[( name_parts.length - 1 )];
720
-
721
- for ( let parti = 0; parti < name_parts.length; parti++ ) {
722
- let var_name = `${ var_base }.name.${ desired_pattern[ parti ] }`
723
- let value = name_parts[ parti ];
724
- await scope.setVar( scope, var_name, value );
725
- }
726
-
727
- // Continue to next page...?
728
- await scope.afterStep(scope, {waitForShowIf: true});
802
+ return wrapPromiseWithTimeout(
803
+ scope.steps.set_al_name( scope, var_base, name_str ),
804
+ scope.timeout
805
+ );
729
806
  });
730
807
 
731
808
 
732
- When(/I set the address of ?(?:the var(?:iable)?)? "([^"]+)" to "([^"]+)"/, async (var_base, address_str) => {
809
+ When(/I set the address of ?(?:the var(?:iable)?)? "([^"]+)" to "([^"]+)"/, { timeout: -1 }, async (var_base, address_str) => {
733
810
  /* Given a variable name and a four-part address formatted as below,
734
811
  * sets the appropriate Assembly Line address fields.
735
812
  *
@@ -740,24 +817,10 @@ When(/I set the address of ?(?:the var(?:iable)?)? "([^"]+)" to "([^"]+)"/, asyn
740
817
  * 1. When I set the address of the variable "users[0]" to "112 Southampton St., Unit 1, Boston, MA 02118"
741
818
  * 1. When I set the address of the var "users[0]" to "112 Southampton St., Unit 1, Boston, MA 02118"
742
819
  */
743
- // Get all the parts of the address properly separated
744
- // First, split apart each line of the address to [address line 1, address line 2, city, state zip]...
745
- let split = address_str.split(/,\s*/);
746
- // ...then, separate the state and the zip code...
747
- let last_parts = (split.pop()).split(/\s+/);
748
- // ...and add them back in, to end up with [address line 1, address line 2, city, state, zip].
749
- let address_parts = split.concat( last_parts );
750
- let start = `${ var_base }.address.`;
751
-
752
- let index = 0;
753
- await scope.setVar( scope, `${start}address`, address_parts[0] );
754
- // Ensure it's without 'Unit '/'unit '
755
- await scope.setVar( scope, `${start}unit`, (address_parts[1].toLowerCase()).replace(/unit\s*/, '') );
756
- await scope.setVar( scope, `${start}city`, address_parts[2] );
757
- await scope.setVar( scope, `${start}state`, address_parts[3] );
758
- await scope.setVar( scope, `${start}zip`, address_parts[4] );
759
-
760
- await scope.afterStep(scope, {waitForShowIf: true});
820
+ return wrapPromiseWithTimeout(
821
+ scope.steps.set_al_address( scope, var_base, address_str ),
822
+ scope.timeout
823
+ );
761
824
  });
762
825
 
763
826
  // Next macro - download sequence?
@@ -788,7 +851,7 @@ Given(/the final Scenario status should be "(passed|failed)"/i, async ( expected
788
851
  /* WARNING: If a Step will fail, this Step must come first.
789
852
  *
790
853
  * Prepare to test that the Scenario completes with the correct status. */
791
- scope.expected_status = expected_status;
854
+ scope.expected_status = expected_status.toUpperCase();
792
855
  });
793
856
 
794
857
 
@@ -803,18 +866,23 @@ After(async function(scenario) {
803
866
  // Record status before internal report tests can interfere with values
804
867
  await scope.setReportScenarioStatus( scope, { status: scenario.result.status });
805
868
 
806
- if (scenario.result.status !== `passed`) {
869
+ if (scenario.result.status !== `PASSED`) {
807
870
  if ( env_vars.DEBUG ) {
808
- // When debugging locally, pause to allow dev to examine the page
809
- console.log( `Error occurred while running test` );
810
- await scope.page.waitFor( 60 * 1000 );
871
+ console.log( `Non-passed status occurred while running test:`, scenario.result.status );
872
+ // If there's a page visible, give us time to examine it
873
+ if ( scope.page ) { await scope.page.waitForTimeout( 60 * 1000 ); }
811
874
  }
812
875
 
813
876
  await scope.addToReport(scope, { type: `outcome`, value: `**-- Scenario Failed --**` });
814
- // Save/download a picture of the screen during the error
815
- let safe_filename = await scope.getSafeScenarioFilename( scope, { prefix: `error` });
816
- await scope.page.screenshot({ path: `${ safe_filename }.jpg`, type: `jpeg`, fullPage: true });
877
+ if ( scope.page ) {
878
+ // Save/download a picture of the screen that's showing during the unexpected status
879
+ let safe_filename = await scope.getSafeScenarioFilename( scope, { prefix: `error` });
880
+ await scope.page.screenshot({ path: `${ safe_filename }.jpg`, type: `jpeg`, fullPage: true });
881
+ }
882
+ } // Ends if not passed
817
883
 
884
+ if (scenario.result.status === `FAILED`) {
885
+ await scope.addToReport(scope, { type: `outcome`, value: `**-- Scenario Failed --**` });
818
886
  }
819
887
 
820
888
  let original_scenario_status = scenario.result.status;
@@ -831,7 +899,7 @@ After(async function(scenario) {
831
899
  // Maybe this should be in `Before`, but it would be so out of context there...
832
900
  scope.expected_in_report = [];
833
901
  let all_were_included = await scope.reportIncludesAllExpected( scope, { expected });
834
- if ( all_were_included ) { scenario.result.status = `passed`; }
902
+ if ( all_were_included ) { scenario.result.status = `PASSED`; }
835
903
  }
836
904
 
837
905
  // If there is a page open, then sign out and close it
@@ -843,23 +911,26 @@ After(async function(scenario) {
843
911
  try {
844
912
  await scope.page.goto(`${ env_vars.BASE_URL }/user/sign-out`, {waitUntil: `domcontentloaded`});
845
913
  } catch ( err ) {
846
- await scope.addToReport(scope, {
847
- type: `warning`,
848
- value: `Trying to reset for the next test a second time because the first time failed.`
849
- });
914
+ if ( env_vars.DEBUG ) {
915
+ console.log( `Trying to reset for the next test a second time because the first time failed.` );
916
+ }
917
+
850
918
  try {
851
919
  await scope.page.goto(`${ env_vars.BASE_URL }/user/sign-out`, {waitUntil: `domcontentloaded`});
920
+
852
921
  } catch ( err ) {
853
922
  await scope.addToReport(scope, {
854
923
  type: `warning`,
855
- value: `This test ${ original_scenario_status }, but also there was an error while trying to reset for the next test. Re-running until you don't get this error would probably be a good idea.`
924
+ value: `This test ${ original_scenario_status }, but also there `
925
+ + `was an error while trying to reset for the next test. You can try `
926
+ + `re-running the test or checking your server.`
856
927
  });
857
928
  }
858
929
  }
859
930
 
860
931
  await scope.page.close();
861
932
  scope.page = null;
862
- }
933
+ } // ends if scope.page
863
934
  });
864
935
 
865
936
  AfterAll(async function() {