appium-xcuitest-driver 5.16.1 → 6.0.1
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 +21 -0
- package/build/lib/commands/gesture.d.ts +19 -24
- package/build/lib/commands/gesture.d.ts.map +1 -1
- package/build/lib/commands/gesture.js +65 -172
- package/build/lib/commands/gesture.js.map +1 -1
- package/build/lib/commands/log.d.ts.map +1 -1
- package/build/lib/commands/log.js +20 -11
- package/build/lib/commands/log.js.map +1 -1
- package/build/lib/device-log/ios-crash-log.d.ts +41 -17
- package/build/lib/device-log/ios-crash-log.d.ts.map +1 -1
- package/build/lib/device-log/ios-crash-log.js +48 -11
- package/build/lib/device-log/ios-crash-log.js.map +1 -1
- package/build/lib/driver.d.ts +13 -17
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +0 -1
- package/build/lib/driver.js.map +1 -1
- package/build/lib/execute-method-map.d.ts +3 -2
- package/build/lib/execute-method-map.d.ts.map +1 -1
- package/build/lib/execute-method-map.js +3 -2
- package/build/lib/execute-method-map.js.map +1 -1
- package/lib/commands/gesture.js +65 -186
- package/lib/commands/log.js +20 -10
- package/lib/device-log/ios-crash-log.js +49 -11
- package/lib/driver.js +0 -1
- package/lib/execute-method-map.ts +3 -2
- package/npm-shrinkwrap.json +12 -12
- package/package.json +2 -2
package/lib/commands/gesture.js
CHANGED
|
@@ -4,37 +4,6 @@ import _ from 'lodash';
|
|
|
4
4
|
|
|
5
5
|
const SUPPORTED_GESTURE_DIRECTIONS = ['up', 'down', 'left', 'right'];
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* @param {any} [opts]
|
|
9
|
-
* @returns {string|undefined}
|
|
10
|
-
*/
|
|
11
|
-
function toElementId(opts) {
|
|
12
|
-
if (_.isUndefined(opts)) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
if (_.isString(opts) || _.isNumber(opts)) {
|
|
16
|
-
return String(opts);
|
|
17
|
-
}
|
|
18
|
-
if ('elementId' in opts || 'element' in opts) {
|
|
19
|
-
return util.unwrapElement(opts);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
25
|
-
* @param {XCUITestDriver} driver
|
|
26
|
-
* @param {Element|string} [elementId]
|
|
27
|
-
* @returns {Promise<string>}
|
|
28
|
-
*/
|
|
29
|
-
async function toElementOrApplicationId(driver, elementId) {
|
|
30
|
-
if (!_.isUndefined(elementId)) {
|
|
31
|
-
return util.unwrapElement(elementId);
|
|
32
|
-
}
|
|
33
|
-
return util.unwrapElement(
|
|
34
|
-
await driver.findNativeElementOrElements(`class name`, `XCUIElementTypeApplication`, false),
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
7
|
/**
|
|
39
8
|
* Converts the given value to a float number.
|
|
40
9
|
*
|
|
@@ -43,7 +12,7 @@ async function toElementOrApplicationId(driver, elementId) {
|
|
|
43
12
|
* @param {string} paramName
|
|
44
13
|
* @returns {number}
|
|
45
14
|
*/
|
|
46
|
-
function
|
|
15
|
+
function requireFloat(value, paramName) {
|
|
47
16
|
const num = parseFloat(String(value));
|
|
48
17
|
if (Number.isNaN(num)) {
|
|
49
18
|
throw new errors.InvalidArgumentError(
|
|
@@ -53,24 +22,6 @@ function asFloat(value, paramName) {
|
|
|
53
22
|
return num;
|
|
54
23
|
}
|
|
55
24
|
|
|
56
|
-
/**
|
|
57
|
-
* Converts the given value to an integer number.
|
|
58
|
-
*
|
|
59
|
-
* @throws If `value` is `NaN`
|
|
60
|
-
* @param {any} value
|
|
61
|
-
* @param {string} paramName
|
|
62
|
-
* @returns {number}
|
|
63
|
-
*/
|
|
64
|
-
function asInt(value, paramName) {
|
|
65
|
-
const num = parseInt(String(value), 10);
|
|
66
|
-
if (Number.isNaN(num)) {
|
|
67
|
-
throw new errors.InvalidArgumentError(
|
|
68
|
-
`"${paramName}" parameter should be a valid integer. "${value}" is given instead`,
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
return num;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
25
|
/**
|
|
75
26
|
*
|
|
76
27
|
* @param {any[]} gestures
|
|
@@ -327,8 +278,8 @@ const helpers = {
|
|
|
327
278
|
if (!_.isNil(distance)) {
|
|
328
279
|
params.distance = distance;
|
|
329
280
|
}
|
|
330
|
-
|
|
331
|
-
return await this.proxyCommand(
|
|
281
|
+
const endpoint = elementId ? `/wda/element/${util.unwrapElement(elementId)}/scroll` : '/wda/scroll';
|
|
282
|
+
return await this.proxyCommand(endpoint, 'POST', params);
|
|
332
283
|
},
|
|
333
284
|
/**
|
|
334
285
|
* @param {import('./types').Direction} direction
|
|
@@ -346,8 +297,8 @@ const helpers = {
|
|
|
346
297
|
if (!_.isNil(velocity)) {
|
|
347
298
|
params.velocity = velocity;
|
|
348
299
|
}
|
|
349
|
-
|
|
350
|
-
|
|
300
|
+
const endpoint = elementId ? `/wda/element/${util.unwrapElement(elementId)}/swipe` : '/wda/swipe';
|
|
301
|
+
await this.proxyCommand(endpoint, 'POST', params);
|
|
351
302
|
},
|
|
352
303
|
/**
|
|
353
304
|
* Performs a pinch gesture on the given element or on the Application element.
|
|
@@ -366,18 +317,18 @@ const helpers = {
|
|
|
366
317
|
*/
|
|
367
318
|
async mobilePinch(scale, velocity, elementId) {
|
|
368
319
|
const params = {
|
|
369
|
-
scale:
|
|
370
|
-
velocity:
|
|
320
|
+
scale: requireFloat(scale, 'scale'),
|
|
321
|
+
velocity: requireFloat(velocity, 'velocity'),
|
|
371
322
|
};
|
|
372
|
-
|
|
373
|
-
|
|
323
|
+
const endpoint = elementId ? `/wda/element/${util.unwrapElement(elementId)}/pinch` : '/wda/pinch';
|
|
324
|
+
await this.proxyCommand(endpoint, 'POST', params);
|
|
374
325
|
},
|
|
375
326
|
/**
|
|
376
327
|
* Performs double tap gesture on the given element or on the screen.
|
|
377
328
|
*
|
|
378
|
-
* @param {Element|string} [elementId] - The internal element identifier (as hexadecimal hash string) to double tap on.
|
|
379
|
-
* @param {number} [x] - The _x_ coordinate (float value) to double tap on.
|
|
380
|
-
* @param {number} [y] - The _y_ coordinate (float value) to double tap on.
|
|
329
|
+
* @param {Element|string} [elementId] - The internal element identifier (as hexadecimal hash string) to double tap on. The Application element will be used if this parameter is not provided.
|
|
330
|
+
* @param {number} [x] - The _x_ coordinate (float value) to double tap on.
|
|
331
|
+
* @param {number} [y] - The _y_ coordinate (float value) to double tap on.
|
|
381
332
|
* @returns {Promise<void>}
|
|
382
333
|
* @this {XCUITestDriver}
|
|
383
334
|
* @example
|
|
@@ -387,16 +338,8 @@ const helpers = {
|
|
|
387
338
|
* ```
|
|
388
339
|
*/
|
|
389
340
|
async mobileDoubleTap(elementId, x, y) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
return await this.proxyCommand(`/wda/element/${elementId}/doubleTap`, 'POST');
|
|
393
|
-
}
|
|
394
|
-
// Double tap coordinates
|
|
395
|
-
const params = {
|
|
396
|
-
x: asFloat(x, 'x'),
|
|
397
|
-
y: asFloat(y, 'y'),
|
|
398
|
-
};
|
|
399
|
-
return await this.proxyCommand('/wda/doubleTap', 'POST', params);
|
|
341
|
+
const endpoint = elementId ? `/wda/element/${util.unwrapElement(elementId)}/doubleTap` : '/wda/doubleTap';
|
|
342
|
+
await this.proxyCommand(endpoint, 'POST', {x, y});
|
|
400
343
|
},
|
|
401
344
|
/**
|
|
402
345
|
* Performs two finger tap gesture on the given element or on the application element.
|
|
@@ -413,16 +356,16 @@ const helpers = {
|
|
|
413
356
|
* ```
|
|
414
357
|
*/
|
|
415
358
|
async mobileTwoFingerTap(elementId) {
|
|
416
|
-
|
|
417
|
-
|
|
359
|
+
const endpoint = elementId ? `/wda/element/${util.unwrapElement(elementId)}/twoFingerTap` : '/wda/twoFingerTap';
|
|
360
|
+
await this.proxyCommand(endpoint, 'POST');
|
|
418
361
|
},
|
|
419
362
|
/**
|
|
420
363
|
* Performs a "long press" gesture on the given element or on the screen.
|
|
421
364
|
*
|
|
422
365
|
* @param {number} duration - The duration (in seconds) of the gesture.
|
|
423
|
-
* @param {number} [y] - The _y_ coordinate (float value) to
|
|
424
|
-
* @param {number} [x] - The _x_ coordinate (float value) to
|
|
425
|
-
* @param {Element|string} [elementId] - The internal element identifier (as hexadecimal hash string) to double tap on.
|
|
366
|
+
* @param {number} [y] - The _y_ coordinate (float value) to hold on.
|
|
367
|
+
* @param {number} [x] - The _x_ coordinate (float value) to hold on.
|
|
368
|
+
* @param {Element|string} [elementId] - The internal element identifier (as hexadecimal hash string) to double tap on. The Application element will be used if this parameter is not provided.
|
|
426
369
|
* @this {XCUITestDriver}
|
|
427
370
|
* @see https://developer.apple.com/documentation/xctest/xcuielement/1618663-pressforduration?language=objc
|
|
428
371
|
* @example
|
|
@@ -434,33 +377,24 @@ const helpers = {
|
|
|
434
377
|
* ```
|
|
435
378
|
*/
|
|
436
379
|
async mobileTouchAndHold(duration, x, y, elementId) {
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
return await this.proxyCommand(`/wda/element/${elementId}/touchAndHold`, 'POST', params);
|
|
443
|
-
}
|
|
444
|
-
// Long tap coordinates
|
|
445
|
-
params.x = asFloat(x, 'x');
|
|
446
|
-
params.y = asFloat(y, 'y');
|
|
447
|
-
return await this.proxyCommand('/wda/touchAndHold', 'POST', params);
|
|
380
|
+
const endpoint = elementId ? `/wda/element/${util.unwrapElement(elementId)}/touchAndHold` : '/wda/touchAndHold';
|
|
381
|
+
await this.proxyCommand(endpoint, 'POST', {
|
|
382
|
+
duration: requireFloat(duration, 'duration'),
|
|
383
|
+
x, y,
|
|
384
|
+
});
|
|
448
385
|
},
|
|
449
386
|
/**
|
|
450
387
|
* Performs tap gesture by coordinates on the given element or on the screen.
|
|
451
388
|
*
|
|
452
389
|
* @param {number} x - The _x_ coordinate (float value) to tap on. If `elementId` is provided, this is computed relative to the element; otherwise it is computed relative to the active Application element.
|
|
453
390
|
* @param {number} y - The _y_ coordinate (float value) to tap on. If `elementId` is provided, this is computed relative to the element; otherwise it is computed relative to the active Application element.
|
|
454
|
-
* @param {string|Element} [elementId] - The internal element identifier (as hexadecimal hash string) to tap on.
|
|
391
|
+
* @param {string|Element} [elementId] - The internal element identifier (as hexadecimal hash string) to tap on. The Application element will be used if this parameter is not provided.
|
|
455
392
|
* @this {XCUITestDriver}
|
|
456
393
|
* @returns {Promise<void>}
|
|
457
394
|
*/
|
|
458
|
-
async mobileTap(x, y, elementId
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
y: asFloat(y, 'y'),
|
|
462
|
-
};
|
|
463
|
-
return await this.proxyCommand(`/wda/tap/${elementId}`, 'POST', params);
|
|
395
|
+
async mobileTap(x, y, elementId) {
|
|
396
|
+
const endpoint = elementId ? `/wda/element/${util.unwrapElement(elementId)}/tap` : '/wda/tap';
|
|
397
|
+
await this.proxyCommand(endpoint, 'POST', {x, y});
|
|
464
398
|
},
|
|
465
399
|
/**
|
|
466
400
|
* Performs drag and drop gesture by coordinates on the given element or on the screen.
|
|
@@ -489,16 +423,15 @@ const helpers = {
|
|
|
489
423
|
*/
|
|
490
424
|
async mobileDragFromToForDuration(duration, fromX, fromY, toX, toY, elementId) {
|
|
491
425
|
const params = {
|
|
492
|
-
duration:
|
|
493
|
-
fromX:
|
|
494
|
-
fromY:
|
|
495
|
-
toX:
|
|
496
|
-
toY:
|
|
426
|
+
duration: requireFloat(duration, 'duration'),
|
|
427
|
+
fromX: requireFloat(fromX, 'fromX'),
|
|
428
|
+
fromY: requireFloat(fromY, 'fromY'),
|
|
429
|
+
toX: requireFloat(toX, 'toX'),
|
|
430
|
+
toY: requireFloat(toY, 'toY'),
|
|
497
431
|
};
|
|
498
|
-
elementId = toElementId(elementId);
|
|
499
432
|
return elementId
|
|
500
433
|
? // Drag element
|
|
501
|
-
await this.proxyCommand(`/wda/element/${elementId}/dragfromtoforduration`, 'POST', params)
|
|
434
|
+
await this.proxyCommand(`/wda/element/${util.unwrapElement(elementId)}/dragfromtoforduration`, 'POST', params)
|
|
502
435
|
: // Drag coordinates
|
|
503
436
|
await this.proxyCommand('/wda/dragfromtoforduration', 'POST', params);
|
|
504
437
|
},
|
|
@@ -531,9 +464,9 @@ const helpers = {
|
|
|
531
464
|
toY,
|
|
532
465
|
) {
|
|
533
466
|
const params = {
|
|
534
|
-
pressDuration:
|
|
535
|
-
holdDuration:
|
|
536
|
-
velocity:
|
|
467
|
+
pressDuration: requireFloat(pressDuration, 'pressDuration'),
|
|
468
|
+
holdDuration: requireFloat(holdDuration, 'holdDuration'),
|
|
469
|
+
velocity: requireFloat(velocity, 'velocity'),
|
|
537
470
|
};
|
|
538
471
|
fromElementId = fromElementId ? util.unwrapElement(fromElementId) : undefined;
|
|
539
472
|
if (fromElementId) {
|
|
@@ -550,19 +483,20 @@ const helpers = {
|
|
|
550
483
|
params,
|
|
551
484
|
);
|
|
552
485
|
}
|
|
553
|
-
params.fromX =
|
|
554
|
-
params.fromY =
|
|
555
|
-
params.toX =
|
|
556
|
-
params.toY =
|
|
486
|
+
params.fromX = requireFloat(fromX, 'fromX');
|
|
487
|
+
params.fromY = requireFloat(fromY, 'fromY');
|
|
488
|
+
params.toX = requireFloat(toX, 'toX');
|
|
489
|
+
params.toY = requireFloat(toY, 'toY');
|
|
557
490
|
return await this.proxyCommand('/wda/pressAndDragWithVelocity', 'POST', params);
|
|
558
491
|
},
|
|
559
492
|
/**
|
|
560
493
|
* Sends one or more taps with one or more touch points.
|
|
561
494
|
*
|
|
562
495
|
* @since 1.17.1
|
|
563
|
-
* @param {
|
|
564
|
-
* @param {number}
|
|
565
|
-
* @param {
|
|
496
|
+
* @param {number} [numberOfTaps=1] - Number of taps to perform.
|
|
497
|
+
* @param {number} [numberOfTouches=1] - Number of touch points to use.
|
|
498
|
+
* @param {string|Element} [elementId] - The internal element identifier (as hexadecimal hash string) to perform one or more taps.
|
|
499
|
+
* The Application element will be used if this parameter is not provided.
|
|
566
500
|
* @returns {Promise<void>}
|
|
567
501
|
* @this {XCUITestDriver}
|
|
568
502
|
* @see https://developer.apple.com/documentation/xctest/xcuielement/1618671-tapwithnumberoftaps?language=objc
|
|
@@ -573,17 +507,14 @@ const helpers = {
|
|
|
573
507
|
* @driver.execute_script 'mobile: tapWithNumberOfTaps', {element: e.ref, numberOfTaps: 2, numberOfTouches: 1}
|
|
574
508
|
* ```
|
|
575
509
|
*/
|
|
576
|
-
async mobileTapWithNumberOfTaps(
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
numberOfTouches: asInt(numberOfTouches, 'numberOfTouches'),
|
|
585
|
-
};
|
|
586
|
-
return await this.proxyCommand(`/wda/element/${elementId}/tapWithNumberOfTaps`, 'POST', params);
|
|
510
|
+
async mobileTapWithNumberOfTaps(numberOfTouches = 1, numberOfTaps = 1, elementId = undefined) {
|
|
511
|
+
const endpoint = elementId
|
|
512
|
+
? `/wda/element/${util.unwrapElement(elementId)}/tapWithNumberOfTaps`
|
|
513
|
+
: '/wda/tapWithNumberOfTaps';
|
|
514
|
+
return await this.proxyCommand(endpoint, 'POST', {
|
|
515
|
+
numberOfTaps,
|
|
516
|
+
numberOfTouches,
|
|
517
|
+
});
|
|
587
518
|
},
|
|
588
519
|
/**
|
|
589
520
|
* Performs a "force press" on the given element or coordinates.
|
|
@@ -593,13 +524,13 @@ const helpers = {
|
|
|
593
524
|
* @param {number} [y] - The _y_ coordinate of the gesture. If `elementId` is set, this is calculated relative to its position; otherwise it's calculated relative to the active Application.
|
|
594
525
|
* @param {number} [duration] - The duraiton (in seconds) of the force press. If this is provided, `pressure` must also be provided.
|
|
595
526
|
* @param {number} [pressure] - A float value defining the pressure of the force press. If this is provided, `duration` must also be provided.
|
|
596
|
-
* @param {string|Element} [elementId] - The internal element identifier (as hexadecimal hash string) to perform one or more taps.
|
|
527
|
+
* @param {string|Element} [elementId] - The internal element identifier (as hexadecimal hash string) to perform one or more taps.
|
|
528
|
+
* The Application element will be used if this parameter is not provided.
|
|
597
529
|
* @returns {Promise<void>}
|
|
598
530
|
* @this {XCUITestDriver}
|
|
599
531
|
*/
|
|
600
532
|
async mobileForcePress(x, y, duration, pressure, elementId) {
|
|
601
|
-
|
|
602
|
-
const endpoint = elementId ? `/wda/element/${elementId}/forceTouch` : `/wda/forceTouch`;
|
|
533
|
+
const endpoint = elementId ? `/wda/element/${util.unwrapElement(elementId)}/forceTouch` : `/wda/forceTouch`;
|
|
603
534
|
return await this.proxyCommand(endpoint, 'POST', {x, y, duration, pressure});
|
|
604
535
|
},
|
|
605
536
|
/**
|
|
@@ -625,7 +556,6 @@ const helpers = {
|
|
|
625
556
|
* ```
|
|
626
557
|
*/
|
|
627
558
|
async mobileSelectPickerWheelValue(elementId, order, offset, value, maxAttempts) {
|
|
628
|
-
elementId = /** @type {string} */ (toElementId(elementId));
|
|
629
559
|
if (!elementId) {
|
|
630
560
|
throw new errors.InvalidArgumentError(
|
|
631
561
|
'elementId is expected to be set for selectPickerWheelValue method',
|
|
@@ -639,7 +569,7 @@ const helpers = {
|
|
|
639
569
|
}
|
|
640
570
|
const params = {order};
|
|
641
571
|
if (offset) {
|
|
642
|
-
params.offset =
|
|
572
|
+
params.offset = requireFloat(offset, 'offset');
|
|
643
573
|
}
|
|
644
574
|
if (!_.isNil(value)) {
|
|
645
575
|
params.value = value;
|
|
@@ -647,15 +577,16 @@ const helpers = {
|
|
|
647
577
|
if (!_.isNil(maxAttempts)) {
|
|
648
578
|
params.maxAttempts = maxAttempts;
|
|
649
579
|
}
|
|
650
|
-
return await this.proxyCommand(`/wda/pickerwheel/${elementId}/select`, 'POST', params);
|
|
580
|
+
return await this.proxyCommand(`/wda/pickerwheel/${util.unwrapElement(elementId)}/select`, 'POST', params);
|
|
651
581
|
},
|
|
652
582
|
/**
|
|
653
583
|
* Performs a rotate gesture on the given element.
|
|
654
584
|
*
|
|
655
585
|
* @see https://developer.apple.com/documentation/xctest/xcuielement/1618665-rotate?language=objc
|
|
656
|
-
* @param {string|Element} elementId - The internal element identifier (as hexadecimal hash string) to perform the gesture on.
|
|
657
586
|
* @param {number} rotation - The rotation gesture (in radians)
|
|
658
587
|
* @param {number} velocity - The velocity (in radians-per-second) of the gesture.
|
|
588
|
+
* @param {string|Element} [elementId] - The internal element identifier (as hexadecimal hash string) to perform the gesture on.
|
|
589
|
+
* The Application element will be used if this parameter is not provided.
|
|
659
590
|
* @returns {Promise<void>}
|
|
660
591
|
* @this {XCUITestDriver}
|
|
661
592
|
* @example
|
|
@@ -670,65 +601,13 @@ const helpers = {
|
|
|
670
601
|
* ));
|
|
671
602
|
* ```
|
|
672
603
|
*/
|
|
673
|
-
async mobileRotateElement(
|
|
674
|
-
elementId = /** @type {string} */ (toElementId(elementId));
|
|
675
|
-
if (!elementId) {
|
|
676
|
-
throw new errors.InvalidArgumentError(
|
|
677
|
-
'Element id is expected to be set for rotateElement method',
|
|
678
|
-
);
|
|
679
|
-
}
|
|
604
|
+
async mobileRotateElement(rotation, velocity, elementId) {
|
|
680
605
|
const params = {
|
|
681
|
-
rotation:
|
|
682
|
-
velocity:
|
|
606
|
+
rotation: requireFloat(rotation, 'rotation'),
|
|
607
|
+
velocity: requireFloat(velocity, 'velocity'),
|
|
683
608
|
};
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
/**
|
|
687
|
-
* @this {XCUITestDriver}
|
|
688
|
-
*/
|
|
689
|
-
async getCoordinates(gesture) {
|
|
690
|
-
// defaults
|
|
691
|
-
let coordinates = {x: 0, y: 0, areOffsets: false};
|
|
692
|
-
|
|
693
|
-
let optionX = null;
|
|
694
|
-
if (gesture.options.x) {
|
|
695
|
-
optionX = asFloat(gesture.options.x, 'x');
|
|
696
|
-
}
|
|
697
|
-
let optionY = null;
|
|
698
|
-
if (gesture.options.y) {
|
|
699
|
-
optionY = asFloat(gesture.options.y, 'y');
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
// figure out the element coordinates.
|
|
703
|
-
const elementId = toElementId(gesture.options);
|
|
704
|
-
if (elementId) {
|
|
705
|
-
let rect = await this.getElementRect(elementId);
|
|
706
|
-
let pos = {x: rect.x, y: rect.y};
|
|
707
|
-
let size = {w: rect.width, h: rect.height};
|
|
708
|
-
|
|
709
|
-
// defaults
|
|
710
|
-
let offsetX = 0;
|
|
711
|
-
let offsetY = 0;
|
|
712
|
-
|
|
713
|
-
// get the real offsets
|
|
714
|
-
if (optionX || optionY) {
|
|
715
|
-
offsetX = optionX || 0;
|
|
716
|
-
offsetY = optionY || 0;
|
|
717
|
-
} else {
|
|
718
|
-
offsetX = size.w / 2;
|
|
719
|
-
offsetY = size.h / 2;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
// apply the offsets
|
|
723
|
-
coordinates.x = pos.x + offsetX;
|
|
724
|
-
coordinates.y = pos.y + offsetY;
|
|
725
|
-
} else {
|
|
726
|
-
// moveTo coordinates are passed in as offsets
|
|
727
|
-
coordinates.areOffsets = gesture.action === 'moveTo';
|
|
728
|
-
coordinates.x = optionX || 0;
|
|
729
|
-
coordinates.y = optionY || 0;
|
|
730
|
-
}
|
|
731
|
-
return coordinates;
|
|
609
|
+
const endpoint = elementId ? `/wda/element/${util.unwrapElement(elementId)}/rotate` : '/wda/rotate';
|
|
610
|
+
return await this.proxyCommand(endpoint, 'POST', params);
|
|
732
611
|
},
|
|
733
612
|
};
|
|
734
613
|
|
package/lib/commands/log.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
+
import B from 'bluebird';
|
|
2
3
|
import {DEFAULT_WS_PATHNAME_PREFIX} from 'appium/driver';
|
|
3
4
|
import {IOSCrashLog} from '../device-log/ios-crash-log';
|
|
4
5
|
import {IOSSimulatorLog} from '../device-log/ios-simulator-log';
|
|
@@ -124,17 +125,26 @@ export default {
|
|
|
124
125
|
this.logs.safariConsole = new SafariConsoleLog(!!this.opts.showSafariConsoleLog);
|
|
125
126
|
this.logs.safariNetwork = new SafariNetworkLog(!!this.opts.showSafariNetworkLog);
|
|
126
127
|
}
|
|
127
|
-
try {
|
|
128
|
-
await this.logs.syslog.startCapture();
|
|
129
|
-
} catch (err) {
|
|
130
|
-
log.warn(`Continuing without capturing device logs: ${err.message}`);
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
await this.logs.crashlog.startCapture();
|
|
134
|
-
await this.logs.safariConsole.startCapture();
|
|
135
|
-
await this.logs.safariNetwork.startCapture();
|
|
136
128
|
|
|
137
|
-
|
|
129
|
+
let didStartSyslog = false;
|
|
130
|
+
/** @type {Promise[]} */
|
|
131
|
+
const promises = [
|
|
132
|
+
(async () => {
|
|
133
|
+
try {
|
|
134
|
+
await this.logs.syslog.startCapture();
|
|
135
|
+
didStartSyslog = true;
|
|
136
|
+
} catch (err) {
|
|
137
|
+
log.debug(err.stack);
|
|
138
|
+
log.warn(`Continuing without capturing device logs: ${err.message}`);
|
|
139
|
+
}
|
|
140
|
+
})(),
|
|
141
|
+
this.logs.crashlog.startCapture(),
|
|
142
|
+
this.logs.safariConsole.startCapture(),
|
|
143
|
+
this.logs.safariNetwork.startCapture(),
|
|
144
|
+
];
|
|
145
|
+
await B.all(promises);
|
|
146
|
+
|
|
147
|
+
return didStartSyslog;
|
|
138
148
|
},
|
|
139
149
|
|
|
140
150
|
/**
|
|
@@ -8,22 +8,34 @@ import Pyidevice from '../py-ios-device-client';
|
|
|
8
8
|
|
|
9
9
|
const REAL_DEVICE_MAGIC = '3620bbb0-fb9f-4b62-a668-896f2edc4d88';
|
|
10
10
|
const MAGIC_SEP = '/';
|
|
11
|
+
// The file format has been changed from '.crash' to '.ips' since Monterey.
|
|
12
|
+
const CRASH_REPORTS_GLOB_PATTERN = '**/*.@(crash|ips)';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} LogRecord
|
|
16
|
+
* @property {number} timestamp
|
|
17
|
+
* @property {string} level
|
|
18
|
+
* @property {string} message
|
|
19
|
+
*/
|
|
11
20
|
|
|
12
21
|
class IOSCrashLog {
|
|
13
22
|
constructor(opts = {}) {
|
|
14
23
|
this.udid = opts.udid;
|
|
15
24
|
this.pyideviceClient = this.udid ? new Pyidevice(this.udid) : null;
|
|
16
|
-
const
|
|
25
|
+
const root = process.env.HOME || '/';
|
|
17
26
|
const logDir = opts.udid
|
|
18
|
-
? path.resolve(
|
|
19
|
-
: path.resolve(
|
|
20
|
-
this.logDir = logDir || path.resolve(
|
|
27
|
+
? path.resolve(root, 'Library', 'Logs', 'CrashReporter', 'MobileDevice')
|
|
28
|
+
: path.resolve(root, 'Library', 'Logs', 'DiagnosticReports');
|
|
29
|
+
this.logDir = logDir || path.resolve(root, 'Library', 'Logs', 'DiagnosticReports');
|
|
21
30
|
this.prevLogs = [];
|
|
22
31
|
this.logsSinceLastRequest = [];
|
|
23
32
|
this.phoneName = null;
|
|
24
33
|
this.sim = opts.sim;
|
|
25
34
|
}
|
|
26
35
|
|
|
36
|
+
/**
|
|
37
|
+
* @returns {Promise<string[]>}
|
|
38
|
+
*/
|
|
27
39
|
async _gatherFromRealDevice() {
|
|
28
40
|
if (await this.pyideviceClient?.assertExists(false)) {
|
|
29
41
|
return (await /** @type {Pyidevice} */ (this.pyideviceClient).listCrashes()).map(
|
|
@@ -40,17 +52,24 @@ class IOSCrashLog {
|
|
|
40
52
|
log.debug(`Crash reports root '${crashLogsRoot}' does not exist. Got nothing to gather.`);
|
|
41
53
|
return [];
|
|
42
54
|
}
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
return await fs.glob(CRASH_REPORTS_GLOB_PATTERN, {
|
|
56
|
+
cwd: crashLogsRoot,
|
|
57
|
+
absolute: true,
|
|
58
|
+
});
|
|
45
59
|
}
|
|
46
60
|
|
|
61
|
+
/**
|
|
62
|
+
* @returns {Promise<string[]>}
|
|
63
|
+
*/
|
|
47
64
|
async _gatherFromSimulator() {
|
|
48
65
|
if (!(await fs.exists(this.logDir))) {
|
|
49
66
|
log.debug(`Crash reports root '${this.logDir}' does not exist. Got nothing to gather.`);
|
|
50
67
|
return [];
|
|
51
68
|
}
|
|
52
|
-
|
|
53
|
-
|
|
69
|
+
const foundFiles = await fs.glob(CRASH_REPORTS_GLOB_PATTERN, {
|
|
70
|
+
cwd: this.logDir,
|
|
71
|
+
absolute: true,
|
|
72
|
+
});
|
|
54
73
|
// For Simulator only include files, that contain current UDID
|
|
55
74
|
return await B.filter(foundFiles, async (x) => {
|
|
56
75
|
try {
|
|
@@ -62,18 +81,30 @@ class IOSCrashLog {
|
|
|
62
81
|
});
|
|
63
82
|
}
|
|
64
83
|
|
|
84
|
+
/**
|
|
85
|
+
* @returns {Promise<string[]>}
|
|
86
|
+
*/
|
|
65
87
|
async getCrashes() {
|
|
66
88
|
return this.udid ? await this._gatherFromRealDevice() : await this._gatherFromSimulator();
|
|
67
89
|
}
|
|
68
90
|
|
|
91
|
+
/**
|
|
92
|
+
* @returns {Promise<void>}
|
|
93
|
+
*/
|
|
69
94
|
async startCapture() {
|
|
70
95
|
this.prevLogs = await this.getCrashes();
|
|
71
96
|
}
|
|
72
97
|
|
|
98
|
+
/**
|
|
99
|
+
* @returns {Promise<void>}
|
|
100
|
+
*/
|
|
73
101
|
async stopCapture() {
|
|
74
102
|
// needed for consistent API with other logs
|
|
75
103
|
}
|
|
76
104
|
|
|
105
|
+
/**
|
|
106
|
+
* @returns {Promise<LogRecord[]>}
|
|
107
|
+
*/
|
|
77
108
|
async getLogs() {
|
|
78
109
|
let crashFiles = await this.getCrashes();
|
|
79
110
|
let diff = _.difference(crashFiles, this.prevLogs, this.logsSinceLastRequest);
|
|
@@ -81,19 +112,26 @@ class IOSCrashLog {
|
|
|
81
112
|
return await this.filesToJSON(diff);
|
|
82
113
|
}
|
|
83
114
|
|
|
115
|
+
/**
|
|
116
|
+
* @returns {Promise<LogRecord[]>}
|
|
117
|
+
*/
|
|
84
118
|
async getAllLogs() {
|
|
85
119
|
let crashFiles = await this.getCrashes();
|
|
86
120
|
let logFiles = _.difference(crashFiles, this.prevLogs);
|
|
87
121
|
return await this.filesToJSON(logFiles);
|
|
88
122
|
}
|
|
89
123
|
|
|
124
|
+
/**
|
|
125
|
+
* @param {string[]} paths
|
|
126
|
+
* @returns {Promise<LogRecord[]>}
|
|
127
|
+
*/
|
|
90
128
|
async filesToJSON(paths) {
|
|
91
129
|
const tmpRoot = await tempDir.openDir();
|
|
92
130
|
try {
|
|
93
|
-
return (
|
|
131
|
+
return /** @type {LogRecord[]} */ ((
|
|
94
132
|
await B.map(paths, async (fullPath) => {
|
|
95
133
|
if (_.includes(fullPath, REAL_DEVICE_MAGIC)) {
|
|
96
|
-
const fileName = _.last(fullPath.split(MAGIC_SEP));
|
|
134
|
+
const fileName = /** @type {string} */ (_.last(fullPath.split(MAGIC_SEP)));
|
|
97
135
|
try {
|
|
98
136
|
// @ts-expect-error If pyideviceClient is not defined, then the exception will be caught below
|
|
99
137
|
await this.pyideviceClient.exportCrash(fileName, tmpRoot);
|
|
@@ -113,7 +151,7 @@ class IOSCrashLog {
|
|
|
113
151
|
message: await fs.readFile(fullPath, 'utf8'),
|
|
114
152
|
};
|
|
115
153
|
})
|
|
116
|
-
).filter(Boolean);
|
|
154
|
+
).filter(Boolean));
|
|
117
155
|
} finally {
|
|
118
156
|
await fs.rimraf(tmpRoot);
|
|
119
157
|
}
|
package/lib/driver.js
CHANGED
|
@@ -2018,7 +2018,6 @@ class XCUITestDriver extends BaseDriver {
|
|
|
2018
2018
|
mobileForcePress = commands.gestureExtensions.mobileForcePress;
|
|
2019
2019
|
mobileSelectPickerWheelValue = commands.gestureExtensions.mobileSelectPickerWheelValue;
|
|
2020
2020
|
mobileRotateElement = commands.gestureExtensions.mobileRotateElement;
|
|
2021
|
-
getCoordinates = commands.gestureExtensions.getCoordinates;
|
|
2022
2021
|
|
|
2023
2022
|
/*-------+
|
|
2024
2023
|
| IOHID |
|
|
@@ -61,7 +61,7 @@ export const executeMethodMap = {
|
|
|
61
61
|
'mobile: tapWithNumberOfTaps': {
|
|
62
62
|
command: 'mobileTapWithNumberOfTaps',
|
|
63
63
|
params: {
|
|
64
|
-
|
|
64
|
+
optional: ['numberOfTouches', 'numberOfTaps', 'elementId'],
|
|
65
65
|
},
|
|
66
66
|
},
|
|
67
67
|
// https://developer.apple.com/documentation/xctest/xcuielement/1618663-pressforduration?language=objc
|
|
@@ -84,7 +84,8 @@ export const executeMethodMap = {
|
|
|
84
84
|
'mobile: rotateElement': {
|
|
85
85
|
command: 'mobileRotateElement',
|
|
86
86
|
params: {
|
|
87
|
-
required: ['
|
|
87
|
+
required: ['rotation', 'velocity'],
|
|
88
|
+
optional: ['elementId'],
|
|
88
89
|
},
|
|
89
90
|
},
|
|
90
91
|
// https://developer.apple.com/documentation/xctest/xcuicoordinate/3551692-pressforduration?language=objc
|