Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/plugins/default-browser-emulator/test/polyfills.test.ts
1029 views
1
import * as http from 'http';
2
import { inspect } from 'util';
3
import * as Helpers from '@secret-agent/testing/helpers';
4
import { ITestHttpServer } from '@secret-agent/testing/helpers';
5
import Puppet from '@secret-agent/puppet';
6
import IPuppetContext from '@secret-agent/interfaces/IPuppetContext';
7
import Log from '@secret-agent/commons/Logger';
8
import CorePlugins from '@secret-agent/core/lib/CorePlugins';
9
import { IBoundLog } from '@secret-agent/interfaces/ILog';
10
import { getOverrideScript } from '../lib/DomOverridesBuilder';
11
import BrowserEmulator from '../index';
12
import DomExtractor = require('./DomExtractor');
13
14
const { log } = Log(module);
15
const selectBrowserMeta = BrowserEmulator.selectBrowserMeta();
16
17
let puppet: Puppet;
18
let httpServer: ITestHttpServer<http.Server>;
19
let context: IPuppetContext;
20
beforeAll(async () => {
21
puppet = new Puppet(selectBrowserMeta.browserEngine);
22
Helpers.onClose(() => puppet.close(), true);
23
await puppet.start();
24
const plugins = new CorePlugins({ selectBrowserMeta }, log as IBoundLog);
25
plugins.browserEmulator.onNewPuppetPage = null;
26
context = await puppet.newContext(plugins, log);
27
Helpers.onClose(() => context.close().catch(), true);
28
httpServer = await Helpers.runHttpServer({ onlyCloseOnFinal: true });
29
});
30
31
afterAll(Helpers.afterAll);
32
afterEach(Helpers.afterEach);
33
34
const debug = process.env.DEBUG || false;
35
36
test('it should be able to add polyfills', async () => {
37
const page = await createPage();
38
39
const objectTestProperties = {
40
length: {
41
_$type: 'number',
42
_$flags: 'c',
43
_$value: 0,
44
},
45
name: {
46
_$type: 'string',
47
_$flags: 'c',
48
_$value: 'ObjectTest',
49
},
50
arguments: {
51
_$type: 'object',
52
_$flags: '',
53
_$value: null,
54
},
55
caller: {
56
_$type: 'object',
57
_$flags: '',
58
_$value: null,
59
},
60
prototype: {
61
_$protos: ['Object.prototype'],
62
creationTime: {
63
_$flags: 'ce',
64
_$accessException: 'TypeError: Illegal invocation',
65
_$get: 'function get creationTime() { [native code] }',
66
_$getToStringToString: 'function toString() { [native code] }',
67
},
68
'Symbol(Symbol.toStringTag)': {
69
_$type: 'string',
70
_$flags: 'c',
71
_$value: 'ObjectTest',
72
},
73
_$type: 'object',
74
_$flags: '',
75
},
76
'new()': {
77
_$constructorException: "TypeError: Cannot read property '0' of undefined",
78
_$type: 'constructor',
79
},
80
_$type: 'function',
81
_$function: 'function ObjectTest() { [native code] }',
82
_$flags: 'cw',
83
_$value: 'function ObjectTest() { [native code] }',
84
_$invocation: "TypeError: Cannot read property '0' of undefined",
85
};
86
const chromeProperty = {
87
_$flags: 'ce',
88
_$type: 'string',
89
_$value: 'I am chrome',
90
};
91
await page.addNewDocumentScript(
92
getOverrideScript('polyfill.add', {
93
itemsToAdd: [
94
{
95
path: 'window',
96
propertyName: 'chromey',
97
prevProperty: 'Atomics',
98
property: chromeProperty,
99
},
100
{
101
path: 'window',
102
propertyName: 'ObjectTest',
103
prevProperty: 'chromey',
104
property: objectTestProperties,
105
},
106
],
107
}).script,
108
false,
109
);
110
await Promise.all([
111
page.navigate(httpServer.url),
112
page.mainFrame.waitOn('frame-lifecycle', event => event.name === 'load'),
113
]);
114
115
const json = await page.mainFrame.evaluate(
116
`new (${DomExtractor.toString()})('window').run(window, 'window', ['windowKeys','chromey','ObjectTest'])`,
117
false,
118
);
119
const result = JSON.parse(json as any);
120
121
const windowKeys = result.windowKeys;
122
const window = result.window;
123
// test chrome property
124
if (debug) {
125
console.log('chromey', inspect(window.chromey, false, null, true));
126
}
127
expect(window.chromey).toStrictEqual(chromeProperty);
128
expect(windowKeys.indexOf('chromey')).toBe(windowKeys.indexOf('Atomics') + 1);
129
130
// test ObjectTest property
131
if (debug) {
132
console.log('ObjectTest', inspect(window.ObjectTest, false, null, true));
133
}
134
expect(window.ObjectTest).toStrictEqual(objectTestProperties);
135
expect(windowKeys.indexOf('ObjectTest')).toBe(windowKeys.indexOf('chromey') + 1);
136
}, 60e3);
137
138
test('it should be able to remove properties', async () => {
139
const page = await createPage();
140
141
await page.addNewDocumentScript(
142
getOverrideScript('polyfill.remove', {
143
itemsToRemove: [
144
{ path: 'window', propertyName: 'Atomics' },
145
{ path: 'window.Array', propertyName: 'from' },
146
],
147
}).script,
148
false,
149
);
150
await Promise.all([
151
page.navigate(httpServer.url),
152
page.mainFrame.waitOn('frame-lifecycle', event => event.name === 'load'),
153
]);
154
155
expect(await page.mainFrame.evaluate(`!!window.Atomics`, false)).not.toBeTruthy();
156
expect(await page.mainFrame.evaluate(`!!Array.from`, false)).not.toBeTruthy();
157
});
158
159
test('it should be able to change properties', async () => {
160
const page = await createPage();
161
162
await page.addNewDocumentScript(
163
getOverrideScript('polyfill.modify', {
164
itemsToModify: [
165
{
166
path: 'window.Navigator.prototype.registerProtocolHandler.name',
167
propertyName: '_$value',
168
property: 'notTheRightName',
169
},
170
{
171
path: 'window.Navigator.prototype.registerProtocolHandler',
172
propertyName: '_$function',
173
property: 'function registerProtocolHandler() { [unnative code] }',
174
},
175
],
176
}).script,
177
false,
178
);
179
await Promise.all([
180
page.navigate(httpServer.url),
181
page.mainFrame.waitOn('frame-lifecycle', event => event.name === 'load'),
182
]);
183
184
const protocolToString = await page.mainFrame.evaluate(
185
`window.Navigator.prototype.registerProtocolHandler.toString()`,
186
false,
187
);
188
const protocolName = await page.mainFrame.evaluate(
189
`window.Navigator.prototype.registerProtocolHandler.name`,
190
false,
191
);
192
193
expect(protocolName).toBe('notTheRightName');
194
expect(protocolToString).toBe('function registerProtocolHandler() { [unnative code] }');
195
});
196
197
test('it should be able to change property order', async () => {
198
const page = await createPage();
199
200
const startNavigatorKeys = (await page.mainFrame.evaluate(
201
`Object.keys(window.Navigator.prototype)`,
202
false,
203
)) as string[];
204
205
await page.addNewDocumentScript(
206
getOverrideScript('polyfill.reorder', {
207
itemsToReorder: [
208
{
209
path: 'window.Navigator.prototype',
210
propertyName: startNavigatorKeys[10],
211
throughProperty: startNavigatorKeys[12],
212
prevProperty: startNavigatorKeys[1],
213
},
214
{
215
path: 'window.Navigator.prototype',
216
propertyName: startNavigatorKeys[18],
217
throughProperty: startNavigatorKeys[18],
218
prevProperty: startNavigatorKeys[12],
219
},
220
],
221
}).script,
222
false,
223
);
224
await new Promise(setImmediate);
225
await Promise.all([
226
page.navigate(httpServer.url),
227
page.mainFrame.waitOn('frame-lifecycle', event => event.name === 'load'),
228
]);
229
230
const keyOrder = (await page.mainFrame.evaluate(
231
`Object.keys(window.Navigator.prototype)`,
232
false,
233
)) as string[];
234
235
const prop1Index = keyOrder.indexOf(startNavigatorKeys[10]);
236
expect(keyOrder[prop1Index - 1]).toBe(startNavigatorKeys[1]);
237
238
const prop2Index = keyOrder.indexOf(startNavigatorKeys[18]);
239
expect(keyOrder[prop2Index - 1]).toBe(startNavigatorKeys[12]);
240
});
241
242
test('it should be able to change window property order', async () => {
243
const page = await createPage();
244
const windowKeys = await page.mainFrame.evaluate(`Object.keys(window)`, false);
245
246
const itemsToReorder = [
247
{
248
path: 'window',
249
propertyName: windowKeys[10],
250
throughProperty: windowKeys[12],
251
prevProperty: windowKeys[1],
252
},
253
{
254
path: 'window',
255
propertyName: windowKeys[18],
256
throughProperty: windowKeys[18],
257
prevProperty: windowKeys[12],
258
},
259
{
260
path: 'window',
261
propertyName: windowKeys[25],
262
throughProperty: windowKeys[50],
263
prevProperty: windowKeys[23],
264
},
265
];
266
await page.addNewDocumentScript(
267
getOverrideScript('polyfill.reorder', {
268
itemsToReorder,
269
}).script,
270
false,
271
);
272
await Promise.all([
273
page.navigate(httpServer.url),
274
page.mainFrame.waitOn('frame-lifecycle', event => event.name === 'load'),
275
]);
276
const windowKeysAfter = (await page.mainFrame.evaluate(`Object.keys(window)`, false)) as string[];
277
278
const prop1Index = windowKeysAfter.indexOf(windowKeys[10]);
279
expect(windowKeysAfter[prop1Index - 1]).toBe(windowKeys[1]);
280
281
const prop2Index = windowKeysAfter.indexOf(windowKeys[18]);
282
expect(windowKeysAfter[prop2Index - 1]).toBe(windowKeys[12]);
283
284
const prop3Index = windowKeysAfter.indexOf(windowKeys[25]);
285
expect(windowKeysAfter[prop3Index - 1]).toBe(windowKeys[23]);
286
287
expect(windowKeysAfter.indexOf(windowKeys[26])).toBe(prop3Index + 1);
288
expect(windowKeysAfter.indexOf(windowKeys[50])).toBe(prop3Index + 25);
289
}, 10e3);
290
291
async function createPage() {
292
const page = await context.newPage();
293
Helpers.onClose(() => page.close());
294
page.on('page-error', console.log);
295
if (debug) {
296
page.on('console', console.log);
297
}
298
return page;
299
}
300
301