@devicelab/maestro-runner-darwin-arm64 1.1.28 → 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.
- package/README.md +30 -3
- package/bin/maestro-runner +0 -0
- package/drivers/android/devicelab-android-driver-test.apk +0 -0
- package/drivers/android/devicelab-android-driver.apk +0 -0
- package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/ATTRIBUTION.md +16 -0
- package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Lifecycle.swift +33 -0
- package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Snapshot.swift +195 -88
- package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests.swift +3 -0
- package/drivers/ios/WebDriverAgent/CHANGELOG.md +202 -0
- package/drivers/ios/WebDriverAgent/README.md +18 -9
- package/drivers/ios/WebDriverAgent/Scripts/ci/build-real.sh +6 -10
- package/drivers/ios/WebDriverAgent/Scripts/fetch-prebuilt-wda.mjs +3 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/project.pbxproj +119 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCAXClient_iOS+FBSnapshotReqParams.m +8 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +3 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +30 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.h +53 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.m +124 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.h +6 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +100 -23
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHinge.h +41 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHinge.m +215 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBRotation.m +8 -3
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBFind.m +20 -7
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.m +110 -50
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.h +7 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.m +20 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.m +14 -11
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBCustomCommands.m +86 -10
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBElementCommands.m +57 -7
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBSessionCommands.m +3 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Info.plist +2 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBHTTPServer.m +39 -16
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBScreenRecordingRequest.m +7 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.h +22 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.m +78 -12
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.h +5 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.m +10 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBWebServer.m +4 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.h +2 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.m +20 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.h +8 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.m +2 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBHIDOperations.h +41 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBHIDOperations.m +244 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBImageProcessor.m +3 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.h +11 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.m +38 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMjpegServer.m +5 -3
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.h +17 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.m +76 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.h +2 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.m +6 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.h +2 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.m +2 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettingsHandler.m +48 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.m +121 -13
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXPath.m +181 -128
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCache.m +8 -5
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCacheNode.h +1 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/WebDriverAgentLib.h +1 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/COORDINATE_PROBE.md +3 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBCoordinateProbeViewController.m +18 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/SceneDelegate.m +33 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/ViewController.m +26 -7
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBConfigurationTests.m +52 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBDisplayGestureTests.m +541 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBElementSwipingTests.m +95 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBImageProcessorTests.m +34 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBScreenTests.m +392 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBSessionIntegrationTests.m +97 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBTypingTest.m +33 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBW3CTouchActionsIntegrationTests.m +126 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m +111 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceHelperTests.m +97 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceRotationTests.m +53 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIElementFBFindTests.m +113 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests_tvOS/FBTVFocusIntegrationTests.m +21 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests_watchOS/WDAAppLifecycleIntegrationTests.swift +12 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBCustomCommandsTests.m +61 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBElementUtilitiesTests.m +40 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBHTTPServerSessionTests.m +22 -4
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBHTTPServerTests.m +199 -5
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBLRUCacheTests.m +34 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBMathUtilsTests.m +45 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBSessionTests.m +201 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBXPathTests.m +101 -0
- package/drivers/ios/WebDriverAgent/docs/hinge-angle.md +54 -0
- package/drivers/ios/WebDriverAgent/docs/multiple-displays.md +168 -0
- package/drivers/ios/WebDriverAgent/lib/webdriveragent.ts +1 -2
- package/drivers/ios/WebDriverAgent/lib/xcodebuild.ts +58 -10
- package/drivers/ios/WebDriverAgent/package.json +8 -15
- package/drivers/ios/WebDriverAgent/test/functional/helpers/display-settings.ts +49 -0
- package/drivers/ios/WebDriverAgent/test/functional/helpers/simulator.ts +7 -7
- package/drivers/ios/WebDriverAgent/test/functional/webdriveragent-e2e.spec.ts +4 -0
- package/drivers/ios/WebDriverAgent/test/unit/xcodebuild.spec.ts +230 -0
- package/drivers/ios/WebDriverAgent/tsconfig.json +2 -1
- package/drivers/ios/devicelab-ios-agent/README.md +12 -0
- package/drivers/ios/devicelab-ios-agent/device/DevicelabIOSAgent.app/DevicelabIOSAgent +0 -0
- package/drivers/ios/devicelab-ios-agent/device/DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests +0 -0
- package/drivers/ios/devicelab-ios-agent/device/manifest.json +3 -3
- package/drivers/ios/devicelab-ios-agent/simulator/DevicelabIOSAgent.app/DevicelabIOSAgent +0 -0
- package/drivers/ios/devicelab-ios-agent/simulator/DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests +0 -0
- package/drivers/ios/devicelab-ios-agent/simulator/manifest.json +3 -3
- package/package.json +1 -1
- package/drivers/ios/WebDriverAgent/.editorconfig +0 -11
- package/drivers/ios/WebDriverAgent/.github/ISSUE_TEMPLATE.md +0 -4
- package/drivers/ios/WebDriverAgent/.github/actions/xcode-setup/action.yml +0 -32
- package/drivers/ios/WebDriverAgent/.github/dependabot.yml +0 -22
- package/drivers/ios/WebDriverAgent/.github/workflows/functional-test.yml +0 -103
- package/drivers/ios/WebDriverAgent/.github/workflows/pr-title.yml +0 -10
- package/drivers/ios/WebDriverAgent/.github/workflows/publish.js.yml +0 -112
- package/drivers/ios/WebDriverAgent/.github/workflows/unit-test.yml +0 -32
- package/drivers/ios/WebDriverAgent/.github/workflows/wda-package.yml +0 -114
- package/drivers/ios/WebDriverAgent/.github/workflows/wda-tests.yml +0 -246
- package/drivers/ios/WebDriverAgent/oxfmt.config.mjs +0 -6
- package/drivers/ios/WebDriverAgent/oxlint.config.mjs +0 -6
- package/drivers/ios/WebDriverAgent/release.config.mjs +0 -14
|
@@ -120,14 +120,27 @@
|
|
|
120
120
|
- (NSArray<XCUIElement *> *)fb_descendantsMatchingIdentifier:(NSString *)accessibilityId
|
|
121
121
|
shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch
|
|
122
122
|
{
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
// Standard snapshot attributes let XCTest evaluate a structured predicate
|
|
124
|
+
// without WDA's per-snapshot block callback, while preserving wdName semantics.
|
|
125
|
+
// Local benchmarks showed lower end-to-end lookup latency; see measurements
|
|
126
|
+
// and regression checks in https://github.com/appium/WebDriverAgent/pull/1286.
|
|
127
|
+
NSPredicate *predicate = [NSPredicate predicateWithFormat:
|
|
128
|
+
@"(identifier != nil AND identifier != '' AND identifier == %@) OR "
|
|
129
|
+
"((identifier == nil OR identifier == '') AND label != nil AND label != '' AND label == %@)",
|
|
130
|
+
accessibilityId, accessibilityId];
|
|
131
|
+
XCUIElementQuery *query = [[self.fb_query descendantsMatchingType:XCUIElementTypeAny]
|
|
132
|
+
matchingPredicate:predicate];
|
|
133
|
+
NSMutableArray<XCUIElement *> *result = [NSMutableArray arrayWithArray:
|
|
134
|
+
[self.class fb_extractMatchingElementsFromQuery:query
|
|
135
|
+
shouldReturnAfterFirstMatch:shouldReturnAfterFirstMatch]];
|
|
136
|
+
id<FBXCElementSnapshot> cachedSnapshot = [self fb_cachedSnapshotWithQuery:query];
|
|
137
|
+
if ([predicate evaluateWithObject:cachedSnapshot]) {
|
|
138
|
+
if (shouldReturnAfterFirstMatch || result.count == 0) {
|
|
139
|
+
return @[self];
|
|
127
140
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
141
|
+
[result insertObject:self atIndex:0];
|
|
142
|
+
}
|
|
143
|
+
return result.copy;
|
|
131
144
|
}
|
|
132
145
|
|
|
133
146
|
@end
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
#import "XCUIElement+FBScrolling.h"
|
|
10
10
|
|
|
11
11
|
#import "FBErrorBuilder.h"
|
|
12
|
+
#import "FBExceptions.h"
|
|
12
13
|
#import "FBLogger.h"
|
|
14
|
+
#import "FBScreen.h"
|
|
13
15
|
#import "FBMacros.h"
|
|
14
16
|
#import "FBMathUtils.h"
|
|
15
17
|
#import "FBXCodeCompatibility.h"
|
|
@@ -20,6 +22,7 @@
|
|
|
20
22
|
#import "XCUIElement+FBUID.h"
|
|
21
23
|
#import "XCUIApplication.h"
|
|
22
24
|
#import "XCUICoordinate.h"
|
|
25
|
+
#import "XCUIScreen.h"
|
|
23
26
|
#import "XCUIElement+FBIsVisible.h"
|
|
24
27
|
#import "XCUIElement+FBVisibleFrame.h"
|
|
25
28
|
#import "XCUIElement.h"
|
|
@@ -35,14 +38,40 @@ const CGFloat FBScrollTouchProportion = 0.75f;
|
|
|
35
38
|
|
|
36
39
|
#if !TARGET_OS_TV
|
|
37
40
|
|
|
41
|
+
static void FBValidateScreenForScrolling(void)
|
|
42
|
+
{
|
|
43
|
+
NSError *error = nil;
|
|
44
|
+
XCUIScreen *screen = [FBScreen currentScreenWithError:&error];
|
|
45
|
+
if (nil == screen) {
|
|
46
|
+
// Match active-app detection: never continue on a different coordinate space
|
|
47
|
+
// when the selected display is unavailable, including for void scroll APIs.
|
|
48
|
+
@throw [NSException exceptionWithName:FBInvalidArgumentException
|
|
49
|
+
reason:error.localizedDescription
|
|
50
|
+
userInfo:nil];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static CGRect FBScrollFrameInApplication(CGRect frame, XCUIScreen *screen,
|
|
55
|
+
XCUIApplication *application)
|
|
56
|
+
{
|
|
57
|
+
#if !TARGET_OS_WATCH
|
|
58
|
+
// XCTest's visibleFrame is in portrait screen coordinates on both main and
|
|
59
|
+
// secondary displays. The live anchor's frame uses the app's orientation.
|
|
60
|
+
CGSize size = CGSizeMake(CGRectGetWidth(screen.bounds) / screen.scale,
|
|
61
|
+
CGRectGetHeight(screen.bounds) / screen.scale);
|
|
62
|
+
frame = FBRectFromPortraitCoordinates(frame, size, application.interfaceOrientation);
|
|
63
|
+
#endif
|
|
64
|
+
return frame;
|
|
65
|
+
}
|
|
66
|
+
|
|
38
67
|
@interface FBXCElementSnapshotWrapper (FBScrolling)
|
|
39
68
|
|
|
40
69
|
- (BOOL)fb_scrollUpByNormalizedDistance:(CGFloat)distance anchorElement:(XCUIElement *)anchorElement;
|
|
41
70
|
- (BOOL)fb_scrollDownByNormalizedDistance:(CGFloat)distance anchorElement:(XCUIElement *)anchorElement;
|
|
42
71
|
- (BOOL)fb_scrollLeftByNormalizedDistance:(CGFloat)distance anchorElement:(XCUIElement *)anchorElement;
|
|
43
72
|
- (BOOL)fb_scrollRightByNormalizedDistance:(CGFloat)distance anchorElement:(XCUIElement *)anchorElement;
|
|
44
|
-
- (BOOL)fb_scrollByNormalizedVector:(CGVector)normalizedScrollVector anchorElement:(XCUIElement *)anchorElement;
|
|
45
|
-
- (BOOL)fb_scrollByVector:(CGVector)vector anchorElement:(XCUIElement *)anchorElement error:(NSError **)error;
|
|
73
|
+
- (BOOL)fb_scrollByNormalizedVector:(CGVector)normalizedScrollVector anchorElement:(XCUIElement *)anchorElement error:(NSError **)error;
|
|
74
|
+
- (BOOL)fb_scrollByVector:(CGVector)vector inFrame:(CGRect)frame anchorElement:(XCUIElement *)anchorElement velocity:(CGFloat)velocity error:(NSError **)error;
|
|
46
75
|
|
|
47
76
|
@end
|
|
48
77
|
|
|
@@ -126,6 +155,9 @@ static XCUIElement *FBLiveElementForSnapshot(id<FBXCElementSnapshot> snapshot, X
|
|
|
126
155
|
scrollDirection:(FBXCUIElementScrollDirection)scrollDirection
|
|
127
156
|
error:(NSError **)error
|
|
128
157
|
{
|
|
158
|
+
if (nil == [FBScreen currentScreenWithError:error]) {
|
|
159
|
+
return NO;
|
|
160
|
+
}
|
|
129
161
|
FBXCElementSnapshotWrapper *prescrollSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:[self fb_customSnapshot]];
|
|
130
162
|
|
|
131
163
|
if (prescrollSnapshot.isWDVisible) {
|
|
@@ -202,7 +234,7 @@ static XCUIElement *FBLiveElementForSnapshot(id<FBXCElementSnapshot> snapshot, X
|
|
|
202
234
|
|
|
203
235
|
// The scroll view's own identity is stable across scroll steps, so it only needs
|
|
204
236
|
// to be resolved to a live element once, up front; its frame does not, since it can
|
|
205
|
-
// change across scroll steps (
|
|
237
|
+
// change across scroll steps (keyboard or dynamic layout).
|
|
206
238
|
XCUIElement *scrollViewElement = FBLiveElementForSnapshot(scrollView, self.application);
|
|
207
239
|
if (nil == scrollViewElement) {
|
|
208
240
|
return
|
|
@@ -217,34 +249,27 @@ static XCUIElement *FBLiveElementForSnapshot(id<FBXCElementSnapshot> snapshot, X
|
|
|
217
249
|
// Scrolling till cell is visible and get current value of frames
|
|
218
250
|
while (![self fb_isEquivalentElementSnapshotVisible:prescrollSnapshot] && scrollCount < maxScrollCount) {
|
|
219
251
|
BOOL didScroll;
|
|
252
|
+
__strong NSError *scrollError = nil;
|
|
220
253
|
@autoreleasepool {
|
|
221
|
-
// Re-snapshotting the scroll view every step keeps its frame from drifting too far
|
|
222
|
-
// out of sync with the live anchor element's frame used to resolve touch points.
|
|
223
254
|
scrollViewWrapped = [FBXCElementSnapshotWrapper ensureWrapped:[scrollViewElement fb_customSnapshot]];
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
didScroll = scrollDirection == FBXCUIElementScrollDirectionVertical ?
|
|
233
|
-
[scrollViewWrapped fb_scrollDownByNormalizedDistance:normalizedScrollDistance
|
|
234
|
-
anchorElement:scrollViewElement] :
|
|
235
|
-
[scrollViewWrapped fb_scrollRightByNormalizedDistance:normalizedScrollDistance
|
|
236
|
-
anchorElement:scrollViewElement];
|
|
237
|
-
}
|
|
255
|
+
CGFloat distance = targetCellIndex < visibleCellIndex ? normalizedScrollDistance : -normalizedScrollDistance;
|
|
256
|
+
CGVector vector = scrollDirection == FBXCUIElementScrollDirectionVertical
|
|
257
|
+
? CGVectorMake(0, distance) : CGVectorMake(distance, 0);
|
|
258
|
+
NSError *stepError = nil;
|
|
259
|
+
didScroll = [scrollViewWrapped fb_scrollByNormalizedVector:vector
|
|
260
|
+
anchorElement:scrollViewElement
|
|
261
|
+
error:&stepError];
|
|
262
|
+
scrollError = stepError;
|
|
238
263
|
scrollCount++;
|
|
239
264
|
// Wait for scroll animation
|
|
240
265
|
[self fb_waitUntilStableWithTimeout:FBConfiguration.sharedInstance.animationCoolOffTimeout];
|
|
241
266
|
}
|
|
242
267
|
// The `error` out-param must not be written from inside the autorelease pool above.
|
|
243
268
|
if (!didScroll) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
269
|
+
if (error) {
|
|
270
|
+
*error = scrollError;
|
|
271
|
+
}
|
|
272
|
+
return NO;
|
|
248
273
|
}
|
|
249
274
|
}
|
|
250
275
|
|
|
@@ -260,14 +285,39 @@ static XCUIElement *FBLiveElementForSnapshot(id<FBXCElementSnapshot> snapshot, X
|
|
|
260
285
|
// Trying fb_cachedSnapshot first
|
|
261
286
|
FBXCElementSnapshotWrapper *targetCellSnapshotWrapped = [FBXCElementSnapshotWrapper ensureWrapped:[self fb_customSnapshot]];
|
|
262
287
|
targetCellSnapshot = [targetCellSnapshotWrapped fb_parentCellSnapshot];
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
288
|
+
XCUIScreen *screen = [FBScreen currentScreenWithError:error];
|
|
289
|
+
if (nil == screen) {
|
|
290
|
+
return NO;
|
|
291
|
+
}
|
|
292
|
+
CGRect visibleFrame = FBScrollFrameInApplication(
|
|
293
|
+
[FBXCElementSnapshotWrapper ensureWrapped:targetCellSnapshot].fb_visibleFrame, screen, self.application);
|
|
294
|
+
|
|
295
|
+
CGRect targetFrame = targetCellSnapshot.frame;
|
|
296
|
+
// A cell can be clipped at the leading edge after a drag. Move back toward
|
|
297
|
+
// that edge instead of always scrolling down/right and hiding it further.
|
|
298
|
+
CGVector scrollVector = CGVectorMake(
|
|
299
|
+
CGRectGetMinX(visibleFrame) > CGRectGetMinX(targetFrame)
|
|
300
|
+
? CGRectGetMinX(visibleFrame) - CGRectGetMinX(targetFrame)
|
|
301
|
+
: CGRectGetMaxX(visibleFrame) - CGRectGetMaxX(targetFrame),
|
|
302
|
+
CGRectGetMinY(visibleFrame) > CGRectGetMinY(targetFrame)
|
|
303
|
+
? CGRectGetMinY(visibleFrame) - CGRectGetMinY(targetFrame)
|
|
304
|
+
: CGRectGetMaxY(visibleFrame) - CGRectGetMaxY(targetFrame));
|
|
305
|
+
// UIKit consumes the start of a drag before recognizing a pan. Include that
|
|
306
|
+
// margin so a small clipped edge is actually moved into view.
|
|
307
|
+
if (fabs(scrollVector.dx) > 1) {
|
|
308
|
+
scrollVector.dx += copysign(FBFuzzyPointThreshold, scrollVector.dx);
|
|
309
|
+
}
|
|
310
|
+
if (fabs(scrollVector.dy) > 1) {
|
|
311
|
+
scrollVector.dy += copysign(FBFuzzyPointThreshold, scrollVector.dy);
|
|
312
|
+
}
|
|
313
|
+
// Short fast corrections can turn into a flick and overshoot the target.
|
|
314
|
+
CGFloat velocity = MIN(FBTouchVelocity, MAX(fabs(scrollVector.dx), fabs(scrollVector.dy)) / FBTouchEventDelay);
|
|
268
315
|
scrollViewWrapped = [FBXCElementSnapshotWrapper ensureWrapped:[scrollViewElement fb_customSnapshot]];
|
|
316
|
+
CGRect scrollingFrame = FBScrollFrameInApplication(scrollViewWrapped.visibleFrame, screen, self.application);
|
|
269
317
|
return [scrollViewWrapped fb_scrollByVector:scrollVector
|
|
318
|
+
inFrame:scrollingFrame
|
|
270
319
|
anchorElement:scrollViewElement
|
|
320
|
+
velocity:velocity
|
|
271
321
|
error:error];
|
|
272
322
|
}
|
|
273
323
|
|
|
@@ -296,64 +346,73 @@ static XCUIElement *FBLiveElementForSnapshot(id<FBXCElementSnapshot> snapshot, X
|
|
|
296
346
|
|
|
297
347
|
@implementation FBXCElementSnapshotWrapper (FBScrolling)
|
|
298
348
|
|
|
299
|
-
- (CGRect)scrollingFrame
|
|
300
|
-
{
|
|
301
|
-
return self.visibleFrame;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
349
|
- (BOOL)fb_scrollUpByNormalizedDistance:(CGFloat)distance
|
|
305
350
|
anchorElement:(XCUIElement *)anchorElement
|
|
306
351
|
{
|
|
307
|
-
|
|
352
|
+
FBValidateScreenForScrolling();
|
|
353
|
+
return [self fb_scrollByNormalizedVector:CGVectorMake(0.0, distance) anchorElement:anchorElement error:nil];
|
|
308
354
|
}
|
|
309
355
|
|
|
310
356
|
- (BOOL)fb_scrollDownByNormalizedDistance:(CGFloat)distance
|
|
311
357
|
anchorElement:(XCUIElement *)anchorElement
|
|
312
358
|
{
|
|
313
|
-
|
|
359
|
+
FBValidateScreenForScrolling();
|
|
360
|
+
return [self fb_scrollByNormalizedVector:CGVectorMake(0.0, -distance) anchorElement:anchorElement error:nil];
|
|
314
361
|
}
|
|
315
362
|
|
|
316
363
|
- (BOOL)fb_scrollLeftByNormalizedDistance:(CGFloat)distance
|
|
317
364
|
anchorElement:(XCUIElement *)anchorElement
|
|
318
365
|
{
|
|
319
|
-
|
|
366
|
+
FBValidateScreenForScrolling();
|
|
367
|
+
return [self fb_scrollByNormalizedVector:CGVectorMake(distance, 0.0) anchorElement:anchorElement error:nil];
|
|
320
368
|
}
|
|
321
369
|
|
|
322
370
|
- (BOOL)fb_scrollRightByNormalizedDistance:(CGFloat)distance
|
|
323
371
|
anchorElement:(XCUIElement *)anchorElement
|
|
324
372
|
{
|
|
325
|
-
|
|
373
|
+
FBValidateScreenForScrolling();
|
|
374
|
+
return [self fb_scrollByNormalizedVector:CGVectorMake(-distance, 0.0) anchorElement:anchorElement error:nil];
|
|
326
375
|
}
|
|
327
376
|
|
|
328
377
|
- (BOOL)fb_scrollByNormalizedVector:(CGVector)normalizedScrollVector
|
|
329
378
|
anchorElement:(XCUIElement *)anchorElement
|
|
379
|
+
error:(NSError **)error
|
|
330
380
|
{
|
|
331
|
-
|
|
332
|
-
|
|
381
|
+
XCUIScreen *screen = [FBScreen currentScreenWithError:error];
|
|
382
|
+
if (nil == screen) {
|
|
383
|
+
return NO;
|
|
384
|
+
}
|
|
385
|
+
CGRect frame = FBScrollFrameInApplication(self.visibleFrame, screen, anchorElement.application);
|
|
386
|
+
CGVector scrollVector = CGVectorMake(CGRectGetWidth(frame) * normalizedScrollVector.dx,
|
|
387
|
+
CGRectGetHeight(frame) * normalizedScrollVector.dy
|
|
333
388
|
);
|
|
334
|
-
return [self fb_scrollByVector:scrollVector anchorElement:anchorElement error:
|
|
389
|
+
return [self fb_scrollByVector:scrollVector inFrame:frame anchorElement:anchorElement velocity:FBTouchVelocity error:error];
|
|
335
390
|
}
|
|
336
391
|
|
|
337
392
|
- (BOOL)fb_scrollByVector:(CGVector)vector
|
|
393
|
+
inFrame:(CGRect)frame
|
|
338
394
|
anchorElement:(XCUIElement *)anchorElement
|
|
395
|
+
velocity:(CGFloat)velocity
|
|
339
396
|
error:(NSError **)error
|
|
340
397
|
{
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
CGRectGetHeight(self.scrollingFrame) * FBScrollTouchProportion
|
|
344
|
-
);
|
|
345
|
-
scrollBoundingVector.dx = (CGFloat)floor(copysign(scrollBoundingVector.dx, vector.dx));
|
|
346
|
-
scrollBoundingVector.dy = (CGFloat)floor(copysign(scrollBoundingVector.dy, vector.dy));
|
|
347
|
-
|
|
398
|
+
// Keep the vector and clipped frame in the same coordinate space throughout
|
|
399
|
+
// this multi-drag gesture. Rotation or folding during a gesture is unsupported.
|
|
348
400
|
NSInteger preciseScrollAttemptsCount = 20;
|
|
349
401
|
CGVector CGZeroVector = CGVectorMake(0, 0);
|
|
350
402
|
BOOL shouldFinishScrolling = NO;
|
|
351
403
|
while (!shouldFinishScrolling) {
|
|
404
|
+
CGVector scrollBoundingVector = CGVectorMake(
|
|
405
|
+
(CGFloat)floor(copysign(CGRectGetWidth(frame) * FBScrollTouchProportion, vector.dx)),
|
|
406
|
+
(CGFloat)floor(copysign(CGRectGetHeight(frame) * FBScrollTouchProportion, vector.dy)));
|
|
352
407
|
CGVector scrollVector = CGVectorMake(fabs(vector.dx) > fabs(scrollBoundingVector.dx) ? scrollBoundingVector.dx : vector.dx,
|
|
353
408
|
fabs(vector.dy) > fabs(scrollBoundingVector.dy) ? scrollBoundingVector.dy : vector.dy);
|
|
354
409
|
vector = CGVectorMake(vector.dx - scrollVector.dx, vector.dy - scrollVector.dy);
|
|
355
410
|
shouldFinishScrolling = FBVectorFuzzyEqualToVector(vector, CGZeroVector, 1) || --preciseScrollAttemptsCount <= 0;
|
|
356
|
-
if (![self fb_scrollAncestorScrollViewByVectorWithinScrollViewFrame:scrollVector
|
|
411
|
+
if (![self fb_scrollAncestorScrollViewByVectorWithinScrollViewFrame:scrollVector
|
|
412
|
+
inFrame:frame
|
|
413
|
+
anchorElement:anchorElement
|
|
414
|
+
velocity:velocity
|
|
415
|
+
error:error]) {
|
|
357
416
|
return NO;
|
|
358
417
|
}
|
|
359
418
|
}
|
|
@@ -370,10 +429,11 @@ static XCUIElement *FBLiveElementForSnapshot(id<FBXCElementSnapshot> snapshot, X
|
|
|
370
429
|
}
|
|
371
430
|
|
|
372
431
|
- (BOOL)fb_scrollAncestorScrollViewByVectorWithinScrollViewFrame:(CGVector)vector
|
|
432
|
+
inFrame:(CGRect)scrollingFrame
|
|
373
433
|
anchorElement:(XCUIElement *)anchorElement
|
|
434
|
+
velocity:(CGFloat)velocity
|
|
374
435
|
error:(NSError **)error
|
|
375
436
|
{
|
|
376
|
-
CGRect scrollingFrame = self.scrollingFrame;
|
|
377
437
|
// wdFrame matches scrollingFrame's coordinate space; raw .frame can be pre-scaled or
|
|
378
438
|
// dimension-swapped and drift out of sync with it (appium/appium#16185).
|
|
379
439
|
CGRect anchorFrame = anchorElement.wdFrame;
|
|
@@ -395,7 +455,7 @@ static XCUIElement *FBLiveElementForSnapshot(id<FBXCElementSnapshot> snapshot, X
|
|
|
395
455
|
|
|
396
456
|
[startCoordinate pressForDuration:FBTouchEventDelay
|
|
397
457
|
thenDragToCoordinate:endCoordinate
|
|
398
|
-
withVelocity:
|
|
458
|
+
withVelocity:velocity
|
|
399
459
|
thenHoldForDuration:FBTouchEventDelay];
|
|
400
460
|
return YES;
|
|
401
461
|
}
|
|
@@ -21,6 +21,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
21
21
|
*/
|
|
22
22
|
BOOL FBTypeText(NSString *text, NSUInteger typingSpeed, NSError **error);
|
|
23
23
|
|
|
24
|
+
#if TARGET_OS_IOS
|
|
25
|
+
/**
|
|
26
|
+
Waits up to `timeout` seconds for a hittable key. A miss does not fail the caller.
|
|
27
|
+
*/
|
|
28
|
+
void FBWaitForHittableKey(XCUIApplication * _Nullable application, NSTimeInterval timeout);
|
|
29
|
+
#endif
|
|
30
|
+
|
|
24
31
|
@interface XCUIElement (FBTyping)
|
|
25
32
|
|
|
26
33
|
/**
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
#import "FBConfiguration.h"
|
|
12
12
|
#import "FBErrorBuilder.h"
|
|
13
|
+
#import "FBKeyboard.h"
|
|
13
14
|
#import "FBXCElementSnapshotWrapper.h"
|
|
14
15
|
#import "FBXCElementSnapshotWrapper+Helpers.h"
|
|
15
16
|
#import "FBXCodeCompatibility.h"
|
|
@@ -39,6 +40,19 @@ BOOL FBTypeText(NSString *text, NSUInteger typingSpeed, NSError **error)
|
|
|
39
40
|
return [FBXCTestDaemonsProxy synthesizeEventWithRecord:eventRecord error:error];
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
#if TARGET_OS_IOS
|
|
44
|
+
void FBWaitForHittableKey(XCUIApplication *application, NSTimeInterval timeout)
|
|
45
|
+
{
|
|
46
|
+
if (nil == application) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
NSError *visibilityError = nil;
|
|
50
|
+
[FBKeyboard waitUntilVisibleForApplication:application
|
|
51
|
+
timeout:timeout
|
|
52
|
+
error:&visibilityError];
|
|
53
|
+
}
|
|
54
|
+
#endif
|
|
55
|
+
|
|
42
56
|
@interface NSString (FBRepeat)
|
|
43
57
|
|
|
44
58
|
- (NSString *)fb_repeatTimes:(NSUInteger)times;
|
|
@@ -139,6 +153,12 @@ BOOL FBTypeText(NSString *text, NSUInteger typingSpeed, NSError **error)
|
|
|
139
153
|
// partial workaround (keystrokes still don't always land - a known limitation).
|
|
140
154
|
[self typeText:text];
|
|
141
155
|
return YES;
|
|
156
|
+
#elif TARGET_OS_IOS
|
|
157
|
+
NSTimeInterval keyboardWait = FBConfiguration.sharedInstance.keyboardWaitTimeout;
|
|
158
|
+
if (keyboardWait > 0) {
|
|
159
|
+
FBWaitForHittableKey(self.application, keyboardWait);
|
|
160
|
+
}
|
|
161
|
+
return FBTypeText(text, frequency, error);
|
|
142
162
|
#else
|
|
143
163
|
return FBTypeText(text, frequency, error);
|
|
144
164
|
#endif
|
|
@@ -141,18 +141,21 @@
|
|
|
141
141
|
return;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
FBConfiguration.sharedInstance
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
144
|
+
XCUIApplication *application = self.application;
|
|
145
|
+
// Recursive synchronization also covers nested stability checks on this thread.
|
|
146
|
+
@synchronized (FBConfiguration.sharedInstance) {
|
|
147
|
+
NSTimeInterval previousTimeout = FBConfiguration.sharedInstance.waitForIdleTimeout;
|
|
148
|
+
BOOL previousQuiescence = application.fb_shouldWaitForQuiescence;
|
|
149
|
+
@try {
|
|
150
|
+
FBConfiguration.sharedInstance.waitForIdleTimeout = timeout;
|
|
151
|
+
application.fb_shouldWaitForQuiescence = YES;
|
|
152
|
+
[[[application applicationImpl] currentProcess]
|
|
153
|
+
fb_waitForQuiescenceIncludingAnimationsIdle:YES];
|
|
154
|
+
} @finally {
|
|
155
|
+
application.fb_shouldWaitForQuiescence = previousQuiescence;
|
|
156
|
+
FBConfiguration.sharedInstance.waitForIdleTimeout = previousTimeout;
|
|
157
|
+
}
|
|
154
158
|
}
|
|
155
|
-
FBConfiguration.sharedInstance.waitForIdleTimeout = previousTimeout;
|
|
156
159
|
}
|
|
157
160
|
|
|
158
161
|
- (void)fb_raiseStaleElementExceptionWithError:(NSError *)error __attribute__((noreturn))
|
|
@@ -29,10 +29,12 @@
|
|
|
29
29
|
#import "XCUIApplication.h"
|
|
30
30
|
#import "XCUIApplication+FBHelpers.h"
|
|
31
31
|
#import "XCUIDevice+FBHelpers.h"
|
|
32
|
+
#import "XCUIDevice+FBHinge.h"
|
|
32
33
|
#import "XCUIDevice+FBVoiceOver.h"
|
|
33
34
|
#import "XCUIElement.h"
|
|
34
35
|
#import "XCUIElement+FBIsVisible.h"
|
|
35
36
|
#import "XCUIElementQuery.h"
|
|
37
|
+
#import "XCUIScreen.h"
|
|
36
38
|
#import "FBUnattachedAppLauncher.h"
|
|
37
39
|
|
|
38
40
|
@implementation FBCustomCommands
|
|
@@ -65,6 +67,8 @@
|
|
|
65
67
|
[[FBRoute GET:@"/wda/batteryInfo"] respondWithTarget:self action:@selector(handleGetBatteryInfo:)],
|
|
66
68
|
#endif
|
|
67
69
|
[[FBRoute POST:@"/wda/pressButton"] respondWithTarget:self action:@selector(handlePressButtonCommand:)],
|
|
70
|
+
[[FBRoute GET:@"/wda/pressableButtons"] respondWithTarget:self action:@selector(handleGetPressableButtons:)],
|
|
71
|
+
[[FBRoute GET:@"/wda/pressableButtons"].withoutSession respondWithTarget:self action:@selector(handleGetPressableButtons:)],
|
|
68
72
|
#if TARGET_OS_WATCH
|
|
69
73
|
[[FBRoute POST:@"/wda/rotateDigitalCrown"] respondWithTarget:self action:@selector(handleRotateDigitalCrownCommand:)],
|
|
70
74
|
[[FBRoute POST:@"/wda/performHandGesture"] respondWithTarget:self action:@selector(handlePerformHandGestureCommand:)],
|
|
@@ -74,6 +78,10 @@
|
|
|
74
78
|
[[FBRoute POST:@"/wda/expectNotification"] respondWithTarget:self action:@selector(handleExpectNotification:)],
|
|
75
79
|
[[FBRoute POST:@"/wda/siri/activate"] respondWithTarget:self action:@selector(handleActivateSiri:)],
|
|
76
80
|
[[FBRoute POST:@"/wda/apps/launchUnattached"].withoutSession respondWithTarget:self action:@selector(handleLaunchUnattachedApp:)],
|
|
81
|
+
[[FBRoute GET:@"/wda/device/hingeAngle"] respondWithTarget:self action:@selector(handleGetSimulatedHingeAngle:)],
|
|
82
|
+
[[FBRoute GET:@"/wda/device/hingeAngle"].withoutSession respondWithTarget:self action:@selector(handleGetSimulatedHingeAngle:)],
|
|
83
|
+
[[FBRoute POST:@"/wda/device/hingeAngle"] respondWithTarget:self action:@selector(handleSetSimulatedHingeAngle:)],
|
|
84
|
+
[[FBRoute POST:@"/wda/device/hingeAngle"].withoutSession respondWithTarget:self action:@selector(handleSetSimulatedHingeAngle:)],
|
|
77
85
|
[[FBRoute GET:@"/wda/device/info"] respondWithTarget:self action:@selector(handleGetDeviceInfo:)],
|
|
78
86
|
[[FBRoute POST:@"/wda/resetAppAuth"] respondWithTarget:self action:@selector(handleResetAppAuth:)],
|
|
79
87
|
[[FBRoute GET:@"/wda/device/info"].withoutSession respondWithTarget:self action:@selector(handleGetDeviceInfo:)],
|
|
@@ -157,17 +165,48 @@
|
|
|
157
165
|
|
|
158
166
|
+ (id<FBResponsePayload>)handleGetScreen:(FBRouteRequest *)request
|
|
159
167
|
{
|
|
168
|
+
NSError *error = nil;
|
|
169
|
+
XCUIScreen *screen = [FBScreen currentScreenWithError:&error];
|
|
170
|
+
if (nil == screen) {
|
|
171
|
+
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:error.localizedDescription
|
|
172
|
+
traceback:nil]);
|
|
173
|
+
}
|
|
160
174
|
XCUIApplication *app = XCUIApplication.fb_systemApplication;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
175
|
+
CGSize screenSize = CGSizeMake(CGRectGetWidth(screen.bounds) / screen.scale,
|
|
176
|
+
CGRectGetHeight(screen.bounds) / screen.scale);
|
|
177
|
+
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
178
|
+
XCUIApplication *activeApp = request.session ? request.session.activeApplication : XCUIApplication.fb_activeApplication;
|
|
179
|
+
UIInterfaceOrientation orientation = activeApp.interfaceOrientation;
|
|
180
|
+
// Screen bounds describe the panel's native orientation. Some physical iPads
|
|
181
|
+
// expose landscape-native bounds even while the application is in portrait.
|
|
182
|
+
// Normalize the full screen separately from compatibility-mode app frames.
|
|
183
|
+
if (UIInterfaceOrientationIsPortrait(orientation) && screenSize.width > screenSize.height) {
|
|
184
|
+
screenSize = CGSizeMake(screenSize.height, screenSize.width);
|
|
185
|
+
}
|
|
186
|
+
screenSize = FBAdjustDimensionsForApplication(screenSize, orientation);
|
|
169
187
|
#endif
|
|
170
188
|
|
|
189
|
+
// SpringBoard can expose hidden status bars belonging to other displays, and
|
|
190
|
+
// Duo also exposes full-screen containers for its side-mounted status UI.
|
|
191
|
+
// Only a visible horizontal bar at the top of this display can be subtracted
|
|
192
|
+
// from a rectangular viewport.
|
|
193
|
+
CGSize statusBarSize = CGSizeZero;
|
|
194
|
+
for (XCUIElement *statusBar in app.statusBars.allElementsBoundByIndex) {
|
|
195
|
+
// Resolve the element before reading its screen: displayID can be zero on
|
|
196
|
+
// an unresolved query result even when the bar belongs to the main screen.
|
|
197
|
+
CGRect frame = statusBar.frame;
|
|
198
|
+
if (statusBar.screen.displayID != screen.displayID || !statusBar.fb_isVisible) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (fabs(CGRectGetMinY(frame)) <= 1 && fabs(CGRectGetMinX(frame)) <= 1
|
|
202
|
+
&& fabs(CGRectGetWidth(frame) - screenSize.width) <= 1
|
|
203
|
+
&& CGRectGetHeight(frame) > 0 && CGRectGetHeight(frame) < screenSize.height
|
|
204
|
+
&& CGRectGetHeight(frame) < CGRectGetWidth(frame)) {
|
|
205
|
+
statusBarSize = frame.size;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
171
210
|
return FBResponseWithObject(
|
|
172
211
|
@{
|
|
173
212
|
@"screenSize":@{@"width": @(screenSize.width),
|
|
@@ -176,8 +215,8 @@
|
|
|
176
215
|
@"statusBarSize": @{@"width": @(statusBarSize.width),
|
|
177
216
|
@"height": @(statusBarSize.height),
|
|
178
217
|
},
|
|
179
|
-
@"displayId": @(
|
|
180
|
-
@"scale": @(
|
|
218
|
+
@"displayId": @(screen.displayID),
|
|
219
|
+
@"scale": @(screen.scale),
|
|
181
220
|
});
|
|
182
221
|
}
|
|
183
222
|
|
|
@@ -315,6 +354,12 @@
|
|
|
315
354
|
return FBResponseWithOK();
|
|
316
355
|
}
|
|
317
356
|
|
|
357
|
+
+ (id<FBResponsePayload>)handleGetPressableButtons:(FBRouteRequest *)request
|
|
358
|
+
{
|
|
359
|
+
NSArray *xctestButtonNames = [FBAvailableButtonNames().allKeys sortedArrayUsingSelector:@selector(compare:)];
|
|
360
|
+
return FBResponseWithObject(@{@"xctest": xctestButtonNames});
|
|
361
|
+
}
|
|
362
|
+
|
|
318
363
|
#if TARGET_OS_WATCH
|
|
319
364
|
+ (id<FBResponsePayload>)handleRotateDigitalCrownCommand:(FBRouteRequest *)request
|
|
320
365
|
{
|
|
@@ -777,4 +822,35 @@
|
|
|
777
822
|
return FBResponseWithObject(result);
|
|
778
823
|
}
|
|
779
824
|
|
|
825
|
+
+ (id<FBResponsePayload>)handleGetSimulatedHingeAngle:(FBRouteRequest *)request
|
|
826
|
+
{
|
|
827
|
+
if (!XCUIDevice.sharedDevice.fb_supportsHingeAngleReading) {
|
|
828
|
+
return FBResponseWithStatus([FBCommandStatus unsupportedOperationErrorWithMessage:
|
|
829
|
+
@"Hinge angle reading is unavailable on this device" traceback:nil]);
|
|
830
|
+
}
|
|
831
|
+
NSError *error;
|
|
832
|
+
NSNumber *angle = [XCUIDevice.sharedDevice fb_getSimulatedHingeAngle:&error];
|
|
833
|
+
return nil == angle ? FBResponseWithUnknownError(error) : FBResponseWithObject(angle);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
+ (id<FBResponsePayload>)handleSetSimulatedHingeAngle:(FBRouteRequest *)request
|
|
837
|
+
{
|
|
838
|
+
id angle = request.arguments[@"angle"];
|
|
839
|
+
if (![angle isKindOfClass:NSNumber.class]
|
|
840
|
+
|| CFGetTypeID((__bridge CFTypeRef)angle) == CFBooleanGetTypeID()
|
|
841
|
+
|| !isfinite([angle doubleValue]) || [angle doubleValue] < 0 || [angle doubleValue] > 180) {
|
|
842
|
+
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:
|
|
843
|
+
@"'angle' must be a finite number between 0 and 180 degrees" traceback:nil]);
|
|
844
|
+
}
|
|
845
|
+
if (!XCUIDevice.sharedDevice.fb_canAttemptSimulatedHingeAngleInjection) {
|
|
846
|
+
return FBResponseWithStatus([FBCommandStatus unsupportedOperationErrorWithMessage:
|
|
847
|
+
@"Setting a simulated hinge angle requires a compatible foldable device" traceback:nil]);
|
|
848
|
+
}
|
|
849
|
+
NSError *error;
|
|
850
|
+
if (![XCUIDevice.sharedDevice fb_setSimulatedHingeAngle:[angle doubleValue] error:&error]) {
|
|
851
|
+
return FBResponseWithUnknownError(error);
|
|
852
|
+
}
|
|
853
|
+
return FBResponseWithOK();
|
|
854
|
+
}
|
|
855
|
+
|
|
780
856
|
@end
|