appium-xcuitest-driver 10.12.2 → 10.13.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 (40) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/lib/commands/context.d.ts +130 -161
  3. package/build/lib/commands/context.d.ts.map +1 -1
  4. package/build/lib/commands/context.js +122 -107
  5. package/build/lib/commands/context.js.map +1 -1
  6. package/build/lib/commands/execute.js +1 -1
  7. package/build/lib/commands/execute.js.map +1 -1
  8. package/build/lib/commands/general.js +1 -1
  9. package/build/lib/commands/general.js.map +1 -1
  10. package/build/lib/commands/gesture.d.ts +103 -119
  11. package/build/lib/commands/gesture.d.ts.map +1 -1
  12. package/build/lib/commands/gesture.js +98 -138
  13. package/build/lib/commands/gesture.js.map +1 -1
  14. package/build/lib/commands/screenshots.d.ts.map +1 -1
  15. package/build/lib/commands/screenshots.js +3 -5
  16. package/build/lib/commands/screenshots.js.map +1 -1
  17. package/build/lib/commands/timeouts.js +1 -1
  18. package/build/lib/commands/timeouts.js.map +1 -1
  19. package/build/lib/commands/web.d.ts +199 -202
  20. package/build/lib/commands/web.d.ts.map +1 -1
  21. package/build/lib/commands/web.js +206 -174
  22. package/build/lib/commands/web.js.map +1 -1
  23. package/build/lib/driver.d.ts +2 -1
  24. package/build/lib/driver.d.ts.map +1 -1
  25. package/build/lib/driver.js +10 -4
  26. package/build/lib/driver.js.map +1 -1
  27. package/build/lib/execute-method-map.d.ts.map +1 -1
  28. package/build/lib/execute-method-map.js +0 -1
  29. package/build/lib/execute-method-map.js.map +1 -1
  30. package/lib/commands/{context.js → context.ts} +172 -145
  31. package/lib/commands/execute.js +1 -1
  32. package/lib/commands/general.js +1 -1
  33. package/lib/commands/{gesture.js → gesture.ts} +225 -183
  34. package/lib/commands/screenshots.js +3 -5
  35. package/lib/commands/timeouts.js +1 -1
  36. package/lib/commands/{web.js → web.ts} +305 -263
  37. package/lib/driver.ts +11 -4
  38. package/lib/execute-method-map.ts +0 -1
  39. package/npm-shrinkwrap.json +13 -43
  40. package/package.json +1 -1
@@ -3,6 +3,11 @@ import {timing, util} from 'appium/support';
3
3
  import {retryInterval} from 'asyncbox';
4
4
  import B, {TimeoutError, AggregateError} from 'bluebird';
5
5
  import _ from 'lodash';
6
+ import {assertSimulator} from '../utils';
7
+ import type {XCUITestDriver} from '../driver';
8
+ import type {Element, Cookie, Size, Position, Rect} from '@appium/types';
9
+ import type {AtomsElement} from './types';
10
+ import type {CalibrationData} from '../types';
6
11
 
7
12
  const IPHONE_TOP_BAR_HEIGHT = 71;
8
13
  const IPHONE_SCROLLED_TOP_BAR_HEIGHT = 41;
@@ -43,21 +48,23 @@ const ON_APP_CRASH_EVENT = 'app_crash';
43
48
  const VISIBLE = 'visible';
44
49
  const INVISIBLE = 'invisible';
45
50
  const DETECT = 'detect';
46
- const VISIBILITIES = [VISIBLE, INVISIBLE, DETECT];
51
+ const VISIBILITIES = [VISIBLE, INVISIBLE, DETECT] as const;
47
52
 
48
53
  // The position of Safari's tab (search bar).
49
54
  // Since iOS 15, the bar is the bottom by default.
50
55
  const TAB_BAR_POSITION_TOP = 'top';
51
56
  const TAB_BAR_POSITION_BOTTOM = 'bottom';
52
- const TAB_BAR_POSSITIONS = [TAB_BAR_POSITION_TOP, TAB_BAR_POSITION_BOTTOM];
57
+ const TAB_BAR_POSSITIONS = [TAB_BAR_POSITION_TOP, TAB_BAR_POSITION_BOTTOM] as const;
53
58
 
54
59
  /**
55
- * @this {XCUITestDriver}
60
+ * Sets the current web frame context.
61
+ *
62
+ * @param frame - Frame identifier (number, string, or null to return to default content)
56
63
  * @group Mobile Web Only
57
- * @param {number|string|null} frame
58
- * @returns {Promise<void>}
64
+ * @throws {errors.NotImplementedError} If not in a web context
65
+ * @throws {errors.NoSuchFrameError} If the specified frame is not found
59
66
  */
60
- export async function setFrame(frame) {
67
+ export async function setFrame(this: XCUITestDriver, frame: number | string | null): Promise<void> {
61
68
  if (!this.isWebContext()) {
62
69
  throw new errors.NotImplementedError();
63
70
  }
@@ -70,12 +77,12 @@ export async function setFrame(frame) {
70
77
 
71
78
  if (hasElementId(frame)) {
72
79
  const atomsElement = this.getAtomsElement(frame);
73
- const value = await this.executeAtom('get_frame_window', [atomsElement]);
80
+ const value = await this.executeAtom('get_frame_window', [atomsElement]) as {WINDOW: string};
74
81
  this.log.debug(`Entering new web frame: '${value.WINDOW}'`);
75
82
  this.curWebFrames.unshift(value.WINDOW);
76
83
  } else {
77
84
  const atom = _.isNumber(frame) ? 'frame_by_index' : 'frame_by_id_or_name';
78
- const value = await this.executeAtom(atom, [frame]);
85
+ const value = await this.executeAtom(atom, [frame]) as {WINDOW?: string} | null;
79
86
  if (_.isNull(value) || _.isUndefined(value.WINDOW)) {
80
87
  throw new errors.NoSuchFrameError();
81
88
  }
@@ -85,29 +92,30 @@ export async function setFrame(frame) {
85
92
  }
86
93
 
87
94
  /**
88
- * @this {XCUITestDriver}
95
+ * Gets the value of a CSS property for an element.
96
+ *
97
+ * @param propertyName - Name of the CSS property
98
+ * @param el - Element to get the property from
89
99
  * @group Mobile Web Only
90
- * @param {string} propertyName
91
- * @param {Element | string} el
92
- * @returns {Promise<string>}
100
+ * @throws {errors.NotImplementedError} If not in a web context
93
101
  */
94
- export async function getCssProperty(propertyName, el) {
102
+ export async function getCssProperty(this: XCUITestDriver, propertyName: string, el: Element | string): Promise<string> {
95
103
  if (!this.isWebContext()) {
96
104
  throw new errors.NotImplementedError();
97
105
  }
98
106
 
99
107
  const atomsElement = this.getAtomsElement(el);
100
- return await this.executeAtom('get_value_of_css_property', [atomsElement, propertyName]);
108
+ return await this.executeAtom('get_value_of_css_property', [atomsElement, propertyName]) as string;
101
109
  }
102
110
 
103
111
  /**
104
- * Submit the form an element is in
112
+ * Submits the form that contains the specified element.
105
113
  *
106
- * @param {string|Element} el - the element ID
114
+ * @param el - The element ID or element object
107
115
  * @group Mobile Web Only
108
- * @this {XCUITestDriver}
116
+ * @throws {errors.NotImplementedError} If not in a web context
109
117
  */
110
- export async function submit(el) {
118
+ export async function submit(this: XCUITestDriver, el: string | Element): Promise<void> {
111
119
  if (!this.isWebContext()) {
112
120
  throw new errors.NotImplementedError();
113
121
  }
@@ -117,62 +125,69 @@ export async function submit(el) {
117
125
  }
118
126
 
119
127
  /**
120
- * @this {XCUITestDriver}
128
+ * Refreshes the current page.
129
+ *
121
130
  * @group Mobile Web Only
131
+ * @throws {errors.NotImplementedError} If not in a web context
122
132
  */
123
- export async function refresh() {
133
+ export async function refresh(this: XCUITestDriver): Promise<void> {
124
134
  if (!this.isWebContext()) {
125
135
  throw new errors.NotImplementedError();
126
136
  }
127
137
 
128
- await (/** @type {RemoteDebugger} */ (this.remote)).execute('window.location.reload()');
138
+ await this.remote.execute('window.location.reload()');
129
139
  }
130
140
 
131
141
  /**
132
- * @this {XCUITestDriver}
142
+ * Gets the current page URL.
143
+ *
133
144
  * @group Mobile Web Only
134
- * @returns {Promise<string>}
145
+ * @throws {errors.NotImplementedError} If not in a web context
135
146
  */
136
- export async function getUrl() {
147
+ export async function getUrl(this: XCUITestDriver): Promise<string> {
137
148
  if (!this.isWebContext()) {
138
149
  throw new errors.NotImplementedError();
139
150
  }
140
151
 
141
- return await (/** @type {RemoteDebugger} */ (this.remote)).execute('window.location.href');
152
+ return await this.remote.execute('window.location.href') as string;
142
153
  }
143
154
 
144
155
  /**
145
- * @this {XCUITestDriver}
156
+ * Gets the current page title.
157
+ *
146
158
  * @group Mobile Web Only
147
- * @returns {Promise<string>}
159
+ * @throws {errors.NotImplementedError} If not in a web context
148
160
  */
149
- export async function title() {
161
+ export async function title(this: XCUITestDriver): Promise<string> {
150
162
  if (!this.isWebContext()) {
151
163
  throw new errors.NotImplementedError();
152
164
  }
153
165
 
154
- return await (/** @type {RemoteDebugger} */ (this.remote)).execute('window.document.title');
166
+ return await this.remote.execute('window.document.title') as string;
155
167
  }
156
168
 
157
169
  /**
158
- * @this {XCUITestDriver}
170
+ * Gets all cookies for the current page.
171
+ *
172
+ * Cookie values are automatically URI-decoded.
173
+ *
159
174
  * @group Mobile Web Only
160
- * @returns {Promise<import('@appium/types').Cookie[]>}
175
+ * @throws {errors.NotImplementedError} If not in a web context
161
176
  */
162
- export async function getCookies() {
177
+ export async function getCookies(this: XCUITestDriver): Promise<Cookie[]> {
163
178
  if (!this.isWebContext()) {
164
179
  throw new errors.NotImplementedError();
165
180
  }
166
181
 
167
182
  // get the cookies from the remote debugger, or an empty object
168
- const {cookies} = await (/** @type {RemoteDebugger} */ (this.remote)).getCookies();
183
+ const {cookies} = await this.remote.getCookies();
169
184
 
170
185
  // the value is URI encoded, so decode it safely
171
186
  return cookies.map((cookie) => {
172
187
  if (!_.isEmpty(cookie.value)) {
173
188
  try {
174
189
  cookie.value = decodeURI(cookie.value);
175
- } catch (error) {
190
+ } catch (error: any) {
176
191
  this.log.debug(
177
192
  `Cookie ${cookie.name} was not decoded successfully. Cookie value: ${cookie.value}`,
178
193
  );
@@ -185,12 +200,15 @@ export async function getCookies() {
185
200
  }
186
201
 
187
202
  /**
188
- * @this {XCUITestDriver}
203
+ * Sets a cookie for the current page.
204
+ *
205
+ * If the cookie's path is not specified, it defaults to '/'.
206
+ *
207
+ * @param cookie - Cookie object to set
189
208
  * @group Mobile Web Only
190
- * @param {import('@appium/types').Cookie} cookie
191
- * @returns {Promise<void>}
209
+ * @throws {errors.NotImplementedError} If not in a web context
192
210
  */
193
- export async function setCookie(cookie) {
211
+ export async function setCookie(this: XCUITestDriver, cookie: Cookie): Promise<void> {
194
212
  if (!this.isWebContext()) {
195
213
  throw new errors.NotImplementedError();
196
214
  }
@@ -214,12 +232,15 @@ export async function setCookie(cookie) {
214
232
  }
215
233
 
216
234
  /**
217
- * @this {XCUITestDriver}
218
- * @param {string} cookieName
219
- * @returns {Promise<void>}
235
+ * Deletes a cookie by name.
236
+ *
237
+ * If the cookie is not found, the operation is silently ignored.
238
+ *
239
+ * @param cookieName - Name of the cookie to delete
220
240
  * @group Mobile Web Only
241
+ * @throws {errors.NotImplementedError} If not in a web context
221
242
  */
222
- export async function deleteCookie(cookieName) {
243
+ export async function deleteCookie(this: XCUITestDriver, cookieName: string): Promise<void> {
223
244
  if (!this.isWebContext()) {
224
245
  throw new errors.NotImplementedError();
225
246
  }
@@ -235,11 +256,12 @@ export async function deleteCookie(cookieName) {
235
256
  }
236
257
 
237
258
  /**
238
- * @this {XCUITestDriver}
259
+ * Deletes all cookies for the current page.
260
+ *
239
261
  * @group Mobile Web Only
240
- * @returns {Promise<void>}
262
+ * @throws {errors.NotImplementedError} If not in a web context
241
263
  */
242
- export async function deleteCookies() {
264
+ export async function deleteCookies(this: XCUITestDriver): Promise<void> {
243
265
  if (!this.isWebContext()) {
244
266
  throw new errors.NotImplementedError();
245
267
  }
@@ -249,11 +271,12 @@ export async function deleteCookies() {
249
271
  }
250
272
 
251
273
  /**
252
- * @this {XCUITestDriver}
253
- * @param {Element | string} el
254
- * @returns {Element | string}
274
+ * Caches a web element for later use.
275
+ *
276
+ * @param el - Element to cache
277
+ * @returns The cached element wrapper
255
278
  */
256
- export function cacheWebElement(el) {
279
+ export function cacheWebElement(this: XCUITestDriver, el: Element | string): Element | string {
257
280
  if (!_.isPlainObject(el)) {
258
281
  return el;
259
282
  }
@@ -269,71 +292,78 @@ export function cacheWebElement(el) {
269
292
  }
270
293
 
271
294
  /**
272
- * @this {XCUITestDriver}
273
- * @param {any} response
274
- * @returns {any}
295
+ * Recursively caches all web elements in a response object.
296
+ *
297
+ * @param response - Response object that may contain web elements
298
+ * @returns Response with cached element wrappers
275
299
  */
276
- export function cacheWebElements(response) {
277
- const toCached = (/** @type {any} */ v) => (_.isArray(v) || _.isPlainObject(v)) ? this.cacheWebElements(v) : v;
300
+ export function cacheWebElements(this: XCUITestDriver, response: any): any {
301
+ const toCached = (v: any) => (_.isArray(v) || _.isPlainObject(v)) ? this.cacheWebElements(v) : v;
278
302
 
279
303
  if (_.isArray(response)) {
280
304
  return response.map(toCached);
281
305
  } else if (_.isPlainObject(response)) {
282
- const result = {...response, ...(/** @type {Element} */ (this.cacheWebElement(response)))};
306
+ const result = {...response, ...(this.cacheWebElement(response) as Element)};
283
307
  return _.toPairs(result).reduce((acc, [key, value]) => {
284
308
  acc[key] = toCached(value);
285
309
  return acc;
286
- }, {});
310
+ }, {} as any);
287
311
  }
288
312
  return response;
289
313
  }
290
314
 
291
315
  /**
292
- * @param {string} atom
293
- * @param {unknown[]} args
294
- * @returns {Promise<any>}
316
+ * Executes a Selenium atom script in the current web context.
317
+ *
318
+ * @param atom - Name of the atom to execute
319
+ * @param args - Arguments to pass to the atom
320
+ * @param alwaysDefaultFrame - If true, always use the default frame instead of current frames
295
321
  * @privateRemarks This should return `Promise<T>` where `T` extends `unknown`, but that's going to cause a lot of things to break.
296
- * @this {XCUITestDriver}
297
322
  */
298
- export async function executeAtom(atom, args, alwaysDefaultFrame = false) {
299
- let frames = alwaysDefaultFrame === true ? [] : this.curWebFrames;
300
- let promise = (/** @type {RemoteDebugger} */ (this.remote)).executeAtom(atom, args, frames);
323
+ export async function executeAtom(this: XCUITestDriver, atom: string, args: unknown[], alwaysDefaultFrame: boolean = false): Promise<any> {
324
+ const frames = alwaysDefaultFrame === true ? [] : this.curWebFrames;
325
+ const promise = this.remote.executeAtom(atom, args, frames);
301
326
  return await this.waitForAtom(promise);
302
327
  }
303
328
 
304
329
  /**
305
- * @this {XCUITestDriver}
306
- * @param {string} atom
307
- * @param {any[]} args
330
+ * Executes a Selenium atom script asynchronously.
331
+ *
332
+ * @param atom - Name of the atom to execute
333
+ * @param args - Arguments to pass to the atom
308
334
  */
309
- export async function executeAtomAsync(atom, args) {
335
+ export async function executeAtomAsync(this: XCUITestDriver, atom: string, args: any[]): Promise<any> {
310
336
  // save the resolve and reject methods of the promise to be waited for
311
- let promise = new B((resolve, reject) => {
337
+ const promise = new B((resolve, reject) => {
312
338
  this.asyncPromise = {resolve, reject};
313
339
  });
314
- await (/** @type {RemoteDebugger} */ (this.remote)).executeAtomAsync(atom, args, this.curWebFrames);
340
+ await this.remote.executeAtomAsync(atom, args, this.curWebFrames);
315
341
  return await this.waitForAtom(promise);
316
342
  }
317
343
 
318
344
  /**
319
- * @template {string} S
320
- * @param {S|Element<S>} elOrId
321
- * @returns {import('./types').AtomsElement<S>}
322
- * @this {XCUITestDriver}
345
+ * Gets the atoms-compatible element representation.
346
+ *
347
+ * @template S - Element identifier type
348
+ * @param elOrId - Element or element ID
349
+ * @returns Atoms-compatible element object
350
+ * @throws {errors.StaleElementReferenceError} If the element is not in the cache
323
351
  */
324
- export function getAtomsElement(elOrId) {
352
+ export function getAtomsElement<S extends string = string>(this: XCUITestDriver, elOrId: S | Element<S>): AtomsElement<S> {
325
353
  const elId = util.unwrapElement(elOrId);
326
354
  if (!this.webElementsCache?.has(elId)) {
327
355
  throw new errors.StaleElementReferenceError();
328
356
  }
329
- return {ELEMENT: this.webElementsCache.get(elId)};
357
+ return {ELEMENT: this.webElementsCache.get(elId)} as AtomsElement<S>;
330
358
  }
331
359
 
332
360
  /**
333
- * @param {readonly any[]} [args]
334
- * @this {XCUITestDriver}
361
+ * Converts elements in an argument array to atoms-compatible format.
362
+ *
363
+ * @param args - Array of arguments that may contain elements
364
+ * @returns Array with elements converted to atoms format
335
365
  */
336
- export function convertElementsForAtoms(args = []) {
366
+ export function convertElementsForAtoms(this: XCUITestDriver, args: readonly any[] = []): any[] {
337
367
  return args.map((arg) => {
338
368
  if (hasElementId(arg)) {
339
369
  try {
@@ -350,19 +380,22 @@ export function convertElementsForAtoms(args = []) {
350
380
  }
351
381
 
352
382
  /**
383
+ * Extracts the element ID from an element object.
353
384
  *
354
- * @param {any} element
355
- * @returns {string | undefined}
385
+ * @param element - Element object
386
+ * @returns Element ID if found, undefined otherwise
356
387
  */
357
- export function getElementId(element) {
388
+ export function getElementId(element: any): string | undefined {
358
389
  return element?.ELEMENT || element?.[W3C_WEB_ELEMENT_IDENTIFIER];
359
390
  }
360
391
 
361
392
  /**
362
- * @param {any} element
363
- * @returns {element is Element}
393
+ * Checks if an object has an element ID (type guard).
394
+ *
395
+ * @param element - Object to check
396
+ * @returns True if the object has an element ID
364
397
  */
365
- export function hasElementId(element) {
398
+ export function hasElementId(element: any): element is Element {
366
399
  return (
367
400
  util.hasValue(element) &&
368
401
  (util.hasValue(element.ELEMENT) || util.hasValue(element[W3C_WEB_ELEMENT_IDENTIFIER]))
@@ -370,24 +403,32 @@ export function hasElementId(element) {
370
403
  }
371
404
 
372
405
  /**
373
- * @this {XCUITestDriver}
374
- * @param {string} strategy
375
- * @param {string} selector
376
- * @param {boolean} [many]
377
- * @param {Element | string | null} [ctx]
378
- * @returns {Promise<Element | Element[]>}
406
+ * Finds one or more web elements using the specified strategy.
407
+ *
408
+ * @param strategy - Locator strategy (e.g., 'id', 'css selector')
409
+ * @param selector - Selector value
410
+ * @param many - If true, returns array of elements; if false, returns single element
411
+ * @param ctx - Optional context element to search within
412
+ * @returns Element or array of elements
413
+ * @throws {errors.NoSuchElementError} If element not found and many is false
379
414
  */
380
- export async function findWebElementOrElements(strategy, selector, many, ctx) {
415
+ export async function findWebElementOrElements(
416
+ this: XCUITestDriver,
417
+ strategy: string,
418
+ selector: string,
419
+ many?: boolean,
420
+ ctx?: Element | string | null,
421
+ ): Promise<Element | Element[]> {
381
422
  const contextElement = _.isNil(ctx) ? null : this.getAtomsElement(ctx);
382
423
  const atomName = many ? 'find_elements' : 'find_element_fragment';
383
- let element;
424
+ let element: any;
384
425
  const doFind = async () => {
385
426
  element = await this.executeAtom(atomName, [strategy, selector, contextElement]);
386
427
  return !_.isNull(element);
387
428
  };
388
429
  try {
389
430
  await this.implicitWaitForCondition(doFind);
390
- } catch (err) {
431
+ } catch (err: any) {
391
432
  if (err.message && _.isFunction(err.message.match) && err.message.match(/Condition unmet/)) {
392
433
  // condition was not met setting res to empty array
393
434
  element = [];
@@ -406,27 +447,33 @@ export async function findWebElementOrElements(strategy, selector, many, ctx) {
406
447
  }
407
448
 
408
449
  /**
409
- * @this {XCUITestDriver}
410
- * @param {number} x
411
- * @param {number} y
450
+ * Clicks at the specified web coordinates.
451
+ *
452
+ * Coordinates are automatically translated from web to native coordinates.
453
+ *
454
+ * @param x - X coordinate in web space
455
+ * @param y - Y coordinate in web space
412
456
  */
413
- export async function clickWebCoords(x, y) {
457
+ export async function clickWebCoords(this: XCUITestDriver, x: number, y: number): Promise<void> {
414
458
  const {x: translatedX, y: translatedY} = await this.translateWebCoords(x, y);
415
459
  await this.mobileTap(translatedX, translatedY);
416
460
  }
417
461
 
418
462
  /**
419
- * @this {XCUITestDriver}
420
- * @returns {Promise<boolean>}
463
+ * Determines if the current Safari session is running on an iPhone.
464
+ *
465
+ * The result is cached after the first call.
466
+ *
467
+ * @returns True if running on iPhone, false otherwise
421
468
  */
422
- export async function getSafariIsIphone() {
469
+ export async function getSafariIsIphone(this: XCUITestDriver): Promise<boolean> {
423
470
  if (_.isBoolean(this._isSafariIphone)) {
424
471
  return this._isSafariIphone;
425
472
  }
426
473
  try {
427
- const userAgent = /** @type {string} */ (await this.execute('return navigator.userAgent'));
474
+ const userAgent = await this.execute('return navigator.userAgent') as string;
428
475
  this._isSafariIphone = userAgent.toLowerCase().includes('iphone');
429
- } catch (err) {
476
+ } catch (err: any) {
430
477
  this.log.warn(`Unable to find device type from useragent. Assuming iPhone`);
431
478
  this.log.debug(`Error: ${err.message}`);
432
479
  }
@@ -434,15 +481,16 @@ export async function getSafariIsIphone() {
434
481
  }
435
482
 
436
483
  /**
437
- * @this {XCUITestDriver}
438
- * @returns {Promise<import('@appium/types').Size>}
484
+ * Gets the device size from Safari's perspective.
485
+ *
486
+ * Returns normalized dimensions (width <= height).
487
+ *
488
+ * @returns Device size with width and height
439
489
  */
440
- export async function getSafariDeviceSize() {
490
+ export async function getSafariDeviceSize(this: XCUITestDriver): Promise<Size> {
441
491
  const script =
442
492
  'return {height: window.screen.availHeight * window.devicePixelRatio, width: window.screen.availWidth * window.devicePixelRatio};';
443
- const {width, height} = /** @type {import('@appium/types').Size} */ (
444
- await this.execute(script)
445
- );
493
+ const {width, height} = await this.execute(script) as Size;
446
494
  const [normHeight, normWidth] = height > width ? [height, width] : [width, height];
447
495
  return {
448
496
  width: normWidth,
@@ -451,10 +499,13 @@ export async function getSafariDeviceSize() {
451
499
  }
452
500
 
453
501
  /**
454
- * @this {XCUITestDriver}
455
- * @returns {Promise<boolean>}
502
+ * Determines if the current device has a notch (iPhone X and later).
503
+ *
504
+ * The result is cached after the first call.
505
+ *
506
+ * @returns True if device has a notch, false otherwise
456
507
  */
457
- export async function getSafariIsNotched() {
508
+ export async function getSafariIsNotched(this: XCUITestDriver): Promise<boolean> {
458
509
  if (_.isBoolean(this._isSafariNotched)) {
459
510
  return this._isSafariNotched;
460
511
  }
@@ -466,7 +517,7 @@ export async function getSafariIsNotched() {
466
517
  this._isSafariNotched = true;
467
518
  }
468
519
  }
469
- } catch (err) {
520
+ } catch (err: any) {
470
521
  this.log.warn(
471
522
  `Unable to find device type from dimensions. Assuming the device is not notched`,
472
523
  );
@@ -476,9 +527,20 @@ export async function getSafariIsNotched() {
476
527
  }
477
528
 
478
529
  /**
479
- * @this {XCUITestDriver}
530
+ * Calculates and applies extra offset for web coordinate translation.
531
+ *
532
+ * Takes into account Safari UI elements like tab bars, smart app banners, and device notches.
533
+ * Modifies wvPos and realDims in place.
534
+ *
535
+ * @param wvPos - WebView position object (modified in place)
536
+ * @param realDims - Real dimensions object (modified in place)
537
+ * @throws {errors.InvalidArgumentError} If Safari tab bar position is invalid
480
538
  */
481
- export async function getExtraTranslateWebCoordsOffset(wvPos, realDims) {
539
+ export async function getExtraTranslateWebCoordsOffset(
540
+ this: XCUITestDriver,
541
+ wvPos: {x: number; y: number},
542
+ realDims: {w: number; h: number},
543
+ ): Promise<void> {
482
544
  let topOffset = 0;
483
545
  let bottomOffset = 0;
484
546
 
@@ -489,7 +551,7 @@ export async function getExtraTranslateWebCoordsOffset(wvPos, realDims) {
489
551
  const {
490
552
  nativeWebTapTabBarVisibility,
491
553
  nativeWebTapSmartAppBannerVisibility,
492
- safariTabBarPosition = util.compareVersions(/** @type {string} */ (this.opts.platformVersion), '>=', '15.0') &&
554
+ safariTabBarPosition = util.compareVersions(this.opts.platformVersion as string, '>=', '15.0') &&
493
555
  isIphone
494
556
  ? TAB_BAR_POSITION_BOTTOM
495
557
  : TAB_BAR_POSITION_TOP,
@@ -498,14 +560,14 @@ export async function getExtraTranslateWebCoordsOffset(wvPos, realDims) {
498
560
  let bannerVisibility = _.lowerCase(String(nativeWebTapSmartAppBannerVisibility));
499
561
  const tabBarPosition = _.lowerCase(String(safariTabBarPosition));
500
562
 
501
- if (!VISIBILITIES.includes(tabBarVisibility)) {
563
+ if (!VISIBILITIES.includes(tabBarVisibility as any)) {
502
564
  tabBarVisibility = DETECT;
503
565
  }
504
- if (!VISIBILITIES.includes(bannerVisibility)) {
566
+ if (!VISIBILITIES.includes(bannerVisibility as any)) {
505
567
  bannerVisibility = DETECT;
506
568
  }
507
569
 
508
- if (!TAB_BAR_POSSITIONS.includes(tabBarPosition)) {
570
+ if (!TAB_BAR_POSSITIONS.includes(tabBarPosition as any)) {
509
571
  throw new errors.InvalidArgumentError(
510
572
  `${safariTabBarPosition} is invalid as Safari tab bar position. Available positions are ${TAB_BAR_POSSITIONS}.`,
511
573
  );
@@ -516,12 +578,12 @@ export async function getExtraTranslateWebCoordsOffset(wvPos, realDims) {
516
578
  const orientation = realDims.h > realDims.w ? 'PORTRAIT' : 'LANDSCAPE';
517
579
 
518
580
  const notchOffset = isNotched
519
- ? util.compareVersions(/** @type {string} */ (this.opts.platformVersion), '=', '13.0')
581
+ ? util.compareVersions(this.opts.platformVersion as string, '=', '13.0')
520
582
  ? IPHONE_X_NOTCH_OFFSET_IOS_13
521
583
  : IPHONE_X_NOTCH_OFFSET_IOS
522
584
  : 0;
523
585
 
524
- const isScrolled = await this.execute('return document.documentElement.scrollTop > 0');
586
+ const isScrolled = await this.execute('return document.documentElement.scrollTop > 0') as boolean;
525
587
  if (isScrolled) {
526
588
  topOffset = IPHONE_SCROLLED_TOP_BAR_HEIGHT + notchOffset;
527
589
 
@@ -571,12 +633,17 @@ export async function getExtraTranslateWebCoordsOffset(wvPos, realDims) {
571
633
  }
572
634
 
573
635
  /**
574
- * @this {XCUITestDriver}
575
- * @param {boolean} isIphone
576
- * @param {string} bannerVisibility
577
- * @returns {Promise<number>}
636
+ * Calculates additional offset for native web tap based on smart app banner visibility.
637
+ *
638
+ * @param isIphone - Whether the device is an iPhone
639
+ * @param bannerVisibility - Banner visibility setting ('visible', 'invisible', or 'detect')
640
+ * @returns Additional offset in pixels
578
641
  */
579
- export async function getExtraNativeWebTapOffset(isIphone, bannerVisibility) {
642
+ export async function getExtraNativeWebTapOffset(
643
+ this: XCUITestDriver,
644
+ isIphone: boolean,
645
+ bannerVisibility: string,
646
+ ): Promise<number> {
580
647
  let offset = 0;
581
648
 
582
649
  if (bannerVisibility === VISIBLE) {
@@ -585,9 +652,7 @@ export async function getExtraNativeWebTapOffset(isIphone, bannerVisibility) {
585
652
  : IPAD_WEB_COORD_SMART_APP_BANNER_OFFSET;
586
653
  } else if (bannerVisibility === DETECT) {
587
654
  // try to see if there is an Smart App Banner
588
- const banners = /** @type {import('@appium/types').Element[]} */ (
589
- await this.findNativeElementOrElements('accessibility id', 'Close app download offer', true)
590
- );
655
+ const banners = await this.findNativeElementOrElements('accessibility id', 'Close app download offer', true) as Element[];
591
656
  if (banners?.length) {
592
657
  offset += isIphone
593
658
  ? IPHONE_WEB_COORD_SMART_APP_BANNER_OFFSET
@@ -600,11 +665,13 @@ export async function getExtraNativeWebTapOffset(isIphone, bannerVisibility) {
600
665
  }
601
666
 
602
667
  /**
603
- * @this {XCUITestDriver}
604
- * @param {any} el
605
- * @returns {Promise<void>}
668
+ * Performs a native tap on a web element.
669
+ *
670
+ * Attempts to use a simple native tap first, falling back to coordinate-based tapping if needed.
671
+ *
672
+ * @param el - Element to tap
606
673
  */
607
- export async function nativeWebTap(el) {
674
+ export async function nativeWebTap(this: XCUITestDriver, el: any): Promise<void> {
608
675
  const atomsElement = this.getAtomsElement(el);
609
676
 
610
677
  // if strict native tap, do not try to do it with WDA directly
@@ -616,25 +683,26 @@ export async function nativeWebTap(el) {
616
683
  }
617
684
  this.log.warn('Unable to do simple native web tap. Attempting to convert coordinates');
618
685
 
619
- const [size, coordinates] =
620
- /** @type {[import('@appium/types').Size, import('@appium/types').Position]} */ (
621
- await B.Promise.all([
622
- this.executeAtom('get_size', [atomsElement]),
623
- this.executeAtom('get_top_left_coordinates', [atomsElement]),
624
- ])
625
- );
686
+ const [size, coordinates] = await B.Promise.all([
687
+ this.executeAtom('get_size', [atomsElement]),
688
+ this.executeAtom('get_top_left_coordinates', [atomsElement]),
689
+ ]) as [Size, Position];
626
690
  const {width, height} = size;
627
691
  const {x, y} = coordinates;
628
692
  await this.clickWebCoords(x + width / 2, y + height / 2);
629
693
  }
630
694
 
631
695
  /**
632
- * @this {XCUITestDriver}
633
- * @param {number} x
634
- * @param {number} y
635
- * @returns {Promise<import('@appium/types').Position>}
696
+ * Translates web coordinates to native screen coordinates.
697
+ *
698
+ * Uses calibration data if available, otherwise falls back to legacy algorithm.
699
+ *
700
+ * @param x - X coordinate in web space
701
+ * @param y - Y coordinate in web space
702
+ * @returns Translated position in native coordinates
703
+ * @throws {Error} If no WebView is found or if translation fails
636
704
  */
637
- export async function translateWebCoords(x, y) {
705
+ export async function translateWebCoords(this: XCUITestDriver, x: number, y: number): Promise<Position> {
638
706
  this.log.debug(`Translating web coordinates (${JSON.stringify({x, y})}) to native coordinates`);
639
707
 
640
708
  if (this.webviewCalibrationResult) {
@@ -642,7 +710,7 @@ export async function translateWebCoords(x, y) {
642
710
  const { offsetX, offsetY, pixelRatioX, pixelRatioY } = this.webviewCalibrationResult;
643
711
  const cmd = '(function () {return {innerWidth: window.innerWidth, innerHeight: window.innerHeight, ' +
644
712
  'outerWidth: window.outerWidth, outerHeight: window.outerHeight}; })()';
645
- const wvDims = await (/** @type {RemoteDebugger} */ (this.remote)).execute(cmd);
713
+ const wvDims = await this.remote.execute(cmd) as {innerWidth: number; innerHeight: number; outerWidth: number; outerHeight: number};
646
714
  // https://tripleodeon.com/2011/12/first-understand-your-screen/
647
715
  const shouldApplyPixelRatio = wvDims.innerWidth > wvDims.outerWidth
648
716
  || wvDims.innerHeight > wvDims.outerHeight;
@@ -658,17 +726,14 @@ export async function translateWebCoords(x, y) {
658
726
  }
659
727
 
660
728
  // absolutize web coords
661
- /** @type {import('@appium/types').Element|undefined|string} */
662
- let webview;
729
+ let webview: Element | undefined | string;
663
730
  try {
664
- webview = /** @type {import('@appium/types').Element|undefined} */ (
665
- await retryInterval(
666
- 5,
667
- 100,
668
- async () =>
669
- await this.findNativeElementOrElements('class name', 'XCUIElementTypeWebView', false),
670
- )
671
- );
731
+ webview = await retryInterval(
732
+ 5,
733
+ 100,
734
+ async () =>
735
+ await this.findNativeElementOrElements('class name', 'XCUIElementTypeWebView', false),
736
+ ) as Element | undefined;
672
737
  } catch {}
673
738
 
674
739
  if (!webview) {
@@ -677,12 +742,12 @@ export async function translateWebCoords(x, y) {
677
742
 
678
743
  webview = util.unwrapElement(webview);
679
744
 
680
- const rect = /** @type {Rect} */ (await this.proxyCommand(`/element/${webview}/rect`, 'GET'));
745
+ const rect = await this.proxyCommand(`/element/${webview}/rect`, 'GET') as Rect;
681
746
  const wvPos = {x: rect.x, y: rect.y};
682
747
  const realDims = {w: rect.width, h: rect.height};
683
748
 
684
749
  const cmd = '(function () { return {w: window.innerWidth, h: window.innerHeight}; })()';
685
- const wvDims = await (/** @type {RemoteDebugger} */ (this.remote)).execute(cmd);
750
+ const wvDims = await this.remote.execute(cmd) as {w: number; h: number};
686
751
 
687
752
  // keep track of implicit wait, and set locally to 0
688
753
  // https://github.com/appium/appium/issues/14988
@@ -727,18 +792,23 @@ export async function translateWebCoords(x, y) {
727
792
  }
728
793
 
729
794
  /**
730
- * @this {XCUITestDriver}
731
- * @returns {Promise<boolean>}
795
+ * Checks if an alert is currently present.
796
+ *
797
+ * @returns True if an alert is present, false otherwise
732
798
  */
733
- export async function checkForAlert() {
799
+ export async function checkForAlert(this: XCUITestDriver): Promise<boolean> {
734
800
  return _.isString(await this.getAlertText());
735
801
  }
736
802
 
737
803
  /**
738
- * @param {Promise<any>} promise
739
- * @this {XCUITestDriver}
804
+ * Waits for an atom promise to resolve, monitoring for alerts during execution.
805
+ *
806
+ * @param promise - Promise returned by atom execution
807
+ * @returns The result of the atom execution
808
+ * @throws {errors.UnexpectedAlertOpenError} If an alert appears during execution
809
+ * @throws {errors.TimeoutError} If the atom execution times out
740
810
  */
741
- export async function waitForAtom(promise) {
811
+ export async function waitForAtom(this: XCUITestDriver, promise: Promise<any>): Promise<any> {
742
812
  const timer = new timing.Timer().start();
743
813
 
744
814
  const atomWaitTimeoutMs = _.isNumber(this.opts.webviewAtomWaitTimeout) && this.opts.webviewAtomWaitTimeout > 0
@@ -747,10 +817,10 @@ export async function waitForAtom(promise) {
747
817
  // need to check for alert while the atom is being executed.
748
818
  // so notify ourselves when it happens
749
819
  const timedAtomPromise = B.resolve(promise).timeout(atomWaitTimeoutMs);
750
- const handlePromiseError = async (p) => {
820
+ const handlePromiseError = async (p: Promise<any>) => {
751
821
  try {
752
822
  return await p;
753
- } catch (err) {
823
+ } catch (err: any) {
754
824
  const originalError = err instanceof AggregateError ? err[0] : err;
755
825
  this.log.debug(`Error received while executing atom: ${originalError.message}`);
756
826
  throw (
@@ -770,8 +840,8 @@ export async function waitForAtom(promise) {
770
840
  // ...otherwise make sure there is no unexpected alert covering the element
771
841
  this._waitingAtoms.count++;
772
842
 
773
- let onAlertCallback;
774
- let onAppCrashCallback;
843
+ let onAlertCallback: (() => void) | undefined;
844
+ let onAppCrashCallback: ((err: any) => void) | undefined;
775
845
  try {
776
846
  // only restart the monitor if it is not running already
777
847
  if (this._waitingAtoms.alertMonitor.isResolved()) {
@@ -782,7 +852,7 @@ export async function waitForAtom(promise) {
782
852
  if (await this.checkForAlert()) {
783
853
  this._waitingAtoms.alertNotifier.emit(ON_OBSTRUCTING_ALERT_EVENT);
784
854
  }
785
- } catch (err) {
855
+ } catch (err: any) {
786
856
  if (isErrorType(err, errors.InvalidElementStateError)) {
787
857
  this._waitingAtoms.alertNotifier.emit(ON_APP_CRASH_EVENT, err);
788
858
  }
@@ -817,23 +887,25 @@ export async function waitForAtom(promise) {
817
887
  }
818
888
 
819
889
  /**
820
- * @param {string} navType
821
- * @this {XCUITestDriver}
890
+ * Performs browser navigation (back, forward, etc.) using history API.
891
+ *
892
+ * @param navType - Navigation type (e.g., 'back', 'forward')
822
893
  */
823
- export async function mobileWebNav(navType) {
824
- (/** @type {RemoteDebugger} */ (this.remote)).allowNavigationWithoutReload = true;
894
+ export async function mobileWebNav(this: XCUITestDriver, navType: string): Promise<void> {
895
+ this.remote.allowNavigationWithoutReload = true;
825
896
  try {
826
897
  await this.executeAtom('execute_script', [`history.${navType}();`, null]);
827
898
  } finally {
828
- (/** @type {RemoteDebugger} */ (this.remote)).allowNavigationWithoutReload = false;
899
+ this.remote.allowNavigationWithoutReload = false;
829
900
  }
830
901
  }
831
902
 
832
903
  /**
833
- * @this {XCUITestDriver}
834
- * @returns {string} The base url which could be used to access WDA HTTP endpoints.
904
+ * Gets the base URL for accessing WDA HTTP endpoints.
905
+ *
906
+ * @returns The base URL (e.g., 'http://127.0.0.1:8100')
835
907
  */
836
- export function getWdaLocalhostRoot() {
908
+ export function getWdaLocalhostRoot(this: XCUITestDriver): string {
837
909
  const remotePort =
838
910
  ((this.isRealDevice() ? this.opts.wdaRemotePort : null)
839
911
  ?? this.wda?.url?.port
@@ -853,31 +925,28 @@ export function getWdaLocalhostRoot() {
853
925
  * The returned value could also be used to manually transform web coordinates
854
926
  * to real devices ones in client scripts.
855
927
  *
856
- * @this {XCUITestDriver}
857
- * @returns {Promise<import('../types').CalibrationData>}
928
+ * @returns Calibration data with offset and pixel ratio information
929
+ * @throws {errors.NotImplementedError} If not in a web context
858
930
  */
859
- export async function mobileCalibrateWebToRealCoordinatesTranslation() {
931
+ export async function mobileCalibrateWebToRealCoordinatesTranslation(this: XCUITestDriver): Promise<CalibrationData> {
860
932
  if (!this.isWebContext()) {
861
933
  throw new errors.NotImplementedError('This API can only be called from a web context');
862
934
  }
863
935
 
864
936
  const currentUrl = await this.getUrl();
865
937
  await this.setUrl(`${this.getWdaLocalhostRoot()}/calibrate`);
866
- const {width, height} = /** @type {import('@appium/types').Rect} */(
867
- await this.proxyCommand('/window/rect', 'GET')
868
- );
938
+ const {width, height} = await this.proxyCommand('/window/rect', 'GET') as Rect;
869
939
  const [centerX, centerY] = [width / 2, height / 2];
870
940
  const errorPrefix = 'Cannot determine web view coordinates offset. Are you in Safari context?';
871
941
 
872
- const performCalibrationTap = async (/** @type {number} */ tapX, /** @type {number} */ tapY) => {
942
+ const performCalibrationTap = async (tapX: number, tapY: number): Promise<Position> => {
873
943
  await this.mobileTap(tapX, tapY);
874
- /** @type {import('@appium/types').Position} */
875
- let result;
944
+ let result: Position;
876
945
  try {
877
946
  const title = await this.title();
878
947
  this.log.debug(JSON.stringify(title));
879
- result = _.isPlainObject(title) ? title : JSON.parse(title);
880
- } catch (e) {
948
+ result = _.isPlainObject(title) ? title as unknown as Position : JSON.parse(title) as Position;
949
+ } catch (e: any) {
881
950
  throw new Error(`${errorPrefix} Original error: ${e.message}`);
882
951
  }
883
952
  const {x, y} = result;
@@ -912,7 +981,7 @@ export async function mobileCalibrateWebToRealCoordinatesTranslation() {
912
981
  // restore the previous url
913
982
  await this.setUrl(currentUrl);
914
983
  }
915
- const result = /** @type {import('../types').CalibrationData} */ (this.webviewCalibrationResult);
984
+ const result = this.webviewCalibrationResult as CalibrationData;
916
985
  return {
917
986
  ...result,
918
987
  offsetX: Math.round(result.offsetX),
@@ -920,24 +989,10 @@ export async function mobileCalibrateWebToRealCoordinatesTranslation() {
920
989
  };
921
990
  }
922
991
 
923
- /**
924
- * @typedef {Object} SafariOpts
925
- * @property {object} preferences An object containing Safari settings to be updated.
926
- * The list of available setting names and their values could be retrieved by
927
- * changing the corresponding Safari settings in the UI and then inspecting
928
- * 'Library/Preferences/com.apple.mobilesafari.plist' file inside of
929
- * com.apple.mobilesafari app container.
930
- * The full path to the Mobile Safari's container could be retrieved from
931
- * `xcrun simctl get_app_container <sim_udid> com.apple.mobilesafari data`
932
- * command output.
933
- * Use the `xcrun simctl spawn <sim_udid> defaults read <path_to_plist>` command
934
- * to print the plist content to the Terminal.
935
- */
936
-
937
992
  /**
938
993
  * Updates Mobile Safari preferences on an iOS Simulator
939
994
  *
940
- * @param {import('@appium/types').StringRecord} preferences - An object containing Safari settings to be updated.
995
+ * @param preferences - An object containing Safari settings to be updated.
941
996
  * The list of available setting names and their values can be retrieved by changing the
942
997
  * corresponding Safari settings in the UI and then inspecting
943
998
  * `Library/Preferences/com.apple.mobilesafari.plist` file inside of the `com.apple.mobilesafari`
@@ -947,33 +1002,31 @@ export async function mobileCalibrateWebToRealCoordinatesTranslation() {
947
1002
  * the plist content to the Terminal.
948
1003
  *
949
1004
  * @group Simulator Only
950
- * @returns {Promise<void>}
951
- * @throws {Error} if run on a real device or if the preferences argument is invalid
952
- * @this {XCUITestDriver}
1005
+ * @throws {Error} If run on a real device
1006
+ * @throws {errors.InvalidArgumentError} If the preferences argument is invalid
953
1007
  */
954
- export async function mobileUpdateSafariPreferences(preferences) {
955
- if (!this.isSimulator()) {
956
- throw new Error('This extension is only available for Simulator');
957
- }
1008
+ export async function mobileUpdateSafariPreferences(this: XCUITestDriver, preferences: Record<string, any>): Promise<void> {
1009
+ const simulator = assertSimulator.call(this, 'Updating Safari preferences');
958
1010
  if (!_.isPlainObject(preferences)) {
959
1011
  throw new errors.InvalidArgumentError('"preferences" argument must be a valid object');
960
1012
  }
961
1013
 
962
1014
  this.log.debug(`About to update Safari preferences: ${JSON.stringify(preferences)}`);
963
- await /** @type {import('appium-ios-simulator').Simulator} */ (this.device).updateSafariSettings(preferences);
1015
+ await simulator.updateSafariSettings(preferences);
964
1016
  }
965
1017
 
966
1018
  /**
967
- * @this {XCUITestDriver}
968
- * @param {timing.Timer} timer
969
- * @returns {Promise<InstanceType<typeof errors.TimeoutError>>}
1019
+ * Generates a timeout error with detailed information about atom execution failure.
1020
+ *
1021
+ * @param timer - Timer instance to get duration from
1022
+ * @returns Timeout error with descriptive message
970
1023
  */
971
- async function generateAtomTimeoutError(timer) {
1024
+ async function generateAtomTimeoutError(this: XCUITestDriver, timer: timing.Timer): Promise<InstanceType<typeof errors.TimeoutError>> {
972
1025
  let message = (
973
1026
  `The remote Safari debugger did not respond to the requested ` +
974
1027
  `command after ${timer.getDuration().asMilliSeconds}ms. `
975
1028
  );
976
- message += (await this.remote?.isJavascriptExecutionBlocked()) ? (
1029
+ message += (await this._remote?.isJavascriptExecutionBlocked()) ? (
977
1030
  `It appears that JavaScript execution is blocked, ` +
978
1031
  `which could be caused by either a modal dialog obstructing the current page, ` +
979
1032
  `or a JavaScript routine monopolizing the event loop.`
@@ -991,38 +1044,41 @@ async function generateAtomTimeoutError(timer) {
991
1044
  }
992
1045
 
993
1046
  /**
994
- * @this {XCUITestDriver}
995
- * @param {any} atomsElement
996
- * @returns {Promise<boolean>}
1047
+ * Attempts to tap a web element using native element matching.
1048
+ *
1049
+ * Tries to find a native element by matching text content, then taps it directly.
1050
+ *
1051
+ * @param atomsElement - Atoms-compatible element to tap
1052
+ * @returns True if the native tap was successful, false otherwise
997
1053
  */
998
- async function tapWebElementNatively(atomsElement) {
1054
+ async function tapWebElementNatively(this: XCUITestDriver, atomsElement: AtomsElement): Promise<boolean> {
999
1055
  // try to get the text of the element, which will be accessible in the
1000
1056
  // native context
1001
1057
  try {
1002
1058
  const [text1, text2] = await B.all([
1003
1059
  this.executeAtom('get_text', [atomsElement]),
1004
1060
  this.executeAtom('get_attribute_value', [atomsElement, 'value'])
1005
- ]);
1061
+ ]) as [string | null, string | null];
1006
1062
  const text = text1 || text2;
1007
1063
  if (!text) {
1008
1064
  return false;
1009
1065
  }
1010
1066
 
1011
- const els = await this.findNativeElementOrElements('accessibility id', text, true);
1067
+ const els = await this.findNativeElementOrElements('accessibility id', text, true) as Element[];
1012
1068
  if (![1, 2].includes(els.length)) {
1013
1069
  return false;
1014
1070
  }
1015
1071
 
1016
1072
  const el = els[0];
1017
1073
  // use tap because on iOS 11.2 and below `nativeClick` crashes WDA
1018
- const rect = /** @type {import('@appium/types').Rect} */ (await this.proxyCommand(
1074
+ const rect = await this.proxyCommand(
1019
1075
  `/element/${util.unwrapElement(el)}/rect`, 'GET'
1020
- ));
1076
+ ) as Rect;
1021
1077
  if (els.length > 1) {
1022
1078
  const el2 = els[1];
1023
- const rect2 = /** @type {import('@appium/types').Rect} */ (await this.proxyCommand(
1079
+ const rect2 = await this.proxyCommand(
1024
1080
  `/element/${util.unwrapElement(el2)}/rect`, 'GET',
1025
- ));
1081
+ ) as Rect;
1026
1082
 
1027
1083
  if (
1028
1084
  rect.x !== rect2.x || rect.y !== rect2.y
@@ -1034,7 +1090,7 @@ async function tapWebElementNatively(atomsElement) {
1034
1090
  }
1035
1091
  await this.mobileTap(rect.x + rect.width / 2, rect.y + rect.height / 2);
1036
1092
  return true;
1037
- } catch (err) {
1093
+ } catch (err: any) {
1038
1094
  // any failure should fall through and trigger the more elaborate
1039
1095
  // method of clicking
1040
1096
  this.log.warn(`Error attempting to click: ${err.message}`);
@@ -1043,10 +1099,12 @@ async function tapWebElementNatively(atomsElement) {
1043
1099
  }
1044
1100
 
1045
1101
  /**
1046
- * @param {any} id
1047
- * @returns {boolean}
1102
+ * Validates if a value is a valid element identifier.
1103
+ *
1104
+ * @param id - Value to validate
1105
+ * @returns True if the value is a valid element identifier
1048
1106
  */
1049
- function isValidElementIdentifier(id) {
1107
+ function isValidElementIdentifier(id: any): boolean {
1050
1108
  if (!_.isString(id) && !_.isNumber(id)) {
1051
1109
  return false;
1052
1110
  }
@@ -1060,14 +1118,20 @@ function isValidElementIdentifier(id) {
1060
1118
  }
1061
1119
 
1062
1120
  /**
1063
- * Creates a JavaScript Cookie
1121
+ * Creates a JavaScript cookie string.
1064
1122
  *
1065
- * @param {string} key
1066
- * @param {string} value
1067
- * @param {CookieOptions} [options={}]
1068
- * @returns {string}
1123
+ * @param key - Cookie name
1124
+ * @param value - Cookie value
1125
+ * @param options - Cookie options (expires, path, domain, secure, httpOnly)
1126
+ * @returns Cookie string suitable for document.cookie
1069
1127
  */
1070
- function createJSCookie(key, value, options = {}) {
1128
+ function createJSCookie(key: string, value: string, options: {
1129
+ expires?: string;
1130
+ path?: string;
1131
+ domain?: string;
1132
+ secure?: boolean;
1133
+ httpOnly?: boolean;
1134
+ } = {}): string {
1071
1135
  return [
1072
1136
  encodeURIComponent(key),
1073
1137
  '=',
@@ -1080,34 +1144,12 @@ function createJSCookie(key, value, options = {}) {
1080
1144
  }
1081
1145
 
1082
1146
  /**
1083
- * @this {XCUITestDriver}
1084
- * @param {import('@appium/types').Cookie} cookie
1085
- * @returns {Promise<any>}
1147
+ * Deletes a cookie via the remote debugger.
1148
+ *
1149
+ * @param cookie - Cookie object to delete
1086
1150
  */
1087
- async function _deleteCookie(cookie) {
1151
+ async function _deleteCookie(this: XCUITestDriver, cookie: Cookie): Promise<any> {
1088
1152
  const url = `http${cookie.secure ? 's' : ''}://${cookie.domain}${cookie.path}`;
1089
- return await (/** @type {RemoteDebugger} */ (this.remote)).deleteCookie(cookie.name, url);
1153
+ return await this.remote.deleteCookie(cookie.name, url);
1090
1154
  }
1091
1155
 
1092
- /**
1093
- * @typedef {Object} CookieOptions
1094
- * @property {string} [expires]
1095
- * @property {string} [path]
1096
- * @property {string} [domain]
1097
- * @property {boolean} [secure]
1098
- * @property {boolean} [httpOnly]
1099
- */
1100
-
1101
- /**
1102
- * @typedef {import('../driver').XCUITestDriver} XCUITestDriver
1103
- * @typedef {import('@appium/types').Rect} Rect
1104
- */
1105
-
1106
- /**
1107
- * @template {string} [S=string]
1108
- * @typedef {import('@appium/types').Element<S>} Element
1109
- */
1110
-
1111
- /**
1112
- * @typedef {import('appium-remote-debugger').RemoteDebugger} RemoteDebugger
1113
- */