Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/puppet/test/TestPage.ts
1030 views
1
import { IPuppetPage } from '@secret-agent/interfaces/IPuppetPage';
2
import { IPuppetFrame } from '@secret-agent/interfaces/IPuppetFrame';
3
import { IKeyboardKey } from '@secret-agent/interfaces/IKeyboardLayoutUS';
4
import { keyDefinitions } from '@secret-agent/puppet-chrome/interfaces/USKeyboardLayout';
5
import IPuppetContext from '@secret-agent/interfaces/IPuppetContext';
6
import * as Fs from 'fs';
7
8
export interface ITestPage extends IPuppetPage {
9
click(selector: string): Promise<void>;
10
type(text: string): Promise<void>;
11
attachFrame(frameId: string, url: string): Promise<IPuppetFrame>;
12
detachFrame(frameId: string): Promise<void>;
13
goto(url: string, waitOnLifecycle?: string, timeoutMs?: number): Promise<void>;
14
setContent(content: string): Promise<void>;
15
waitForPopup(): Promise<IPuppetPage>;
16
}
17
18
export function capturePuppetContextLogs(context: IPuppetContext, id: string): void {
19
const outDir = process.env.SA_SESSIONS_DIR;
20
21
if (!Fs.existsSync(outDir)) Fs.mkdirSync(outDir, { recursive: true });
22
23
context.on('devtools-message', x => {
24
Fs.appendFileSync(`${outDir}/${id}.txt`, `${JSON.stringify(x)}\n`);
25
});
26
}
27
28
export function createTestPage(page: IPuppetPage) {
29
const castPage = page as ITestPage;
30
castPage.attachFrame = attachFrame.bind(page, page);
31
castPage.detachFrame = detachFrame.bind(page, page);
32
castPage.click = click.bind(page, page);
33
castPage.type = type.bind(page, page);
34
castPage.setContent = setContent.bind(page, page);
35
castPage.waitForPopup = waitForPopup.bind(page, page);
36
castPage.goto = goto.bind(page, page);
37
return castPage;
38
}
39
40
export async function type(page: IPuppetPage, text: string) {
41
for (const char of text) {
42
if (char in keyDefinitions) await page.keyboard.press(char as IKeyboardKey);
43
else await page.keyboard.sendCharacter(char);
44
}
45
}
46
47
export async function click(page: IPuppetPage, selector: string) {
48
const coordinates: any = await page.evaluate(`(()=>{
49
const rect = document.querySelector('${selector}').getBoundingClientRect();
50
return {
51
x: rect.x,
52
y: rect.y
53
};
54
})();`);
55
await page.mouse.move(coordinates.x, coordinates.y);
56
await page.mouse.down();
57
await page.mouse.up();
58
}
59
60
export async function attachFrame(page: IPuppetPage, frameId: string, url: string) {
61
const framePromise = page.waitOn('frame-created');
62
await page.evaluate(`
63
(async () => {
64
const frame = document.createElement('iframe');
65
frame.src = '${url}';
66
frame.id = '${frameId}';
67
document.body.appendChild(frame);
68
await new Promise(x => (frame.onload = x));
69
})()`);
70
const { frame } = await framePromise;
71
return page.frames.find(x => x.id === frame.id);
72
}
73
74
export async function detachFrame(page: IPuppetPage, frameId: string) {
75
await page.evaluate(`((frameId) => {
76
document.getElementById(frameId).remove();
77
})('${frameId}')`);
78
}
79
80
export async function goto(
81
page: IPuppetPage,
82
url: string,
83
waitOnLifecycle: 'load' | 'DOMContentLoaded' = 'load',
84
timeoutMs?: number,
85
) {
86
waitOnLifecycle ??= 'load';
87
timeoutMs ??= 30e3;
88
const [loader] = await Promise.all([
89
page.navigate(url),
90
page.mainFrame.waitOn('frame-navigated', null, timeoutMs),
91
]);
92
await page.mainFrame.waitForLifecycleEvent(waitOnLifecycle, loader.loaderId, timeoutMs);
93
}
94
95
export async function setContent(page: IPuppetPage, content: string) {
96
// @ts-ignore
97
page.mainFrame.defaultLoaderId = null;
98
await page.evaluate(`((content) => {
99
window.stop();
100
document.open();
101
document.write(content);
102
document.close();
103
})(${JSON.stringify(content)})`);
104
}
105
106
export async function waitForPopup(page: IPuppetPage) {
107
return new Promise<IPuppetPage>(resolve => {
108
page.popupInitializeFn = async popup => resolve(popup);
109
});
110
}
111
112