browsertime 26.3.1 → 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.
Files changed (106) hide show
  1. package/CHANGELOG.md +71 -2
  2. package/README.md +1 -1
  3. package/browserscripts/timings/softNavigations.js +94 -0
  4. package/lib/chrome/har.js +36 -5
  5. package/lib/chrome/settings/chromeAndroidOptions.js +2 -1
  6. package/lib/chrome/webdriver/chromium.js +39 -0
  7. package/lib/chrome/webdriver/setupChromiumOptions.js +86 -31
  8. package/lib/core/engine/collector.js +340 -317
  9. package/lib/core/engine/command/addText.js +93 -12
  10. package/lib/core/engine/command/click.js +239 -139
  11. package/lib/core/engine/command/commandHelper.js +45 -0
  12. package/lib/core/engine/command/cookie.js +85 -0
  13. package/lib/core/engine/command/element.js +37 -2
  14. package/lib/core/engine/command/javaScript.js +14 -24
  15. package/lib/core/engine/command/measure/screenshots.js +114 -0
  16. package/lib/core/engine/command/measure/video.js +85 -0
  17. package/lib/core/engine/command/measure.js +104 -180
  18. package/lib/core/engine/command/mouse/clickAndHold.js +6 -3
  19. package/lib/core/engine/command/mouse/contextClick.js +56 -4
  20. package/lib/core/engine/command/mouse/doubleClick.js +49 -1
  21. package/lib/core/engine/command/mouse/mouseMove.js +50 -1
  22. package/lib/core/engine/command/mouse/singleClick.js +165 -129
  23. package/lib/core/engine/command/navigation.js +6 -6
  24. package/lib/core/engine/command/select.js +95 -3
  25. package/lib/core/engine/command/selectorParser.js +58 -0
  26. package/lib/core/engine/command/set.js +55 -1
  27. package/lib/core/engine/command/wait.js +41 -2
  28. package/lib/core/engine/commands.js +550 -42
  29. package/lib/core/engine/index.js +206 -150
  30. package/lib/core/engine/iteration.js +269 -137
  31. package/lib/firefox/networkManager.js +22 -30
  32. package/lib/firefox/webdriver/builder.js +14 -0
  33. package/lib/firefox/webdriver/firefox.js +1 -1
  34. package/lib/safari/har.js +131 -0
  35. package/lib/safari/native/ios-capture +0 -0
  36. package/lib/safari/native/ios-capture.m +340 -0
  37. package/lib/safari/safariInspectorClient.js +322 -0
  38. package/lib/safari/webdriver/safari.js +303 -13
  39. package/lib/safari/webkitToCdpAdapter.js +241 -0
  40. package/lib/support/cli.js +13 -0
  41. package/lib/support/engineUtils.js +16 -5
  42. package/lib/support/getViewPort.js +5 -0
  43. package/lib/support/har/index.js +41 -3
  44. package/lib/support/usbPower.js +2 -3
  45. package/lib/video/postprocessing/finetune/addTextToVideo.js +4 -0
  46. package/lib/video/postprocessing/finetune/getFont.js +3 -3
  47. package/lib/video/postprocessing/finetune/getTimingMetrics.js +7 -0
  48. package/lib/video/postprocessing/finetune/index.js +26 -9
  49. package/lib/video/screenRecording/ios/iosDeviceRecorder.js +109 -0
  50. package/lib/video/screenRecording/recorder.js +2 -2
  51. package/lib/video/screenRecording/setOrangeBackground.js +3 -0
  52. package/package.json +12 -12
  53. package/types/core/engine/command/addText.d.ts +35 -6
  54. package/types/core/engine/command/addText.d.ts.map +1 -1
  55. package/types/core/engine/command/click.d.ts +87 -16
  56. package/types/core/engine/command/click.d.ts.map +1 -1
  57. package/types/core/engine/command/commandHelper.d.ts +16 -0
  58. package/types/core/engine/command/commandHelper.d.ts.map +1 -0
  59. package/types/core/engine/command/cookie.d.ts +65 -0
  60. package/types/core/engine/command/cookie.d.ts.map +1 -0
  61. package/types/core/engine/command/element.d.ts +19 -1
  62. package/types/core/engine/command/element.d.ts.map +1 -1
  63. package/types/core/engine/command/javaScript.d.ts +4 -0
  64. package/types/core/engine/command/javaScript.d.ts.map +1 -1
  65. package/types/core/engine/command/measure/screenshots.d.ts +15 -0
  66. package/types/core/engine/command/measure/screenshots.d.ts.map +1 -0
  67. package/types/core/engine/command/measure/video.d.ts +26 -0
  68. package/types/core/engine/command/measure/video.d.ts.map +1 -0
  69. package/types/core/engine/command/measure.d.ts +59 -33
  70. package/types/core/engine/command/measure.d.ts.map +1 -1
  71. package/types/core/engine/command/mouse/clickAndHold.d.ts.map +1 -1
  72. package/types/core/engine/command/mouse/contextClick.d.ts +28 -4
  73. package/types/core/engine/command/mouse/contextClick.d.ts.map +1 -1
  74. package/types/core/engine/command/mouse/doubleClick.d.ts +24 -4
  75. package/types/core/engine/command/mouse/doubleClick.d.ts.map +1 -1
  76. package/types/core/engine/command/mouse/mouseMove.d.ts +30 -5
  77. package/types/core/engine/command/mouse/mouseMove.d.ts.map +1 -1
  78. package/types/core/engine/command/mouse/singleClick.d.ts +50 -13
  79. package/types/core/engine/command/mouse/singleClick.d.ts.map +1 -1
  80. package/types/core/engine/command/navigation.d.ts +3 -3
  81. package/types/core/engine/command/select.d.ts +45 -8
  82. package/types/core/engine/command/select.d.ts.map +1 -1
  83. package/types/core/engine/command/selectorParser.d.ts +20 -0
  84. package/types/core/engine/command/selectorParser.d.ts.map +1 -0
  85. package/types/core/engine/command/set.d.ts +34 -7
  86. package/types/core/engine/command/set.d.ts.map +1 -1
  87. package/types/core/engine/command/wait.d.ts +29 -6
  88. package/types/core/engine/command/wait.d.ts.map +1 -1
  89. package/types/core/engine/commands.d.ts +296 -28
  90. package/types/core/engine/commands.d.ts.map +1 -1
  91. package/types/support/engineUtils.d.ts.map +1 -1
  92. package/types/support/getViewPort.d.ts.map +1 -1
  93. package/types/support/usbPower.d.ts.map +1 -1
  94. package/types/video/postprocessing/finetune/addTextToVideo.d.ts.map +1 -1
  95. package/types/video/postprocessing/finetune/getTimingMetrics.d.ts.map +1 -1
  96. package/types/video/postprocessing/finetune/index.d.ts.map +1 -1
  97. package/types/video/screenRecording/ios/iosDeviceRecorder.d.ts +28 -0
  98. package/types/video/screenRecording/ios/iosDeviceRecorder.d.ts.map +1 -0
  99. package/types/video/screenRecording/recorder.d.ts +2 -2
  100. package/types/video/screenRecording/recorder.d.ts.map +1 -1
  101. package/types/video/screenRecording/setOrangeBackground.d.ts.map +1 -1
  102. package/types/video/video.d.ts +1 -1
  103. package/types/video/video.d.ts.map +1 -1
  104. package/visualmetrics/visualmetrics-portable.py +136 -136
  105. package/lib/video/screenRecording/ios/convertToMp4.js +0 -22
  106. package/lib/video/screenRecording/ios/iosRecorder.js +0 -68
@@ -1,4 +1,7 @@
1
+ import webdriver from 'selenium-webdriver';
1
2
  import { getLogger } from '@sitespeed.io/log';
3
+ import { executeCommand } from './commandHelper.js';
4
+ import { parseSelector } from './selectorParser.js';
2
5
  const log = getLogger('browsertime.command.select');
3
6
 
4
7
  /**
@@ -8,17 +11,100 @@ const log = getLogger('browsertime.command.select');
8
11
  * @hideconstructor
9
12
  */
10
13
  export class Select {
11
- constructor(browser) {
14
+ constructor(browser, options) {
12
15
  /**
13
16
  * @private
14
17
  */
15
18
  this.browser = browser;
19
+ /**
20
+ * @private
21
+ */
22
+ this.options = options;
23
+ }
24
+
25
+ /**
26
+ * @private
27
+ */
28
+ async _waitForElement(driver, locator) {
29
+ const timeout = this.options?.timeouts?.elementWait ?? 0;
30
+ if (timeout > 0) {
31
+ await driver.wait(webdriver.until.elementLocated(locator), timeout);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Selects an option in a select element using a unified selector string and a value.
37
+ * Supports CSS selectors (default), and prefix-based strategies:
38
+ * 'id:mySelect', 'name:country', 'class:dropdown'.
39
+ *
40
+ * @async
41
+ * @param {string} selector - The selector string for the select element.
42
+ * @param {string} value - The value of the option to select.
43
+ * @returns {Promise<void>} A promise that resolves when the option is selected.
44
+ * @throws {Error} Throws an error if the select element is not found.
45
+ */
46
+ async run(selector, value) {
47
+ const { locator, description } = parseSelector(selector);
48
+ return executeCommand(
49
+ log,
50
+ 'Could not select value for %s',
51
+ description,
52
+ async () => {
53
+ const driver = this.browser.getDriver();
54
+ await this._waitForElement(driver, locator);
55
+ const element = await driver.findElement(locator);
56
+ await driver.executeScript(
57
+ `arguments[0].value = '${value}'; arguments[0].dispatchEvent(new Event('change'));`,
58
+ element
59
+ );
60
+ },
61
+ this.browser
62
+ );
63
+ }
64
+
65
+ /**
66
+ * Selects an option in a select element by its visible text using a unified selector string.
67
+ *
68
+ * @async
69
+ * @param {string} selector - The selector string for the select element.
70
+ * @param {string} text - The visible text of the option to select.
71
+ * @returns {Promise<void>} A promise that resolves when the option is selected.
72
+ * @throws {Error} Throws an error if the select element or option is not found.
73
+ */
74
+ async runByText(selector, text) {
75
+ const { locator, description } = parseSelector(selector);
76
+ return executeCommand(
77
+ log,
78
+ 'Could not select option by text for %s',
79
+ description,
80
+ async () => {
81
+ const driver = this.browser.getDriver();
82
+ await this._waitForElement(driver, locator);
83
+ const element = await driver.findElement(locator);
84
+ await driver.executeScript(
85
+ `const select = arguments[0];
86
+ const text = arguments[1];
87
+ for (const option of select.options) {
88
+ if (option.text === text || option.textContent.trim() === text) {
89
+ select.value = option.value;
90
+ select.dispatchEvent(new Event('change'));
91
+ return;
92
+ }
93
+ }
94
+ throw new Error('Option with text "' + text + '" not found');`,
95
+ element,
96
+ text
97
+ );
98
+ },
99
+ this.browser
100
+ );
16
101
  }
17
102
 
18
103
  /**
19
104
  * Selects an option in a `<select>` element by its ID and the value of the option.
20
105
  *
21
106
  * @async
107
+ * @private
22
108
  * @param {string} selectId - The ID of the `<select>` element.
23
109
  * @param {string} value - The value of the option to select.
24
110
  * @returns {Promise<void>} A promise that resolves when the option is selected.
@@ -39,6 +125,7 @@ export class Select {
39
125
  * Selects an option in a `<select>` element by its name and the value of the option.
40
126
  *
41
127
  * @async
128
+ * @private
42
129
  * @param {string} selectName - The name of the `<select>` element.
43
130
  * @param {string} value - The value of the option to select.
44
131
  * @returns {Promise<void>} A promise that resolves when the option is selected.
@@ -61,6 +148,7 @@ export class Select {
61
148
  * Selects an option in a `<select>` element by its ID and the index of the option.
62
149
  *
63
150
  * @async
151
+ * @private
64
152
  * @param {string} selectId - The ID of the `<select>` element.
65
153
  * @param {number} index - The index of the option to select.
66
154
  * @returns {Promise<void>} A promise that resolves when the option is selected.
@@ -85,6 +173,7 @@ export class Select {
85
173
  * Selects an option in a `<select>` element by its name and the index of the option.
86
174
  *
87
175
  * @async
176
+ * @private
88
177
  * @param {string} selectName - The name of the `<select>` element.
89
178
  * @param {number} index - The index of the option to select.
90
179
  * @returns {Promise<void>} A promise that resolves when the option is selected.
@@ -109,6 +198,7 @@ export class Select {
109
198
  * Deselects all options in a `<select>` element by its ID.
110
199
  *
111
200
  * @async
201
+ * @private
112
202
  * @param {string} selectId - The ID of the `<select>` element.
113
203
  * @returns {Promise<void>} A promise that resolves when all options are deselected.
114
204
  * @throws {Error} Throws an error if the `<select>` element is not found.
@@ -119,9 +209,9 @@ export class Select {
119
209
  const value = await this.browser.runScript(script, 'CUSTOM');
120
210
  return value;
121
211
  } catch (error) {
122
- log.error('Could not deleselect by select id %s', selectId);
212
+ log.error('Could not deselect by select id %s', selectId);
123
213
  log.verbose(error);
124
- throw new Error(`Could not deleselect by select id ${selectId}`);
214
+ throw new Error(`Could not deselect by select id ${selectId}`);
125
215
  }
126
216
  }
127
217
 
@@ -129,6 +219,7 @@ export class Select {
129
219
  * Retrieves all option values in a `<select>` element by its ID.
130
220
  *
131
221
  * @async
222
+ * @private
132
223
  * @param {string} selectId - The ID of the `<select>` element.
133
224
  * @returns {Promise<string[]>} A promise that resolves with an array of the values of the options.
134
225
  * @throws {Error} Throws an error if the `<select>` element is not found.
@@ -156,6 +247,7 @@ export class Select {
156
247
  * Retrieves the value of the selected option in a `<select>` element by its ID.
157
248
  *
158
249
  * @async
250
+ * @private
159
251
  * @param {string} selectId - The ID of the `<select>` element.
160
252
  * @returns {Promise<string>} A promise that resolves with the value of the selected option.
161
253
  * @throws {Error} Throws an error if the `<select>` element is not found.
@@ -0,0 +1,58 @@
1
+ import { By } from 'selenium-webdriver';
2
+
3
+ /**
4
+ * Parses a selector string into a Selenium By locator.
5
+ * Supports prefix-based strategies:
6
+ * - 'id:value' -> By.id('value')
7
+ * - 'xpath:expression' -> By.xpath('expression')
8
+ * - 'name:value' -> By.name('value')
9
+ * - 'text:value' -> By.xpath('//*[normalize-space(text())="value"]')
10
+ * - 'link:value' -> By.xpath('//a[text()="value"]')
11
+ * - 'class:value' -> By.className('value')
12
+ * - No prefix -> By.css(selector) (CSS selector is the default)
13
+ *
14
+ * @param {string} selector - The selector string to parse.
15
+ * @returns {{ locator: By, description: string }} The parsed locator and a human-readable description.
16
+ */
17
+ export function parseSelector(selector) {
18
+ const colonIndex = selector.indexOf(':');
19
+ if (colonIndex > 0) {
20
+ const prefix = selector.slice(0, colonIndex).toLowerCase();
21
+ const value = selector.slice(colonIndex + 1);
22
+ switch (prefix) {
23
+ case 'id': {
24
+ return { locator: By.id(value), description: `id ${value}` };
25
+ }
26
+ case 'xpath': {
27
+ return { locator: By.xpath(value), description: `xpath ${value}` };
28
+ }
29
+ case 'name': {
30
+ return { locator: By.name(value), description: `name ${value}` };
31
+ }
32
+ case 'text': {
33
+ return {
34
+ locator: By.xpath(`//*[normalize-space(text())='${value}']`),
35
+ description: `text ${value}`
36
+ };
37
+ }
38
+ case 'link': {
39
+ return {
40
+ locator: By.xpath(`//a[text()='${value}']`),
41
+ description: `link text ${value}`
42
+ };
43
+ }
44
+ case 'class': {
45
+ return {
46
+ locator: By.className(value),
47
+ description: `class ${value}`
48
+ };
49
+ }
50
+ default: {
51
+ // Unknown prefix — treat entire string as CSS selector
52
+ return { locator: By.css(selector), description: `css ${selector}` };
53
+ }
54
+ }
55
+ }
56
+ // No prefix — CSS selector
57
+ return { locator: By.css(selector), description: `css ${selector}` };
58
+ }
@@ -1,4 +1,7 @@
1
+ import webdriver from 'selenium-webdriver';
1
2
  import { getLogger } from '@sitespeed.io/log';
3
+ import { executeCommand } from './commandHelper.js';
4
+ import { parseSelector } from './selectorParser.js';
2
5
  const log = getLogger('browsertime.command.set');
3
6
 
4
7
  /**
@@ -8,17 +11,63 @@ const log = getLogger('browsertime.command.set');
8
11
  * @hideconstructor
9
12
  */
10
13
  export class Set {
11
- constructor(browser) {
14
+ constructor(browser, options) {
12
15
  /**
13
16
  * @private
14
17
  */
15
18
  this.browser = browser;
19
+ /**
20
+ * @private
21
+ */
22
+ this.options = options;
23
+ }
24
+
25
+ /**
26
+ * @private
27
+ */
28
+ async _waitForElement(driver, locator) {
29
+ const timeout = this.options?.timeouts?.elementWait ?? 0;
30
+ if (timeout > 0) {
31
+ await driver.wait(webdriver.until.elementLocated(locator), timeout);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Sets a property on an element using a unified selector string.
37
+ * Supports CSS selectors (default), and prefix-based strategies:
38
+ * 'id:myId', 'xpath://input', 'name:field', 'class:input-field'.
39
+ *
40
+ * @async
41
+ * @param {string} selector - The selector string for the element.
42
+ * @param {string} value - The value to set.
43
+ * @param {string} [property='value'] - The property to set: 'value', 'innerText', or 'innerHTML'.
44
+ * @returns {Promise<void>} A promise that resolves when the property is set.
45
+ * @throws {Error} Throws an error if the element is not found.
46
+ */
47
+ async run(selector, value, property = 'value') {
48
+ const { locator, description } = parseSelector(selector);
49
+ return executeCommand(
50
+ log,
51
+ `Could not set ${property} on %s`,
52
+ description,
53
+ async () => {
54
+ const driver = this.browser.getDriver();
55
+ await this._waitForElement(driver, locator);
56
+ const element = await driver.findElement(locator);
57
+ await driver.executeScript(
58
+ `arguments[0].${property} = '${value}';`,
59
+ element
60
+ );
61
+ },
62
+ this.browser
63
+ );
16
64
  }
17
65
 
18
66
  /**
19
67
  * Sets the innerHTML of an element using a CSS selector.
20
68
  *
21
69
  * @async
70
+ * @private
22
71
  * @param {string} html - The HTML string to set as innerHTML.
23
72
  * @param {string} selector - The CSS selector of the element.
24
73
  * @returns {Promise<void>} A promise that resolves when the innerHTML is set.
@@ -41,6 +90,7 @@ export class Set {
41
90
  * Sets the innerHTML of an element using its ID.
42
91
  *
43
92
  * @async
93
+ * @private
44
94
  * @param {string} html - The HTML string to set as innerHTML.
45
95
  * @param {string} id - The ID of the element.
46
96
  * @returns {Promise<void>} A promise that resolves when the innerHTML is set.
@@ -61,6 +111,7 @@ export class Set {
61
111
  * Sets the innerText of an element using a CSS selector.
62
112
  *
63
113
  * @async
114
+ * @private
64
115
  * @param {string} text - The text to set as innerText.
65
116
  * @param {string} selector - The CSS selector of the element.
66
117
  * @returns {Promise<void>} A promise that resolves when the innerText is set.
@@ -83,6 +134,7 @@ export class Set {
83
134
  * Sets the innerText of an element using its ID.
84
135
  *
85
136
  * @async
137
+ * @private
86
138
  * @param {string} text - The text to set as innerText.
87
139
  * @param {string} id - The ID of the element.
88
140
  * @returns {Promise<void>} A promise that resolves when the innerText is set.
@@ -103,6 +155,7 @@ export class Set {
103
155
  * Sets the value of an element using a CSS selector.
104
156
  *
105
157
  * @async
158
+ * @private
106
159
  * @param {string} value - The value to set on the element.
107
160
  * @param {string} selector - The CSS selector of the element.
108
161
  * @returns {Promise<void>} A promise that resolves when the value is set.
@@ -123,6 +176,7 @@ export class Set {
123
176
  * Sets the value of an element using its ID.
124
177
  *
125
178
  * @async
179
+ * @private
126
180
  * @param {string} value - The value to set on the element.
127
181
  * @param {string} id - The ID of the element.
128
182
  * @returns {Promise<void>} A promise that resolves when the value is set.
@@ -1,5 +1,7 @@
1
1
  import webdriver from 'selenium-webdriver';
2
2
  import { getLogger } from '@sitespeed.io/log';
3
+ import { executeCommand } from './commandHelper.js';
4
+ import { parseSelector } from './selectorParser.js';
3
5
  const log = getLogger('browsertime.command.wait');
4
6
  const delay = ms => new Promise(res => setTimeout(res, ms));
5
7
 
@@ -25,6 +27,7 @@ export class Wait {
25
27
  * Waits for an element with a specific ID to be located within a maximum time.
26
28
  *
27
29
  * @async
30
+ * @private
28
31
  * @param {string} id - The ID of the element to wait for.
29
32
  * @param {number} maxTime - Maximum time to wait in milliseconds.
30
33
  * @returns {Promise<void>} A promise that resolves when the element is found or the time times out.
@@ -50,6 +53,7 @@ export class Wait {
50
53
  * Waits for an element with a specific ID to be located and visible within a maximum time.
51
54
  *
52
55
  * @async
56
+ * @private
53
57
  * @param {string} id - The ID of the element to wait for.
54
58
  * @param {number} maxTime - Maximum time to wait in milliseconds.
55
59
  * @returns {Promise<void>} A promise that resolves when the element is found or the time times out.
@@ -59,7 +63,6 @@ export class Wait {
59
63
  const driver = this.browser.getDriver();
60
64
  await this.byId(id, maxTime);
61
65
  try {
62
- driver.findElement;
63
66
  await driver.wait(
64
67
  webdriver.until.elementIsVisible(
65
68
  driver.findElement(webdriver.By.id(id))
@@ -77,6 +80,7 @@ export class Wait {
77
80
  * Waits for an element located by XPath to appear within a maximum time.
78
81
  *
79
82
  * @async
83
+ * @private
80
84
  * @param {string} xpath - The XPath of the element to wait for.
81
85
  * @param {number} maxTime - Maximum time to wait in milliseconds.
82
86
  * @returns {Promise<void>} A promise that resolves when the element is found or the time times out.
@@ -104,6 +108,7 @@ export class Wait {
104
108
  * Waits for an element located by XPath to appear and visible within a maximum time.
105
109
  *
106
110
  * @async
111
+ * @private
107
112
  * @param {string} xpath - The XPath of the element to wait for.
108
113
  * @param {number} maxTime - Maximum time to wait in milliseconds.
109
114
  * @returns {Promise<void>} A promise that resolves when the element is found or the time times out.
@@ -113,7 +118,6 @@ export class Wait {
113
118
  const driver = this.browser.getDriver();
114
119
  await this.byXpath(xpath, maxTime);
115
120
  try {
116
- driver.findElement;
117
121
  await driver.wait(
118
122
  webdriver.until.elementIsVisible(
119
123
  driver.findElement(webdriver.By.xpath(xpath))
@@ -133,6 +137,7 @@ export class Wait {
133
137
  * Waits for an element located by a CSS selector to appear within a maximum time.
134
138
  *
135
139
  * @async
140
+ * @private
136
141
  * @param {string} selector - The CSS selector of the element to wait for.
137
142
  * @param {number} maxTime - Maximum time to wait in milliseconds.
138
143
  * @returns {Promise<void>} A promise that resolves when the element is found or the time times out.
@@ -164,6 +169,7 @@ export class Wait {
164
169
  * Waits for an element located by a CSS selector to be visible within a maximum time.
165
170
  *
166
171
  * @async
172
+ * @private
167
173
  * @param {string} selector - The CSS selector of the element to wait for.
168
174
  * @param {number} maxTime - Maximum time to wait in milliseconds.
169
175
  * @returns {Promise<void>} A promise that resolves when the element is found or the time times out.
@@ -214,6 +220,39 @@ export class Wait {
214
220
  return this.browser.extraWait(this.pageCompleteCheck);
215
221
  }
216
222
 
223
+ /**
224
+ * Waits for an element using a unified selector string to appear within a maximum time.
225
+ * Supports CSS selectors (default), and prefix-based strategies:
226
+ * 'id:myId', 'xpath://div', 'name:field', 'class:loaded'.
227
+ *
228
+ * @async
229
+ * @param {string} selector - The selector string. CSS by default, or use a prefix.
230
+ * @param {Object} [options] - Options for waiting.
231
+ * @param {number} [options.timeout=6000] - Maximum time to wait in milliseconds.
232
+ * @param {boolean} [options.visible=false] - If true, waits for the element to be visible, not just present.
233
+ * @returns {Promise<void>} A promise that resolves when the element is found.
234
+ * @throws {Error} Throws an error if the element is not found within the timeout.
235
+ */
236
+ async run(selector, options = {}) {
237
+ const timeout = options.timeout ?? 6000;
238
+ const visible = options.visible ?? false;
239
+ const { locator, description } = parseSelector(selector);
240
+ return executeCommand(
241
+ log,
242
+ 'Element %s was not located in ' + timeout + ' ms',
243
+ description,
244
+ async () => {
245
+ const driver = this.browser.getDriver();
246
+ await driver.wait(webdriver.until.elementLocated(locator), timeout);
247
+ if (visible) {
248
+ const element = await driver.findElement(locator);
249
+ await driver.wait(webdriver.until.elementIsVisible(element), timeout);
250
+ }
251
+ },
252
+ this.browser
253
+ );
254
+ }
255
+
217
256
  /**
218
257
  * Waits for a JavaScript condition to return a truthy value within a maximum time.
219
258
  *