Skip to content

Commit

Permalink
Allow a developer to wait as a first Step v4. #387. Add test.
Browse files Browse the repository at this point in the history
Will be able to close once we've added this as an establishing
step (in addition to it being a regular step).

Also, generally adds safety measures for when page does not exist.
  • Loading branch information
plocket committed Feb 8, 2022
1 parent 4cbce8c commit 09704e8
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Format:

### Changed
- Use API key to access da server, create projects, pull code, delete projects, and check for server restart.
- Allow a developer to wait as a first Step. See #387.

<!-- ## [3.0.1-peer-deps.1] - 2021-12-07 -->
### Removed
Expand Down
58 changes: 31 additions & 27 deletions lib/scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ const { expect } = require('chai');
const cheerio = require('cheerio');
const path = require('path');
const fs = require('fs');

const safe_filename = require("sanitize-filename");

// Ours
const env_vars = require('./utils/env_vars');
const time = require('./utils/time');


let ordinal_to_integer = {
Expand Down Expand Up @@ -207,45 +209,47 @@ module.exports = {
// If something might be hidden or shown, wait extra time to let it hide or show
// I think `.$()` does not use a timeout
let showif = await scope.page.$('.dashowif');
if ( showif ) { await scope.page.waitFor( scope.showif_timeout ); }
if ( showif ) { await time.waitForTimeout( scope.showif_timeout ); }
},

afterStep: async function afterStep(scope, options = {waitForShowIf: false, navigated: false, waitForTimeout: 0}) {
/* Common things to do after a step, including take care of errors.
* TODO: discuss splitting into `afterField` and `afterStep` or something. Or just change name
* TODO: Update to latest cucumberjs, which as `AfterStep` */

// Use a while loop until the below does not error with `Error: Execution context was destroyed`
// Temporary fix for race condition. See https://github.com/plocket/docassemble-cucumber/issues/190
let error_id_elem;
while ( true ) {
try {
// Waiting for `page` to exist. '#da-retry' does not need to exist, it's incidental at this point.
// If this doesn't throw an error, then the page exists and the result is valid.
error_id_elem = await scope.page.$('#da-retry');
break;
} catch ( err ) {}
}
if ( scope.page ) {
// Use a while loop until the below does not error with `Error: Execution context was destroyed`
// Temporary fix for race condition. See https://github.com/plocket/docassemble-cucumber/issues/190
let error_id_elem;
while ( true ) {
try {
// Waiting for `page` to exist. '#da-retry' does not need to exist, it's incidental at this point.
// If this doesn't throw an error, then the page exists and the result is valid.
error_id_elem = await scope.page.$('#da-retry');
break;
} catch ( err ) {}
}

// If there's a system error, throw it
if ( error_id_elem ) {
let error_elem = await scope.page.$('blockquote')
let error_text_prop = await error_elem.getProperty( 'textContent' );
let system_error_text = await error_text_prop.jsonValue();
// If there's a system error, throw it
if ( error_id_elem ) {
let error_elem = await scope.page.$('blockquote')
let error_text_prop = await error_elem.getProperty( 'textContent' );
let system_error_text = await error_text_prop.jsonValue();

await scope.addToReport(scope, { type: `error`, value: system_error_text });
throw system_error_text;
};
await scope.addToReport(scope, { type: `error`, value: system_error_text });
throw system_error_text;
};

// Wait for elements that might get revealed
if ( options.waitForShowIf ) { await scope.waitForShowIf( scope ); }
} // ends if scope.page

// RESET
// Reset navigation (TODO: discuss use for observational steps)
if ( options.navigated ) { scope.navigated = true; } // Consider `daPageLoad` event
else { scope.navigated = false; }

// WAITING
if ( options.waitForShowIf ) { await scope.waitForShowIf(scope); }
// Plain old waiting. Yeah, I abstracted it into here so only
// one thing was being called at the end
if ( options.waitForTimeout ) { await scope.page.waitFor( options.waitForTimeout ); }
// Plain old waiting. Here to avoid doing this in each individual place
if ( options.waitForTimeout ) { await time.waitForTimeout( options.waitForTimeout ); }
}, // Ends afterStep

tapElement: async function tapElement(scope, elem) {
Expand Down
15 changes: 10 additions & 5 deletions lib/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ const { expect } = require('chai');
const puppeteer = require('puppeteer');
const { v4: uuidv4 } = require('uuid');

// Ours
const scope = require('./scope');
const env_vars = require('./utils/env_vars');
const time = require('./utils/time');

/* Of Note:
- We're using `*=` because sometimes da text has funny characters in it that are hard to anticipate
Expand Down Expand Up @@ -870,13 +872,16 @@ After(async function(scenario) {
if ( env_vars.DEBUG ) {
// When debugging locally, pause to allow dev to examine the page
console.log( `Error occurred while running test` );
await scope.page.waitFor( 60 * 1000 );
// If there's a page visible, give us time to examine it
if ( scope.page ) { await time.waitForTimeout( 60 * 1000 ); }
}

await scope.addToReport(scope, { type: `outcome`, value: `**-- Scenario Failed --**` });
// Save/download a picture of the screen during the error
let safe_filename = await scope.getSafeScenarioFilename( scope, { prefix: `error` });
await scope.page.screenshot({ path: `${ safe_filename }.jpg`, type: `jpeg`, fullPage: true });
if ( scope.page ) {
// Save/download a picture of the screen that's showing during the error
let safe_filename = await scope.getSafeScenarioFilename( scope, { prefix: `error` });
await scope.page.screenshot({ path: `${ safe_filename }.jpg`, type: `jpeg`, fullPage: true });
}

}

Expand Down Expand Up @@ -922,7 +927,7 @@ After(async function(scenario) {

await scope.page.close();
scope.page = null;
}
} // ends if scope.page
});

AfterAll(async function() {
Expand Down
4 changes: 4 additions & 0 deletions tests/features/establishing_steps.feature
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ Scenario: I am able to set a custom wait time before an interview has been loade
Scenario: Interview name includes url args
  Given I start the interview at "url_args.yml&from=theinternets&random=zoo"
  Then I should see the phrase "zoo"

@slow @e4 @wait_first
Scenario: I can wait as a first step in a test
Given I wait 1 second

0 comments on commit 09704e8

Please sign in to comment.