From db1a3acff61220bbf5fdf82b6a32f49d545e5b66 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 16 Dec 2021 16:45:23 +0300 Subject: [PATCH] fix browser hanging when the test switches between windows after switching to iframe (closes #6085) --- src/client/automation/index.js | 3 +++ .../automation/shared-adapter-initializer.ts | 18 ++++++++++++++++++ src/client/driver/driver-link/messages.js | 7 +++++++ src/client/driver/driver.js | 14 ++++++++++++++ src/client/driver/iframe-driver.js | 10 +++++++++- .../multiple-windows/pages/i6085/iframe.html | 7 +++++++ .../multiple-windows/pages/i6085/index.html | 11 +++++++++++ .../multiple-windows/pages/i6085/window.html | 7 +++++++ .../fixtures/multiple-windows/test.js | 4 ++++ .../testcafe-fixtures/i6085.js | 13 +++++++++++++ 10 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/client/automation/shared-adapter-initializer.ts create mode 100644 test/functional/fixtures/multiple-windows/pages/i6085/iframe.html create mode 100644 test/functional/fixtures/multiple-windows/pages/i6085/index.html create mode 100644 test/functional/fixtures/multiple-windows/pages/i6085/window.html create mode 100644 test/functional/fixtures/multiple-windows/testcafe-fixtures/i6085.js diff --git a/src/client/automation/index.js b/src/client/automation/index.js index b624c8b41c..6bf7e35c1b 100644 --- a/src/client/automation/index.js +++ b/src/client/automation/index.js @@ -1,3 +1,6 @@ +// NOTE: Initializer should be the first +import './shared-adapter-initializer'; + import hammerhead from './deps/hammerhead'; import DispatchEventAutomation from './playback/dispatch-event'; import SetScrollAutomation from './playback/set-scroll'; diff --git a/src/client/automation/shared-adapter-initializer.ts b/src/client/automation/shared-adapter-initializer.ts new file mode 100644 index 0000000000..e14f668d6a --- /dev/null +++ b/src/client/automation/shared-adapter-initializer.ts @@ -0,0 +1,18 @@ +import hammerhead from './deps/hammerhead'; +import testCafeCore from './deps/testcafe-core'; +import { initializeAdapter } from '../../shared/adapter/index'; +import { ScrollOptions } from '../../test-run/commands/options'; +import { getOffsetOptions } from './utils/offsets'; + +const { nativeMethods, Promise } = hammerhead; +const { domUtils, ScrollAutomation } = testCafeCore; + + +initializeAdapter({ + PromiseCtor: Promise, + nativeMethods: nativeMethods, + scroll: (el: any, scrollOptions: ScrollOptions) => new ScrollAutomation(el, scrollOptions).run(), + + getOffsetOptions: getOffsetOptions, + isDomElement: domUtils.isDomElement, +}); diff --git a/src/client/driver/driver-link/messages.js b/src/client/driver/driver-link/messages.js index e22f06b4e4..eed7752ebd 100644 --- a/src/client/driver/driver-link/messages.js +++ b/src/client/driver/driver-link/messages.js @@ -17,6 +17,7 @@ export const TYPE = { restoreChildLink: 'driver|restore-child-link', childWindowIsLoadedInIFrame: 'driver|child-window-is-loaded-in-iframe', childWindowIsOpenedInIFrame: 'driver|child-window-is-opened-in-iframe', + stopInternalFromFrame: 'driver|stop-internal-from-iframe', hasPendingActionFlags: 'driver|has-pending-action-flags', }; @@ -150,6 +151,12 @@ export class ChildWindowIsOpenedInFrameMessage extends InterDriverMessage { } } +export class StopInternalFromFrameMessage extends InterDriverMessage { + constructor () { + super(TYPE.stopInternalFromFrame); + } +} + export class HasPendingActionFlagsMessage extends InterDriverMessage { constructor () { super(TYPE.hasPendingActionFlags); diff --git a/src/client/driver/driver.js b/src/client/driver/driver.js index ad2ee1752b..9ffaaffac5 100644 --- a/src/client/driver/driver.js +++ b/src/client/driver/driver.js @@ -822,6 +822,17 @@ export default class Driver extends serviceUtils.EventEmitter { this._onChildWindowOpened({ window: wnd, windowId: msg.windowId }); } + _handleStopInternalFromFrame (msg, wnd) { + sendConfirmationMessage({ + requestMsgId: msg.id, + window: wnd, + }); + + this.contextStorage.setItem(this.EXECUTING_IN_IFRAME_FLAG, false); + + this._stopInternal(); + } + _initChildDriverListening () { messageSandbox.on(messageSandbox.SERVICE_MSG_RECEIVED_EVENT, e => { const msg = e.message; @@ -837,6 +848,9 @@ export default class Driver extends serviceUtils.EventEmitter { case MESSAGE_TYPE.childWindowIsLoadedInIFrame: this._handleChildWindowIsLoadedInIFrame(msg, window); break; + case MESSAGE_TYPE.stopInternalFromFrame: + this._handleStopInternalFromFrame(msg, window); + break; case MESSAGE_TYPE.setAsMaster: this._handleSetAsMasterMessage(msg, window); break; diff --git a/src/client/driver/iframe-driver.js b/src/client/driver/iframe-driver.js index 89e002cbb8..d1c790cedc 100644 --- a/src/client/driver/iframe-driver.js +++ b/src/client/driver/iframe-driver.js @@ -10,9 +10,13 @@ import Driver from './driver'; import ContextStorage from './storage'; import DriverStatus from './status'; import ParentIframeDriverLink from './driver-link/iframe/parent'; -import { ChildWindowIsOpenedInFrameMessage, TYPE as MESSAGE_TYPE } from './driver-link/messages'; import IframeNativeDialogTracker from './native-dialog-tracker/iframe'; +import { + ChildWindowIsOpenedInFrameMessage, + StopInternalFromFrameMessage, + TYPE as MESSAGE_TYPE, +} from './driver-link/messages'; const messageSandbox = eventSandbox.message; @@ -40,6 +44,10 @@ export default class IframeDriver extends Driver { messageSandbox.sendServiceMsg(new ChildWindowIsOpenedInFrameMessage(), window.top); } + _stopInternal () { + messageSandbox.sendServiceMsg(new StopInternalFromFrameMessage(), window.top); + } + // Messaging between drivers _initParentDriverListening () { eventSandbox.message.on(eventSandbox.message.SERVICE_MSG_RECEIVED_EVENT, e => { diff --git a/test/functional/fixtures/multiple-windows/pages/i6085/iframe.html b/test/functional/fixtures/multiple-windows/pages/i6085/iframe.html new file mode 100644 index 0000000000..a8f3586bdd --- /dev/null +++ b/test/functional/fixtures/multiple-windows/pages/i6085/iframe.html @@ -0,0 +1,7 @@ + + + + +

iframe

+ + diff --git a/test/functional/fixtures/multiple-windows/pages/i6085/index.html b/test/functional/fixtures/multiple-windows/pages/i6085/index.html new file mode 100644 index 0000000000..20ca07ac35 --- /dev/null +++ b/test/functional/fixtures/multiple-windows/pages/i6085/index.html @@ -0,0 +1,11 @@ + + + + + gh-6085 + + +gh-6085 + + + diff --git a/test/functional/fixtures/multiple-windows/pages/i6085/window.html b/test/functional/fixtures/multiple-windows/pages/i6085/window.html new file mode 100644 index 0000000000..e769eca5f9 --- /dev/null +++ b/test/functional/fixtures/multiple-windows/pages/i6085/window.html @@ -0,0 +1,7 @@ + + + + +

window

+ + diff --git a/test/functional/fixtures/multiple-windows/test.js b/test/functional/fixtures/multiple-windows/test.js index 7eccfd2b1c..5d8f4f7b56 100644 --- a/test/functional/fixtures/multiple-windows/test.js +++ b/test/functional/fixtures/multiple-windows/test.js @@ -152,6 +152,10 @@ describe('Multiple windows', () => { return runTests('testcafe-fixtures/i6680.js'); }); + it('Should not hang if switching to a window from iframe', () => { + return runTests('testcafe-fixtures/i6085.js'); + }); + describe('API', () => { it('Open child window', () => { return runTests('testcafe-fixtures/api/api-test.js', 'Open child window', { only: 'chrome' }); diff --git a/test/functional/fixtures/multiple-windows/testcafe-fixtures/i6085.js b/test/functional/fixtures/multiple-windows/testcafe-fixtures/i6085.js new file mode 100644 index 0000000000..89e17b0269 --- /dev/null +++ b/test/functional/fixtures/multiple-windows/testcafe-fixtures/i6085.js @@ -0,0 +1,13 @@ +const newWindowUrl = 'http://localhost:3000/fixtures/multiple-windows/pages/i6085/window.html'; + +fixture `Should not hang if switching to a window from iframe` + .page `http://localhost:3000/fixtures/multiple-windows/pages/i6085/index.html`; + +test('Should not hang if switching to a window from iframe', async t => { + const newWindow = await t.openWindow(newWindowUrl); + + await t.switchToParentWindow(); + await t.switchToIframe('iframe'); + await t.switchToWindow(newWindow); + await t.click('h1'); +});