Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/puppet/test/Mouse.test.ts
1031 views
1
import { IKeyboardKey } from '@secret-agent/interfaces/IKeyboardLayoutUS';
2
import Log from '@secret-agent/commons/Logger';
3
import IPuppetContext from '@secret-agent/interfaces/IPuppetContext';
4
import CorePlugins from '@secret-agent/core/lib/CorePlugins';
5
import { IBoundLog } from '@secret-agent/interfaces/ILog';
6
import Core from '@secret-agent/core';
7
import { TestServer } from './server';
8
import { createTestPage, ITestPage } from './TestPage';
9
import Puppet from '../index';
10
import CustomBrowserEmulator from './_CustomBrowserEmulator';
11
12
const { log } = Log(module);
13
const browserEmulatorId = CustomBrowserEmulator.id;
14
15
describe('Mouse', () => {
16
let server: TestServer;
17
let page: ITestPage;
18
let puppet: Puppet;
19
let context: IPuppetContext;
20
21
beforeAll(async () => {
22
Core.use(CustomBrowserEmulator);
23
server = await TestServer.create(0);
24
puppet = new Puppet(CustomBrowserEmulator.selectBrowserMeta().browserEngine);
25
await puppet.start();
26
const plugins = new CorePlugins({ browserEmulatorId }, log as IBoundLog);
27
context = await puppet.newContext(plugins, log);
28
});
29
30
afterEach(async () => {
31
await page.close();
32
});
33
34
beforeEach(async () => {
35
page = createTestPage(await context.newPage());
36
server.reset();
37
});
38
39
afterAll(async () => {
40
await server.stop();
41
await context.close();
42
await puppet.close();
43
});
44
45
it('should click the document', async () => {
46
await page.evaluate(`(() => {
47
window.clickPromise = new Promise(resolve => {
48
document.addEventListener('click', event => {
49
resolve({
50
type: event.type,
51
detail: event.detail,
52
clientX: event.clientX,
53
clientY: event.clientY,
54
isTrusted: event.isTrusted,
55
button: event.button,
56
});
57
});
58
});
59
})()`);
60
await page.mouse.move(50, 60);
61
await page.mouse.down();
62
await page.mouse.up();
63
const event: any = await page.evaluate(`window.clickPromise`);
64
expect(event.type).toBe('click');
65
expect(event.detail).toBe(1);
66
expect(event.clientX).toBe(50);
67
expect(event.clientY).toBe(60);
68
expect(event.isTrusted).toBe(true);
69
expect(event.button).toBe(0);
70
});
71
72
it('should dblclick the div', async () => {
73
await page.setContent(`<div style='width: 100px; height: 100px;'>Click me</div>`);
74
await page.evaluate(`(() => {
75
window.dblclickPromise = new Promise(resolve => {
76
document.querySelector('div').addEventListener('dblclick', event => {
77
resolve({
78
type: event.type,
79
detail: event.detail,
80
clientX: event.clientX,
81
clientY: event.clientY,
82
isTrusted: event.isTrusted,
83
button: event.button,
84
});
85
});
86
});
87
})()`);
88
await page.mouse.move(50, 60);
89
const opts = { clickCount: 2 };
90
await page.mouse.down(opts);
91
await page.mouse.up(opts);
92
const event: any = await page.evaluate(`window.dblclickPromise`);
93
expect(event.type).toBe('dblclick');
94
expect(event.detail).toBe(2);
95
expect(event.clientX).toBe(50);
96
expect(event.clientY).toBe(60);
97
expect(event.isTrusted).toBe(true);
98
expect(event.button).toBe(0);
99
});
100
101
it('should select the text with mouse', async () => {
102
await page.goto(`${server.baseUrl}/input/textarea.html`);
103
await page.click('textarea');
104
const text = "This is the text that we are going to try to select. Let's see how it goes.";
105
await page.type(text);
106
// Firefox needs an extra frame here after typing or it will fail to set the scrollTop
107
await page.evaluate(`new Promise(requestAnimationFrame)`);
108
await page.evaluate(`(document.querySelector('textarea').scrollTop = 0)`);
109
const { x, y } = await page.evaluate(textareaDimensions);
110
await page.mouse.move(x + 2, y + 2);
111
await page.mouse.down();
112
await page.mouse.move(200, 200);
113
await page.mouse.up();
114
expect(
115
await page.evaluate(`(() => {
116
const textarea = document.querySelector('textarea');
117
return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
118
})()`),
119
).toBe(text);
120
});
121
122
it('should trigger hover state', async () => {
123
await page.goto(`${server.baseUrl}/input/scrollable.html`);
124
await hover(page, '#button-6');
125
expect(await page.evaluate(`document.querySelector('button:hover').id`)).toBe('button-6');
126
await hover(page, '#button-2');
127
expect(await page.evaluate(`document.querySelector('button:hover').id`)).toBe('button-2');
128
});
129
130
it('should trigger hover state on disabled button', async () => {
131
await page.goto(`${server.baseUrl}/input/scrollable.html`);
132
await page.evaluate('document.querySelector("#button-6").disabled = true');
133
await hover(page, '#button-6');
134
expect(await page.evaluate(`document.querySelector('button:hover').id`)).toBe('button-6');
135
});
136
137
it('should trigger hover state with removed window.Node', async () => {
138
await page.goto(`${server.baseUrl}/input/scrollable.html`);
139
await page.evaluate(`delete window.Node`);
140
await hover(page, '#button-6');
141
expect(await page.evaluate(` document.querySelector('button:hover').id`)).toBe('button-6');
142
});
143
144
it('should set modifier keys on click', async () => {
145
await page.goto(`${server.baseUrl}/input/scrollable.html`);
146
await page.evaluate(`document
147
.querySelector('#button-3')
148
.addEventListener('mousedown', e => (window.lastEvent = e), true);`);
149
150
const modifiers = new Map<IKeyboardKey, string>([
151
['Shift', 'shiftKey'],
152
['Control', 'ctrlKey'],
153
['Alt', 'altKey'],
154
['Meta', 'metaKey'],
155
]);
156
157
// In Firefox, the Meta modifier only exists on Mac
158
// if (options.FIREFOX && !MAC) delete modifiers.Meta;
159
for (const [modifier, key] of modifiers) {
160
await page.keyboard.down(modifier);
161
await page.click('#button-3');
162
if (!(await page.evaluate(`window.lastEvent['${key}']`)))
163
throw new Error(`${key} should be true`);
164
await page.keyboard.up(modifier);
165
}
166
await page.click('#button-3');
167
for (const [, key] of modifiers) {
168
const result = await page.evaluate(`window.lastEvent['${key}']`);
169
expect(result).not.toBe(true);
170
}
171
});
172
173
// PUPPETEER doesn't support drag and drop. Playwright has a solution that dispatches events to the page, a la
174
// https://gist.github.com/wardnath/0aa9f293ee964c3a2bc149d9e924822e
175
// eslint-disable-next-line jest/no-disabled-tests
176
describe.skip('Drag and Drop', () => {
177
it('should work', async () => {
178
await page.goto(`${server.baseUrl}/drag-n-drop.html`);
179
await hover(page, '#source');
180
await page.mouse.down();
181
await hover(page, '#target');
182
await page.mouse.up();
183
expect(
184
await page.evaluate(
185
`document.querySelector('#target').contains(document.querySelector('#source'))`,
186
),
187
).toBe(true); // could not find source in target
188
});
189
});
190
});
191
192
const textareaDimensions = `(()=>{
193
const rect = document.querySelector('textarea').getBoundingClientRect();
194
return {
195
x: rect.left,
196
y: rect.top,
197
width: rect.width,
198
height: rect.height,
199
};
200
})();`;
201
202
async function hover(page: ITestPage, selector: string) {
203
const dimensions: any = await page.evaluate(`(()=>{
204
const rect = document.querySelector('${selector}').getBoundingClientRect();
205
return {
206
x: rect.left,
207
y: rect.top,
208
width: rect.width,
209
height: rect.height,
210
scrollY: window.scrollY
211
};
212
})();`);
213
await page.mouse.move(dimensions.x, dimensions.y);
214
}
215
216