Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/client/lib/Interactor.ts
1028 views
1
import {
2
IInteractionGroup,
3
IInteractionGroups,
4
IInteractionStep,
5
IKeyboardCommand,
6
IMousePosition as ICoreMousePosition,
7
InteractionCommand as CoreCommand,
8
MouseButton,
9
} from '@secret-agent/interfaces/IInteractions';
10
import StateMachine from 'awaited-dom/base/StateMachine';
11
import { ISuperElement, ISuperNode } from 'awaited-dom/base/interfaces/super';
12
import AwaitedPath from 'awaited-dom/base/AwaitedPath';
13
import { IKeyboardKeyCode } from '@secret-agent/interfaces/IKeyboardLayoutUS';
14
import IInteractions, {
15
Command,
16
ICommand,
17
IInteraction,
18
IMousePosition,
19
} from '../interfaces/IInteractions';
20
import CoreFrameEnvironment from './CoreFrameEnvironment';
21
22
const { getState } = StateMachine<ISuperElement | ISuperNode, { awaitedPath: AwaitedPath }>();
23
24
const COMMAND_POS: { [k: string]: number } = {
25
waitForNode: 0,
26
waitForElementVisible: 1,
27
waitForMillis: 2,
28
click: 3,
29
doubleclick: 4,
30
clickDown: 5,
31
scroll: 6,
32
move: 7,
33
clickUp: 8,
34
keyPress: 9,
35
keyDown: 10,
36
type: 11,
37
keyUp: 12,
38
};
39
const MAX_COMMAND_POS = Object.keys(COMMAND_POS).length;
40
41
export default class Interactor {
42
public static async run(
43
coreFrame: CoreFrameEnvironment,
44
interactions: IInteractions,
45
): Promise<void> {
46
const interactionGroups = convertToInteractionGroups(interactions);
47
await coreFrame.interact(interactionGroups);
48
}
49
}
50
51
function convertToInteractionGroups(interactions: IInteractions): IInteractionGroups {
52
let lastPosition: ICoreMousePosition = [0, 0];
53
const interactionGroups: IInteractionGroups = [];
54
interactions.forEach(interaction => {
55
if (typeof interaction === 'string') {
56
const interactionStep: IInteractionStep = convertCommandToInteractionStep(interaction);
57
interactionStep.mousePosition = lastPosition;
58
interactionGroups.push([interactionStep]);
59
} else {
60
const interactionGroup = convertInteractionToInteractionGroup(interaction);
61
lastPosition = interactionGroup[interactionGroup.length - 1].mousePosition;
62
interactionGroups.push(interactionGroup);
63
}
64
});
65
return interactionGroups;
66
}
67
68
function convertToCoreMousePosition(mousePosition: IMousePosition): ICoreMousePosition {
69
if (Array.isArray(mousePosition)) {
70
return mousePosition;
71
}
72
const { awaitedPath } = getState(mousePosition);
73
if (!awaitedPath) throw new Error(`Element not found -> ${mousePosition}`);
74
return awaitedPath.toJSON();
75
}
76
77
function convertInteractionToInteractionGroup(interaction: IInteraction): IInteractionGroup {
78
const iGroup: IInteractionGroup = [];
79
80
Object.entries(interaction).forEach(([key, value]) => {
81
switch (key) {
82
case Command.scroll: {
83
const command = CoreCommand.scroll;
84
const mousePosition = convertToCoreMousePosition(value);
85
return iGroup.push({ command, mousePosition });
86
}
87
case Command.move: {
88
const command = CoreCommand.move;
89
const mousePosition = convertToCoreMousePosition(value);
90
return iGroup.push({ command, mousePosition });
91
}
92
93
case Command.click: {
94
const command = CoreCommand.click;
95
const mousePosition = convertToCoreMousePosition(value);
96
return iGroup.push({ command, mousePosition });
97
}
98
case Command.clickLeft: {
99
const command = CoreCommand.click;
100
const mousePosition = convertToCoreMousePosition(value);
101
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.left });
102
}
103
case Command.clickMiddle: {
104
const command = CoreCommand.click;
105
const mousePosition = convertToCoreMousePosition(value);
106
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.middle });
107
}
108
case Command.clickRight: {
109
const command = CoreCommand.click;
110
const mousePosition = convertToCoreMousePosition(value);
111
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.right });
112
}
113
114
case Command.clickUp: {
115
const command = CoreCommand.clickUp;
116
const mousePosition = convertToCoreMousePosition(value);
117
return iGroup.push({ command, mousePosition });
118
}
119
case Command.clickUpLeft: {
120
const command = CoreCommand.clickUp;
121
const mousePosition = convertToCoreMousePosition(value);
122
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.left });
123
}
124
case Command.clickUpMiddle: {
125
const command = CoreCommand.clickUp;
126
const mousePosition = convertToCoreMousePosition(value);
127
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.middle });
128
}
129
case Command.clickUpRight: {
130
const command = CoreCommand.clickUp;
131
const mousePosition = convertToCoreMousePosition(value);
132
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.right });
133
}
134
135
case Command.clickDown: {
136
const command = CoreCommand.clickDown;
137
const mousePosition = convertToCoreMousePosition(value);
138
return iGroup.push({ command, mousePosition });
139
}
140
case Command.clickDownLeft: {
141
const command = CoreCommand.clickDown;
142
const mousePosition = convertToCoreMousePosition(value);
143
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.left });
144
}
145
case Command.clickDownMiddle: {
146
const command = CoreCommand.clickDown;
147
const mousePosition = convertToCoreMousePosition(value);
148
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.middle });
149
}
150
case Command.clickDownRight: {
151
const command = CoreCommand.clickDown;
152
const mousePosition = convertToCoreMousePosition(value);
153
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.right });
154
}
155
156
case Command.doubleclick: {
157
const command = CoreCommand.doubleclick;
158
const mousePosition = convertToCoreMousePosition(value);
159
return iGroup.push({ command, mousePosition });
160
}
161
case Command.doubleclickLeft: {
162
const command = CoreCommand.doubleclick;
163
const mousePosition = convertToCoreMousePosition(value);
164
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.left });
165
}
166
case Command.doubleclickMiddle: {
167
const command = CoreCommand.doubleclick;
168
const mousePosition = convertToCoreMousePosition(value);
169
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.middle });
170
}
171
case Command.doubleclickRight: {
172
const command = CoreCommand.doubleclick;
173
const mousePosition = convertToCoreMousePosition(value);
174
return iGroup.push({ command, mousePosition, mouseButton: MouseButton.right });
175
}
176
177
case Command.keyPress: {
178
const command = CoreCommand.type;
179
return iGroup.push({ command, keyboardCommands: [{ keyCode: value as IKeyboardKeyCode }] });
180
}
181
case Command.keyDown: {
182
const command = CoreCommand.type;
183
return iGroup.push({ command, keyboardCommands: [{ down: value as IKeyboardKeyCode }] });
184
}
185
case Command.keyUp: {
186
const command = CoreCommand.type;
187
return iGroup.push({ command, keyboardCommands: [{ up: value as IKeyboardKeyCode }] });
188
}
189
case Command.type: {
190
const command = CoreCommand.type;
191
const keyboardCommands: IKeyboardCommand[] = [];
192
if (typeof value === 'string') {
193
keyboardCommands.push({ string: value });
194
} else if (typeof value === 'number') {
195
keyboardCommands.push({ keyCode: value as IKeyboardKeyCode });
196
}
197
return iGroup.push({ command, keyboardCommands });
198
}
199
200
case Command.waitForNode: {
201
const command = CoreCommand.waitForNode;
202
const { awaitedPath } = getState(value);
203
const jsPath = awaitedPath.toJSON();
204
return iGroup.push({ command, delayNode: jsPath });
205
}
206
case Command.waitForElementVisible: {
207
const command = CoreCommand.waitForElementVisible;
208
const { awaitedPath } = getState(value);
209
const jsPath = awaitedPath.toJSON();
210
return iGroup.push({ command, delayElement: jsPath });
211
}
212
case Command.waitForMillis: {
213
const command = CoreCommand.waitForMillis;
214
return iGroup.push({ command, delayMillis: value });
215
}
216
}
217
});
218
219
const sortedIGroup = iGroup.sort((a, b) => {
220
const aPos: number = COMMAND_POS[a.command] || MAX_COMMAND_POS;
221
const bPos: number = COMMAND_POS[b.command] || MAX_COMMAND_POS;
222
if (aPos < bPos) {
223
return -1;
224
}
225
if (aPos > bPos) {
226
return 1;
227
}
228
return 0;
229
});
230
231
return sortedIGroup;
232
}
233
234
function convertCommandToInteractionStep(interaction: ICommand): IInteractionStep {
235
switch (interaction) {
236
case Command.move: {
237
return { command: CoreCommand.move };
238
break;
239
}
240
case Command.scroll: {
241
return { command: CoreCommand.scroll };
242
break;
243
}
244
case Command.click: {
245
return { command: CoreCommand.click };
246
break;
247
}
248
case Command.clickLeft: {
249
return { command: CoreCommand.click, mouseButton: MouseButton.left };
250
break;
251
}
252
case Command.clickMiddle: {
253
return { command: CoreCommand.click, mouseButton: MouseButton.middle };
254
break;
255
}
256
case Command.clickRight: {
257
return { command: CoreCommand.click, mouseButton: MouseButton.right };
258
break;
259
}
260
case Command.doubleclick: {
261
return { command: CoreCommand.doubleclick };
262
break;
263
}
264
case Command.doubleclickLeft: {
265
return { command: CoreCommand.doubleclick, mouseButton: MouseButton.left };
266
break;
267
}
268
case Command.doubleclickMiddle: {
269
return { command: CoreCommand.doubleclick, mouseButton: MouseButton.middle };
270
break;
271
}
272
case Command.doubleclickRight: {
273
return { command: CoreCommand.doubleclick, mouseButton: MouseButton.right };
274
break;
275
}
276
case Command.clickUp: {
277
return { command: CoreCommand.clickUp };
278
break;
279
}
280
case Command.clickUpLeft: {
281
return { command: CoreCommand.clickUp, mouseButton: MouseButton.left };
282
break;
283
}
284
case Command.clickUpMiddle: {
285
return { command: CoreCommand.clickUp, mouseButton: MouseButton.middle };
286
break;
287
}
288
case Command.clickUpRight: {
289
return { command: CoreCommand.clickUp, mouseButton: MouseButton.right };
290
break;
291
}
292
case Command.clickDown: {
293
return { command: CoreCommand.clickDown };
294
break;
295
}
296
case Command.clickDownLeft: {
297
return { command: CoreCommand.clickDown, mouseButton: MouseButton.left };
298
break;
299
}
300
case Command.clickDownMiddle: {
301
return { command: CoreCommand.clickDown, mouseButton: MouseButton.middle };
302
break;
303
}
304
case Command.clickDownRight: {
305
return { command: CoreCommand.clickDown, mouseButton: MouseButton.right };
306
break;
307
}
308
default: {
309
throw new Error(`unsupported command string: ${interaction}`);
310
}
311
}
312
}
313
314