appium-webdriveragent 16.14.1 → 16.14.2
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 +6 -0
- package/README.md +9 -8
- package/WebDriverAgent.xcodeproj/project.pbxproj +4 -0
- package/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.m +110 -50
- package/WebDriverAgentLib/Commands/FBCustomCommands.m +8 -1
- package/WebDriverAgentLib/Commands/FBElementCommands.m +30 -1
- package/WebDriverAgentLib/Info.plist +2 -2
- package/WebDriverAgentLib/Utilities/FBMathUtils.h +11 -0
- package/WebDriverAgentLib/Utilities/FBMathUtils.m +38 -0
- package/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.m +121 -13
- package/WebDriverAgentTests/IntegrationApp/COORDINATE_PROBE.md +3 -1
- package/WebDriverAgentTests/IntegrationApp/Classes/FBCoordinateProbeViewController.m +18 -0
- package/WebDriverAgentTests/IntegrationTests/FBDisplayGestureTests.m +541 -0
- package/WebDriverAgentTests/IntegrationTests/FBScreenTests.m +13 -7
- package/WebDriverAgentTests/IntegrationTests/FBW3CTouchActionsIntegrationTests.m +126 -0
- package/WebDriverAgentTests/UnitTests/FBMathUtilsTests.m +45 -0
- package/package.json +1 -1
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
#import <XCTest/XCTest.h>
|
|
10
10
|
|
|
11
11
|
#import "FBIntegrationTestCase.h"
|
|
12
|
+
#import "FBConfiguration.h"
|
|
13
|
+
#import "FBMathUtils.h"
|
|
14
|
+
#import "FBScreen.h"
|
|
12
15
|
|
|
13
16
|
#import "XCUIElement.h"
|
|
14
17
|
#import "XCUIDevice.h"
|
|
@@ -21,6 +24,7 @@
|
|
|
21
24
|
#import "XCSynthesizedEventRecord.h"
|
|
22
25
|
#import "XCPointerEventPath.h"
|
|
23
26
|
#import "XCPointerEvent.h"
|
|
27
|
+
#import "XCUIScreen.h"
|
|
24
28
|
|
|
25
29
|
@interface FBW3CTouchActionsIntegrationTestsPart1 : FBIntegrationTestCase
|
|
26
30
|
@end
|
|
@@ -32,9 +36,36 @@
|
|
|
32
36
|
|
|
33
37
|
@implementation FBW3CTouchActionsIntegrationTestsPart1
|
|
34
38
|
|
|
39
|
+
- (void)testDisplayIndependentActionsAfterSelectedDisplayDisappears
|
|
40
|
+
{
|
|
41
|
+
// setUp already opens the Alerts fixture; relaunching would reset that page
|
|
42
|
+
// and break the subsequent touch tests that share it.
|
|
43
|
+
NSNumber *previous = FBConfiguration.sharedInstance.currentDisplayId;
|
|
44
|
+
[self addTeardownBlock:^{ FBConfiguration.sharedInstance.currentDisplayId = previous; }];
|
|
45
|
+
NSArray *sequences = @[
|
|
46
|
+
@{@"id": @"keyboard", @"type": @"key", @"actions": @[@{@"type": @"keyDown", @"value": @"a"}, @{@"type": @"keyUp", @"value": @"a"}]},
|
|
47
|
+
@{@"id": @"finger", @"type": @"pointer", @"parameters": @{@"pointerType": @"touch"}, @"actions": @[@{@"type": @"pause", @"duration": @10}]}
|
|
48
|
+
];
|
|
49
|
+
for (NSDictionary *sequence in sequences) {
|
|
50
|
+
for (NSNumber *display in @[@1, @987654]) {
|
|
51
|
+
FBConfiguration.sharedInstance.currentDisplayId = display;
|
|
52
|
+
NSError *error = nil;
|
|
53
|
+
FBW3CActionsSynthesizer *synthesizer = [[FBW3CActionsSynthesizer alloc] initWithActions:@[sequence] forApplication:self.testedApplication elementCache:nil error:&error];
|
|
54
|
+
XCSynthesizedEventRecord *record = [synthesizer synthesizeWithError:&error];
|
|
55
|
+
XCTAssertNotNil(record, @"Display-independent %@ should succeed: %@", sequence[@"type"], error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
35
60
|
- (void)verifyGesture:(NSArray<NSDictionary<NSString *, id> *> *)gesture orientation:(UIDeviceOrientation)orientation
|
|
36
61
|
{
|
|
37
62
|
[[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:orientation];
|
|
63
|
+
// XCTest may report the new interface orientation while the Alerts button
|
|
64
|
+
// still has transposed geometry from the rotation transition. The fixture's
|
|
65
|
+
// button is wider than it is tall in every settled orientation. Wait for that
|
|
66
|
+
// layout before resolving the W3C action's element-origin coordinates.
|
|
67
|
+
XCUIElement *button = self.testedApplication.buttons[FBShowAlertButtonName];
|
|
68
|
+
FBAssertWaitTillBecomesTrue(CGRectGetWidth(button.frame) > CGRectGetHeight(button.frame));
|
|
38
69
|
NSError *error;
|
|
39
70
|
XCTAssertTrue([self.testedApplication fb_performW3CActions:gesture elementCache:nil error:&error]);
|
|
40
71
|
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count > 0);
|
|
@@ -53,11 +84,106 @@
|
|
|
53
84
|
|
|
54
85
|
- (void)tearDown
|
|
55
86
|
{
|
|
87
|
+
FBConfiguration.sharedInstance.currentDisplayId = nil;
|
|
56
88
|
[self clearAlert];
|
|
57
89
|
[self resetOrientation];
|
|
58
90
|
[super tearDown];
|
|
59
91
|
}
|
|
60
92
|
|
|
93
|
+
- (nullable XCSynthesizedEventRecord *)tapRecordWithError:(NSError **)error
|
|
94
|
+
{
|
|
95
|
+
NSArray *gesture = @[@{
|
|
96
|
+
@"type": @"pointer", @"id": @"finger", @"parameters": @{@"pointerType": @"touch"},
|
|
97
|
+
@"actions": @[
|
|
98
|
+
@{@"type": @"pointerMove", @"x": @100, @"y": @100, @"duration": @0},
|
|
99
|
+
@{@"type": @"pointerDown", @"button": @0},
|
|
100
|
+
@{@"type": @"pointerUp", @"button": @0},
|
|
101
|
+
],
|
|
102
|
+
}];
|
|
103
|
+
FBW3CActionsSynthesizer *synthesizer = [[FBW3CActionsSynthesizer alloc] initWithActions:gesture
|
|
104
|
+
forApplication:self.testedApplication
|
|
105
|
+
elementCache:nil
|
|
106
|
+
error:error];
|
|
107
|
+
return [synthesizer synthesizeWithError:error];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
- (NSArray<XCPointerEvent *> *)pointerEventsOfRecord:(XCSynthesizedEventRecord *)record
|
|
111
|
+
{
|
|
112
|
+
XCPointerEventPath *eventPath = record.eventPaths.firstObject;
|
|
113
|
+
return eventPath.pointerEvents;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
- (void)testMainDisplaySettingKeepsDefaultCoordinates
|
|
117
|
+
{
|
|
118
|
+
for (NSNumber *orientation in @[@(UIDeviceOrientationPortrait),
|
|
119
|
+
@(UIDeviceOrientationLandscapeLeft),
|
|
120
|
+
@(UIDeviceOrientationLandscapeRight)]) {
|
|
121
|
+
[[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:orientation.integerValue];
|
|
122
|
+
NSError *error = nil;
|
|
123
|
+
FBConfiguration.sharedInstance.currentDisplayId = nil;
|
|
124
|
+
XCSynthesizedEventRecord *baseline = [self tapRecordWithError:&error];
|
|
125
|
+
FBConfiguration.sharedInstance.currentDisplayId = @([FBScreen displayID]);
|
|
126
|
+
XCSynthesizedEventRecord *selected = [self tapRecordWithError:&error];
|
|
127
|
+
XCTAssertNotNil(baseline);
|
|
128
|
+
XCTAssertNotNil(selected);
|
|
129
|
+
XCTAssertNil(error);
|
|
130
|
+
NSArray<XCPointerEvent *> *baselineEvents = [self pointerEventsOfRecord:baseline];
|
|
131
|
+
NSArray<XCPointerEvent *> *selectedEvents = [self pointerEventsOfRecord:selected];
|
|
132
|
+
XCTAssertEqual(baselineEvents.count, selectedEvents.count);
|
|
133
|
+
for (NSUInteger i = 0; i < baselineEvents.count; i++) {
|
|
134
|
+
XCTAssertTrue(CGPointEqualToPoint(baselineEvents[i].coordinate, selectedEvents[i].coordinate));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
- (void)testUnavailableDisplaySettingIsRejected
|
|
140
|
+
{
|
|
141
|
+
long long maxID = 0;
|
|
142
|
+
for (NSDictionary<NSString *, id> *screen in [FBScreen screensWithError:nil]) {
|
|
143
|
+
maxID = MAX(maxID, [screen[@"displayId"] longLongValue]);
|
|
144
|
+
}
|
|
145
|
+
FBConfiguration.sharedInstance.currentDisplayId = @(maxID + 1);
|
|
146
|
+
NSError *error = nil;
|
|
147
|
+
XCTAssertNil([self tapRecordWithError:&error]);
|
|
148
|
+
XCTAssertNotNil(error);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
- (void)testSecondaryDisplaySetting
|
|
152
|
+
{
|
|
153
|
+
XCUIScreen *secondary = nil;
|
|
154
|
+
NSError *error = nil;
|
|
155
|
+
NSArray<NSDictionary<NSString *, id> *> *screens = [FBScreen screensWithError:&error];
|
|
156
|
+
XCTAssertNotNil(screens);
|
|
157
|
+
XCTAssertNil(error);
|
|
158
|
+
for (NSDictionary<NSString *, id> *screen in screens) {
|
|
159
|
+
long long displayID = [screen[@"displayId"] longLongValue];
|
|
160
|
+
if (displayID != [FBScreen displayID]) {
|
|
161
|
+
secondary = [FBScreen screenWithDisplayID:displayID error:&error];
|
|
162
|
+
XCTAssertNotNil(secondary);
|
|
163
|
+
XCTAssertNil(error);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (nil == secondary) {
|
|
168
|
+
XCTSkip(@"The device has no secondary display");
|
|
169
|
+
}
|
|
170
|
+
XCSynthesizedEventRecord *baseline = [self tapRecordWithError:&error];
|
|
171
|
+
FBConfiguration.sharedInstance.currentDisplayId = @(secondary.displayID);
|
|
172
|
+
XCSynthesizedEventRecord *selected = [self tapRecordWithError:&error];
|
|
173
|
+
XCTAssertNotNil(selected);
|
|
174
|
+
XCTAssertNil(error);
|
|
175
|
+
XCTAssertEqual(selected.displayID, (unsigned long long)secondary.displayID);
|
|
176
|
+
XCUIScreen *main = XCUIScreen.mainScreen;
|
|
177
|
+
CGVector offset = FBDisplayCoordinateOffset(
|
|
178
|
+
CGSizeMake(main.bounds.size.width / main.scale, main.bounds.size.height / main.scale),
|
|
179
|
+
CGSizeMake(secondary.bounds.size.width / secondary.scale, secondary.bounds.size.height / secondary.scale),
|
|
180
|
+
selected.interfaceOrientation);
|
|
181
|
+
CGPoint expected = [self pointerEventsOfRecord:baseline].firstObject.coordinate;
|
|
182
|
+
CGPoint actual = [self pointerEventsOfRecord:selected].firstObject.coordinate;
|
|
183
|
+
XCTAssertEqual(actual.x, expected.x + offset.dx);
|
|
184
|
+
XCTAssertEqual(actual.y, expected.y + offset.dy);
|
|
185
|
+
}
|
|
186
|
+
|
|
61
187
|
- (void)testErroneousGestures
|
|
62
188
|
{
|
|
63
189
|
NSArray<NSArray<NSDictionary<NSString *, id> *> *> *invalidGestures =
|
|
@@ -148,4 +148,49 @@
|
|
|
148
148
|
XCTAssertTrue(endOffset.dx == -1 && endOffset.dy == -1);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
- (void)testDisplayCoordinateOffsets
|
|
152
|
+
{
|
|
153
|
+
CGSize mainSize = CGSizeMake(402, 874);
|
|
154
|
+
CGSize innerSize = CGSizeMake(669, 951);
|
|
155
|
+
NSArray<NSNumber *> *orientations = @[
|
|
156
|
+
@(UIInterfaceOrientationPortrait),
|
|
157
|
+
@(UIInterfaceOrientationLandscapeLeft),
|
|
158
|
+
@(UIInterfaceOrientationLandscapeRight),
|
|
159
|
+
@(UIInterfaceOrientationPortraitUpsideDown),
|
|
160
|
+
];
|
|
161
|
+
NSArray<NSValue *> *expected = @[
|
|
162
|
+
[NSValue valueWithCGPoint:CGPointZero],
|
|
163
|
+
[NSValue valueWithCGPoint:CGPointMake(0, 77)],
|
|
164
|
+
[NSValue valueWithCGPoint:CGPointMake(267, 0)],
|
|
165
|
+
[NSValue valueWithCGPoint:CGPointMake(267, 77)],
|
|
166
|
+
];
|
|
167
|
+
for (NSUInteger i = 0; i < orientations.count; i++) {
|
|
168
|
+
UIInterfaceOrientation orientation = orientations[i].integerValue;
|
|
169
|
+
CGVector offset = FBDisplayCoordinateOffset(mainSize, innerSize, orientation);
|
|
170
|
+
XCTAssertEqual(offset.dx, expected[i].CGPointValue.x);
|
|
171
|
+
XCTAssertEqual(offset.dy, expected[i].CGPointValue.y);
|
|
172
|
+
CGVector sameDisplay = FBDisplayCoordinateOffset(mainSize, mainSize, orientation);
|
|
173
|
+
XCTAssertEqual(sameDisplay.dx, 0);
|
|
174
|
+
XCTAssertEqual(sameDisplay.dy, 0);
|
|
175
|
+
CGVector reverse = FBDisplayCoordinateOffset(innerSize, mainSize, orientation);
|
|
176
|
+
XCTAssertEqual(reverse.dx, -expected[i].CGPointValue.x);
|
|
177
|
+
XCTAssertEqual(reverse.dy, -expected[i].CGPointValue.y);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
- (void)testPortraitVisibleFrameRotation
|
|
182
|
+
{
|
|
183
|
+
CGSize size = CGSizeMake(669, 951);
|
|
184
|
+
CGRect clipped = CGRectMake(200, 100, 300, 400);
|
|
185
|
+
XCTAssertTrue(CGRectEqualToRect(FBRectFromPortraitCoordinates(clipped, size, UIInterfaceOrientationPortrait), clipped));
|
|
186
|
+
XCTAssertTrue(CGRectEqualToRect(FBRectFromPortraitCoordinates(clipped, size, UIInterfaceOrientationLandscapeLeft), CGRectMake(451, 200, 400, 300)));
|
|
187
|
+
XCTAssertTrue(CGRectEqualToRect(FBRectFromPortraitCoordinates(clipped, size, UIInterfaceOrientationLandscapeRight), CGRectMake(100, 169, 400, 300)));
|
|
188
|
+
XCTAssertTrue(CGRectEqualToRect(FBRectFromPortraitCoordinates(clipped, size, UIInterfaceOrientationPortraitUpsideDown), CGRectMake(169, 451, 300, 400)));
|
|
189
|
+
// Observed on Duo's inner landscape display: keep the clipping in the same
|
|
190
|
+
// coordinate space as the element's reported frame.
|
|
191
|
+
XCTAssertTrue(CGRectEqualToRect(FBRectFromPortraitCoordinates(CGRectMake(236, 104, 379, 827), size, UIInterfaceOrientationLandscapeLeft), CGRectMake(20, 236, 827, 379)));
|
|
192
|
+
XCTAssertTrue(CGRectIsEmpty(FBRectFromPortraitCoordinates(CGRectZero, size, UIInterfaceOrientationLandscapeLeft)));
|
|
193
|
+
XCTAssertTrue(CGRectIsNull(FBRectFromPortraitCoordinates(CGRectNull, size, UIInterfaceOrientationLandscapeLeft)));
|
|
194
|
+
}
|
|
195
|
+
|
|
151
196
|
@end
|