@devicelab/maestro-runner-darwin-arm64 1.1.29 → 1.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +15 -4
  2. package/bin/maestro-runner +0 -0
  3. package/drivers/android/devicelab-android-driver-test.apk +0 -0
  4. package/drivers/android/devicelab-android-driver.apk +0 -0
  5. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/ATTRIBUTION.md +16 -0
  6. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Lifecycle.swift +33 -0
  7. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Snapshot.swift +195 -88
  8. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests.swift +3 -0
  9. package/drivers/ios/WebDriverAgent/CHANGELOG.md +202 -0
  10. package/drivers/ios/WebDriverAgent/README.md +18 -9
  11. package/drivers/ios/WebDriverAgent/Scripts/ci/build-real.sh +6 -10
  12. package/drivers/ios/WebDriverAgent/Scripts/fetch-prebuilt-wda.mjs +3 -2
  13. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/project.pbxproj +119 -0
  14. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCAXClient_iOS+FBSnapshotReqParams.m +8 -0
  15. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +3 -0
  16. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +30 -1
  17. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.h +53 -0
  18. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.m +124 -0
  19. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.h +6 -0
  20. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +100 -23
  21. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHinge.h +41 -0
  22. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHinge.m +215 -0
  23. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBRotation.m +8 -3
  24. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBFind.m +20 -7
  25. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.m +110 -50
  26. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.h +7 -0
  27. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.m +20 -0
  28. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.m +14 -11
  29. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBCustomCommands.m +86 -10
  30. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBElementCommands.m +57 -7
  31. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBSessionCommands.m +3 -2
  32. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Info.plist +2 -2
  33. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBHTTPServer.m +39 -16
  34. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBScreenRecordingRequest.m +7 -2
  35. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.h +22 -0
  36. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.m +78 -12
  37. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.h +5 -1
  38. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.m +10 -1
  39. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBWebServer.m +4 -1
  40. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.h +2 -2
  41. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.m +20 -0
  42. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.h +8 -0
  43. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.m +2 -0
  44. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBHIDOperations.h +41 -0
  45. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBHIDOperations.m +244 -0
  46. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBImageProcessor.m +3 -1
  47. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.h +11 -0
  48. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.m +38 -0
  49. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMjpegServer.m +5 -3
  50. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.h +17 -0
  51. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.m +76 -1
  52. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.h +2 -2
  53. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.m +6 -2
  54. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.h +2 -0
  55. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.m +2 -0
  56. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettingsHandler.m +48 -0
  57. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.m +121 -13
  58. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXPath.m +181 -128
  59. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCache.m +8 -5
  60. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCacheNode.h +1 -1
  61. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/WebDriverAgentLib.h +1 -0
  62. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/COORDINATE_PROBE.md +3 -1
  63. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBCoordinateProbeViewController.m +18 -0
  64. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/SceneDelegate.m +33 -0
  65. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/ViewController.m +26 -7
  66. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBConfigurationTests.m +52 -0
  67. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBDisplayGestureTests.m +541 -0
  68. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBElementSwipingTests.m +95 -0
  69. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBImageProcessorTests.m +34 -0
  70. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBScreenTests.m +392 -0
  71. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBSessionIntegrationTests.m +97 -0
  72. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBTypingTest.m +33 -0
  73. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBW3CTouchActionsIntegrationTests.m +126 -0
  74. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m +111 -0
  75. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceHelperTests.m +97 -0
  76. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceRotationTests.m +53 -0
  77. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIElementFBFindTests.m +113 -0
  78. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests_tvOS/FBTVFocusIntegrationTests.m +21 -0
  79. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests_watchOS/WDAAppLifecycleIntegrationTests.swift +12 -0
  80. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBCustomCommandsTests.m +61 -0
  81. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBElementUtilitiesTests.m +40 -0
  82. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBHTTPServerSessionTests.m +22 -4
  83. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBHTTPServerTests.m +199 -5
  84. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBLRUCacheTests.m +34 -0
  85. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBMathUtilsTests.m +45 -0
  86. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBSessionTests.m +201 -0
  87. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBXPathTests.m +101 -0
  88. package/drivers/ios/WebDriverAgent/docs/hinge-angle.md +54 -0
  89. package/drivers/ios/WebDriverAgent/docs/multiple-displays.md +168 -0
  90. package/drivers/ios/WebDriverAgent/lib/webdriveragent.ts +1 -2
  91. package/drivers/ios/WebDriverAgent/lib/xcodebuild.ts +58 -10
  92. package/drivers/ios/WebDriverAgent/package.json +8 -15
  93. package/drivers/ios/WebDriverAgent/test/functional/helpers/display-settings.ts +49 -0
  94. package/drivers/ios/WebDriverAgent/test/functional/helpers/simulator.ts +7 -7
  95. package/drivers/ios/WebDriverAgent/test/functional/webdriveragent-e2e.spec.ts +4 -0
  96. package/drivers/ios/WebDriverAgent/test/unit/xcodebuild.spec.ts +230 -0
  97. package/drivers/ios/WebDriverAgent/tsconfig.json +2 -1
  98. package/drivers/ios/devicelab-ios-agent/README.md +12 -0
  99. package/drivers/ios/devicelab-ios-agent/device/DevicelabIOSAgent.app/DevicelabIOSAgent +0 -0
  100. package/drivers/ios/devicelab-ios-agent/device/DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests +0 -0
  101. package/drivers/ios/devicelab-ios-agent/device/manifest.json +3 -3
  102. package/drivers/ios/devicelab-ios-agent/simulator/DevicelabIOSAgent.app/DevicelabIOSAgent +0 -0
  103. package/drivers/ios/devicelab-ios-agent/simulator/DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests +0 -0
  104. package/drivers/ios/devicelab-ios-agent/simulator/manifest.json +3 -3
  105. package/package.json +1 -1
  106. package/drivers/ios/WebDriverAgent/.editorconfig +0 -11
  107. package/drivers/ios/WebDriverAgent/.github/ISSUE_TEMPLATE.md +0 -4
  108. package/drivers/ios/WebDriverAgent/.github/actions/xcode-setup/action.yml +0 -32
  109. package/drivers/ios/WebDriverAgent/.github/dependabot.yml +0 -22
  110. package/drivers/ios/WebDriverAgent/.github/workflows/functional-test.yml +0 -103
  111. package/drivers/ios/WebDriverAgent/.github/workflows/pr-title.yml +0 -10
  112. package/drivers/ios/WebDriverAgent/.github/workflows/publish.js.yml +0 -112
  113. package/drivers/ios/WebDriverAgent/.github/workflows/unit-test.yml +0 -32
  114. package/drivers/ios/WebDriverAgent/.github/workflows/wda-package.yml +0 -114
  115. package/drivers/ios/WebDriverAgent/.github/workflows/wda-tests.yml +0 -246
  116. package/drivers/ios/WebDriverAgent/oxfmt.config.mjs +0 -6
  117. package/drivers/ios/WebDriverAgent/oxlint.config.mjs +0 -6
  118. package/drivers/ios/WebDriverAgent/release.config.mjs +0 -14
@@ -9,7 +9,71 @@
9
9
  #import <XCTest/XCTest.h>
10
10
 
11
11
  #import "FBIntegrationTestCase.h"
12
+ #import "FBConfiguration.h"
12
13
  #import "FBScreen.h"
14
+ #import "FBRunLoopSpinner.h"
15
+ #import "XCUIDevice+FBRotation.h"
16
+ #import "XCUIDevice+FBHinge.h"
17
+ #import "XCUIElement.h"
18
+ #import "FBActiveAppDetectionPoint.h"
19
+ #import "FBExceptions.h"
20
+ #import "FBScreenRecordingRequest.h"
21
+ #import "FBScreenshot.h"
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
31
+
32
+ @interface FBScreen (FBLookupTesting)
33
+ + (nullable XCUIScreen *)screenWithDisplayID:(long long)displayID device:(id)device error:(NSError **)error;
34
+ @end
35
+
36
+ @interface FBEmptyScreenDouble : NSObject
37
+ @property (nonatomic) CGRect bounds;
38
+ @end
39
+ @implementation FBEmptyScreenDouble
40
+ @end
41
+
42
+ @interface FBScreenDeviceDouble : NSObject
43
+ @property (nonatomic) BOOL nativeLookupAvailable;
44
+ @property (nonatomic) BOOL enumerationUnavailable;
45
+ @property (nonatomic) NSUInteger enumerationCount;
46
+ @property (nonatomic, strong) XCUIScreen *nativeScreen;
47
+ @property (nonatomic, strong) NSArray<XCUIScreen *> *screens;
48
+ @property (nonatomic, strong) NSError *lookupError;
49
+ @property (nonatomic, strong) NSError *enumerationError;
50
+ @end
51
+
52
+ @implementation FBScreenDeviceDouble
53
+ - (BOOL)respondsToSelector:(SEL)selector
54
+ {
55
+ if (selector == NSSelectorFromString(@"screensOrError:") && self.enumerationUnavailable) {
56
+ return NO;
57
+ }
58
+ return selector == @selector(screenWithDisplayID:orError:)
59
+ ? self.nativeLookupAvailable : [super respondsToSelector:selector];
60
+ }
61
+ - (XCUIScreen *)screenWithDisplayID:(long long)displayID orError:(NSError **)error
62
+ {
63
+ if (NULL != error) {
64
+ *error = self.lookupError;
65
+ }
66
+ return self.nativeScreen;
67
+ }
68
+ - (NSArray<XCUIScreen *> *)screensOrError:(NSError **)error
69
+ {
70
+ self.enumerationCount++;
71
+ if (NULL != error) {
72
+ *error = self.enumerationError;
73
+ }
74
+ return self.screens;
75
+ }
76
+ @end
13
77
 
14
78
  @interface FBScreenTests : FBIntegrationTestCase
15
79
  @end
@@ -22,6 +86,12 @@
22
86
  [self launchApplication];
23
87
  }
24
88
 
89
+ - (void)tearDown
90
+ {
91
+ FBConfiguration.sharedInstance.currentDisplayId = nil;
92
+ [super tearDown];
93
+ }
94
+
25
95
  - (void)testDisplayID
26
96
  {
27
97
  XCTAssertGreaterThanOrEqual([FBScreen displayID], 0LL);
@@ -50,9 +120,331 @@
50
120
  XCTAssertNotNil(mainScreen[@"traits"]);
51
121
  }
52
122
 
123
+ - (void)testScreenWithDisplayID
124
+ {
125
+ NSError *error = nil;
126
+ for (NSDictionary<NSString *, id> *entry in [FBScreen screensWithError:&error]) {
127
+ long long displayID = [entry[@"displayId"] longLongValue];
128
+ XCUIScreen *screen = [FBScreen screenWithDisplayID:displayID error:&error];
129
+ XCTAssertNotNil(screen);
130
+ XCTAssertNil(error);
131
+ XCTAssertEqual(screen.displayID, displayID);
132
+ }
133
+
134
+ XCTAssertNil([FBScreen screenWithDisplayID:[self unknownDisplayID] error:&error]);
135
+ XCTAssertNotNil(error);
136
+ NSMutableArray<NSNumber *> *availableIDs = [NSMutableArray array];
137
+ for (NSDictionary<NSString *, id> *availableScreen in [FBScreen screensWithError:nil]) {
138
+ [availableIDs addObject:availableScreen[@"displayId"]];
139
+ }
140
+ NSString *availableDisplays = [NSString stringWithFormat:@"Available display ids: [%@]",
141
+ [availableIDs componentsJoinedByString:@", "]];
142
+ XCTAssertTrue([error.localizedDescription containsString:availableDisplays]);
143
+ }
144
+
145
+ - (void)testNativeScreenLookupDoesNotEnumerateOnSuccess
146
+ {
147
+ FBScreenDeviceDouble *device = [FBScreenDeviceDouble new];
148
+ device.nativeLookupAvailable = YES;
149
+ device.nativeScreen = XCUIScreen.mainScreen;
150
+ NSError *error = nil;
151
+ XCTAssertEqualObjects([FBScreen screenWithDisplayID:device.nativeScreen.displayID device:device error:&error],
152
+ device.nativeScreen);
153
+ XCTAssertNil(error);
154
+ XCTAssertEqual(device.enumerationCount, 0UL);
155
+ }
156
+
157
+ - (void)testNativeScreenPlaceholderMustBeAvailable
158
+ {
159
+ FBScreenDeviceDouble *device = [FBScreenDeviceDouble new];
160
+ device.nativeLookupAvailable = YES;
161
+ device.nativeScreen = (id)[FBEmptyScreenDouble new];
162
+ device.screens = @[XCUIScreen.mainScreen];
163
+ NSError *error = nil;
164
+ XCTAssertNil([FBScreen screenWithDisplayID:-1 device:device error:&error]);
165
+ XCTAssertNotNil(error);
166
+ XCTAssertEqual(device.enumerationCount, 1UL);
167
+ }
168
+
169
+ - (void)testNativeScreenLookupFailureIncludesAvailableIDs
170
+ {
171
+ FBScreenDeviceDouble *device = [FBScreenDeviceDouble new];
172
+ device.nativeLookupAvailable = YES;
173
+ device.screens = @[XCUIScreen.mainScreen];
174
+ device.lookupError = [NSError errorWithDomain:@"NativeLookup" code:1 userInfo:nil];
175
+ NSError *error = nil;
176
+ XCTAssertNil([FBScreen screenWithDisplayID:-1 device:device error:&error]);
177
+ XCTAssertEqual(device.enumerationCount, 1UL);
178
+ XCTAssertTrue([error.localizedDescription containsString:@"Available display ids"]);
179
+ XCTAssertEqualObjects(error.userInfo[NSUnderlyingErrorKey], device.lookupError);
180
+ }
181
+
182
+ - (void)testNativeScreenLookupErrorSurvivesEnumerationFailure
183
+ {
184
+ FBScreenDeviceDouble *device = [FBScreenDeviceDouble new];
185
+ device.nativeLookupAvailable = YES;
186
+ device.lookupError = [NSError errorWithDomain:@"NativeLookup" code:1 userInfo:nil];
187
+ device.enumerationError = [NSError errorWithDomain:@"Enumeration" code:2 userInfo:nil];
188
+ NSError *error = nil;
189
+ XCTAssertNil([FBScreen screenWithDisplayID:-1 device:device error:&error]);
190
+ XCTAssertEqualObjects(error, device.lookupError);
191
+ }
192
+
193
+ - (void)testScreenLookupWithoutNativeSelector
194
+ {
195
+ FBScreenDeviceDouble *device = [FBScreenDeviceDouble new];
196
+ device.screens = @[XCUIScreen.mainScreen];
197
+ NSError *error = nil;
198
+ XCTAssertEqualObjects([FBScreen screenWithDisplayID:XCUIScreen.mainScreen.displayID device:device error:&error],
199
+ XCUIScreen.mainScreen);
200
+ XCTAssertNil(error);
201
+ XCTAssertEqual(device.enumerationCount, 1UL);
202
+ XCTAssertNil([FBScreen screenWithDisplayID:-1 device:device error:&error]);
203
+ XCTAssertTrue([error.localizedDescription containsString:@"Available display ids"]);
204
+ device.screens = nil;
205
+ device.enumerationError = [NSError errorWithDomain:@"Enumeration" code:2 userInfo:nil];
206
+ XCTAssertNil([FBScreen screenWithDisplayID:-1 device:device error:&error]);
207
+ XCTAssertEqualObjects(error, device.enumerationError);
208
+ }
209
+
210
+ - (void)testScreenLookupWithoutDeviceScreenSelectors
211
+ {
212
+ FBScreenDeviceDouble *device = [FBScreenDeviceDouble new];
213
+ device.enumerationUnavailable = YES;
214
+ NSError *error = nil;
215
+ XCTAssertEqualObjects([FBScreen screenWithDisplayID:XCUIScreen.mainScreen.displayID device:device error:&error],
216
+ XCUIScreen.mainScreen);
217
+ XCTAssertNil(error);
218
+ XCTAssertNil([FBScreen screenWithDisplayID:-1 device:device error:&error]);
219
+ XCTAssertNotNil(error);
220
+ XCTAssertEqual(device.enumerationCount, 0UL);
221
+ }
222
+
223
+ - (void)testCurrentScreenDefaultsToMainScreen
224
+ {
225
+ NSError *error = nil;
226
+ XCTAssertTrue([FBScreen currentScreenWithError:&error].isMainScreen);
227
+ XCTAssertNil(error);
228
+ }
229
+
230
+ - (void)testCurrentScreenFollowsSetting
231
+ {
232
+ FBConfiguration.sharedInstance.currentDisplayId = @([FBScreen displayID]);
233
+ NSError *error = nil;
234
+ XCTAssertEqual([FBScreen currentScreenWithError:&error].displayID, [FBScreen displayID]);
235
+ XCTAssertNil(error);
236
+ XCTAssertNotNil([FBScreenshot takeInOriginalResolutionWithQuality:0 error:&error]);
237
+ XCTAssertNil(error);
238
+
239
+ FBConfiguration.sharedInstance.currentDisplayId = @([self unknownDisplayID]);
240
+ XCTAssertNil([FBScreen currentScreenWithError:&error]);
241
+ XCTAssertNotNil(error);
242
+ error = nil;
243
+ XCTAssertNil([FBScreenshot takeInOriginalResolutionWithQuality:0 error:&error]);
244
+ XCTAssertNotNil(error);
245
+ }
246
+
247
+ - (void)testSessionResetClearsCurrentDisplay
248
+ {
249
+ FBConfiguration.sharedInstance.currentDisplayId = @([FBScreen displayID]);
250
+ [FBConfiguration.sharedInstance resetSessionSettings];
251
+ XCTAssertNil(FBConfiguration.sharedInstance.currentDisplayId);
252
+ }
253
+
254
+ - (void)testSecondaryDisplayDoesNotHitTestMainDisplay
255
+ {
256
+ NSArray<NSDictionary<NSString *, id> *> *screens = [FBScreen screensWithError:nil];
257
+ BOOL testedSecondaryDisplay = NO;
258
+ for (NSDictionary<NSString *, id> *screen in screens) {
259
+ if ([screen[@"isMain"] boolValue]) {
260
+ continue;
261
+ }
262
+ testedSecondaryDisplay = YES;
263
+ FBConfiguration.sharedInstance.currentDisplayId = screen[@"displayId"];
264
+ XCTAssertNil(FBActiveAppDetectionPoint.sharedInstance.axElement);
265
+ }
266
+ XCTSkipIf(!testedSecondaryDisplay, @"Requires a secondary display");
267
+ }
268
+
269
+ - (void)testActiveAppDetectionRejectsUnavailableDisplay
270
+ {
271
+ FBConfiguration.sharedInstance.currentDisplayId = @([self unknownDisplayID]);
272
+ XCTAssertThrowsSpecificNamed(FBActiveAppDetectionPoint.sharedInstance.axElement,
273
+ NSException, FBInvalidArgumentException);
274
+ }
275
+
276
+ - (void)testRecordingUsesSelectedDisplay
277
+ {
278
+ XCTSkipIf(nil == NSClassFromString(@"XCTScreenRecordingRequest"), @"Screen recording is unavailable");
279
+ FBScreenRecordingRequest *request = [[FBScreenRecordingRequest alloc] initWithFps:10 codec:0];
280
+ NSError *error = nil;
281
+ id defaultRequest = [request toNativeRequestWithError:&error];
282
+ XCTAssertNotNil(defaultRequest);
283
+ XCTAssertNil(error);
284
+ XCTAssertEqualObjects([defaultRequest valueForKey:@"screenID"], @([FBScreen displayID]));
285
+
286
+ for (NSDictionary<NSString *, id> *screen in [FBScreen screensWithError:nil]) {
287
+ FBConfiguration.sharedInstance.currentDisplayId = screen[@"displayId"];
288
+ id nativeRequest = [request toNativeRequestWithError:&error];
289
+ XCTAssertNotNil(nativeRequest);
290
+ XCTAssertNil(error);
291
+ XCTAssertEqualObjects([nativeRequest valueForKey:@"screenID"], screen[@"displayId"]);
292
+ }
293
+
294
+ FBConfiguration.sharedInstance.currentDisplayId = @([self unknownDisplayID]);
295
+ XCTAssertNil([request toNativeRequestWithError:&error]);
296
+ XCTAssertNotNil(error);
297
+ XCTAssertTrue([error.localizedDescription containsString:@"Available display ids"]);
298
+ }
299
+
300
+ - (long long)unknownDisplayID
301
+ {
302
+ long long maxID = 0;
303
+ for (NSDictionary<NSString *, id> *screen in [FBScreen screensWithError:nil]) {
304
+ maxID = MAX(maxID, [screen[@"displayId"] longLongValue]);
305
+ }
306
+ return maxID + 1;
307
+ }
308
+
53
309
  - (void)testScreenScale
54
310
  {
55
311
  XCTAssertTrue([FBScreen scale] >= 2);
56
312
  }
57
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
+
58
450
  @end
@@ -16,6 +16,7 @@
16
16
  #import "FBTestMacros.h"
17
17
  #import "FBUnattachedAppLauncher.h"
18
18
  #import "XCUIApplication+FBHelpers.h"
19
+ #import "XCUIApplication+FBQuiescence.h"
19
20
  #import "XCUIApplication.h"
20
21
 
21
22
  @interface FBSession (Tests)
@@ -101,6 +102,102 @@ static NSString *const SETTINGS_BUNDLE_ID = @"com.apple.Preferences";
101
102
  FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:testedApp.bundleID]);
102
103
  }
103
104
 
105
+ - (NSString *)startSessionWithoutApplicationUnderTest
106
+ {
107
+ NSString *bundleId = (NSString *)self.testedApplication.bundleID;
108
+ [self.testedApplication terminate];
109
+ FBAssertWaitTillBecomesTrue(self.testedApplication.state == XCUIApplicationStateNotRunning);
110
+ self.session = [FBSession initWithApplication:nil];
111
+ return bundleId;
112
+ }
113
+
114
+ - (void)testApplicationCanBeLaunchedAsApplicationUnderTest
115
+ {
116
+ NSString *bundleId = [self startSessionWithoutApplicationUnderTest];
117
+ XCUIApplication *app = [self.session launchApplicationWithBundleId:bundleId
118
+ shouldWaitForQuiescence:nil
119
+ arguments:nil
120
+ environment:nil
121
+ asApplicationUnderTest:YES];
122
+ FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:bundleId]);
123
+ XCTAssertEqual(app, self.session.activeApplication);
124
+ XCTAssertTrue(app.fb_shouldWaitForQuiescence);
125
+
126
+ XCUIApplication *settingsApp = [self.session launchApplicationWithBundleId:SETTINGS_BUNDLE_ID
127
+ shouldWaitForQuiescence:nil
128
+ arguments:nil
129
+ environment:nil];
130
+ FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:SETTINGS_BUNDLE_ID]);
131
+ XCTAssertTrue(settingsApp.fb_shouldWaitForQuiescence);
132
+ }
133
+
134
+ - (void)testApplicationLaunchedAsApplicationUnderTestRespectsQuiescenceArgument
135
+ {
136
+ NSString *bundleId = [self startSessionWithoutApplicationUnderTest];
137
+ XCUIApplication *app = [self.session launchApplicationWithBundleId:bundleId
138
+ shouldWaitForQuiescence:@NO
139
+ arguments:nil
140
+ environment:nil
141
+ asApplicationUnderTest:YES];
142
+ FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:bundleId]);
143
+ XCTAssertEqual(app, self.session.activeApplication);
144
+ XCTAssertFalse(app.fb_shouldWaitForQuiescence);
145
+ }
146
+
147
+ - (void)testCrashOfApplicationLaunchedAsApplicationUnderTestIsDetected
148
+ {
149
+ NSString *bundleId = [self startSessionWithoutApplicationUnderTest];
150
+ XCUIApplication *app = [self.session launchApplicationWithBundleId:bundleId
151
+ shouldWaitForQuiescence:nil
152
+ arguments:nil
153
+ environment:nil
154
+ asApplicationUnderTest:YES];
155
+ FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:bundleId]);
156
+ [app terminate];
157
+ FBAssertWaitTillBecomesTrue(app.state == XCUIApplicationStateNotRunning);
158
+ XCTAssertThrowsSpecificNamed(self.session.activeApplication, NSException, FBApplicationCrashedException);
159
+ }
160
+
161
+ - (void)testApplicationIsNotLaunchedAsApplicationUnderTestByDefault
162
+ {
163
+ NSString *bundleId = [self startSessionWithoutApplicationUnderTest];
164
+ XCUIApplication *app = [self.session launchApplicationWithBundleId:bundleId
165
+ shouldWaitForQuiescence:nil
166
+ arguments:nil
167
+ environment:nil];
168
+ FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:bundleId]);
169
+ XCTAssertFalse(app.fb_shouldWaitForQuiescence);
170
+ [app terminate];
171
+ FBAssertWaitTillBecomesTrue(app.state == XCUIApplicationStateNotRunning);
172
+ XCTAssertNoThrow(self.session.activeApplication);
173
+ }
174
+
175
+ // DELETE /session while /wda/apps/launch is still launching the app as the application under
176
+ // test: whichever of them finishes first, the app must not outlive the session. Settings takes
177
+ // long enough to launch for the deletion to usually land in the middle.
178
+ - (void)testApplicationLaunchedAsApplicationUnderTestDoesNotOutliveConcurrentSessionDeletion
179
+ {
180
+ [self startSessionWithoutApplicationUnderTest];
181
+ XCUIApplication *settingsApp = [[XCUIApplication alloc] initWithBundleIdentifier:SETTINGS_BUNDLE_ID];
182
+ [settingsApp terminate];
183
+ FBAssertWaitTillBecomesTrue(settingsApp.state == XCUIApplicationStateNotRunning);
184
+ FBSession *session = self.session;
185
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
186
+ dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
187
+ [session kill];
188
+ });
189
+ @try {
190
+ [session launchApplicationWithBundleId:SETTINGS_BUNDLE_ID
191
+ shouldWaitForQuiescence:nil
192
+ arguments:nil
193
+ environment:nil
194
+ asApplicationUnderTest:YES];
195
+ } @catch (NSException *e) {
196
+ XCTAssertEqualObjects(e.name, FBSessionDoesNotExistException);
197
+ }
198
+ FBAssertWaitTillBecomesTrue(settingsApp.state == XCUIApplicationStateNotRunning);
199
+ }
200
+
104
201
  - (void)testLaunchUnattachedApp
105
202
  {
106
203
  [FBUnattachedAppLauncher launchAppWithBundleId:SETTINGS_BUNDLE_ID];
@@ -8,6 +8,7 @@
8
8
 
9
9
  #import <XCTest/XCTest.h>
10
10
 
11
+ #import "FBConfiguration.h"
11
12
  #import "FBIntegrationTestCase.h"
12
13
  #import "XCUIElement.h"
13
14
  #import "XCUIElement+FBTyping.h"
@@ -24,6 +25,12 @@
24
25
  [self goToAttributesPage];
25
26
  }
26
27
 
28
+ - (void)tearDown
29
+ {
30
+ FBConfiguration.sharedInstance.keyboardWaitTimeout = 0;
31
+ [super tearDown];
32
+ }
33
+
27
34
  - (void)testTextTyping
28
35
  {
29
36
  NSString *text = @"Happy typing";
@@ -49,6 +56,32 @@
49
56
  XCTAssertEqualObjects(textField.value, expectedText);
50
57
  }
51
58
 
59
+ - (void)testTextTypingImmediatelyAfterFocus
60
+ {
61
+ // The field is a storyboard UIKit text field. This passes with the pause off,
62
+ // which is the default, and it does not reproduce a dropped character.
63
+ NSString *text = @"TheQuickBrownFox123!";
64
+ XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
65
+ [textField tap];
66
+ NSError *error;
67
+ XCTAssertTrue([textField fb_typeText:text shouldClear:NO error:&error]);
68
+ XCTAssertNil(error);
69
+ XCTAssertEqualObjects(textField.value, text);
70
+ }
71
+
72
+ - (void)testTextTypingWithKeyboardWaitTimeout
73
+ {
74
+ // The field is a storyboard UIKit text field. This does not reproduce a dropped character.
75
+ FBConfiguration.sharedInstance.keyboardWaitTimeout = 0.5;
76
+ NSString *text = @"TheQuickBrownFox123!";
77
+ XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
78
+ [textField tap];
79
+ NSError *error;
80
+ XCTAssertTrue([textField fb_typeText:text shouldClear:NO error:&error]);
81
+ XCTAssertNil(error);
82
+ XCTAssertEqualObjects(textField.value, text);
83
+ }
84
+
52
85
  - (void)testTextClearing
53
86
  {
54
87
  XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];