Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/plugins/default-browser-emulator/test/iframe.test.ts
1029 views
1
import { Helpers } from '@secret-agent/testing';
2
import Core, { Session } from '@secret-agent/core';
3
import { InteractionCommand } from '@secret-agent/interfaces/IInteractions';
4
import { ITestKoaServer } from '@secret-agent/testing/helpers';
5
import CoreServerConnection from '@secret-agent/core/server/ConnectionToClient';
6
import { IPuppetPage } from '@secret-agent/interfaces/IPuppetPage';
7
8
let koaServer: ITestKoaServer;
9
let coreServerConnection: CoreServerConnection;
10
beforeAll(async () => {
11
coreServerConnection = Core.addConnection();
12
Helpers.onClose(() => coreServerConnection.disconnect(), true);
13
koaServer = await Helpers.runKoaServer();
14
});
15
afterAll(Helpers.afterAll);
16
afterEach(Helpers.afterEach);
17
18
test('should have a chrome object on iframes', async () => {
19
const page = await createPage();
20
21
const frameType = await page.evaluate(`(() => {
22
const iframe = document.createElement('iframe');
23
iframe.srcdoc = 'blank page';
24
document.body.appendChild(iframe);
25
26
const result = typeof iframe.contentWindow.chrome;
27
iframe.remove();
28
29
return result;
30
})();`);
31
expect(frameType).toBe('object');
32
});
33
34
test('should not break toString across frames', async () => {
35
const page = await createPage();
36
37
const toStrings = await page.evaluate(`(() => {
38
const iframe = document.createElement('iframe');
39
document.body.appendChild(iframe);
40
41
const contentWindow = iframe.contentWindow;
42
const fnCallWithFrame = contentWindow.Function.prototype.toString.call(Function.prototype.toString);
43
const fnToString = Function.toString + '';
44
45
return {
46
fnToString,
47
fnCallWithFrame
48
}
49
})();`);
50
51
const { fnToString, fnCallWithFrame } = toStrings as any;
52
expect(fnToString).toBe(fnCallWithFrame);
53
});
54
55
test('should not break iframe functions', async () => {
56
const page = await createPage();
57
58
const testFuncReturnValue = 'TESTSTRING';
59
await page.evaluate(`((returnValue) => {
60
const { document } = window; // eslint-disable-line
61
const body = document.querySelector('body');
62
const iframe = document.createElement('iframe');
63
iframe.srcdoc = 'foobar';
64
body.appendChild(iframe);
65
iframe.contentWindow.mySuperFunction = () => returnValue;
66
})("${testFuncReturnValue}")`);
67
68
const realReturn = await page.evaluate(
69
`document.querySelector('iframe').contentWindow.mySuperFunction()`,
70
);
71
await page.close();
72
expect(realReturn).toBe(testFuncReturnValue);
73
});
74
75
test('should have chrome object in all kinds of iframes', async () => {
76
const page = await createPage();
77
78
const basiciframe = await page.evaluate(`(() => {
79
const el = document.createElement('iframe');
80
document.body.appendChild(el);
81
return typeof el.contentWindow.chrome;
82
})()`);
83
84
const sandboxSOiframe = await page.evaluate(`(() => {
85
const el = document.createElement('iframe');
86
el.setAttribute('sandbox', 'allow-same-origin');
87
document.body.appendChild(el);
88
return typeof el.contentWindow.chrome;
89
})()`);
90
91
const sandboxSOASiframe = await page.evaluate(`(() => {
92
const el = document.createElement('iframe');
93
el.setAttribute('sandbox', 'allow-same-origin allow-scripts');
94
document.body.appendChild(el);
95
return typeof el.contentWindow.chrome;
96
})()`);
97
98
const srcdociframe = await page.evaluate(`(() => {
99
const el = document.createElement('iframe');
100
el.srcdoc = 'blank page, boys.';
101
document.body.appendChild(el);
102
return typeof el.contentWindow.chrome;
103
})()`);
104
105
await page.close();
106
expect(basiciframe).toBe('object');
107
expect(sandboxSOiframe).toBe('object');
108
expect(sandboxSOASiframe).toBe('object');
109
expect(srcdociframe).toBe('object');
110
});
111
112
test('should have plugins in frames', async () => {
113
const page = await createPage();
114
115
const plugins = await page.evaluate<number>(`window.navigator.plugins.length`);
116
const iframePlugins = await page.evaluate<number>(`(() => {
117
const iframe = document.createElement('iframe');
118
iframe.srcdoc = 'page intentionally left blank';
119
document.body.appendChild(iframe);
120
121
return iframe.contentWindow.navigator.plugins.length;
122
})()`);
123
expect(plugins).toBe(iframePlugins);
124
});
125
126
test('should not be able to detect contentWindow overrides', async () => {
127
const page = await createPage();
128
129
const results = await page.evaluate<any>(`(() => {
130
const results = {};
131
132
const iframe = document.createElement('iframe');
133
iframe.srcdoc = 'page intentionally left blank';
134
document.body.appendChild(iframe);
135
136
const descriptors = Object.getOwnPropertyDescriptors(HTMLIFrameElement.prototype);
137
results.descriptorToString = descriptors.contentWindow.get.toString();
138
results.descriptorToStringToString = descriptors.contentWindow.get.toString.toString();
139
results.windowType = typeof iframe.contentWindow;
140
results.noProxySignature = !iframe.srcdoc.toString.hasOwnProperty('[[IsRevoked]]');
141
return results;
142
})()`);
143
expect(results.descriptorToString).toBe('function get contentWindow() { [native code] }');
144
expect(results.descriptorToStringToString).toBe('function toString() { [native code] }');
145
expect(results.noProxySignature).toBe(true);
146
expect(results.windowType).toBe('object');
147
});
148
149
test('should emulate contentWindow features', async () => {
150
const page = await createPage();
151
152
const results: any = await page.evaluate(`(() => {
153
const results = {};
154
155
const iframe = document.createElement('iframe');
156
iframe.srcdoc = 'page intentionally left blank';
157
document.body.appendChild(iframe);
158
159
results.doesExist = !!iframe.contentWindow; // Verify iframe isn't remapped to main window
160
results.isNotAClone = iframe.contentWindow !== window; // Verify iframe isn't remapped to main window
161
results.selfIsNotWindow = iframe.contentWindow.self !== window;
162
results.selfIsNotWindowTop = iframe.contentWindow.self !== window.top;
163
results.selfIsAWindow = iframe.contentWindow.self instanceof Window;
164
results.topIsNotSame = iframe.contentWindow.top !== iframe.contentWindow;
165
results.frameElementMatches = iframe.contentWindow.frameElement === iframe;
166
167
return results;
168
})()`);
169
170
await page.close();
171
172
expect(results.doesExist).toBe(true);
173
expect(results.isNotAClone).toBe(true);
174
expect(results.selfIsNotWindow).toBe(true);
175
expect(results.selfIsNotWindowTop).toBe(true);
176
expect(results.selfIsAWindow).toBe(true);
177
expect(results.topIsNotSame).toBe(true);
178
});
179
180
test('should handle a removed frame', async () => {
181
const meta = await coreServerConnection.createSession();
182
const tab = Session.getTab(meta);
183
Helpers.needsClosing.push(tab.session);
184
await tab.goto(koaServer.baseUrl);
185
await tab.waitForLoad('PaintingStable');
186
const navigatorPlatform = await tab.puppetPage.evaluate<boolean>(`(() => {
187
try {
188
const numberOfIframes = window.length;
189
const div = document.createElement('div');
190
div.setAttribute('style', 'display:none');
191
document.body.appendChild(div);
192
div.innerHTML = '<div style="height: 100vh;width: 100vw;position: absolute;left:-10000px;visibility: hidden;"><iframe></iframe></div>';
193
const iframeWindow = window[numberOfIframes];
194
div.parentNode.removeChild(div);
195
return iframeWindow.navigator.platform;
196
} catch (error) {
197
console.error(error);
198
}
199
})()`);
200
await tab.puppetPage.close();
201
expect(navigatorPlatform).toBe(tab.session.plugins.browserEmulator.operatingSystemPlatform);
202
});
203
204
// only run this test manually
205
// eslint-disable-next-line jest/no-disabled-tests
206
test.skip('should not break recaptcha popup', async () => {
207
const meta = await coreServerConnection.createSession();
208
const tab = Session.getTab(meta);
209
Helpers.needsClosing.push(tab.session);
210
const page = tab.puppetPage;
211
212
await tab.goto('https://www.fbdemo.com/invisible-captcha/index.html');
213
214
await tab.interact([
215
{
216
command: InteractionCommand.click,
217
mousePosition: ['window', 'document', ['querySelector', '#tswname']],
218
},
219
{
220
command: InteractionCommand.type,
221
keyboardCommands: [{ string: 'foo' }],
222
},
223
]);
224
await tab.interact([
225
{
226
command: InteractionCommand.click,
227
mousePosition: ['window', 'document', ['querySelector', '#tswemail']],
228
},
229
{
230
command: InteractionCommand.type,
231
keyboardCommands: [{ string: '[email protected]' }],
232
},
233
]);
234
await tab.interact([
235
{
236
command: InteractionCommand.click,
237
mousePosition: ['window', 'document', ['querySelector', '#tswcomments']],
238
},
239
{
240
command: InteractionCommand.type,
241
keyboardCommands: [
242
{
243
string:
244
'In the depth of winter, I finally learned that within me there lay an invincible summer.',
245
},
246
],
247
},
248
]);
249
await tab.interact([
250
{
251
command: InteractionCommand.click,
252
mousePosition: ['window', 'document', ['querySelector', '#tswsubmit']],
253
},
254
]);
255
await tab.waitForMillis(1000);
256
257
const { hasRecaptchaPopup } = await page.evaluate(`(() => {
258
const hasRecaptchaPopup = !!document.querySelectorAll('iframe[title="recaptcha challenge"]')
259
.length;
260
return { hasRecaptchaPopup };
261
})()`);
262
263
await tab.close();
264
265
expect(hasRecaptchaPopup).toBe(true);
266
});
267
268
async function createPage(): Promise<IPuppetPage> {
269
const meta = await coreServerConnection.createSession();
270
const tab = Session.getTab(meta);
271
Helpers.needsClosing.push(tab.session);
272
await tab.goto(koaServer.baseUrl);
273
await tab.waitForLoad('PaintingStable');
274
return tab.puppetPage;
275
}
276
277