Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/testsuite/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ class GlobalsContext extends Context {
args[0] = client.api;
}

// TODO: implement this PR for global hooks as well:
// https://github.com/nightwatchjs/nightwatch/pull/4325
// (this.currentRunnable is not available here because it is never
// set for the GlobalsContext).
return this.module[fnName].apply(this.module, args);
}

Expand Down
33 changes: 31 additions & 2 deletions lib/testsuite/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class TestSuite {
this.mochaContext = new Promise(resolve => {
this.mochaContextResolve = resolve;
});

this.__waitForQueueExecStart = {};
}

get api() {
Expand Down Expand Up @@ -83,6 +85,10 @@ class TestSuite {
return definedValue === undefined ? this.settings.end_session_on_fail : definedValue;
}

get waitForQueueExecStart() {
return this.__waitForQueueExecStart;
}

get isES6Async() {
return this.client && (this.client.isES6AsyncTestcase || this.client.isES6AsyncTestHook);
}
Expand Down Expand Up @@ -468,6 +474,10 @@ class TestSuite {
}

return this.handleRunnable(hookName, () => {
if (this.context instanceof Context) {
this.waitForQueueExecStart.enabled = this.context.isES6Async(hookName);
}

return this.hooks[hookName].run(this.client);
}).then(result => {
if (this.context.hasHook(hookName)) {
Expand Down Expand Up @@ -498,6 +508,18 @@ class TestSuite {
this.client.reporter.setCurrentSection({testName: `__global_${hookName}_hook`});

return this.handleRunnable(hookName, async () => {
// for global hooks, always wait a few milliseconds for the queue to start executing
// for both sync and async syntax. This is done as a temporary fix for the issue:
// https://github.com/nightwatchjs/nightwatch/issues/3847#issuecomment-1937787866
//
// Once the above issue is fixed, the true/false value below shouldn't matter anymore
// because the promise returned by `this.runFn` in `runnable.js` will be resolved only
// after the `queue:finished` event is emitted (for both sync and async).
// Also, even if it did matter, it would be a bit difficult to determine the sync/async
// nature of the global hooks defined in plugins and then set the value accordingly. So,
// it's better to always wait keep it to true for global hooks (if no performance issues).
this.waitForQueueExecStart.enabled = true;

if (this.globalsInstance) {
await this.globalsInstance.runPluginHook(hookName, [this.settings]);
}
Expand Down Expand Up @@ -836,7 +858,13 @@ class TestSuite {
testcase: this.reporter.testResults.currentTestName
});

return this.handleRunnable(this.testcase.testName, () => this.testcase.run(this.client));
return this.handleRunnable(this.testcase.testName, () => {
if (this.context instanceof Context) {
this.waitForQueueExecStart.enabled = this.context.isES6Async(this.testcase.testName);
}

return this.testcase.run(this.client);
});
})
.catch(err => {
return err;
Expand Down Expand Up @@ -987,7 +1015,8 @@ class TestSuite {

async executeRunnable(name, fn) {
this.currentRunnable = new Runnable(name, fn, {
isES6Async: this.isES6Async
isES6Async: this.isES6Async,
waitForQueueExecStart: this.waitForQueueExecStart
});

if (!this.context) {
Expand Down
20 changes: 19 additions & 1 deletion lib/testsuite/runnable.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class Runnable {
};

this.isES6Async = opts.isES6Async;
this.waitForQueueExecStart = opts.waitForQueueExecStart;

this.createPromise();
this.setRunFn(runFn);
}
Expand Down Expand Up @@ -145,7 +147,23 @@ class Runnable {
});
};

if (this.isES6Async) {
// `this.waitForQueueExecStart` is used to wait for a few milliseconds before
// calling `deferredFn` to give the queue a chance to start executing.
//
// Only useful for async TC/hook functions because for sync TCs we never reach here
// (`this.runFn` does not return Promise) and for sync hooks we only resolve the promise
// returned by `this.runFn` once the `queue:finished` event is emitted.
//
// For global hooks, the promise returned by `this.runFn` is always resolved immediately
// for both sync and async hooks, which leads to the below specified issue:
// https://github.com/nightwatchjs/nightwatch/issues/3847#issuecomment-1937787866
// So, as a workaround, we always wait for the queue to start executing for global hooks.
//
// `this.isES6Async` below gives stale data -- it gives the state of the
// previous test case/hook that was executed and not for the current one
// but keeping it anyways for backward compatibility (if we were waiting extra
// for some tc/hooks, we can continue to do so).
if (this.waitForQueueExecStart?.enabled || this.isES6Async) {
// the timeout is needed for situations where there is an async function without any await commands
setTimeout(() => deferredFn(), 20);

Expand Down
11 changes: 6 additions & 5 deletions test/src/runner/testRunnerEs6Async.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('testRunner ES6 Async', function () {


it('test Runner with ES6 fluent api basic sample', function() {
let testsPath = path.join(__dirname, '../../sampletests/es6await/basicSampleTest.js');
const testsPath = path.join(__dirname, '../../sampletests/es6await/basicSampleTest.js');
MockServer.addMock({
url: '/wd/hub/session/1352110219202/cookie/test_cookie',
method: 'GET',
Expand Down Expand Up @@ -58,7 +58,7 @@ describe('testRunner ES6 Async', function () {
})
}, false, true);

let globals = {
const globals = {
waitForConditionPollInterval: 150,
waitForConditionTimeout: 100,
retryAssertionTimeout: 150,
Expand All @@ -81,7 +81,7 @@ describe('testRunner ES6 Async', function () {
});

it('test Runner with ES6 async/await tests basic sample', function () {
let testsPath = path.join(__dirname, '../../sampletests/es6await/selenium');
const testsPath = path.join(__dirname, '../../sampletests/es6await/selenium');
MockServer.addMock({
url: '/wd/hub/session/1352110219202/cookie',
method: 'GET',
Expand All @@ -100,7 +100,7 @@ describe('testRunner ES6 Async', function () {
})
}, true);

let globals = {
const globals = {
waitForConditionPollInterval: 50,

reporter(results) {
Expand Down Expand Up @@ -309,7 +309,8 @@ describe('testRunner ES6 Async', function () {
};

return runTests(testsPath, settings({
globals
globals,
skip_testcases_on_fail: false // true by default
}));
});

Expand Down