/
iframe-driver.js
130 lines (100 loc) · 4.37 KB
/
iframe-driver.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import {
Promise,
eventSandbox,
utils,
} from './deps/hammerhead';
import { pageUnloadBarrier } from './deps/testcafe-core';
import { IframeStatusBar } from './deps/testcafe-ui';
import Driver from './driver';
import ContextStorage from './storage';
import DriverStatus from './status';
import ParentIframeDriverLink from './driver-link/iframe/parent';
import IframeNativeDialogTracker from './native-dialog-tracker/iframe';
import {
ChildWindowIsOpenedInFrameMessage,
StopInternalFromFrameMessage,
TYPE as MESSAGE_TYPE,
} from './driver-link/messages';
const messageSandbox = eventSandbox.message;
export default class IframeDriver extends Driver {
constructor (testRunId, options) {
super(testRunId, {}, {}, options);
this.lastParentDriverMessageId = null;
this.parentDriverLink = new ParentIframeDriverLink(window.parent);
this._initParentDriverListening();
}
// Errors handling
_onJsError () {
// NOTE: do nothing because hammerhead sends js error to the top window directly
}
_onConsoleMessage () {
// NOTE: do nothing because hammerhead sends console messages to the top window directly
}
// NOTE: when the new page is opened in the iframe we send a message to the top window
// to start waiting for the new page is loaded
_onChildWindowOpened () {
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 => {
const msg = e.message;
pageUnloadBarrier
.wait(0)
.then(() => {
// NOTE: the parent driver repeats commands sent to a child driver if it doesn't get a confirmation
// from the child in time. However, confirmations sent by child drivers may be delayed when the browser
// is heavily loaded. That's why the child driver should ignore repeated messages from its parent.
if (msg.type === MESSAGE_TYPE.executeCommand) {
if (this.lastParentDriverMessageId === msg.id)
return;
this.lastParentDriverMessageId = msg.id;
this.readyPromise.then(() => {
this.speed = msg.testSpeed;
this.parentDriverLink.sendConfirmationMessage(msg.id);
this._onCommand(msg.command);
});
}
if (msg.type === MESSAGE_TYPE.setNativeDialogHandler) {
this.nativeDialogsTracker.setHandler(msg.dialogHandler);
this._setNativeDialogHandlerInIframes(msg.dialogHandler);
}
});
});
}
// Commands handling
_onSwitchToMainWindowCommand (command) {
this._switchToMainWindow(command);
}
// Routing
_onReady (status) {
this.parentDriverLink.onCommandExecuted(status);
}
async _isInCommandExecution () {
if (utils.dom.isCrossDomainWindows(window, window.parent))
return await this.parentDriverLink.hasPendingActionFlags();
return this._hasPendingActionFlags(this.contextStorage);
}
async _init () {
const id = await this.parentDriverLink.establishConnection();
this.contextStorage = new ContextStorage(window, id, this.windowId);
if (this._failIfClientCodeExecutionIsInterrupted())
return;
const inCommandExecution = await this._isInCommandExecution();
if (!inCommandExecution)
return;
this.contextStorage.setItem(this.COMMAND_EXECUTING_FLAG, false);
this.contextStorage.setItem(this.EXECUTING_IN_IFRAME_FLAG, false);
this._onReady(new DriverStatus({ isCommandResult: true }));
}
// API
start () {
this.nativeDialogsTracker = new IframeNativeDialogTracker(this.dialogHandler);
this.statusBar = new IframeStatusBar();
const initializePromise = this._init();
this.readyPromise = Promise.all([this.readyPromise, initializePromise]);
}
}