appium-webdriveragent 16.14.0 → 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 +13 -0
- package/README.md +9 -8
- package/WebDriverAgent.xcodeproj/project.pbxproj +4 -0
- package/WebDriverAgentLib/Categories/XCAXClient_iOS+FBSnapshotReqParams.m +8 -0
- package/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.m +110 -50
- package/WebDriverAgentLib/Commands/FBCustomCommands.m +42 -10
- 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/IntegrationApp/Classes/ViewController.m +15 -0
- package/WebDriverAgentTests/IntegrationTests/FBConfigurationTests.m +52 -0
- package/WebDriverAgentTests/IntegrationTests/FBDisplayGestureTests.m +541 -0
- package/WebDriverAgentTests/IntegrationTests/FBScreenTests.m +148 -0
- package/WebDriverAgentTests/IntegrationTests/FBW3CTouchActionsIntegrationTests.m +126 -0
- package/WebDriverAgentTests/UnitTests/FBMathUtilsTests.m +45 -0
- package/package.json +1 -1
|
@@ -11,11 +11,23 @@
|
|
|
11
11
|
#import "FBIntegrationTestCase.h"
|
|
12
12
|
#import "FBConfiguration.h"
|
|
13
13
|
#import "FBScreen.h"
|
|
14
|
+
#import "FBRunLoopSpinner.h"
|
|
15
|
+
#import "XCUIDevice+FBRotation.h"
|
|
16
|
+
#import "XCUIDevice+FBHinge.h"
|
|
17
|
+
#import "XCUIElement.h"
|
|
14
18
|
#import "FBActiveAppDetectionPoint.h"
|
|
15
19
|
#import "FBExceptions.h"
|
|
16
20
|
#import "FBScreenRecordingRequest.h"
|
|
17
21
|
#import "FBScreenshot.h"
|
|
18
22
|
#import "XCUIScreen.h"
|
|
23
|
+
#import "FBCustomCommands.h"
|
|
24
|
+
#import "FBRouteRequest.h"
|
|
25
|
+
#import "FBResponsePayload.h"
|
|
26
|
+
#import "RouteResponse.h"
|
|
27
|
+
|
|
28
|
+
@interface FBCustomCommands (FBScreenInfoTesting)
|
|
29
|
+
+ (id<FBResponsePayload>)handleGetScreen:(FBRouteRequest *)request;
|
|
30
|
+
@end
|
|
19
31
|
|
|
20
32
|
@interface FBScreen (FBLookupTesting)
|
|
21
33
|
+ (nullable XCUIScreen *)screenWithDisplayID:(long long)displayID device:(id)device error:(NSError **)error;
|
|
@@ -299,4 +311,140 @@
|
|
|
299
311
|
XCTAssertTrue([FBScreen scale] >= 2);
|
|
300
312
|
}
|
|
301
313
|
|
|
314
|
+
- (NSDictionary *)screenInfoResponse
|
|
315
|
+
{
|
|
316
|
+
FBRouteRequest *request = [FBRouteRequest routeRequestWithURL:[NSURL URLWithString:@"http://localhost:8100/wda/screen"]
|
|
317
|
+
parameters:@{}
|
|
318
|
+
arguments:@{}];
|
|
319
|
+
RouteResponse *response = [RouteResponse new];
|
|
320
|
+
[[FBCustomCommands handleGetScreen:request] dispatchWithResponse:response];
|
|
321
|
+
return [NSJSONSerialization JSONObjectWithData:response.responseData options:0 error:nil][@"value"];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
- (void)testScreenInfoFollowsDisplaySelection
|
|
325
|
+
{
|
|
326
|
+
for (NSDictionary *screen in [FBScreen screensWithError:nil]) {
|
|
327
|
+
FBConfiguration.sharedInstance.currentDisplayId = screen[@"displayId"];
|
|
328
|
+
NSDictionary *info = [self screenInfoResponse];
|
|
329
|
+
XCTAssertNil(info[@"error"]);
|
|
330
|
+
XCTAssertEqualObjects(info[@"displayId"], screen[@"displayId"]);
|
|
331
|
+
XCTAssertEqualObjects(info[@"scale"], screen[@"scale"]);
|
|
332
|
+
double width = [info[@"screenSize"][@"width"] doubleValue];
|
|
333
|
+
double height = [info[@"screenSize"][@"height"] doubleValue];
|
|
334
|
+
double scale = [screen[@"scale"] doubleValue];
|
|
335
|
+
double expectedWidth = [screen[@"bounds"][@"width"] doubleValue] / scale;
|
|
336
|
+
double expectedHeight = [screen[@"bounds"][@"height"] doubleValue] / scale;
|
|
337
|
+
XCTAssertEqualWithAccuracy(MIN(width, height), MIN(expectedWidth, expectedHeight), 0.01);
|
|
338
|
+
XCTAssertEqualWithAccuracy(MAX(width, height), MAX(expectedWidth, expectedHeight), 0.01);
|
|
339
|
+
double barHeight = [info[@"statusBarSize"][@"height"] doubleValue];
|
|
340
|
+
XCTAssertGreaterThanOrEqual(barHeight, 0);
|
|
341
|
+
XCTAssertLessThan(barHeight, height);
|
|
342
|
+
if (barHeight > 0) {
|
|
343
|
+
XCTAssertEqualWithAccuracy([info[@"statusBarSize"][@"width"] doubleValue], width, 1);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// tearDown restores the default display even when an assertion fails.
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
- (void)testScreenInfoFollowsDisplayReset
|
|
350
|
+
{
|
|
351
|
+
// Exercise reset independently of the display-selection assertions.
|
|
352
|
+
FBConfiguration.sharedInstance.currentDisplayId = @([self unknownDisplayID]);
|
|
353
|
+
FBConfiguration.sharedInstance.currentDisplayId = nil;
|
|
354
|
+
XCTAssertEqualObjects([self screenInfoResponse][@"displayId"], @([FBScreen displayID]));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
- (void)testScreenInfoRejectsUnavailableDisplay
|
|
358
|
+
{
|
|
359
|
+
FBConfiguration.sharedInstance.currentDisplayId = @([self unknownDisplayID]);
|
|
360
|
+
NSDictionary *info = [self screenInfoResponse];
|
|
361
|
+
XCTAssertEqualObjects(info[@"error"], @"invalid argument");
|
|
362
|
+
XCTAssertTrue([info[@"message"] containsString:@"Available display ids"]);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
- (void)testScreenInfoAcrossPortraitAndLandscapeOrientations
|
|
366
|
+
{
|
|
367
|
+
XCTSkipIf(XCUIDevice.sharedDevice.fb_canAttemptSimulatedHingeAngleInjection,
|
|
368
|
+
@"Foldable orientations are covered by testScreenInfoAcrossFoldStates");
|
|
369
|
+
XCUIElement *window = self.testedApplication.windows.firstMatch;
|
|
370
|
+
(void)window.frame;
|
|
371
|
+
XCTSkipIf(window.screen.displayID != XCUIScreen.mainScreen.displayID,
|
|
372
|
+
@"Requires the fixture on the main display");
|
|
373
|
+
[self addTeardownBlock:^{
|
|
374
|
+
[[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:UIDeviceOrientationPortrait];
|
|
375
|
+
}];
|
|
376
|
+
BOOL expectsVisibleBar = UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad;
|
|
377
|
+
for (NSNumber *orientation in @[@(UIDeviceOrientationPortrait),
|
|
378
|
+
@(UIDeviceOrientationLandscapeLeft),
|
|
379
|
+
@(UIDeviceOrientationLandscapeRight),
|
|
380
|
+
@(UIDeviceOrientationPortrait)]) {
|
|
381
|
+
XCTAssertTrue([[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:orientation.integerValue]);
|
|
382
|
+
__block NSDictionary *info = nil;
|
|
383
|
+
BOOL isPortrait = UIDeviceOrientationIsPortrait(orientation.integerValue);
|
|
384
|
+
// Include portrait before and after rotation: landscape-native iPad panels
|
|
385
|
+
// must swap their screen bounds back and retain the visible top status bar.
|
|
386
|
+
// Wait for rotation rather than accepting a transient zero crop.
|
|
387
|
+
XCTAssertTrue([[[FBRunLoopSpinner new] timeout:5] spinUntilTrue:^BOOL {
|
|
388
|
+
info = [self screenInfoResponse];
|
|
389
|
+
double width = [info[@"screenSize"][@"width"] doubleValue];
|
|
390
|
+
double height = [info[@"screenSize"][@"height"] doubleValue];
|
|
391
|
+
double barWidth = [info[@"statusBarSize"][@"width"] doubleValue];
|
|
392
|
+
double barHeight = [info[@"statusBarSize"][@"height"] doubleValue];
|
|
393
|
+
if (nil != info[@"error"] || width <= 0 || height <= 0
|
|
394
|
+
|| (isPortrait ? width >= height : width <= height)) {
|
|
395
|
+
return NO;
|
|
396
|
+
}
|
|
397
|
+
if (barHeight == 0) {
|
|
398
|
+
return !expectsVisibleBar && !isPortrait && barWidth == 0;
|
|
399
|
+
}
|
|
400
|
+
return barHeight > 0 && barHeight < height && fabs(barWidth - width) <= 1;
|
|
401
|
+
}], @"Unexpected screen metadata at orientation %@: %@", orientation, info);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
- (void)testScreenInfoAcrossFoldStates
|
|
406
|
+
{
|
|
407
|
+
XCUIDevice *device = XCUIDevice.sharedDevice;
|
|
408
|
+
XCTSkipIf(!device.fb_canAttemptSimulatedHingeAngleInjection, @"Requires a foldable simulator");
|
|
409
|
+
NSError *error = nil;
|
|
410
|
+
NSNumber *originalAngle = [device fb_getSimulatedHingeAngle:&error];
|
|
411
|
+
XCTAssertNotNil(originalAngle, @"%@", error);
|
|
412
|
+
[self addTeardownBlock:^{
|
|
413
|
+
if (nil != originalAngle) {
|
|
414
|
+
[device fb_setSimulatedHingeAngle:originalAngle.doubleValue error:nil];
|
|
415
|
+
}
|
|
416
|
+
}];
|
|
417
|
+
for (NSNumber *angle in @[@0, @90, @180, @0]) {
|
|
418
|
+
[self.testedApplication terminate];
|
|
419
|
+
XCTAssertTrue([device fb_setSimulatedHingeAngle:angle.doubleValue error:&error], @"%@", error);
|
|
420
|
+
[self launchApplication];
|
|
421
|
+
XCUIElement *window = self.testedApplication.windows.firstMatch;
|
|
422
|
+
CGRect frame = window.frame;
|
|
423
|
+
FBConfiguration.sharedInstance.currentDisplayId = @(window.screen.displayID);
|
|
424
|
+
__block NSDictionary *info = nil;
|
|
425
|
+
XCTAssertTrue([[[FBRunLoopSpinner new] timeout:5] spinUntilTrue:^BOOL {
|
|
426
|
+
info = [self screenInfoResponse];
|
|
427
|
+
double width = [info[@"screenSize"][@"width"] doubleValue];
|
|
428
|
+
double height = [info[@"screenSize"][@"height"] doubleValue];
|
|
429
|
+
double barHeight = [info[@"statusBarSize"][@"height"] doubleValue];
|
|
430
|
+
return nil == info[@"error"] && fabs(width - frame.size.width) <= 1
|
|
431
|
+
&& fabs(height - frame.size.height) <= 1
|
|
432
|
+
&& barHeight >= 0 && barHeight < height
|
|
433
|
+
&& (barHeight == 0 || fabs([info[@"statusBarSize"][@"width"] doubleValue] - width) <= 1)
|
|
434
|
+
&& (angle.doubleValue == 0 || barHeight == 0);
|
|
435
|
+
}], @"Unexpected screen metadata at hinge angle %@: %@", angle, info);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
- (void)testScreenInfoIncludesTopStatusBarOnSingleDisplay
|
|
440
|
+
{
|
|
441
|
+
XCTSkipIf([FBScreen screensWithError:nil].count != 1, @"Requires a single-display device");
|
|
442
|
+
// The integration fixture uses the ordinary visible portrait status bar.
|
|
443
|
+
// An unresolved XCUIElement has displayID == 0; it must not be filtered out.
|
|
444
|
+
NSDictionary *info = [self screenInfoResponse];
|
|
445
|
+
XCTAssertGreaterThan([info[@"statusBarSize"][@"height"] doubleValue], 0);
|
|
446
|
+
XCTAssertEqualWithAccuracy([info[@"statusBarSize"][@"width"] doubleValue],
|
|
447
|
+
[info[@"screenSize"][@"width"] doubleValue], 1);
|
|
448
|
+
}
|
|
449
|
+
|
|
302
450
|
@end
|
|
@@ -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
|