nightwatch icon indicating copy to clipboard operation
nightwatch copied to clipboard

a failing async test causes any browser object calls to fail in the afterEach step

Open chris-jackson-actionqa opened this issue 2 years ago • 0 comments

Describe the bug

When using async for the test, the afterEach hook can hang if that tests fails. It only hangs if you invoke the browser object. In my example below, I'm just pausing for one second.

If you remove the async from the test, the afterEach hook runs fine. If you remove the browser.pause call from the afterEach, it also runs fine. If the test passes, then it runs fine.

When running the test, you'll see that the console.log('Made it here') never gets invoked. Then the afterEach call will time out.

Sample test

sampleTest.js

// Please add the sample test here

describe('repro afterEach', () => {
  afterEach(async (browser, done) => {
    await browser.pause(1_000);
    console.log('made it here');
    browser.end(done);
  });

  it('fail on purpose', async (browser) => {
    browser.url('http://example.com/');
    browser.expect.element('body > div > h1').text.to.contain('blah');
  });
});

Run with command

$ nightwatch test/sampleTest.js -e chrome

Verbose output

debug.log

<!-- Include the verbose output, if possible (run nightwatch with `--verbose` argument) -->

❯ npx nightwatch -e chrome --verbose

[repro afterEach] Test Suite
────────────────────────────────────────────────────────
⠋ Starting ChromeDriver on port 9515...
 Starting ChromeDriver with server_path=/Users/cjackson/Documents/nwrepro/node_modules/chromedriver/lib/chromedriver/chromedriver...
   Request POST /session  
   {
     desiredCapabilities: {
       browserName: 'chrome',
       'goog:chromeOptions': { w3c: true, args: [] },
       name: 'repro afterEach'
     },
     capabilities: {
       alwaysMatch: {
         browserName: 'chrome',
         'goog:chromeOptions': { w3c: true, args: [] }
       }
     }
⠦ Starting ChromeDriver on port 9515...
   Response 200 POST /session (1316ms)
   {
     value: {
       capabilities: {
         acceptInsecureCerts: false,
         browserName: 'chrome',
         browserVersion: '101.0.4951.64',
         chrome: {
           chromedriverVersion: '101.0.4951.41 (93c720db8323b3ec10d056025ab95c23a31997c9-refs/branch-heads/4951@{#904})',
           userDataDir: '/var/folders/91/l15sfgjs7cg2d0htqwh6h93w0000gn/T/.com.google.Chrome.uQRJ6L'
         },
         'goog:chromeOptions': { debuggerAddress: 'localhost:52475' },
         networkConnectionEnabled: false,
         pageLoadStrategy: 'normal',
         platformName: 'mac os x',
         proxy: {},
         setWindowRect: true,
         strictFileInteractability: false,
         timeouts: { implicit: 0, pageLoad: 300000, script: 30000 },
         unhandledPromptBehavior: 'dismiss and notify',
         'webauthn:extension:credBlob': true,
         'webauthn:extension:largeBlob': true,
         'webauthn:virtualAuthenticators': true
       },
       sessionId: '370fe688bdb4bc37e3c0514f4418a4a3'
     }
ℹ Connected to ChromeDriver on port 9515 (1383ms).
  Using: chrome (101.0.4951.64) on MAC OS X.

 Received session with ID: 370fe688bdb4bc37e3c0514f4418a4a3

 → Running [before]:
 → Completed [before].



  Running fail on purpose:
───────────────────────────────────────────────────────────────────────────────────────────────────
 → Running [beforeEach]:
 → Completed [beforeEach].
 → Running [afterEach]:
 
 → Running command: url ('http://example.com/')
  ⠋ Loading url: http://example.com/
   Request POST /session/370fe688bdb4bc37e3c0514f4418a4a3/url  
  ⠴ Loading url: http://example.com/
   Response 200 POST /session/370fe688bdb4bc37e3c0514f4418a4a3/url (415ms)
  ℹ Loaded url http://example.com/ in 416ms
  → Completed command: url ('http://example.com/') (417ms)
 
 → Running command: expect.element ('body > div > h1')
   Request POST /session/370fe688bdb4bc37e3c0514f4418a4a3/elements  
   { using: 'css selector', value: 'body > div > h1' }
   Response 200 POST /session/370fe688bdb4bc37e3c0514f4418a4a3/elements (16ms)
   {
     value: [
       {
         'element-6066-11e4-a52e-4f735466cecf': '10746a0c-62f9-45b2-9464-bff810997d66'
       }
     ]
  }
   Request GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text  

   Response 200 GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text (19ms)
   { value: 'Example Domain' }
   Request GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text  

   Response 200 GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text (8ms)
   { value: 'Example Domain' }
   Request GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text  

   Response 200 GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text (8ms)
   { value: 'Example Domain' }
   Request GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text  

   Response 200 GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text (6ms)
   { value: 'Example Domain' }
   Request GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text  

   Response 200 GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text (8ms)
   { value: 'Example Domain' }
   Request GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text  

   Response 200 GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text (9ms)
   { value: 'Example Domain' }
   Request GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text  

   Response 200 GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text (8ms)
   { value: 'Example Domain' }
   Request GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text  

   Response 200 GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text (7ms)
   { value: 'Example Domain' }
   Request GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text  

   Response 200 GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text (7ms)
   { value: 'Example Domain' }
   Request GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text  

   Response 200 GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text (8ms)
   { value: 'Example Domain' }
   Request GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text  

   Response 200 GET /session/370fe688bdb4bc37e3c0514f4418a4a3/element/10746a0c-62f9-45b2-9464-bff810997d66/text (8ms)
   { value: 'Example Domain' }
  ✖ Expected element <body > div > h1> text to contain: "blah" - expected "contain 'blah'" but got: "Example Domain" (5152ms)
    at DescribeInstance.<anonymous> (/Users/cjackson/Documents/nwrepro/tests/reproAfterEach.js:9:20) 

  → Completed command: expect.element ('body > div > h1') (5153ms)
  TimeoutError: done() callback timeout of 20000ms was reached while executing "afterEach". Make sure to call the done() callback when the operation finishes.
   
 → Running [after]:
 → Completed [after].
 
 → Running command: end ()
 
 → Running command: session ('delete', [Function])
   Request DELETE /session/370fe688bdb4bc37e3c0514f4418a4a3  

   Response 200 DELETE /session/370fe688bdb4bc37e3c0514f4418a4a3 (53ms)
   { value: null }
  → Completed command: session ('delete', [Function]) (54ms)
 Wrote log file to: /Users/cjackson/Documents/nwrepro/logs/reproAfterEach_chromedriver.log.
  → Completed command: end () (57ms)

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────

  TEST FAILURE (21.57s): 
   - 1 error during execution; 
   - 1 assertions failed; 0 passed



 TimeoutError: done() callback timeout of 20000ms was reached while executing "afterEach". Make sure to call the done() callback when the operation finishes.
   

 ✖ 1) reproAfterEach
 – fail on purpose 
   Expected element <body > div > h1> text to contain: "blah" - expected "contain 'blah'" but got: "Example Domain" (5152ms)
       at DescribeInstance.<anonymous> (/Users/cjackson/Documents/nwrepro/tests/reproAfterEach.js:9:20)

 ChromeDriver process closed.
 Wrote report file to: tests_output/CHROME_101.0.4951.64__reproAfterEach.xml.

Configuration

nightwatch.json

//
// Refer to the online docs for more details:
// https://nightwatchjs.org/gettingstarted/configuration/
//
//  _   _  _         _      _                     _          _
// | \ | |(_)       | |    | |                   | |        | |
// |  \| | _   __ _ | |__  | |_ __      __  __ _ | |_   ___ | |__
// | . ` || | / _` || '_ \ | __|\ \ /\ / / / _` || __| / __|| '_ \
// | |\  || || (_| || | | || |_  \ V  V / | (_| || |_ | (__ | | | |
// \_| \_/|_| \__, ||_| |_| \__|  \_/\_/   \__,_| \__| \___||_| |_|
//             __/ |
//            |___/
//

module.exports = {
  // An array of folders (excluding subfolders) where your tests are located;
  // if this is not specified, the test source must be passed as the second argument to the test runner.
  src_folders: ['tests'],

  // See https://nightwatchjs.org/guide/working-with-page-objects/using-page-objects.html
  page_objects_path: ['node_modules/nightwatch/examples/pages/'],

  // See https://nightwatchjs.org/guide/extending-nightwatch/custom-commands.html
  custom_commands_path: ['node_modules/nightwatch/examples/custom-commands/'],

  // See https://nightwatchjs.org/guide/extending-nightwatch/custom-assertions.html
  custom_assertions_path: '',

  // See https://nightwatchjs.org/guide/extending-nightwatch/plugin-api.html
  plugins: [],

  // See https://nightwatchjs.org/guide/#external-globals
  globals_path: '',

  webdriver: {},

  test_settings: {
    default: {
      disable_error_log: false,
      launch_url: 'https://nightwatchjs.org',

      screenshots: {
        enabled: false,
        path: 'screens',
        on_failure: true,
      },

      desiredCapabilities: {
        browserName: 'firefox',
      },

      webdriver: {
        start_process: true,
        server_path: '',
      },
    },

    safari: {
      desiredCapabilities: {
        browserName: 'safari',
        alwaysMatch: {
          acceptInsecureCerts: false,
        },
      },
      webdriver: {
        start_process: true,
        server_path: '',
      },
    },

    firefox: {
      desiredCapabilities: {
        browserName: 'firefox',
        alwaysMatch: {
          acceptInsecureCerts: true,
          'moz:firefoxOptions': {
            args: [
              // '-headless',
              // '-verbose'
            ],
          },
        },
      },
      webdriver: {
        start_process: true,
        server_path: '',
        cli_args: [
          // very verbose geckodriver logs
          // '-vv'
        ],
      },
    },

    chrome: {
      desiredCapabilities: {
        browserName: 'chrome',
        'goog:chromeOptions': {
          // More info on Chromedriver: https://sites.google.com/a/chromium.org/chromedriver/
          //
          // w3c:false tells Chromedriver to run using the legacy JSONWire protocol (not required in Chrome 78)
          w3c: true,
          args: [
            //'--no-sandbox',
            //'--ignore-certificate-errors',
            //'--allow-insecure-localhost',
            //'--headless'
          ],
        },
      },

      webdriver: {
        start_process: true,
        server_path: '',
        cli_args: [
          // --verbose
        ],
      },
    },

    edge: {
      desiredCapabilities: {
        browserName: 'MicrosoftEdge',
        'ms:edgeOptions': {
          w3c: true,
          // More info on EdgeDriver: https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/capabilities-edge-options
          args: [
            //'--headless'
          ],
        },
      },

      webdriver: {
        start_process: true,
        // Download msedgedriver from https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/
        //  and set the location below:
        server_path: '',
        cli_args: [
          // --verbose
        ],
      },
    },

    //////////////////////////////////////////////////////////////////////////////////
    // Configuration for when using cucumber-js (https://cucumber.io)                |
    //                                                                               |
    // It uses the bundled examples inside the nightwatch examples folder; feel free |
    // to adapt this to your own project needs                                       |
    //////////////////////////////////////////////////////////////////////////////////
    'cucumber-js': {
      src_folders: ['examples/cucumber-js/features/step_definitions'],

      test_runner: {
        // set cucumber as the runner
        type: 'cucumber',

        // define cucumber specific options
        options: {
          //set the feature path
          feature_path:
            'node_modules/nightwatch/examples/cucumber-js/*/*.feature',

          // start the webdriver session automatically (enabled by default)
          // auto_start_session: true

          // use parallel execution in Cucumber
          // parallel: 2 // set number of workers to use (can also be defined in the cli as --parallel 2
        },
      },
    },

    //////////////////////////////////////////////////////////////////////////////////
    // Configuration for when using the browserstack.com cloud service               |
    //                                                                               |
    // Please set the username and access key by setting the environment variables:  |
    // - BROWSERSTACK_USERNAME                                                       |
    // - BROWSERSTACK_ACCESS_KEY                                                     |
    // .env files are supported                                                      |
    //////////////////////////////////////////////////////////////////////////////////
    browserstack: {
      selenium: {
        host: 'hub.browserstack.com',
        port: 443,
      },
      // More info on configuring capabilities can be found on:
      // https://www.browserstack.com/automate/capabilities?tag=selenium-4
      desiredCapabilities: {
        'bstack:options': {
          userName: '${BROWSERSTACK_USERNAME}',
          accessKey: '${BROWSERSTACK_ACCESS_KEY}',
        },
      },

      disable_error_log: true,
      webdriver: {
        timeout_options: {
          timeout: 15000,
          retry_attempts: 3,
        },
        keep_alive: true,
        start_process: false,
      },
    },

    'browserstack.local': {
      extends: 'browserstack',
      desiredCapabilities: {
        'browserstack.local': true,
      },
    },

    'browserstack.chrome': {
      extends: 'browserstack',
      desiredCapabilities: {
        browserName: 'chrome',
        chromeOptions: {
          w3c: true,
        },
      },
    },

    'browserstack.firefox': {
      extends: 'browserstack',
      desiredCapabilities: {
        browserName: 'firefox',
      },
    },

    'browserstack.ie': {
      extends: 'browserstack',
      desiredCapabilities: {
        browserName: 'internet explorer',
        browserVersion: '11.0',
      },
    },

    'browserstack.safari': {
      extends: 'browserstack',
      desiredCapabilities: {
        browserName: 'safari',
      },
    },

    'browserstack.local_chrome': {
      extends: 'browserstack.local',
      desiredCapabilities: {
        browserName: 'chrome',
      },
    },

    'browserstack.local_firefox': {
      extends: 'browserstack.local',
      desiredCapabilities: {
        browserName: 'firefox',
      },
    },
    //////////////////////////////////////////////////////////////////////////////////
    // Configuration for when using the SauceLabs cloud service                      |
    //                                                                               |
    // Please set the username and access key by setting the environment variables:  |
    // - SAUCE_USERNAME                                                              |
    // - SAUCE_ACCESS_KEY                                                            |
    //////////////////////////////////////////////////////////////////////////////////
    saucelabs: {
      selenium: {
        host: 'ondemand.saucelabs.com',
        port: 443,
      },
      // More info on configuring capabilities can be found on:
      // https://wiki.saucelabs.com/display/DOCS/Test+Configuration+Options
      desiredCapabilities: {
        'sauce:options': {
          username: '${SAUCE_USERNAME}',
          accessKey: '${SAUCE_ACCESS_KEY}',
          // https://docs.saucelabs.com/dev/cli/sauce-connect-proxy/#--region
          // region: 'us-west-1'
          // https://docs.saucelabs.com/dev/test-configuration-options/#tunnelidentifier
          // parentTunnel: '',
          // tunnelIdentifier: '',
        },
      },
      disable_error_log: false,
      webdriver: {
        start_process: false,
      },
    },
    'saucelabs.chrome': {
      extends: 'saucelabs',
      desiredCapabilities: {
        browserName: 'chrome',
        screenResolution: '1280x1024',
        browserVersion: 'latest',
        javascriptEnabled: true,
        acceptSslCerts: true,
        timeZone: 'London',
        chromeOptions: {
          w3c: true,
        },
      },
    },
    'saucelabs.firefox': {
      extends: 'saucelabs',
      desiredCapabilities: {
        browserName: 'firefox',
        screenResolution: '1280x1024',
        browserVersion: 'latest',
        javascriptEnabled: true,
        acceptSslCerts: true,
        timeZone: 'London',
      },
    },
    //////////////////////////////////////////////////////////////////////////////////
    // Configuration for when using the Selenium service, either locally or remote,  |
    //  like Selenium Grid                                                           |
    //////////////////////////////////////////////////////////////////////////////////
    selenium_server: {
      // Selenium Server is running locally and is managed by Nightwatch
      // Install the NPM package @nightwatch/selenium-server or download the selenium server jar file from https://github.com/SeleniumHQ/selenium/releases/, e.g.: selenium-server-4.1.1.jar
      selenium: {
        start_process: true,
        port: 4444,
        server_path: '', // Leave empty if @nightwatch/selenium-server is installed
        command: 'standalone', // Selenium 4 only
        cli_args: {
          //'webdriver.gecko.driver': '',
          //'webdriver.chrome.driver': ''
        },
      },
      webdriver: {
        start_process: false,
        default_path_prefix: '/wd/hub',
      },
    },

    'selenium.chrome': {
      extends: 'selenium_server',
      desiredCapabilities: {
        browserName: 'chrome',
        chromeOptions: {
          w3c: true,
        },
      },
    },

    'selenium.firefox': {
      extends: 'selenium_server',
      desiredCapabilities: {
        browserName: 'firefox',
        'moz:firefoxOptions': {
          args: [
            // '-headless',
            // '-verbose'
          ],
        },
      },
    },
  },
};

Your Environment

Executable Version
nightwatch --version 2.15
npm --version 8.1.2
yarn --version 1.22.17
node --version v16.13.1
Browser driver Version
chromedriver 101.0.4951.41
OS Version
macOS Big Sur 11.6.5

chris-jackson-actionqa avatar May 13 '22 00:05 chris-jackson-actionqa

To be tested with https://github.com/nightwatchjs/nightwatch/pull/3328

AutomatedTester avatar Aug 18 '22 09:08 AutomatedTester

Removing async will fix the issue as I can see it's not required. You can either return a promise or use await both will resolve the issue.

  it('fail on purpose',  async function() {
    browser.url('http://example.com/');
    await browser.expect.element('body > div > h1').text.to.contain('blah');
  });
  it('fail on purpose', async function() {
    browser.url('http://example.com/');
    return browser.expect.element('body > div > h1').text.to.contain('blah');
  });

gravityvi avatar Aug 18 '22 13:08 gravityvi