browsertime 26.3.2 → 27.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/README.md +1 -1
- package/browserscripts/timings/softNavigations.js +94 -0
- package/lib/chrome/har.js +36 -5
- package/lib/chrome/settings/chromeAndroidOptions.js +2 -1
- package/lib/chrome/webdriver/chromium.js +39 -0
- package/lib/chrome/webdriver/setupChromiumOptions.js +86 -31
- package/lib/core/engine/collector.js +340 -317
- package/lib/core/engine/command/addText.js +93 -12
- package/lib/core/engine/command/click.js +239 -139
- package/lib/core/engine/command/commandHelper.js +45 -0
- package/lib/core/engine/command/cookie.js +85 -0
- package/lib/core/engine/command/element.js +37 -2
- package/lib/core/engine/command/javaScript.js +14 -24
- package/lib/core/engine/command/measure/screenshots.js +114 -0
- package/lib/core/engine/command/measure/video.js +85 -0
- package/lib/core/engine/command/measure.js +104 -180
- package/lib/core/engine/command/mouse/clickAndHold.js +6 -3
- package/lib/core/engine/command/mouse/contextClick.js +56 -4
- package/lib/core/engine/command/mouse/doubleClick.js +49 -1
- package/lib/core/engine/command/mouse/mouseMove.js +50 -1
- package/lib/core/engine/command/mouse/singleClick.js +165 -129
- package/lib/core/engine/command/navigation.js +6 -6
- package/lib/core/engine/command/select.js +95 -3
- package/lib/core/engine/command/selectorParser.js +58 -0
- package/lib/core/engine/command/set.js +55 -1
- package/lib/core/engine/command/wait.js +41 -2
- package/lib/core/engine/commands.js +550 -42
- package/lib/core/engine/index.js +206 -150
- package/lib/core/engine/iteration.js +269 -137
- package/lib/firefox/networkManager.js +22 -30
- package/lib/firefox/webdriver/builder.js +14 -0
- package/lib/safari/har.js +131 -0
- package/lib/safari/native/ios-capture +0 -0
- package/lib/safari/native/ios-capture.m +340 -0
- package/lib/safari/safariInspectorClient.js +322 -0
- package/lib/safari/webdriver/safari.js +303 -13
- package/lib/safari/webkitToCdpAdapter.js +241 -0
- package/lib/support/cli.js +13 -0
- package/lib/support/engineUtils.js +16 -5
- package/lib/support/getViewPort.js +5 -0
- package/lib/support/har/index.js +41 -3
- package/lib/support/usbPower.js +2 -3
- package/lib/video/postprocessing/finetune/addTextToVideo.js +4 -0
- package/lib/video/postprocessing/finetune/getFont.js +3 -3
- package/lib/video/postprocessing/finetune/getTimingMetrics.js +7 -0
- package/lib/video/postprocessing/finetune/index.js +26 -9
- package/lib/video/screenRecording/ios/iosDeviceRecorder.js +109 -0
- package/lib/video/screenRecording/recorder.js +2 -2
- package/lib/video/screenRecording/setOrangeBackground.js +3 -0
- package/package.json +11 -11
- package/types/core/engine/command/addText.d.ts +35 -6
- package/types/core/engine/command/addText.d.ts.map +1 -1
- package/types/core/engine/command/click.d.ts +87 -16
- package/types/core/engine/command/click.d.ts.map +1 -1
- package/types/core/engine/command/commandHelper.d.ts +16 -0
- package/types/core/engine/command/commandHelper.d.ts.map +1 -0
- package/types/core/engine/command/cookie.d.ts +65 -0
- package/types/core/engine/command/cookie.d.ts.map +1 -0
- package/types/core/engine/command/element.d.ts +19 -1
- package/types/core/engine/command/element.d.ts.map +1 -1
- package/types/core/engine/command/javaScript.d.ts +4 -0
- package/types/core/engine/command/javaScript.d.ts.map +1 -1
- package/types/core/engine/command/measure/screenshots.d.ts +15 -0
- package/types/core/engine/command/measure/screenshots.d.ts.map +1 -0
- package/types/core/engine/command/measure/video.d.ts +26 -0
- package/types/core/engine/command/measure/video.d.ts.map +1 -0
- package/types/core/engine/command/measure.d.ts +59 -33
- package/types/core/engine/command/measure.d.ts.map +1 -1
- package/types/core/engine/command/mouse/clickAndHold.d.ts.map +1 -1
- package/types/core/engine/command/mouse/contextClick.d.ts +28 -4
- package/types/core/engine/command/mouse/contextClick.d.ts.map +1 -1
- package/types/core/engine/command/mouse/doubleClick.d.ts +24 -4
- package/types/core/engine/command/mouse/doubleClick.d.ts.map +1 -1
- package/types/core/engine/command/mouse/mouseMove.d.ts +30 -5
- package/types/core/engine/command/mouse/mouseMove.d.ts.map +1 -1
- package/types/core/engine/command/mouse/singleClick.d.ts +50 -13
- package/types/core/engine/command/mouse/singleClick.d.ts.map +1 -1
- package/types/core/engine/command/navigation.d.ts +3 -3
- package/types/core/engine/command/select.d.ts +45 -8
- package/types/core/engine/command/select.d.ts.map +1 -1
- package/types/core/engine/command/selectorParser.d.ts +20 -0
- package/types/core/engine/command/selectorParser.d.ts.map +1 -0
- package/types/core/engine/command/set.d.ts +34 -7
- package/types/core/engine/command/set.d.ts.map +1 -1
- package/types/core/engine/command/wait.d.ts +29 -6
- package/types/core/engine/command/wait.d.ts.map +1 -1
- package/types/core/engine/commands.d.ts +296 -28
- package/types/core/engine/commands.d.ts.map +1 -1
- package/types/support/engineUtils.d.ts.map +1 -1
- package/types/support/getViewPort.d.ts.map +1 -1
- package/types/support/usbPower.d.ts.map +1 -1
- package/types/video/postprocessing/finetune/addTextToVideo.d.ts.map +1 -1
- package/types/video/postprocessing/finetune/getTimingMetrics.d.ts.map +1 -1
- package/types/video/postprocessing/finetune/index.d.ts.map +1 -1
- package/types/video/screenRecording/ios/iosDeviceRecorder.d.ts +28 -0
- package/types/video/screenRecording/ios/iosDeviceRecorder.d.ts.map +1 -0
- package/types/video/screenRecording/recorder.d.ts +2 -2
- package/types/video/screenRecording/recorder.d.ts.map +1 -1
- package/types/video/screenRecording/setOrangeBackground.d.ts.map +1 -1
- package/types/video/video.d.ts +1 -1
- package/types/video/video.d.ts.map +1 -1
- package/visualmetrics/visualmetrics-portable.py +136 -136
- package/lib/video/screenRecording/ios/convertToMp4.js +0 -22
- package/lib/video/screenRecording/ios/iosRecorder.js +0 -68
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import webdriver from 'selenium-webdriver';
|
|
2
|
+
|
|
3
|
+
import { parseSelector } from './command/selectorParser.js';
|
|
1
4
|
import { Actions } from './command/actions.js';
|
|
2
5
|
import { AddText } from './command/addText.js';
|
|
3
6
|
import { Click } from './command/click.js';
|
|
@@ -9,6 +12,7 @@ import { Switch } from './command/switch.js';
|
|
|
9
12
|
import { Screenshot } from './command/screenshot.js';
|
|
10
13
|
import { Set } from './command/set.js';
|
|
11
14
|
import { Cache } from './command/cache.js';
|
|
15
|
+
import { Cookie } from './command/cookie.js';
|
|
12
16
|
import { Meta } from './command/meta.js';
|
|
13
17
|
import { Watch as StopWatch } from './command/stopWatch.js';
|
|
14
18
|
import { Select } from './command/select.js';
|
|
@@ -36,7 +40,7 @@ import { SimplePerfProfiler } from './command/simpleperf.js';
|
|
|
36
40
|
* @hideconstructor
|
|
37
41
|
*/
|
|
38
42
|
export class Commands {
|
|
39
|
-
constructor(
|
|
43
|
+
constructor({
|
|
40
44
|
browser,
|
|
41
45
|
engineDelegate,
|
|
42
46
|
index,
|
|
@@ -50,8 +54,8 @@ export class Commands {
|
|
|
50
54
|
asyncScriptsByCategory,
|
|
51
55
|
postURLScripts,
|
|
52
56
|
options
|
|
53
|
-
) {
|
|
54
|
-
const measure = new Measure(
|
|
57
|
+
}) {
|
|
58
|
+
const measure = new Measure({
|
|
55
59
|
browser,
|
|
56
60
|
index,
|
|
57
61
|
pageCompleteCheck,
|
|
@@ -65,7 +69,7 @@ export class Commands {
|
|
|
65
69
|
context,
|
|
66
70
|
screenshotManager,
|
|
67
71
|
options
|
|
68
|
-
);
|
|
72
|
+
});
|
|
69
73
|
|
|
70
74
|
/**
|
|
71
75
|
* Provides functionality to collect perfetto traces.
|
|
@@ -118,9 +122,36 @@ export class Commands {
|
|
|
118
122
|
|
|
119
123
|
/**
|
|
120
124
|
* Provides functionality to perform click actions on elements in a web page using various selectors.
|
|
121
|
-
*
|
|
125
|
+
* Can be called directly as a function with a unified selector string, or use the
|
|
126
|
+
* existing by* methods for backward compatibility.
|
|
127
|
+
* @type {Function & Click}
|
|
128
|
+
*/
|
|
129
|
+
const clickInstance = new Click(browser, pageCompleteCheck, options);
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Click on an element using a unified selector string.
|
|
133
|
+
* Supports CSS selectors (default), and prefixes: 'id:', 'xpath:', 'text:', 'link:', 'name:', 'class:'.
|
|
134
|
+
* @async
|
|
135
|
+
* @example await commands.click('#login-btn');
|
|
136
|
+
* @example await commands.click('id:login-btn');
|
|
137
|
+
* @example await commands.click('text:Submit', { waitForNavigation: true });
|
|
138
|
+
* @param {string} selector - The selector string.
|
|
139
|
+
* @param {Object} [clickOptions] - Options for the click.
|
|
140
|
+
* @param {boolean} [clickOptions.waitForNavigation=false] - If true, waits for the page to complete loading after clicking.
|
|
141
|
+
* @returns {Promise<void>}
|
|
122
142
|
*/
|
|
123
|
-
this.click =
|
|
143
|
+
this.click = async (selector, clickOptions) =>
|
|
144
|
+
clickInstance.run(selector, clickOptions);
|
|
145
|
+
// Preserve all existing by* methods for backward compatibility
|
|
146
|
+
for (const key of Object.getOwnPropertyNames(
|
|
147
|
+
Object.getPrototypeOf(clickInstance)
|
|
148
|
+
)) {
|
|
149
|
+
if (key !== 'constructor' && typeof clickInstance[key] === 'function') {
|
|
150
|
+
this.click[key] = clickInstance[key].bind(clickInstance);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
measure.setClick(this.click);
|
|
124
155
|
|
|
125
156
|
/**
|
|
126
157
|
* Provides functionality to control page scrolling in the browser.
|
|
@@ -130,15 +161,61 @@ export class Commands {
|
|
|
130
161
|
|
|
131
162
|
/**
|
|
132
163
|
* Provides functionality to add text to elements on a web page using various selectors.
|
|
133
|
-
*
|
|
164
|
+
* Can be called directly as a function with a unified selector string, or use the
|
|
165
|
+
* existing by* methods for backward compatibility.
|
|
166
|
+
* @type {Function & AddText}
|
|
134
167
|
*/
|
|
135
|
-
|
|
168
|
+
const addTextInstance = new AddText(browser, options);
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Add text to an element using a unified selector string.
|
|
172
|
+
* @async
|
|
173
|
+
* @example await commands.addText('#search-input', 'search term');
|
|
174
|
+
* @example await commands.addText('id:username', 'myuser');
|
|
175
|
+
* @param {string} selector - The selector string.
|
|
176
|
+
* @param {string} text - The text to add.
|
|
177
|
+
* @returns {Promise<void>}
|
|
178
|
+
*/
|
|
179
|
+
this.addText = async (selector, text) =>
|
|
180
|
+
addTextInstance.run(selector, text);
|
|
181
|
+
// Preserve all existing by* methods for backward compatibility
|
|
182
|
+
for (const key of Object.getOwnPropertyNames(
|
|
183
|
+
Object.getPrototypeOf(addTextInstance)
|
|
184
|
+
)) {
|
|
185
|
+
if (key !== 'constructor' && typeof addTextInstance[key] === 'function') {
|
|
186
|
+
this.addText[key] = addTextInstance[key].bind(addTextInstance);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
136
189
|
|
|
137
190
|
/**
|
|
138
191
|
* Provides functionality to wait for different conditions in the browser.
|
|
139
|
-
*
|
|
192
|
+
* Can be called directly as a function with a unified selector string, or use the
|
|
193
|
+
* existing by* methods for backward compatibility.
|
|
194
|
+
* @type {Function & Wait}
|
|
195
|
+
*/
|
|
196
|
+
const waitInstance = new Wait(browser, pageCompleteCheck);
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Wait for an element using a unified selector string.
|
|
200
|
+
* @async
|
|
201
|
+
* @example await commands.wait('#my-element', { timeout: 5000 });
|
|
202
|
+
* @example await commands.wait('id:loaded', { timeout: 3000, visible: true });
|
|
203
|
+
* @param {string} selector - The selector string.
|
|
204
|
+
* @param {Object} [waitOptions] - Options for waiting.
|
|
205
|
+
* @param {number} [waitOptions.timeout=6000] - Maximum time to wait in ms.
|
|
206
|
+
* @param {boolean} [waitOptions.visible=false] - Wait for visibility.
|
|
207
|
+
* @returns {Promise<void>}
|
|
140
208
|
*/
|
|
141
|
-
this.wait =
|
|
209
|
+
this.wait = async (selector, waitOptions) =>
|
|
210
|
+
waitInstance.run(selector, waitOptions);
|
|
211
|
+
// Preserve all existing by* methods for backward compatibility
|
|
212
|
+
for (const key of Object.getOwnPropertyNames(
|
|
213
|
+
Object.getPrototypeOf(waitInstance)
|
|
214
|
+
)) {
|
|
215
|
+
if (key !== 'constructor' && typeof waitInstance[key] === 'function') {
|
|
216
|
+
this.wait[key] = waitInstance[key].bind(waitInstance);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
142
219
|
|
|
143
220
|
/**
|
|
144
221
|
* Provides functionality for measuring a navigation.
|
|
@@ -197,9 +274,32 @@ export class Commands {
|
|
|
197
274
|
|
|
198
275
|
/**
|
|
199
276
|
* Sets values on HTML elements in the page.
|
|
200
|
-
*
|
|
277
|
+
* Can be called directly as a function with a unified selector string, or use the
|
|
278
|
+
* existing by* methods for backward compatibility.
|
|
279
|
+
* @type {Function & Set}
|
|
280
|
+
*/
|
|
281
|
+
const setInstance = new Set(browser, options);
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Set a property on an element using a unified selector string.
|
|
285
|
+
* @async
|
|
286
|
+
* @example await commands.set('#field', 'new value');
|
|
287
|
+
* @example await commands.set('id:title', '<h1>Hello</h1>', 'innerHTML');
|
|
288
|
+
* @param {string} selector - The selector string for the element.
|
|
289
|
+
* @param {string} setValue - The value to set.
|
|
290
|
+
* @param {string} [property='value'] - The property: 'value', 'innerText', or 'innerHTML'.
|
|
291
|
+
* @returns {Promise<void>}
|
|
201
292
|
*/
|
|
202
|
-
this.set =
|
|
293
|
+
this.set = async (selector, setValue, property) =>
|
|
294
|
+
setInstance.run(selector, setValue, property);
|
|
295
|
+
// Preserve all existing by* methods for backward compatibility
|
|
296
|
+
for (const key of Object.getOwnPropertyNames(
|
|
297
|
+
Object.getPrototypeOf(setInstance)
|
|
298
|
+
)) {
|
|
299
|
+
if (key !== 'constructor' && typeof setInstance[key] === 'function') {
|
|
300
|
+
this.set[key] = setInstance[key].bind(setInstance);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
203
303
|
|
|
204
304
|
/**
|
|
205
305
|
* Stopwatch utility for measuring time intervals.
|
|
@@ -213,6 +313,12 @@ export class Commands {
|
|
|
213
313
|
*/
|
|
214
314
|
this.cache = new Cache(browser, options.browser, cdp);
|
|
215
315
|
|
|
316
|
+
/**
|
|
317
|
+
* Manages browser cookies — get, set, delete individual or all cookies.
|
|
318
|
+
* @type {Cookie}
|
|
319
|
+
*/
|
|
320
|
+
this.cookie = new Cookie(browser);
|
|
321
|
+
|
|
216
322
|
/**
|
|
217
323
|
* Adds metadata to the user journey.
|
|
218
324
|
* @type {Meta}
|
|
@@ -255,39 +361,112 @@ export class Commands {
|
|
|
255
361
|
* Interact with the page using the mouse.
|
|
256
362
|
* @type {Object}
|
|
257
363
|
*/
|
|
364
|
+
const singleClickInstance = new SingleClick(
|
|
365
|
+
browser,
|
|
366
|
+
pageCompleteCheck,
|
|
367
|
+
options
|
|
368
|
+
);
|
|
369
|
+
const doubleClickInstance = new DoubleClick(
|
|
370
|
+
browser,
|
|
371
|
+
pageCompleteCheck,
|
|
372
|
+
options
|
|
373
|
+
);
|
|
374
|
+
const contextClickInstance = new ContextClick(browser, options);
|
|
375
|
+
const mouseMoveInstance = new MouseMove(browser, options);
|
|
376
|
+
const clickAndHoldInstance = new ClickAndHold(browser);
|
|
377
|
+
|
|
258
378
|
this.mouse = {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
* Perform a context click (right-click) on elements in a web page.
|
|
266
|
-
* @type {ContextClick}
|
|
267
|
-
*/
|
|
268
|
-
contextClick: new ContextClick(browser),
|
|
269
|
-
/**
|
|
270
|
-
* Provides functionality to perform a single click action on elements or at specific positions in a web page.
|
|
271
|
-
* @type {SingleClick}
|
|
272
|
-
*/
|
|
273
|
-
singleClick: new SingleClick(browser, pageCompleteCheck),
|
|
274
|
-
/**
|
|
275
|
-
* Provides functionality to perform a double-click action on elements in a web page.
|
|
276
|
-
* @type {DoubleClick}
|
|
277
|
-
*/
|
|
278
|
-
doubleClick: new DoubleClick(browser, pageCompleteCheck),
|
|
279
|
-
/**
|
|
280
|
-
* Provides functionality to click and hold elements on a web page using different strategies.
|
|
281
|
-
* @type {ClickAndHold}
|
|
282
|
-
*/
|
|
283
|
-
clickAndHold: new ClickAndHold(browser)
|
|
379
|
+
moveTo: async selector => mouseMoveInstance.run(selector),
|
|
380
|
+
singleClick: async (selector, mouseOptions) =>
|
|
381
|
+
singleClickInstance.run(selector, mouseOptions),
|
|
382
|
+
doubleClick: async selector => doubleClickInstance.run(selector),
|
|
383
|
+
contextClick: async selector => contextClickInstance.run(selector),
|
|
384
|
+
clickAndHold: clickAndHoldInstance
|
|
284
385
|
};
|
|
285
386
|
|
|
387
|
+
// Preserve backward-compatible methods on each mouse command
|
|
388
|
+
for (const key of Object.getOwnPropertyNames(
|
|
389
|
+
Object.getPrototypeOf(mouseMoveInstance)
|
|
390
|
+
)) {
|
|
391
|
+
if (
|
|
392
|
+
key !== 'constructor' &&
|
|
393
|
+
typeof mouseMoveInstance[key] === 'function'
|
|
394
|
+
) {
|
|
395
|
+
this.mouse.moveTo[key] = mouseMoveInstance[key].bind(mouseMoveInstance);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
for (const key of Object.getOwnPropertyNames(
|
|
399
|
+
Object.getPrototypeOf(singleClickInstance)
|
|
400
|
+
)) {
|
|
401
|
+
if (
|
|
402
|
+
key !== 'constructor' &&
|
|
403
|
+
typeof singleClickInstance[key] === 'function'
|
|
404
|
+
) {
|
|
405
|
+
this.mouse.singleClick[key] =
|
|
406
|
+
singleClickInstance[key].bind(singleClickInstance);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
for (const key of Object.getOwnPropertyNames(
|
|
410
|
+
Object.getPrototypeOf(doubleClickInstance)
|
|
411
|
+
)) {
|
|
412
|
+
if (
|
|
413
|
+
key !== 'constructor' &&
|
|
414
|
+
typeof doubleClickInstance[key] === 'function'
|
|
415
|
+
) {
|
|
416
|
+
this.mouse.doubleClick[key] =
|
|
417
|
+
doubleClickInstance[key].bind(doubleClickInstance);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
for (const key of Object.getOwnPropertyNames(
|
|
421
|
+
Object.getPrototypeOf(contextClickInstance)
|
|
422
|
+
)) {
|
|
423
|
+
if (
|
|
424
|
+
key !== 'constructor' &&
|
|
425
|
+
typeof contextClickInstance[key] === 'function'
|
|
426
|
+
) {
|
|
427
|
+
this.mouse.contextClick[key] =
|
|
428
|
+
contextClickInstance[key].bind(contextClickInstance);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
286
432
|
/**
|
|
287
433
|
* Interact with a select element.
|
|
288
|
-
*
|
|
434
|
+
* Can be called directly as a function with a unified selector string, or use the
|
|
435
|
+
* existing by* methods for backward compatibility.
|
|
436
|
+
* @type {Function & Select}
|
|
437
|
+
*/
|
|
438
|
+
const selectInstance = new Select(browser, options);
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Select an option in a select element using a unified selector string.
|
|
442
|
+
* @async
|
|
443
|
+
* @example await commands.select('#country', 'SE');
|
|
444
|
+
* @example await commands.select('id:language', 'en');
|
|
445
|
+
* @param {string} selector - The selector string for the select element.
|
|
446
|
+
* @param {string} selectValue - The value of the option to select.
|
|
447
|
+
* @returns {Promise<void>}
|
|
448
|
+
*/
|
|
449
|
+
this.select = async (selector, selectValue) =>
|
|
450
|
+
selectInstance.run(selector, selectValue);
|
|
451
|
+
// Preserve all existing by* methods for backward compatibility
|
|
452
|
+
for (const key of Object.getOwnPropertyNames(
|
|
453
|
+
Object.getPrototypeOf(selectInstance)
|
|
454
|
+
)) {
|
|
455
|
+
if (key !== 'constructor' && typeof selectInstance[key] === 'function') {
|
|
456
|
+
this.select[key] = selectInstance[key].bind(selectInstance);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Select an option in a select element by its visible text.
|
|
462
|
+
* @async
|
|
463
|
+
* @example await commands.select.byText('#country', 'Sweden');
|
|
464
|
+
* @param {string} selector - The selector string for the select element.
|
|
465
|
+
* @param {string} text - The visible text of the option to select.
|
|
466
|
+
* @returns {Promise<void>}
|
|
289
467
|
*/
|
|
290
|
-
this.select =
|
|
468
|
+
this.select.byText = async (selector, text) =>
|
|
469
|
+
selectInstance.runByText(selector, text);
|
|
291
470
|
|
|
292
471
|
/**
|
|
293
472
|
* Selenium's action sequence functionality.
|
|
@@ -297,9 +476,338 @@ export class Commands {
|
|
|
297
476
|
this.action = new Actions(browser);
|
|
298
477
|
|
|
299
478
|
/**
|
|
300
|
-
*
|
|
301
|
-
* @
|
|
479
|
+
* Types text into an element identified by a CSS selector.
|
|
480
|
+
* This is a convenience method that wraps {@link AddText#bySelector addText.bySelector}
|
|
481
|
+
* with a more conventional parameter order (selector first, then text).
|
|
482
|
+
* @async
|
|
483
|
+
* @example await commands.type('#search-input', 'search term');
|
|
484
|
+
* @param {string} selector - The CSS selector of the element.
|
|
485
|
+
* @param {string} text - The text to type into the element.
|
|
486
|
+
* @returns {Promise<void>} A promise that resolves when the text has been added.
|
|
487
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
488
|
+
* @type {Function}
|
|
489
|
+
*/
|
|
490
|
+
this.type = async (selector, text) => {
|
|
491
|
+
return this.addText(selector, text);
|
|
492
|
+
};
|
|
493
|
+
this.element = new Element(browser, options);
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Finds an element using a CSS selector, with optional waiting and visibility check.
|
|
497
|
+
* @async
|
|
498
|
+
* @example const element = await commands.find('.my-element', { timeout: 5000, visible: true });
|
|
499
|
+
* @param {string} selector - The CSS selector of the element.
|
|
500
|
+
* @param {Object} [options] - Options for finding the element.
|
|
501
|
+
* @param {number} [options.timeout] - Maximum time in milliseconds to wait for the element. Defaults to the configured --timeouts.elementWait value.
|
|
502
|
+
* @param {boolean} [options.visible=false] - If true, waits for the element to be visible.
|
|
503
|
+
* @returns {Promise<WebElement>} A promise that resolves to the WebElement found.
|
|
504
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
505
|
+
* @type {Function}
|
|
506
|
+
*/
|
|
507
|
+
this.find = this.element.find.bind(this.element);
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Checks if an element matching the selector exists in the DOM.
|
|
511
|
+
* Unlike find(), this does not throw if the element is not found.
|
|
512
|
+
* @async
|
|
513
|
+
* @example
|
|
514
|
+
* if (await commands.exists('#cookie-banner')) {
|
|
515
|
+
* await commands.click('#accept-cookies');
|
|
516
|
+
* }
|
|
517
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
518
|
+
* @param {Object} [existsOptions] - Options.
|
|
519
|
+
* @param {number} [existsOptions.timeout=0] - Maximum time to wait for the element. Default 0 (no wait).
|
|
520
|
+
* @returns {Promise<boolean>} True if the element exists.
|
|
521
|
+
* @type {Function}
|
|
522
|
+
*/
|
|
523
|
+
this.exists = async (selector, existsOptions = {}) => {
|
|
524
|
+
const { locator } = parseSelector(selector);
|
|
525
|
+
const timeout = existsOptions.timeout ?? 0;
|
|
526
|
+
const driver = browser.getDriver();
|
|
527
|
+
try {
|
|
528
|
+
await (timeout > 0
|
|
529
|
+
? driver.wait(webdriver.until.elementLocated(locator), timeout)
|
|
530
|
+
: driver.findElement(locator));
|
|
531
|
+
return true;
|
|
532
|
+
} catch {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
const findElement = async selector => {
|
|
538
|
+
const { locator } = parseSelector(selector);
|
|
539
|
+
const driver = browser.getDriver();
|
|
540
|
+
const timeout = options?.timeouts?.elementWait ?? 0;
|
|
541
|
+
if (timeout > 0) {
|
|
542
|
+
await driver.wait(webdriver.until.elementLocated(locator), timeout);
|
|
543
|
+
}
|
|
544
|
+
return driver.findElement(locator);
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Gets the visible text of an element matching the selector.
|
|
549
|
+
* @async
|
|
550
|
+
* @example const text = await commands.getText('#greeting');
|
|
551
|
+
* @example const text = await commands.getText('id:heading');
|
|
552
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
553
|
+
* @returns {Promise<string>} The visible text content.
|
|
554
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
555
|
+
* @type {Function}
|
|
556
|
+
*/
|
|
557
|
+
this.getText = async selector => {
|
|
558
|
+
const element = await findElement(selector);
|
|
559
|
+
return element.getText();
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Gets the value of a form element matching the selector.
|
|
564
|
+
* @async
|
|
565
|
+
* @example const value = await commands.getValue('#price');
|
|
566
|
+
* @example const value = await commands.getValue('id:email');
|
|
567
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
568
|
+
* @returns {Promise<string>} The element's value.
|
|
569
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
570
|
+
* @type {Function}
|
|
571
|
+
*/
|
|
572
|
+
this.getValue = async selector => {
|
|
573
|
+
const element = await findElement(selector);
|
|
574
|
+
return element.getAttribute('value');
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Checks if an element matching the selector is visible/displayed.
|
|
579
|
+
* @async
|
|
580
|
+
* @example const visible = await commands.isVisible('#error-message');
|
|
581
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
582
|
+
* @returns {Promise<boolean>} True if the element is visible.
|
|
583
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
584
|
+
* @type {Function}
|
|
585
|
+
*/
|
|
586
|
+
this.isVisible = async selector => {
|
|
587
|
+
const element = await findElement(selector);
|
|
588
|
+
return element.isDisplayed();
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Gets an attribute value of an element matching the selector.
|
|
593
|
+
* @async
|
|
594
|
+
* @example const href = await commands.getAttribute('#my-link', 'href');
|
|
595
|
+
* @example const dataId = await commands.getAttribute('id:item', 'data-id');
|
|
596
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
597
|
+
* @param {string} attribute - The attribute name.
|
|
598
|
+
* @returns {Promise<string>} The attribute value.
|
|
599
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
600
|
+
* @type {Function}
|
|
601
|
+
*/
|
|
602
|
+
this.getAttribute = async (selector, attribute) => {
|
|
603
|
+
const element = await findElement(selector);
|
|
604
|
+
return element.getAttribute(attribute);
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Checks if a form element matching the selector is enabled.
|
|
609
|
+
* @async
|
|
610
|
+
* @example const enabled = await commands.isEnabled('#submit-btn');
|
|
611
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
612
|
+
* @returns {Promise<boolean>} True if the element is enabled.
|
|
613
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
614
|
+
* @type {Function}
|
|
615
|
+
*/
|
|
616
|
+
this.isEnabled = async selector => {
|
|
617
|
+
const element = await findElement(selector);
|
|
618
|
+
return element.isEnabled();
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Checks if a checkbox or radio button is selected/checked.
|
|
623
|
+
* @async
|
|
624
|
+
* @example const checked = await commands.isChecked('#agree-checkbox');
|
|
625
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
626
|
+
* @returns {Promise<boolean>} True if the element is checked/selected.
|
|
627
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
628
|
+
* @type {Function}
|
|
629
|
+
*/
|
|
630
|
+
this.isChecked = async selector => {
|
|
631
|
+
const element = await findElement(selector);
|
|
632
|
+
return element.isSelected();
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Clears the content of a form element matching the selector.
|
|
637
|
+
* @async
|
|
638
|
+
* @example await commands.clear('#search-input');
|
|
639
|
+
* @example await commands.clear('id:email');
|
|
640
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
641
|
+
* @returns {Promise<void>} A promise that resolves when the element is cleared.
|
|
642
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
643
|
+
* @type {Function}
|
|
644
|
+
*/
|
|
645
|
+
this.clear = async selector => {
|
|
646
|
+
const { locator } = parseSelector(selector);
|
|
647
|
+
const driver = browser.getDriver();
|
|
648
|
+
const timeout = options?.timeouts?.elementWait ?? 0;
|
|
649
|
+
if (timeout > 0) {
|
|
650
|
+
await driver.wait(webdriver.until.elementLocated(locator), timeout);
|
|
651
|
+
}
|
|
652
|
+
const element = await driver.findElement(locator);
|
|
653
|
+
return element.clear();
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Fills multiple form fields at once. Each key is a selector, each value is the text to type.
|
|
658
|
+
* @async
|
|
659
|
+
* @example
|
|
660
|
+
* await commands.fill({
|
|
661
|
+
* '#username': 'admin',
|
|
662
|
+
* '#password': 'secret',
|
|
663
|
+
* 'id:email': 'user@example.com'
|
|
664
|
+
* });
|
|
665
|
+
* @param {Object<string, string>} fields - An object mapping selectors to values.
|
|
666
|
+
* @returns {Promise<void>} A promise that resolves when all fields are filled.
|
|
667
|
+
* @throws {Error} Throws an error if any element is not found.
|
|
668
|
+
* @type {Function}
|
|
669
|
+
*/
|
|
670
|
+
this.fill = async fields => {
|
|
671
|
+
for (const [selector, text] of Object.entries(fields)) {
|
|
672
|
+
await this.addText(selector, text);
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Hovers over an element matching the selector. This is a convenience
|
|
678
|
+
* alias for commands.mouse.moveTo(selector).
|
|
679
|
+
* @async
|
|
680
|
+
* @example await commands.hover('#menu-item');
|
|
681
|
+
* @example await commands.hover('id:tooltip-trigger');
|
|
682
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
683
|
+
* @returns {Promise<void>}
|
|
684
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
685
|
+
* @type {Function}
|
|
686
|
+
*/
|
|
687
|
+
this.hover = async selector => {
|
|
688
|
+
return this.mouse.moveTo(selector);
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Presses a keyboard key. Use key names like 'Enter', 'Tab', 'Escape',
|
|
693
|
+
* 'Backspace', 'ArrowUp', 'ArrowDown', etc.
|
|
694
|
+
* @async
|
|
695
|
+
* @example await commands.press('Enter');
|
|
696
|
+
* @example await commands.press('Tab');
|
|
697
|
+
* @example await commands.press('Escape');
|
|
698
|
+
* @param {string} key - The key name to press (e.g. 'Enter', 'Tab', 'Escape').
|
|
699
|
+
* @returns {Promise<void>}
|
|
700
|
+
* @type {Function}
|
|
701
|
+
*/
|
|
702
|
+
this.press = async key => {
|
|
703
|
+
const keyValue = webdriver.Key[key.toUpperCase()] || key;
|
|
704
|
+
const driver = browser.getDriver();
|
|
705
|
+
await driver.actions({ async: true }).sendKeys(keyValue).perform();
|
|
706
|
+
return driver.actions().clear();
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Gets the title of the current page.
|
|
711
|
+
* @async
|
|
712
|
+
* @example const title = await commands.getTitle();
|
|
713
|
+
* @returns {Promise<string>} The page title.
|
|
714
|
+
* @type {Function}
|
|
715
|
+
*/
|
|
716
|
+
this.getTitle = async () => {
|
|
717
|
+
return browser.getDriver().getTitle();
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Gets the URL of the current page.
|
|
722
|
+
* @async
|
|
723
|
+
* @example const url = await commands.getUrl();
|
|
724
|
+
* @returns {Promise<string>} The current URL.
|
|
725
|
+
* @type {Function}
|
|
726
|
+
*/
|
|
727
|
+
this.getUrl = async () => {
|
|
728
|
+
return browser.getDriver().getCurrentUrl();
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Checks a checkbox or radio button. Does nothing if already checked.
|
|
733
|
+
* @async
|
|
734
|
+
* @example await commands.check('#agree-terms');
|
|
735
|
+
* @example await commands.check('id:newsletter-opt-in');
|
|
736
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
737
|
+
* @returns {Promise<void>}
|
|
738
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
739
|
+
* @type {Function}
|
|
740
|
+
*/
|
|
741
|
+
this.check = async selector => {
|
|
742
|
+
const element = await findElement(selector);
|
|
743
|
+
const checked = await element.isSelected();
|
|
744
|
+
if (!checked) {
|
|
745
|
+
return element.click();
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Unchecks a checkbox. Does nothing if already unchecked.
|
|
751
|
+
* @async
|
|
752
|
+
* @example await commands.uncheck('#agree-terms');
|
|
753
|
+
* @example await commands.uncheck('id:newsletter-opt-in');
|
|
754
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
755
|
+
* @returns {Promise<void>}
|
|
756
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
757
|
+
* @type {Function}
|
|
302
758
|
*/
|
|
303
|
-
this.
|
|
759
|
+
this.uncheck = async selector => {
|
|
760
|
+
const element = await findElement(selector);
|
|
761
|
+
const checked = await element.isSelected();
|
|
762
|
+
if (checked) {
|
|
763
|
+
return element.click();
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Scrolls the page so that the element matching the selector is visible in the viewport.
|
|
769
|
+
* @async
|
|
770
|
+
* @example await commands.scrollIntoView('#footer');
|
|
771
|
+
* @example await commands.scrollIntoView('id:comments-section');
|
|
772
|
+
* @param {string} selector - The CSS selector or prefixed selector.
|
|
773
|
+
* @returns {Promise<void>}
|
|
774
|
+
* @throws {Error} Throws an error if the element is not found.
|
|
775
|
+
* @type {Function}
|
|
776
|
+
*/
|
|
777
|
+
this.scrollIntoView = async selector => {
|
|
778
|
+
const element = await findElement(selector);
|
|
779
|
+
return browser
|
|
780
|
+
.getDriver()
|
|
781
|
+
.executeScript(
|
|
782
|
+
'arguments[0].scrollIntoView({block: "center", behavior: "instant"})',
|
|
783
|
+
element
|
|
784
|
+
);
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Waits until the browser's current URL contains the given string.
|
|
789
|
+
* Useful after form submissions, login redirects, or SPA navigation.
|
|
790
|
+
* @async
|
|
791
|
+
* @example await commands.waitForUrl('dashboard');
|
|
792
|
+
* @example await commands.waitForUrl('/account', { timeout: 10000 });
|
|
793
|
+
* @param {string} pattern - The string to match against the current URL.
|
|
794
|
+
* @param {Object} [urlOptions] - Options.
|
|
795
|
+
* @param {number} [urlOptions.timeout=10000] - Maximum time to wait in milliseconds.
|
|
796
|
+
* @returns {Promise<void>}
|
|
797
|
+
* @throws {Error} Throws an error if the URL does not match within the timeout.
|
|
798
|
+
* @type {Function}
|
|
799
|
+
*/
|
|
800
|
+
this.waitForUrl = async (pattern, urlOptions = {}) => {
|
|
801
|
+
const timeout = urlOptions.timeout ?? 10_000;
|
|
802
|
+
const driver = browser.getDriver();
|
|
803
|
+
try {
|
|
804
|
+
await driver.wait(webdriver.until.urlContains(pattern), timeout);
|
|
805
|
+
} catch {
|
|
806
|
+
const currentUrl = await driver.getCurrentUrl();
|
|
807
|
+
throw new Error(
|
|
808
|
+
`URL did not contain '${pattern}' within ${timeout} ms. Current URL: ${currentUrl}`
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
};
|
|
304
812
|
}
|
|
305
813
|
}
|