appium-webdriveragent 4.15.1 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/WebDriverAgent.xcodeproj/project.pbxproj +0 -20
- package/WebDriverAgentLib/Categories/XCUIApplication+FBAlert.m +9 -20
- package/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +1 -5
- package/WebDriverAgentLib/Categories/XCUIElement+FBIsVisible.m +0 -12
- package/WebDriverAgentLib/Categories/XCUIElement+FBPickerWheel.m +1 -15
- package/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.m +7 -37
- package/WebDriverAgentLib/Categories/XCUIElement+FBTap.m +7 -36
- package/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.h +0 -7
- package/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.m +0 -49
- package/WebDriverAgentLib/Commands/FBCustomCommands.m +2 -4
- package/WebDriverAgentLib/Commands/FBElementCommands.m +37 -19
- package/WebDriverAgentLib/Utilities/FBAppiumActionsSynthesizer.m +23 -15
- package/WebDriverAgentLib/Utilities/FBBaseActionsSynthesizer.h +4 -13
- package/WebDriverAgentLib/Utilities/FBBaseActionsSynthesizer.m +36 -74
- package/WebDriverAgentLib/Utilities/FBKeyboard.m +1 -2
- package/WebDriverAgentLib/Utilities/FBMathUtils.h +0 -6
- package/WebDriverAgentLib/Utilities/FBMathUtils.m +0 -29
- package/WebDriverAgentLib/Utilities/FBScreen.m +2 -9
- package/WebDriverAgentLib/Utilities/FBScreenshot.m +0 -1
- package/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.m +23 -31
- package/WebDriverAgentLib/Utilities/FBXCTestDaemonsProxy.h +0 -4
- package/WebDriverAgentLib/Utilities/FBXCTestDaemonsProxy.m +0 -12
- package/WebDriverAgentTests/IntegrationTests/FBAppiumTouchActionsIntegrationTests.m +0 -16
- package/WebDriverAgentTests/IntegrationTests/FBW3CTouchActionsIntegrationTests.m +0 -19
- package/WebDriverAgentTests/UnitTests/FBMathUtilsTests.m +0 -12
- package/package.json +1 -1
- package/WebDriverAgentLib/Categories/XCUICoordinate+FBFix.h +0 -18
- package/WebDriverAgentLib/Categories/XCUICoordinate+FBFix.m +0 -46
- package/WebDriverAgentTests/IntegrationTests/FBElementScreenshotTests.m +0 -52
- package/WebDriverAgentTests/UnitTests/XCUICoordinateFix.m +0 -63
|
@@ -80,7 +80,7 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
80
80
|
|
|
81
81
|
- (nullable instancetype)initWithActionItem:(NSDictionary<NSString *, id> *)item
|
|
82
82
|
application:(XCUIApplication *)application
|
|
83
|
-
atPosition:(nullable
|
|
83
|
+
atPosition:(nullable XCUICoordinate *)atPosition
|
|
84
84
|
offset:(double)offset
|
|
85
85
|
error:(NSError **)error
|
|
86
86
|
{
|
|
@@ -90,14 +90,14 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
90
90
|
self.application = application;
|
|
91
91
|
self.offset = offset;
|
|
92
92
|
id options = [item objectForKey:FB_OPTIONS_KEY];
|
|
93
|
-
if (atPosition) {
|
|
94
|
-
self.atPosition =
|
|
93
|
+
if (nil != atPosition) {
|
|
94
|
+
self.atPosition = (id) atPosition;
|
|
95
95
|
} else {
|
|
96
|
-
|
|
96
|
+
XCUICoordinate *result = [self coordinatesWithOptions:options error:error];
|
|
97
97
|
if (nil == result) {
|
|
98
98
|
return nil;
|
|
99
99
|
}
|
|
100
|
-
self.atPosition =
|
|
100
|
+
self.atPosition = result;
|
|
101
101
|
}
|
|
102
102
|
self.duration = [self durationWithOptions:options];
|
|
103
103
|
if (self.duration < 0) {
|
|
@@ -124,7 +124,8 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
124
124
|
0.0;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
- (nullable
|
|
127
|
+
- (nullable XCUICoordinate *)coordinatesWithOptions:(nullable NSDictionary<NSString *, id> *)options
|
|
128
|
+
error:(NSError **)error
|
|
128
129
|
{
|
|
129
130
|
if (![options isKindOfClass:NSDictionary.class]) {
|
|
130
131
|
NSString *description = [NSString stringWithFormat:@"'%@' key is mandatory for '%@' action", FB_OPTIONS_KEY, self.class.actionName];
|
|
@@ -169,7 +170,7 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
169
170
|
NSTimeInterval currentOffset = FBMillisToSeconds(self.offset);
|
|
170
171
|
NSMutableArray<XCPointerEventPath *> *result = [NSMutableArray array];
|
|
171
172
|
XCPointerEventPath *currentPath = [[XCPointerEventPath alloc]
|
|
172
|
-
initForTouchAtPoint:self.atPosition
|
|
173
|
+
initForTouchAtPoint:self.atPosition.screenPoint
|
|
173
174
|
offset:currentOffset];
|
|
174
175
|
[result addObject:currentPath];
|
|
175
176
|
currentOffset += FBMillisToSeconds(FB_TAP_DURATION_MS);
|
|
@@ -180,7 +181,8 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
180
181
|
NSNumber *tapCount = [options objectForKey:FB_OPTION_COUNT] ?: @1;
|
|
181
182
|
for (NSInteger times = 1; times < tapCount.integerValue; ++times) {
|
|
182
183
|
currentOffset += FBMillisToSeconds(FB_INTERTAP_MIN_DURATION_MS);
|
|
183
|
-
XCPointerEventPath *nextPath = [[XCPointerEventPath alloc] initForTouchAtPoint:self.atPosition
|
|
184
|
+
XCPointerEventPath *nextPath = [[XCPointerEventPath alloc] initForTouchAtPoint:self.atPosition.screenPoint
|
|
185
|
+
offset:currentOffset];
|
|
184
186
|
[result addObject:nextPath];
|
|
185
187
|
currentOffset += FBMillisToSeconds(FB_TAP_DURATION_MS);
|
|
186
188
|
[nextPath liftUpAtOffset:currentOffset];
|
|
@@ -204,7 +206,7 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
204
206
|
|
|
205
207
|
- (nullable instancetype)initWithActionItem:(NSDictionary<NSString *, id> *)item
|
|
206
208
|
application:(XCUIApplication *)application
|
|
207
|
-
atPosition:(nullable
|
|
209
|
+
atPosition:(nullable XCUICoordinate *)atPosition
|
|
208
210
|
offset:(double)offset
|
|
209
211
|
error:(NSError **)error
|
|
210
212
|
{
|
|
@@ -239,7 +241,7 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
239
241
|
error:(NSError **)error
|
|
240
242
|
{
|
|
241
243
|
XCPointerEventPath *result = [[XCPointerEventPath alloc]
|
|
242
|
-
initForTouchAtPoint:self.atPosition
|
|
244
|
+
initForTouchAtPoint:self.atPosition.screenPoint
|
|
243
245
|
offset:FBMillisToSeconds(self.offset)];
|
|
244
246
|
if (nil != self.pressure && nil != result.pointerEvents.lastObject) {
|
|
245
247
|
XCPointerEvent *pointerEvent = (XCPointerEvent *)result.pointerEvents.lastObject;
|
|
@@ -272,7 +274,7 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
272
274
|
currentItemIndex:(NSUInteger)currentItemIndex
|
|
273
275
|
error:(NSError **)error
|
|
274
276
|
{
|
|
275
|
-
return @[[[XCPointerEventPath alloc] initForTouchAtPoint:self.atPosition
|
|
277
|
+
return @[[[XCPointerEventPath alloc] initForTouchAtPoint:self.atPosition.screenPoint
|
|
276
278
|
offset:FBMillisToSeconds(self.offset)]];
|
|
277
279
|
}
|
|
278
280
|
|
|
@@ -312,7 +314,8 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
312
314
|
}
|
|
313
315
|
}
|
|
314
316
|
NSTimeInterval currentOffset = FBMillisToSeconds(self.offset + self.duration);
|
|
315
|
-
XCPointerEventPath *result = [[XCPointerEventPath alloc] initForTouchAtPoint:self.atPosition
|
|
317
|
+
XCPointerEventPath *result = [[XCPointerEventPath alloc] initForTouchAtPoint:self.atPosition.screenPoint
|
|
318
|
+
offset:currentOffset];
|
|
316
319
|
if (currentItemIndex == allItems.count - 1) {
|
|
317
320
|
[result liftUpAtOffset:currentOffset];
|
|
318
321
|
}
|
|
@@ -353,7 +356,8 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
353
356
|
return nil;
|
|
354
357
|
}
|
|
355
358
|
|
|
356
|
-
[eventPath moveToPoint:self.atPosition
|
|
359
|
+
[eventPath moveToPoint:self.atPosition.screenPoint
|
|
360
|
+
atOffset:FBMillisToSeconds(self.offset)];
|
|
357
361
|
return @[];
|
|
358
362
|
}
|
|
359
363
|
|
|
@@ -509,7 +513,11 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
509
513
|
return nil;
|
|
510
514
|
}
|
|
511
515
|
FBAppiumGestureItem *lastItem = [chain.items lastObject];
|
|
512
|
-
gestureItem = [[gestureItemClass alloc] initWithActionItem:actionItem
|
|
516
|
+
gestureItem = [[gestureItemClass alloc] initWithActionItem:actionItem
|
|
517
|
+
application:self.application
|
|
518
|
+
atPosition:lastItem.atPosition
|
|
519
|
+
offset:chain.durationOffset
|
|
520
|
+
error:error];
|
|
513
521
|
}
|
|
514
522
|
if (nil == gestureItem) {
|
|
515
523
|
return nil;
|
|
@@ -527,7 +535,7 @@ static const double FB_LONG_TAP_DURATION_MS = 600.0;
|
|
|
527
535
|
BOOL isMultiTouch = [self.actions.firstObject isKindOfClass:NSArray.class];
|
|
528
536
|
eventRecord = [[XCSynthesizedEventRecord alloc]
|
|
529
537
|
initWithName:(isMultiTouch ? @"Multi-Finger Touch Action" : @"Single-Finger Touch Action")
|
|
530
|
-
interfaceOrientation:
|
|
538
|
+
interfaceOrientation:self.application.interfaceOrientation];
|
|
531
539
|
for (NSArray<NSDictionary<NSString *, id> *> *action in (isMultiTouch ? self.actions : @[self.actions])) {
|
|
532
540
|
NSArray<NSDictionary<NSString *, id> *> *preprocessedAction = [self preprocessAction:action];
|
|
533
541
|
NSArray<XCPointerEventPath *> *eventPaths = [self eventPathsWithAction:preprocessedAction error:error];
|
|
@@ -51,19 +51,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
51
51
|
@interface FBBaseGestureItem : FBBaseActionItem
|
|
52
52
|
|
|
53
53
|
/*! Absolute position on the screen where the gesure should be performed */
|
|
54
|
-
@property (nonatomic)
|
|
54
|
+
@property (nonatomic) XCUICoordinate *atPosition;
|
|
55
55
|
/*! Gesture duration in milliseconds */
|
|
56
56
|
@property (nonatomic) double duration;
|
|
57
57
|
|
|
58
|
-
/**
|
|
59
|
-
Returns fixed hit point coordinates for the case when XCTest fails to transform element snaapshot properly on screen rotation.
|
|
60
|
-
|
|
61
|
-
@param hitPoint The initial hitpoint coordinates
|
|
62
|
-
@param snapshot Element's snapshot instance
|
|
63
|
-
@return The fixed hit point coordinates, if there is a need to fix them, or the unchanged hit point value
|
|
64
|
-
*/
|
|
65
|
-
- (CGPoint)fixedHitPointWith:(CGPoint)hitPoint forSnapshot:(id<FBXCElementSnapshot>)snapshot;
|
|
66
|
-
|
|
67
58
|
/**
|
|
68
59
|
Calculate absolute gesture position on the screen based on provided element and positionOffset values.
|
|
69
60
|
|
|
@@ -72,9 +63,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
72
63
|
@param error If there is an error, upon return contains an NSError object that describes the problem
|
|
73
64
|
@return Adbsolute gesture position on the screen or nil if the calculation fails (for example, the element is invisible)
|
|
74
65
|
*/
|
|
75
|
-
- (nullable
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
- (nullable XCUICoordinate *)hitpointWithElement:(nullable XCUIElement *)element
|
|
67
|
+
positionOffset:(nullable NSValue *)positionOffset
|
|
68
|
+
error:(NSError **)error;
|
|
78
69
|
|
|
79
70
|
@end
|
|
80
71
|
|
|
@@ -31,7 +31,10 @@
|
|
|
31
31
|
return nil;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
- (NSArray<XCPointerEventPath *> *)addToEventPath:(XCPointerEventPath *)eventPath
|
|
34
|
+
- (NSArray<XCPointerEventPath *> *)addToEventPath:(XCPointerEventPath *)eventPath
|
|
35
|
+
allItems:(NSArray *)allItems
|
|
36
|
+
currentItemIndex:(NSUInteger)currentItemIndex
|
|
37
|
+
error:(NSError **)error
|
|
35
38
|
{
|
|
36
39
|
@throw [[FBErrorBuilder.builder withDescription:@"Override this method in subclasses"] build];
|
|
37
40
|
return nil;
|
|
@@ -41,84 +44,40 @@
|
|
|
41
44
|
|
|
42
45
|
@implementation FBBaseGestureItem
|
|
43
46
|
|
|
44
|
-
- (
|
|
47
|
+
- (nullable XCUICoordinate *)hitpointWithElement:(nullable XCUIElement *)element
|
|
48
|
+
positionOffset:(nullable NSValue *)positionOffset
|
|
49
|
+
error:(NSError **)error
|
|
45
50
|
{
|
|
46
|
-
UIInterfaceOrientation interfaceOrientation = self.application.interfaceOrientation;
|
|
47
|
-
if (interfaceOrientation == UIInterfaceOrientationPortrait) {
|
|
48
|
-
// There is no need to recalculate anything for portrait orientation
|
|
49
|
-
return hitPoint;
|
|
50
|
-
}
|
|
51
|
-
CGRect appFrame = self.application.frame;
|
|
52
|
-
if (@available(iOS 13.0, *)) {
|
|
53
|
-
// For Xcode11 it is always necessary to adjust the tap point coordinates
|
|
54
|
-
return FBInvertPointForApplication(hitPoint, appFrame.size, interfaceOrientation);
|
|
55
|
-
}
|
|
56
|
-
NSArray<id<FBXCElementSnapshot>> *ancestors = [FBXCElementSnapshotWrapper ensureWrapped:snapshot].fb_ancestors;
|
|
57
|
-
id<FBXCElementSnapshot> parentWindow = ancestors.count > 1 ? [ancestors objectAtIndex:ancestors.count - 2] : nil;
|
|
58
|
-
CGRect parentWindowFrame = nil == parentWindow ? snapshot.frame : parentWindow.frame;
|
|
59
|
-
if ((appFrame.size.height > appFrame.size.width && parentWindowFrame.size.height < parentWindowFrame.size.width) ||
|
|
60
|
-
(appFrame.size.height < appFrame.size.width && parentWindowFrame.size.height > parentWindowFrame.size.width)) {
|
|
61
|
-
/*
|
|
62
|
-
This is the indication of the fact that transformation is broken and coordinates should be
|
|
63
|
-
recalculated manually.
|
|
64
|
-
However, upside-down case cannot be covered this way, which is not important for Appium
|
|
65
|
-
*/
|
|
66
|
-
return FBInvertPointForApplication(hitPoint, appFrame.size, interfaceOrientation);
|
|
67
|
-
}
|
|
68
|
-
return hitPoint;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
- (nullable NSValue *)hitpointWithElement:(nullable XCUIElement *)element positionOffset:(nullable NSValue *)positionOffset error:(NSError **)error
|
|
72
|
-
{
|
|
73
|
-
CGPoint hitPoint;
|
|
74
51
|
if (nil == element) {
|
|
52
|
+
CGVector offset = CGVectorMake(positionOffset.CGPointValue.x, positionOffset.CGPointValue.y);
|
|
75
53
|
// Only absolute offset is defined
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
} else {
|
|
86
|
-
// The offset relative to the element is defined
|
|
87
|
-
|
|
88
|
-
id<FBXCElementSnapshot> snapshot = element.fb_isResolvedFromCache.boolValue
|
|
89
|
-
? element.lastSnapshot
|
|
90
|
-
: element.fb_takeSnapshot;
|
|
91
|
-
if (nil == positionOffset) {
|
|
92
|
-
NSValue *hitPointValue = [FBXCElementSnapshotWrapper ensureWrapped:snapshot].fb_hitPoint;
|
|
93
|
-
if (nil != hitPointValue) {
|
|
94
|
-
// short circuit element hitpoint
|
|
95
|
-
return hitPointValue;
|
|
96
|
-
}
|
|
97
|
-
[FBLogger logFmt:@"Will use the frame of '%@' for hit point calculation instead", element.debugDescription];
|
|
98
|
-
}
|
|
99
|
-
CGRect visibleFrame = snapshot.visibleFrame;
|
|
100
|
-
CGRect frame = CGRectIsEmpty(visibleFrame) ? element.frame : visibleFrame;
|
|
101
|
-
if (CGRectIsEmpty(frame)) {
|
|
102
|
-
[FBLogger log:self.application.fb_descriptionRepresentation];
|
|
103
|
-
NSString *description = [NSString stringWithFormat:@"The element '%@' is not visible on the screen and thus is not interactable", element.description];
|
|
104
|
-
if (error) {
|
|
105
|
-
*error = [[FBErrorBuilder.builder withDescription:description] build];
|
|
106
|
-
}
|
|
107
|
-
return nil;
|
|
54
|
+
return [[self.application coordinateWithNormalizedOffset:CGVectorMake(0, 0)] coordinateWithOffset:offset];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// The offset relative to the element is defined
|
|
58
|
+
if (nil == positionOffset) {
|
|
59
|
+
if (element.hittable) {
|
|
60
|
+
// short circuit element hitpoint
|
|
61
|
+
return element.hitPointCoordinate;
|
|
108
62
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// TODO: Shall we throw an exception if hitPoint is out of the element frame?
|
|
63
|
+
[FBLogger logFmt:@"Will use the frame of '%@' for hit point calculation instead", element.debugDescription];
|
|
64
|
+
}
|
|
65
|
+
if (CGRectIsEmpty(element.frame)) {
|
|
66
|
+
[FBLogger log:self.application.fb_descriptionRepresentation];
|
|
67
|
+
NSString *description = [NSString stringWithFormat:@"The element '%@' is not visible on the screen and thus is not interactable",
|
|
68
|
+
element.description];
|
|
69
|
+
if (error) {
|
|
70
|
+
*error = [[FBErrorBuilder.builder withDescription:description] build];
|
|
118
71
|
}
|
|
119
|
-
|
|
72
|
+
return nil;
|
|
120
73
|
}
|
|
121
|
-
|
|
74
|
+
if (nil == positionOffset) {
|
|
75
|
+
return [element coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5)];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
CGVector offset = CGVectorMake(positionOffset.CGPointValue.x, positionOffset.CGPointValue.y);
|
|
79
|
+
// TODO: Shall we throw an exception if hitPoint is out of the element frame?
|
|
80
|
+
return [[element coordinateWithNormalizedOffset:CGVectorMake(0, 0)] coordinateWithOffset:offset];
|
|
122
81
|
}
|
|
123
82
|
|
|
124
83
|
@end
|
|
@@ -179,7 +138,10 @@
|
|
|
179
138
|
|
|
180
139
|
@implementation FBBaseActionsSynthesizer
|
|
181
140
|
|
|
182
|
-
- (instancetype)initWithActions:(NSArray *)actions
|
|
141
|
+
- (instancetype)initWithActions:(NSArray *)actions
|
|
142
|
+
forApplication:(XCUIApplication *)application
|
|
143
|
+
elementCache:(nullable FBElementCache *)elementCache
|
|
144
|
+
error:(NSError **)error
|
|
183
145
|
{
|
|
184
146
|
self = [super init];
|
|
185
147
|
if (self) {
|
|
@@ -63,8 +63,7 @@
|
|
|
63
63
|
}];
|
|
64
64
|
XCUIElement *firstKey = [[app.keyboard descendantsMatchingType:XCUIElementTypeKey]
|
|
65
65
|
matchingPredicate:keySearchPredicate].allElementsBoundByIndex.firstObject;
|
|
66
|
-
return firstKey.exists
|
|
67
|
-
&& (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"13.0") ? firstKey.hittable : firstKey.fb_isVisible);
|
|
66
|
+
return firstKey.exists && firstKey.hittable;
|
|
68
67
|
};
|
|
69
68
|
NSString* errMessage = @"The on-screen keyboard must be present to send keys";
|
|
70
69
|
if (timeout <= 0) {
|
|
@@ -32,12 +32,6 @@ BOOL FBSizeFuzzyEqualToSize(CGSize size1, CGSize size2, CGFloat threshold);
|
|
|
32
32
|
BOOL FBRectFuzzyEqualToRect(CGRect rect1, CGRect rect2, CGFloat threshold);
|
|
33
33
|
|
|
34
34
|
#if !TARGET_OS_TV
|
|
35
|
-
/*! Inverts point if necessary to match location on screen */
|
|
36
|
-
CGPoint FBInvertPointForApplication(CGPoint point, CGSize screenSize, UIInterfaceOrientation orientation);
|
|
37
|
-
|
|
38
|
-
/*! Inverts offset if necessary to match screen orientation */
|
|
39
|
-
CGPoint FBInvertOffsetForOrientation(CGPoint offset, UIInterfaceOrientation orientation);
|
|
40
|
-
|
|
41
35
|
/*! Inverts size if necessary to match current screen orientation */
|
|
42
36
|
CGSize FBAdjustDimensionsForApplication(CGSize actualSize, UIInterfaceOrientation orientation);
|
|
43
37
|
#endif
|
|
@@ -46,35 +46,6 @@ BOOL FBRectFuzzyEqualToRect(CGRect rect1, CGRect rect2, CGFloat threshold)
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
#if !TARGET_OS_TV
|
|
49
|
-
CGPoint FBInvertPointForApplication(CGPoint point, CGSize screenSize, UIInterfaceOrientation orientation)
|
|
50
|
-
{
|
|
51
|
-
switch (orientation) {
|
|
52
|
-
case UIInterfaceOrientationUnknown:
|
|
53
|
-
case UIInterfaceOrientationPortrait:
|
|
54
|
-
return point;
|
|
55
|
-
case UIInterfaceOrientationPortraitUpsideDown:
|
|
56
|
-
return CGPointMake(screenSize.width - point.x, screenSize.height - point.y);
|
|
57
|
-
case UIInterfaceOrientationLandscapeLeft:
|
|
58
|
-
return CGPointMake(point.y, MAX(screenSize.width, screenSize.height) - point.x);
|
|
59
|
-
case UIInterfaceOrientationLandscapeRight:
|
|
60
|
-
return CGPointMake(MIN(screenSize.width, screenSize.height) - point.y, point.x);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
CGPoint FBInvertOffsetForOrientation(CGPoint offset, UIInterfaceOrientation orientation)
|
|
65
|
-
{
|
|
66
|
-
switch (orientation) {
|
|
67
|
-
case UIInterfaceOrientationUnknown:
|
|
68
|
-
case UIInterfaceOrientationPortrait:
|
|
69
|
-
return offset;
|
|
70
|
-
case UIInterfaceOrientationPortraitUpsideDown:
|
|
71
|
-
return CGPointMake(-offset.x, -offset.y);
|
|
72
|
-
case UIInterfaceOrientationLandscapeLeft:
|
|
73
|
-
return CGPointMake(offset.y, -offset.x);
|
|
74
|
-
case UIInterfaceOrientationLandscapeRight:
|
|
75
|
-
return CGPointMake(-offset.y, offset.x);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
49
|
|
|
79
50
|
CGSize FBAdjustDimensionsForApplication(CGSize actualSize, UIInterfaceOrientation orientation)
|
|
80
51
|
{
|
|
@@ -22,17 +22,10 @@
|
|
|
22
22
|
|
|
23
23
|
+ (CGSize)statusBarSizeForApplication:(XCUIApplication *)application
|
|
24
24
|
{
|
|
25
|
-
XCUIApplication *app =
|
|
26
|
-
BOOL expectVisibleBar = YES;
|
|
27
|
-
|
|
25
|
+
XCUIApplication *app = FBApplication.fb_systemApplication;
|
|
28
26
|
// Since iOS 13 the status bar is no longer part of the application, it’s part of the SpringBoard
|
|
29
|
-
if (@available(iOS 13.0, *)) {
|
|
30
|
-
app = FBApplication.fb_systemApplication;
|
|
31
|
-
expectVisibleBar = NO;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
27
|
XCUIElement *mainStatusBar = app.statusBars.allElementsBoundByIndex.firstObject;
|
|
35
|
-
if (
|
|
28
|
+
if (nil == mainStatusBar) {
|
|
36
29
|
return CGSizeZero;
|
|
37
30
|
}
|
|
38
31
|
CGSize result = mainStatusBar.frame.size;
|
|
@@ -228,7 +228,6 @@ NSString *formatTimeInterval(NSTimeInterval interval) {
|
|
|
228
228
|
compressionQuality:(CGFloat)compressionQuality
|
|
229
229
|
error:(NSError **)error
|
|
230
230
|
{
|
|
231
|
-
// TODO: Use native accessors after we drop the support of Xcode 12.4 and below
|
|
232
231
|
Class imageEncodingClass = NSClassFromString(@"XCTImageEncoding");
|
|
233
232
|
if (nil == imageEncodingClass) {
|
|
234
233
|
[[[FBErrorBuilder builder]
|
|
@@ -133,16 +133,16 @@ static NSString *const FB_KEY_ACTIONS = @"actions";
|
|
|
133
133
|
return nil;
|
|
134
134
|
}
|
|
135
135
|
self.duration = durationObj.doubleValue;
|
|
136
|
-
|
|
136
|
+
XCUICoordinate *position = [self positionWithError:error];
|
|
137
137
|
if (nil == position) {
|
|
138
138
|
return nil;
|
|
139
139
|
}
|
|
140
|
-
self.atPosition =
|
|
140
|
+
self.atPosition = position;
|
|
141
141
|
}
|
|
142
142
|
return self;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
- (nullable
|
|
145
|
+
- (nullable XCUICoordinate *)positionWithError:(NSError **)error
|
|
146
146
|
{
|
|
147
147
|
if (nil == self.previousItem) {
|
|
148
148
|
NSString *errorDescription = [NSString stringWithFormat:@"The '%@' action item must be preceded by %@ item", self.actionItem, FB_ACTION_ITEM_TYPE_POINTER_MOVE];
|
|
@@ -151,39 +151,32 @@ static NSString *const FB_KEY_ACTIONS = @"actions";
|
|
|
151
151
|
}
|
|
152
152
|
return nil;
|
|
153
153
|
}
|
|
154
|
-
return
|
|
154
|
+
return self.previousItem.atPosition;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
- (nullable
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
- (nullable XCUICoordinate *)hitpointWithElement:(nullable XCUIElement *)element
|
|
158
|
+
positionOffset:(nullable NSValue *)positionOffset
|
|
159
|
+
error:(NSError **)error
|
|
160
160
|
{
|
|
161
161
|
if (nil == element || nil == positionOffset) {
|
|
162
162
|
return [super hitpointWithElement:element positionOffset:positionOffset error:error];
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
// An offset relative to the element is defined
|
|
166
|
-
|
|
167
|
-
? element.lastSnapshot
|
|
168
|
-
: element.fb_takeSnapshot;
|
|
169
|
-
CGRect frame = snapshot.frame;
|
|
170
|
-
if (CGRectIsEmpty(frame)) {
|
|
166
|
+
if (CGRectIsEmpty(element.frame)) {
|
|
171
167
|
[FBLogger log:self.application.fb_descriptionRepresentation];
|
|
172
|
-
NSString *description = [NSString stringWithFormat:@"The element '%@' is not visible on the screen and thus is not interactable",
|
|
168
|
+
NSString *description = [NSString stringWithFormat:@"The element '%@' is not visible on the screen and thus is not interactable",
|
|
169
|
+
element.description];
|
|
173
170
|
if (error) {
|
|
174
171
|
*error = [[FBErrorBuilder.builder withDescription:description] build];
|
|
175
172
|
}
|
|
176
173
|
return nil;
|
|
177
174
|
}
|
|
178
|
-
|
|
179
|
-
frame = CGRectIsEmpty(visibleFrame) ? frame : visibleFrame;
|
|
175
|
+
|
|
180
176
|
// W3C standard requires that relative element coordinates start at the center of the element's rectangle
|
|
181
|
-
|
|
182
|
-
CGPoint offsetValue = [positionOffset CGPointValue];
|
|
183
|
-
hitPoint = CGPointMake(hitPoint.x + offsetValue.x, hitPoint.y + offsetValue.y);
|
|
177
|
+
CGVector offset = CGVectorMake(positionOffset.CGPointValue.x, positionOffset.CGPointValue.y);
|
|
184
178
|
// TODO: Shall we throw an exception if hitPoint is out of the element frame?
|
|
185
|
-
|
|
186
|
-
return [NSValue valueWithCGPoint:hitPoint];
|
|
179
|
+
return [[element coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5)] coordinateWithOffset:offset];
|
|
187
180
|
}
|
|
188
181
|
|
|
189
182
|
@end
|
|
@@ -220,7 +213,7 @@ static NSString *const FB_KEY_ACTIONS = @"actions";
|
|
|
220
213
|
}
|
|
221
214
|
}
|
|
222
215
|
if (nil == self.pressure) {
|
|
223
|
-
XCPointerEventPath *result = [[XCPointerEventPath alloc] initForTouchAtPoint:self.atPosition
|
|
216
|
+
XCPointerEventPath *result = [[XCPointerEventPath alloc] initForTouchAtPoint:self.atPosition.screenPoint
|
|
224
217
|
offset:FBMillisToSeconds(self.offset)];
|
|
225
218
|
return @[result];
|
|
226
219
|
}
|
|
@@ -247,7 +240,7 @@ static NSString *const FB_KEY_ACTIONS = @"actions";
|
|
|
247
240
|
|
|
248
241
|
@implementation FBPointerMoveItem
|
|
249
242
|
|
|
250
|
-
- (nullable
|
|
243
|
+
- (nullable XCUICoordinate *)positionWithError:(NSError **)error
|
|
251
244
|
{
|
|
252
245
|
static NSArray<NSString *> *supportedOriginTypes;
|
|
253
246
|
static dispatch_once_t onceToken;
|
|
@@ -295,12 +288,9 @@ static NSString *const FB_KEY_ACTIONS = @"actions";
|
|
|
295
288
|
}
|
|
296
289
|
return nil;
|
|
297
290
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
offsetRelativeToRecentPosition = FBInvertOffsetForOrientation(offsetRelativeToRecentPosition, self.application.interfaceOrientation);
|
|
302
|
-
}
|
|
303
|
-
return [NSValue valueWithCGPoint:CGPointMake(recentPosition.x + offsetRelativeToRecentPosition.x, recentPosition.y + offsetRelativeToRecentPosition.y)];
|
|
291
|
+
XCUICoordinate *recentPosition = self.previousItem.atPosition;
|
|
292
|
+
CGVector offsetRelativeToRecentPosition = (nil == x && nil == y) ? CGVectorMake(0, 0) : CGVectorMake(x.floatValue, y.floatValue);
|
|
293
|
+
return [recentPosition coordinateWithOffset:offsetRelativeToRecentPosition];
|
|
304
294
|
}
|
|
305
295
|
|
|
306
296
|
+ (NSString *)actionName
|
|
@@ -314,9 +304,11 @@ static NSString *const FB_KEY_ACTIONS = @"actions";
|
|
|
314
304
|
error:(NSError **)error
|
|
315
305
|
{
|
|
316
306
|
if (nil == eventPath) {
|
|
317
|
-
return @[[[XCPointerEventPath alloc] initForTouchAtPoint:self.atPosition
|
|
307
|
+
return @[[[XCPointerEventPath alloc] initForTouchAtPoint:self.atPosition.screenPoint
|
|
308
|
+
offset:FBMillisToSeconds(self.offset + self.duration)]];
|
|
318
309
|
}
|
|
319
|
-
[eventPath moveToPoint:self.atPosition
|
|
310
|
+
[eventPath moveToPoint:self.atPosition.screenPoint
|
|
311
|
+
atOffset:FBMillisToSeconds(self.offset + self.duration)];
|
|
320
312
|
return @[];
|
|
321
313
|
}
|
|
322
314
|
|
|
@@ -890,7 +882,7 @@ static NSString *const FB_KEY_ACTIONS = @"actions";
|
|
|
890
882
|
{
|
|
891
883
|
XCSynthesizedEventRecord *eventRecord = [[XCSynthesizedEventRecord alloc]
|
|
892
884
|
initWithName:@"W3C Touch Action"
|
|
893
|
-
interfaceOrientation:
|
|
885
|
+
interfaceOrientation:self.application.interfaceOrientation];
|
|
894
886
|
NSMutableDictionary<NSString *, NSDictionary<NSString *, id> *> *actionsMapping = [NSMutableDictionary new];
|
|
895
887
|
NSMutableArray<NSString *> *actionIds = [NSMutableArray new];
|
|
896
888
|
for (NSDictionary<NSString *, id> *action in self.actions) {
|
|
@@ -23,10 +23,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
23
23
|
|
|
24
24
|
+ (id<XCTestManager_ManagerInterface>)testRunnerProxy;
|
|
25
25
|
|
|
26
|
-
#if !TARGET_OS_TV
|
|
27
|
-
+ (UIInterfaceOrientation)orientationWithApplication:(XCUIApplication *)application;
|
|
28
|
-
#endif
|
|
29
|
-
|
|
30
26
|
+ (BOOL)synthesizeEventWithRecord:(XCSynthesizedEventRecord *)record
|
|
31
27
|
error:(NSError *__autoreleasing*)error;
|
|
32
28
|
|
|
@@ -30,7 +30,6 @@ static dispatch_once_t onceTestRunnerDaemonClass;
|
|
|
30
30
|
|
|
31
31
|
+ (void)load
|
|
32
32
|
{
|
|
33
|
-
// XCTRunnerDaemonSession class is only available since Xcode 8.3
|
|
34
33
|
dispatch_once(&onceTestRunnerDaemonClass, ^{
|
|
35
34
|
FBXCTRunnerDaemonSessionClass = objc_lookUpClass("XCTRunnerDaemonSession");
|
|
36
35
|
});
|
|
@@ -60,17 +59,6 @@ static dispatch_once_t onceTestRunnerDaemonClass;
|
|
|
60
59
|
return ((XCTRunnerDaemonSession *)[FBXCTRunnerDaemonSessionClass sharedSession]).daemonProxy;
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
#if !TARGET_OS_TV
|
|
64
|
-
+ (UIInterfaceOrientation)orientationWithApplication:(XCUIApplication *)application
|
|
65
|
-
{
|
|
66
|
-
if (nil == FBXCTRunnerDaemonSessionClass ||
|
|
67
|
-
[[FBXCTRunnerDaemonSessionClass sharedSession] useLegacyEventCoordinateTransformationPath]) {
|
|
68
|
-
return application.interfaceOrientation;
|
|
69
|
-
}
|
|
70
|
-
return UIInterfaceOrientationPortrait;
|
|
71
|
-
}
|
|
72
|
-
#endif
|
|
73
|
-
|
|
74
62
|
+ (BOOL)synthesizeEventWithRecord:(XCSynthesizedEventRecord *)record error:(NSError *__autoreleasing*)error
|
|
75
63
|
{
|
|
76
64
|
__block BOOL didSucceed = NO;
|
|
@@ -194,14 +194,6 @@
|
|
|
194
194
|
|
|
195
195
|
- (void)testDoubleTap
|
|
196
196
|
{
|
|
197
|
-
if ([UIDevice.currentDevice userInterfaceIdiom] == UIUserInterfaceIdiomPad &&
|
|
198
|
-
SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"15.0")) {
|
|
199
|
-
// "tap the element" does not work on iPadOS simulator after change the rotation in iOS 15
|
|
200
|
-
// while selecting the element worked. (I confirmed with getting an element screenshot that
|
|
201
|
-
// the FBShowAlertButtonName was actually selected after changing the orientation.)
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
197
|
NSArray<NSDictionary<NSString *, id> *> *gesture =
|
|
206
198
|
@[@{
|
|
207
199
|
@"action": @"tap",
|
|
@@ -214,16 +206,8 @@
|
|
|
214
206
|
[self verifyGesture:gesture orientation:UIDeviceOrientationLandscapeLeft];
|
|
215
207
|
}
|
|
216
208
|
|
|
217
|
-
// TODO: UIDeviceOrientationLandscapeRight is not so good in iOS 16? (Especially simulators)
|
|
218
209
|
- (void)testPress
|
|
219
210
|
{
|
|
220
|
-
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"15.0")) {
|
|
221
|
-
// Does not work on iOS 15.0.
|
|
222
|
-
// The same action tap the FBShowAlertButtonName after rotating the screen
|
|
223
|
-
// worked with W3C actions. `.click` action did not work.
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
211
|
NSArray<NSDictionary<NSString *, id> *> *gesture =
|
|
228
212
|
@[@{
|
|
229
213
|
@"action": @"press",
|
|
@@ -294,18 +294,8 @@
|
|
|
294
294
|
[self verifyGesture:gesture orientation:UIDeviceOrientationPortrait];
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
-
// TODO: UIDeviceOrientationLandscapeLeft did not work in iOS 16 simumlator.
|
|
298
|
-
// UIDeviceOrientationPortrait was ok.
|
|
299
297
|
- (void)testDoubleTap
|
|
300
298
|
{
|
|
301
|
-
if ([UIDevice.currentDevice userInterfaceIdiom] == UIUserInterfaceIdiomPad &&
|
|
302
|
-
SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"15.0")) {
|
|
303
|
-
// "tap the element" does not work on iPadOS simulator after change the rotation in iOS 15
|
|
304
|
-
// while selecting the element worked. (I confirmed with getting an element screenshot that
|
|
305
|
-
// the FBShowAlertButtonName was actually selected after changing the orientation.)
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
299
|
NSArray<NSDictionary<NSString *, id> *> *gesture =
|
|
310
300
|
@[@{
|
|
311
301
|
@"type": @"pointer",
|
|
@@ -326,17 +316,8 @@
|
|
|
326
316
|
[self verifyGesture:gesture orientation:UIDeviceOrientationLandscapeLeft];
|
|
327
317
|
}
|
|
328
318
|
|
|
329
|
-
// TODO: UIDeviceOrientationLandscapeRight did not work in iOS 16 simumlator.
|
|
330
|
-
// UIDeviceOrientationPortrait was ok.
|
|
331
319
|
- (void)testLongPressWithCombinedPause
|
|
332
320
|
{
|
|
333
|
-
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"15.0")) {
|
|
334
|
-
// Xcode 13 x iOS 14 also does not work while Xcode 12.5 x iOS 14 worked.
|
|
335
|
-
// Skip this test for iOS 15 since iOS 15 works with Xcode 13 at least.
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
|
|
340
321
|
NSArray<NSDictionary<NSString *, id> *> *gesture =
|
|
341
322
|
@[@{
|
|
342
323
|
@"type": @"pointer",
|
|
@@ -98,18 +98,6 @@
|
|
|
98
98
|
XCTAssertFalse(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(-1, -1, 1, 1), 1));
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
- (void)testPointInvertion
|
|
103
|
-
{
|
|
104
|
-
const CGPoint testPoint = CGPointMake(1, 2);
|
|
105
|
-
const CGSize screenSize = CGSizeMake(10, 15);
|
|
106
|
-
const CGFloat t = FBDefaultFrameFuzzyThreshold;
|
|
107
|
-
XCTAssertTrue(FBPointFuzzyEqualToPoint(CGPointMake(1, 2), FBInvertPointForApplication(testPoint, screenSize, UIInterfaceOrientationPortrait), t));
|
|
108
|
-
XCTAssertTrue(FBPointFuzzyEqualToPoint(CGPointMake(9, 13), FBInvertPointForApplication(testPoint, screenSize, UIInterfaceOrientationPortraitUpsideDown), t));
|
|
109
|
-
XCTAssertTrue(FBPointFuzzyEqualToPoint(CGPointMake(2, 14), FBInvertPointForApplication(testPoint, screenSize, UIInterfaceOrientationLandscapeLeft), t));
|
|
110
|
-
XCTAssertTrue(FBPointFuzzyEqualToPoint(CGPointMake(8, 1), FBInvertPointForApplication(testPoint, screenSize, UIInterfaceOrientationLandscapeRight), t));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
101
|
- (void)testSizeInversion
|
|
114
102
|
{
|
|
115
103
|
const CGSize screenSizePortrait = CGSizeMake(10, 15);
|