Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/core/test/detach.test.ts
1029 views
1
import Core, { Session } from '@secret-agent/core';
2
import { Helpers } from '@secret-agent/testing';
3
import { ITestKoaServer } from '@secret-agent/testing/helpers';
4
import {
5
getComputedVisibilityFnName,
6
getNodePointerFnName,
7
} from '@secret-agent/interfaces/jsPathFnNames';
8
import INodePointer from 'awaited-dom/base/INodePointer';
9
import { inspect } from 'util';
10
import { LocationStatus } from '@secret-agent/interfaces/Location';
11
import HumanEmulator from '@secret-agent/plugin-utils/lib/HumanEmulator';
12
import ConnectionToClient from '../server/ConnectionToClient';
13
import CoreServer from '../server';
14
15
inspect.defaultOptions.colors = true;
16
inspect.defaultOptions.depth = null;
17
let koaServer: ITestKoaServer;
18
let connectionToClient: ConnectionToClient;
19
beforeAll(async () => {
20
const coreServer = new CoreServer();
21
await coreServer.listen({ port: 0 });
22
Core.use(
23
class BasicHumanEmulator extends HumanEmulator {
24
static id = 'basic';
25
},
26
);
27
connectionToClient = Core.addConnection();
28
Helpers.onClose(() => connectionToClient.disconnect(), true);
29
Helpers.onClose(() => coreServer.close(), true);
30
koaServer = await Helpers.runKoaServer();
31
});
32
afterAll(Helpers.afterAll);
33
afterEach(Helpers.afterEach);
34
35
const getContentScript = `(() => {
36
let retVal = '';
37
if (document.doctype)
38
retVal = new XMLSerializer().serializeToString(document.doctype);
39
if (document.documentElement)
40
retVal += document.documentElement.outerHTML;
41
return retVal;
42
})()`;
43
44
describe('basic Detach tests', () => {
45
it('can detach a document', async () => {
46
const body = `<!DOCTYPE html><html><head>
47
<meta charset="utf-8">
48
<title>Untitled</title>
49
<meta name="description" content="This is an example of a meta description.">
50
<link rel="stylesheet" type="text/css" href="theme.css">
51
<!--[if lt IE 9]>
52
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
53
<![endif]-->
54
</head>
55
<body>
56
<form action="submit-to-path" method="get" target="_blank">
57
<label for="saddr">Enter your location</label>
58
<input type="text" name="saddr">
59
<input type="hidden" name="daddr" value="350 5th Ave New York, NY 10018 (Empire State Building)">
60
<input type="submit" value="Get directions">
61
</form>
62
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
63
64
</body></html>`;
65
koaServer.get('/basic-detach', ctx => {
66
ctx.body = body;
67
});
68
const meta = await connectionToClient.createSession({
69
humanEmulatorId: 'basic',
70
});
71
const session = Session.get(meta.sessionId);
72
const tab = Session.getTab(meta);
73
await tab.goto(`${koaServer.baseUrl}/basic-detach`);
74
await tab.waitForLoad('DomContentLoaded');
75
76
const bodyAfterLoad = await tab.puppetPage.evaluate(getContentScript);
77
78
const { detachedTab } = await session.detachTab(tab, 'callsite2');
79
const detachedContent = await detachedTab.puppetPage.evaluate(getContentScript);
80
81
expect(detachedContent).toBe(bodyAfterLoad);
82
83
const inputs = await detachedTab.execJsPath([
84
'document',
85
['querySelectorAll', 'input'],
86
[getNodePointerFnName],
87
]);
88
expect(inputs.nodePointer.iterableItems).toHaveLength(3);
89
});
90
91
it('can record all commands sent to a detached frame', async () => {
92
koaServer.get('/nested-detach', ctx => {
93
ctx.body = `
94
<body>
95
<div id="menu1" class="menu">
96
<div class="nested">Nested 1</div>
97
</div>
98
<div id="menu2" class="menu">
99
<div class="nested">Nested 2</div>
100
<div>Not nested</div>
101
</div>
102
<div id="menu3" class="menu">
103
<div class="nested">Nested 3</div>
104
</div>
105
</body>`;
106
});
107
108
const meta = await connectionToClient.createSession({
109
humanEmulatorId: 'basic',
110
});
111
const session = Session.get(meta.sessionId);
112
const tab = Session.getTab(meta);
113
await tab.goto(`${koaServer.baseUrl}/nested-detach`);
114
await tab.waitForLoad(LocationStatus.AllContentLoaded);
115
const { detachedTab } = await session.detachTab(tab, 'callsite1');
116
117
const execJsPath = jest.spyOn(detachedTab.mainFrameEnvironment.jsPath, 'exec');
118
const qsAllResult = await detachedTab.execJsPath([
119
'document',
120
['querySelectorAll', '.menu'],
121
[getNodePointerFnName],
122
]);
123
let counter = 0;
124
for (const pointer of qsAllResult.nodePointer.iterableItems as INodePointer[]) {
125
counter += 1;
126
expect(pointer.type).toBe('HTMLDivElement');
127
const idResult = await detachedTab.execJsPath([pointer.id, 'id']);
128
expect(idResult.value).toBe(`menu${counter}`);
129
const classNameResult = await detachedTab.execJsPath([pointer.id, 'className']);
130
expect(classNameResult.value).toBe(`menu`);
131
132
const boundingRect = await detachedTab.execJsPath([pointer.id, ['getBoundingClientRect']]);
133
expect(boundingRect.value).toMatchObject({
134
x: expect.any(Number),
135
y: expect.any(Number),
136
width: expect.any(Number),
137
height: expect.any(Number),
138
});
139
const nestedResult = await detachedTab.execJsPath([
140
pointer.id,
141
['querySelector', '.nested'],
142
[getNodePointerFnName],
143
]);
144
145
await detachedTab.execJsPath([pointer.id, [getComputedVisibilityFnName]]);
146
147
const nestedTextResult = await detachedTab.execJsPath([
148
nestedResult.nodePointer.id,
149
'textContent',
150
]);
151
expect(nestedTextResult.value).toBe(`Nested ${counter}`);
152
const nestedClassnameResult = await detachedTab.execJsPath([
153
nestedResult.nodePointer.id,
154
'className',
155
]);
156
expect(nestedClassnameResult.value).toBe(`nested`);
157
}
158
const jsPaths = detachedTab.mainFrameEnvironment.jsPath.execHistory;
159
160
// now should be able to create a second detached tab and replay the paths with same result
161
const { detachedTab: secondDetached } = await session.detachTab(tab, 'callsite3');
162
const prefetch = await secondDetached.mainFrameEnvironment.jsPath.runJsPaths(jsPaths, {
163
x: 0,
164
y: 0,
165
});
166
167
const manualResults = [];
168
for (let i = 0; i < execJsPath.mock.calls.length; i += 1) {
169
const [jsPath] = execJsPath.mock.calls[i];
170
const result = await execJsPath.mock.results[i].value;
171
manualResults.push({ result, jsPath });
172
}
173
174
manualResults.sort((a, b) => {
175
return JSON.stringify(a.jsPath).localeCompare(JSON.stringify(b.jsPath));
176
});
177
prefetch.sort((a, b) => {
178
return JSON.stringify(a.jsPath).localeCompare(JSON.stringify(b.jsPath));
179
});
180
181
expect(prefetch).toStrictEqual(manualResults);
182
});
183
});
184
185