Skip to content

Commit

Permalink
Fix 'Testcafe hungs if the window closed immediately after opening' (c…
Browse files Browse the repository at this point in the history
…lose #3762) (#4786)
  • Loading branch information
miherlosev authored Feb 25, 2020
1 parent a37788a commit 32005b2
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 5 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
"source-map-support": "^0.5.16",
"strip-bom": "^2.0.0",
"testcafe-browser-tools": "2.0.9",
"testcafe-hammerhead": "16.1.2",
"testcafe-hammerhead": "16.1.3",
"testcafe-legacy-api": "3.1.11",
"testcafe-reporter-json": "^2.1.0",
"testcafe-reporter-list": "^2.1.0",
Expand Down
22 changes: 20 additions & 2 deletions src/client/driver/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ import {
UncaughtErrorInCustomClientScriptLoadedFromModule,
ChildWindowIsNotLoadedError,
CannotSwitchToWindowError,
CloseChildWindowError
CloseChildWindowError,
ChildWindowClosedBeforeSwitchingError
} from '../../errors/test-run';

import BrowserConsoleMessages from '../../test-run/browser-console-messages';
Expand Down Expand Up @@ -585,6 +586,16 @@ export default class Driver extends serviceUtils.EventEmitter {
return this._createWaitForEventPromise(EMPTY_COMMAND_EVENT, EMPTY_COMMAND_EVENT_WAIT_TIMEOUT);
}

_abortSwitchingToChildWindowIfItClosed () {
if (!this.activeChildWindowDriverLink.driverWindow.closed)
return;

arrayUtils.remove(this.childWindowDriverLinks, this.activeChildWindowDriverLink);
this.activeChildWindowDriverLink = null;

throw new ChildWindowClosedBeforeSwitchingError();
}

_switchToChildWindow (selector) {
this.contextStorage.setItem(this.PENDING_WINDOW_SWITCHING_FLAG, true);

Expand All @@ -601,16 +612,23 @@ export default class Driver extends serviceUtils.EventEmitter {
return this._waitForEmptyCommand();
})
.then(() => {
this._abortSwitchingToChildWindowIfItClosed();
this._stopInternal();

return this.activeChildWindowDriverLink.setAsMaster();
})
.then(() => {
this.contextStorage.setItem(this.PENDING_WINDOW_SWITCHING_FLAG, false);
})
.catch(() => {
.catch(err => {
this.contextStorage.setItem(this.PENDING_WINDOW_SWITCHING_FLAG, false);

if (err instanceof ChildWindowClosedBeforeSwitchingError) {
this._onReady(new DriverStatus());

return;
}

this._onReady(new DriverStatus({
isCommandResult: true,
executionError: new CannotSwitchToWindowError()
Expand Down
6 changes: 6 additions & 0 deletions src/errors/test-run/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -618,3 +618,9 @@ export class RequestHookNotImplementedMethodError extends TestRunErrorBase {
}
}

export class ChildWindowClosedBeforeSwitchingError extends TestRunErrorBase {
constructor () {
super(TEST_RUN_ERRORS.childWindowClosedBeforeSwitchingError);
}
}

4 changes: 4 additions & 0 deletions src/errors/test-run/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,5 +330,9 @@ export default {

[TEST_RUN_ERRORS.closeChildWindowError]: () => `
An error occurred while closing child windows.
`,

[TEST_RUN_ERRORS.childWindowClosedBeforeSwitchingError]: () => `
The child window was closed before TestCafe could switch to it.
`
};
3 changes: 2 additions & 1 deletion src/errors/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ export const TEST_RUN_ERRORS = {
childWindowIsNotLoadedError: 'E68',
childWindowNotFoundError: 'E69',
cannotSwitchToWindowError: 'E70',
closeChildWindowError: 'E71'
closeChildWindowError: 'E71',
childWindowClosedBeforeSwitchingError: 'E72'
};

export const RUNTIME_ERRORS = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index page</title>
</head>
<body>
<button id="openPopUp">Open pop-up window</button>
<div>
<span id="openWndResult"></span>
</div>
<script>
var openPopUpBtn = document.getElementById('openPopUp');
var openWndResultSpan = document.getElementById('openWndResult');

openPopUpBtn.addEventListener('click', function () {
var wnd = window.open('about:blank', '', "left=9999, top=9999, height=100,width=100");

wnd.focus();
wnd.blur();
wnd.close();

openWndResultSpan.textContent = 'The window was closed.';
});
</script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ describe('Allow multiple windows', () => {
expect(errs[0]).to.contain('A JavaScript error occurred on "http://localhost:3000/fixtures/run-options/allow-multiple-windows/pages/handle-errors/page-with-js-error.html"');
});
});

it('Close the window immediately after opening (GH-3762)', () => {
return runTests('testcafe-fixtures/close-window-immediately-after-opeping.js', null, { allowMultipleWindows: true });
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Selector } from 'testcafe';

fixture `Close window immediately after opening`
.page('http://localhost:3000/fixtures/run-options/allow-multiple-windows/pages/close-window-immediately-after-opening/index.html');

test('test', async t => {
await t
.click('#openPopUp')
.expect(Selector('#openWndResult').textContent).eql('The window was closed.');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
The child window was closed before TestCafe could switch to it.

Browser: Chrome 15.0.874.120 / macOS 10.15
Screenshot: /unix/path/with/<tag>

18 |function func1 () {
19 | record = createCallsiteRecord({ byFunctionName: 'func1' });
20 |}
21 |
22 |(function func2 () {
> 23 | func1();
24 |})();
25 |
26 |stackTrace.filter.deattach(stackFilter);
27 |
28 |module.exports = record;

at func2 (testfile.js:23:5)
at Object.<anonymous> (testfile.js:24:3)
7 changes: 6 additions & 1 deletion test/server/test-run-error-formatting-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ const {
ChildWindowIsNotLoadedError,
ChildWindowNotFoundError,
CannotSwitchToWindowError,
CloseChildWindowError
CloseChildWindowError,
ChildWindowClosedBeforeSwitchingError
} = require('../../lib/errors/test-run');

const untestedErrorTypes = Object.keys(TEST_RUN_ERRORS).map(key => TEST_RUN_ERRORS[key]);
Expand Down Expand Up @@ -414,6 +415,10 @@ describe('Error formatting', () => {
it('Should format "closeChildWindowError"', () => {
assertErrorMessage('close-child-window-error', new CloseChildWindowError());
});

it('Should format "childWindowClosedBeforeSwitchingError"', () => {
assertErrorMessage('child-window-closed-before-switching-error', new ChildWindowClosedBeforeSwitchingError());
});
});

describe('Test coverage', () => {
Expand Down

0 comments on commit 32005b2

Please sign in to comment.