@devicelab/maestro-runner-darwin-arm64 1.1.29 → 1.1.31
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 +15 -4
- 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
|
@@ -9,9 +9,67 @@
|
|
|
9
9
|
#import <XCTest/XCTest.h>
|
|
10
10
|
|
|
11
11
|
#import "FBSession.h"
|
|
12
|
+
#import "FBSession-Private.h"
|
|
13
|
+
#import "FBExceptions.h"
|
|
12
14
|
#import "FBConfiguration.h"
|
|
13
15
|
#import "XCUIApplicationDouble.h"
|
|
14
16
|
|
|
17
|
+
@interface FBSession (SystemApplicationTests)
|
|
18
|
+
- (BOOL)fb_isApplicationSameAsSystemApp:(XCUIApplication *)application timeout:(NSTimeInterval)timeout;
|
|
19
|
+
@end
|
|
20
|
+
|
|
21
|
+
static id (^FBSystemApplicationLookup)(void);
|
|
22
|
+
|
|
23
|
+
// Run teardown at the exact point where launch has started but the AUT has
|
|
24
|
+
// not yet been published. No wall-clock sleeps or simulator launch timing.
|
|
25
|
+
@interface FBAUTLaunchApplicationDouble : XCUIApplicationDouble
|
|
26
|
+
@property (nonatomic, copy) void (^duringLaunch)(void);
|
|
27
|
+
@property (nonatomic, copy) NSArray *launchArguments;
|
|
28
|
+
@property (nonatomic, copy) NSDictionary *launchEnvironment;
|
|
29
|
+
@property (nonatomic) BOOL didLaunch;
|
|
30
|
+
@end
|
|
31
|
+
|
|
32
|
+
@implementation FBAUTLaunchApplicationDouble
|
|
33
|
+
+ (id)fb_systemApplication
|
|
34
|
+
{
|
|
35
|
+
return FBSystemApplicationLookup ? FBSystemApplicationLookup() : nil;
|
|
36
|
+
}
|
|
37
|
+
- (void)launch
|
|
38
|
+
{
|
|
39
|
+
self.didLaunch = YES;
|
|
40
|
+
if (self.duringLaunch) {
|
|
41
|
+
self.duringLaunch();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
- (BOOL)fb_isSameAppAs:(id)application
|
|
45
|
+
{
|
|
46
|
+
return self == application;
|
|
47
|
+
}
|
|
48
|
+
@end
|
|
49
|
+
|
|
50
|
+
@interface FBAUTTerminationFailureApplicationDouble : FBAUTLaunchApplicationDouble
|
|
51
|
+
@property (nonatomic) BOOL didAttemptTermination;
|
|
52
|
+
@end
|
|
53
|
+
|
|
54
|
+
@implementation FBAUTTerminationFailureApplicationDouble
|
|
55
|
+
- (void)terminate
|
|
56
|
+
{
|
|
57
|
+
self.didAttemptTermination = YES;
|
|
58
|
+
@throw [NSException exceptionWithName:@"TerminationFailure" reason:nil userInfo:nil];
|
|
59
|
+
}
|
|
60
|
+
@end
|
|
61
|
+
|
|
62
|
+
@interface FBAUTLaunchSession : FBSession
|
|
63
|
+
@property (nonatomic, strong) FBAUTLaunchApplicationDouble *launchApplication;
|
|
64
|
+
@end
|
|
65
|
+
|
|
66
|
+
@implementation FBAUTLaunchSession
|
|
67
|
+
- (XCUIApplication *)makeApplicationWithBundleId:(NSString *)bundleIdentifier
|
|
68
|
+
{
|
|
69
|
+
return (id)self.launchApplication;
|
|
70
|
+
}
|
|
71
|
+
@end
|
|
72
|
+
|
|
15
73
|
@interface FBSessionTests : XCTestCase
|
|
16
74
|
@property (nonatomic, strong) FBSession *session;
|
|
17
75
|
@property (nonatomic, strong) XCUIApplication *testedApplication;
|
|
@@ -31,6 +89,10 @@
|
|
|
31
89
|
|
|
32
90
|
- (void)tearDown
|
|
33
91
|
{
|
|
92
|
+
if ([self.session isKindOfClass:FBAUTLaunchSession.class]) {
|
|
93
|
+
((FBAUTLaunchSession *)self.session).launchApplication.duringLaunch = nil;
|
|
94
|
+
}
|
|
95
|
+
FBSystemApplicationLookup = nil;
|
|
34
96
|
[self.session kill];
|
|
35
97
|
FBConfiguration.sharedInstance.shouldTerminateApp = self.shouldTerminateAppValue;
|
|
36
98
|
[super tearDown];
|
|
@@ -64,4 +126,143 @@
|
|
|
64
126
|
XCTAssertNil([FBSession activeSession]);
|
|
65
127
|
}
|
|
66
128
|
|
|
129
|
+
- (FBAUTLaunchApplicationDouble *)prepareAUTLaunch
|
|
130
|
+
{
|
|
131
|
+
FBAUTLaunchSession *session = [FBAUTLaunchSession new];
|
|
132
|
+
session.identifier = NSUUID.UUID.UUIDString;
|
|
133
|
+
session.launchApplication = [FBAUTLaunchApplicationDouble new];
|
|
134
|
+
[FBSession markSessionActive:session];
|
|
135
|
+
self.session = session;
|
|
136
|
+
return session.launchApplication;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
- (void)launchAsAUT
|
|
140
|
+
{
|
|
141
|
+
[self.session launchApplicationWithBundleId:@"com.facebook.awesome"
|
|
142
|
+
shouldWaitForQuiescence:nil
|
|
143
|
+
arguments:nil
|
|
144
|
+
environment:nil
|
|
145
|
+
asApplicationUnderTest:YES];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
- (void)testCompletedLaunchRegistersAUT
|
|
149
|
+
{
|
|
150
|
+
FBAUTLaunchApplicationDouble *app = [self prepareAUTLaunch];
|
|
151
|
+
XCTAssertNoThrow([self launchAsAUT]);
|
|
152
|
+
XCTAssertEqual([self.session valueForKey:@"testedApplication"], app);
|
|
153
|
+
XCTAssertFalse(app.didTerminate);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
- (void)testDeletedSessionCleansUpInFlightAUTLaunch
|
|
157
|
+
{
|
|
158
|
+
FBAUTLaunchApplicationDouble *app = [self prepareAUTLaunch];
|
|
159
|
+
FBConfiguration.sharedInstance.shouldTerminateApp = YES;
|
|
160
|
+
FBSession *session = self.session;
|
|
161
|
+
app.duringLaunch = ^{ [session kill]; };
|
|
162
|
+
XCTAssertThrowsSpecificNamed([self launchAsAUT], NSException,
|
|
163
|
+
FBSessionDoesNotExistException);
|
|
164
|
+
XCTAssertTrue(app.didTerminate);
|
|
165
|
+
XCTAssertNil([self.session valueForKey:@"testedApplication"]);
|
|
166
|
+
XCTAssertNil(FBSession.activeSession);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
- (void)testTerminationFailurePreservesDeletedSessionErrorAndReleasesClaim
|
|
170
|
+
{
|
|
171
|
+
[self prepareAUTLaunch];
|
|
172
|
+
FBAUTTerminationFailureApplicationDouble *app = [FBAUTTerminationFailureApplicationDouble new];
|
|
173
|
+
((FBAUTLaunchSession *)self.session).launchApplication = app;
|
|
174
|
+
FBConfiguration.sharedInstance.shouldTerminateApp = YES;
|
|
175
|
+
FBSession *session = self.session;
|
|
176
|
+
app.duringLaunch = ^{ [session kill]; };
|
|
177
|
+
|
|
178
|
+
XCTAssertThrowsSpecificNamed([self launchAsAUT], NSException, FBSessionDoesNotExistException);
|
|
179
|
+
XCTAssertTrue(app.didAttemptTermination);
|
|
180
|
+
XCTAssertNil([self.session valueForKey:@"testedApplication"]);
|
|
181
|
+
XCTAssertNil(FBSession.activeSession);
|
|
182
|
+
|
|
183
|
+
// Creating the next session must succeed even when cleanup termination throws.
|
|
184
|
+
// A leaked generation claim would make this fail with a session creation error.
|
|
185
|
+
FBSession *replacement = nil;
|
|
186
|
+
XCTAssertNoThrow(replacement = [FBSession initWithApplication:nil]);
|
|
187
|
+
XCTAssertNotNil(replacement);
|
|
188
|
+
XCTAssertEqual(FBSession.activeSession, replacement);
|
|
189
|
+
[replacement kill];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
- (void)testDeletedSessionPreservesAppWhenTerminationIsDisabled
|
|
193
|
+
{
|
|
194
|
+
FBAUTLaunchApplicationDouble *app = [self prepareAUTLaunch];
|
|
195
|
+
FBSession *session = self.session;
|
|
196
|
+
app.duringLaunch = ^{ [session kill]; };
|
|
197
|
+
XCTAssertThrowsSpecificNamed([self launchAsAUT], NSException,
|
|
198
|
+
FBSessionDoesNotExistException);
|
|
199
|
+
XCTAssertFalse(app.didTerminate);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
- (void)testAbandonedAUTLaunchDoesNotTerminateReplacementSessionApp
|
|
203
|
+
{
|
|
204
|
+
FBAUTLaunchApplicationDouble *app = [self prepareAUTLaunch];
|
|
205
|
+
FBConfiguration.sharedInstance.shouldTerminateApp = YES;
|
|
206
|
+
FBSession *session = self.session;
|
|
207
|
+
__block FBSession *replacement;
|
|
208
|
+
app.duringLaunch = ^{
|
|
209
|
+
[session kill];
|
|
210
|
+
replacement = [FBSession initWithApplication:(id)app];
|
|
211
|
+
};
|
|
212
|
+
XCTAssertThrowsSpecificNamed([self launchAsAUT], NSException,
|
|
213
|
+
FBSessionDoesNotExistException);
|
|
214
|
+
XCTAssertFalse(app.didTerminate);
|
|
215
|
+
XCTAssertEqual(FBSession.activeSession, replacement);
|
|
216
|
+
[replacement kill];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
- (void)testAUTLaunchRejectsAnAlreadyDeletedSession
|
|
220
|
+
{
|
|
221
|
+
FBAUTLaunchApplicationDouble *app = [self prepareAUTLaunch];
|
|
222
|
+
[self.session kill];
|
|
223
|
+
XCTAssertThrowsSpecificNamed([self launchAsAUT], NSException,
|
|
224
|
+
FBSessionDoesNotExistException);
|
|
225
|
+
XCTAssertFalse(app.didLaunch);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
- (void)testSystemApplicationCheckUsesSuppliedApplication
|
|
229
|
+
{
|
|
230
|
+
FBAUTLaunchApplicationDouble *app = [self prepareAUTLaunch];
|
|
231
|
+
FBSystemApplicationLookup = ^id { return app; };
|
|
232
|
+
XCTAssertTrue([self.session fb_isApplicationSameAsSystemApp:(id)app timeout:1.]);
|
|
233
|
+
FBSystemApplicationLookup = ^id { return nil; };
|
|
234
|
+
XCTAssertFalse([self.session fb_isApplicationSameAsSystemApp:(id)app timeout:1.]);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
- (void)testSystemApplicationLookupExceptionSkipsTermination
|
|
238
|
+
{
|
|
239
|
+
FBAUTLaunchApplicationDouble *app = [self prepareAUTLaunch];
|
|
240
|
+
FBConfiguration.sharedInstance.shouldTerminateApp = YES;
|
|
241
|
+
FBSession *session = self.session;
|
|
242
|
+
app.duringLaunch = ^{ [session kill]; };
|
|
243
|
+
FBSystemApplicationLookup = ^id {
|
|
244
|
+
@throw [NSException exceptionWithName:@"LookupFailure" reason:nil userInfo:nil];
|
|
245
|
+
};
|
|
246
|
+
XCTAssertThrowsSpecificNamed([self launchAsAUT], NSException, FBSessionDoesNotExistException);
|
|
247
|
+
XCTAssertFalse(app.didTerminate);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
- (void)testSystemApplicationLookupTimeoutSkipsTermination
|
|
251
|
+
{
|
|
252
|
+
FBAUTLaunchApplicationDouble *app = [self prepareAUTLaunch];
|
|
253
|
+
dispatch_semaphore_t releaseLookup = dispatch_semaphore_create(0);
|
|
254
|
+
XCTestExpectation *finished = [self expectationWithDescription:@"System application lookup finished"];
|
|
255
|
+
FBSystemApplicationLookup = ^id {
|
|
256
|
+
dispatch_semaphore_wait(releaseLookup, DISPATCH_TIME_FOREVER);
|
|
257
|
+
[finished fulfill];
|
|
258
|
+
return nil;
|
|
259
|
+
};
|
|
260
|
+
@try {
|
|
261
|
+
XCTAssertTrue([self.session fb_isApplicationSameAsSystemApp:(id)app timeout:0.01]);
|
|
262
|
+
} @finally {
|
|
263
|
+
dispatch_semaphore_signal(releaseLookup);
|
|
264
|
+
[self waitForExpectationsWithTimeout:5. handler:nil];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
67
268
|
@end
|
|
@@ -16,11 +16,112 @@
|
|
|
16
16
|
#import "FBXCElementSnapshotWrapper+Helpers.h"
|
|
17
17
|
#import "XCTestPrivateSymbols.h"
|
|
18
18
|
|
|
19
|
+
// libxml's node callbacks are thread-local; restore any existing callbacks after each probe.
|
|
20
|
+
static __thread NSUInteger FBReviewCreatedDocs;
|
|
21
|
+
static __thread NSUInteger FBReviewFreedDocs;
|
|
22
|
+
static __thread xmlRegisterNodeFunc FBReviewPreviousRegister;
|
|
23
|
+
static __thread xmlDeregisterNodeFunc FBReviewPreviousDeregister;
|
|
24
|
+
|
|
25
|
+
static void FBReviewRegisterNode(xmlNodePtr node)
|
|
26
|
+
{
|
|
27
|
+
if (node->type == XML_DOCUMENT_NODE) { FBReviewCreatedDocs++; }
|
|
28
|
+
if (NULL != FBReviewPreviousRegister) { FBReviewPreviousRegister(node); }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static void FBReviewDeregisterNode(xmlNodePtr node)
|
|
32
|
+
{
|
|
33
|
+
if (node->type == XML_DOCUMENT_NODE) { FBReviewFreedDocs++; }
|
|
34
|
+
if (NULL != FBReviewPreviousDeregister) { FBReviewPreviousDeregister(node); }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@interface FBThrowingLabelSnapshot : XCElementSnapshotDouble
|
|
38
|
+
@end
|
|
39
|
+
@implementation FBThrowingLabelSnapshot
|
|
40
|
+
- (NSString *)label
|
|
41
|
+
{
|
|
42
|
+
@throw [NSException exceptionWithName:@"SnapshotAttributeFailure" reason:@"test" userInfo:nil];
|
|
43
|
+
}
|
|
44
|
+
@end
|
|
45
|
+
|
|
46
|
+
@interface FBTraversalSnapshot : XCElementSnapshotDouble
|
|
47
|
+
@property (nonatomic, copy) NSArray *childSnapshots;
|
|
48
|
+
@property (nonatomic, weak) FBTraversalSnapshot *parentSnapshot;
|
|
49
|
+
@property (nonatomic) NSUInteger childrenReads;
|
|
50
|
+
@property (nonatomic) NSUInteger frameReads;
|
|
51
|
+
@end
|
|
52
|
+
@implementation FBTraversalSnapshot
|
|
53
|
+
- (NSArray *)children { self.childrenReads++; return self.childSnapshots ?: @[]; }
|
|
54
|
+
- (id)parent { return self.parentSnapshot; }
|
|
55
|
+
- (CGRect)frame { self.frameReads++; return CGRectMake(1, 2, 3, 4); }
|
|
56
|
+
@end
|
|
57
|
+
|
|
19
58
|
@interface FBXPathTests : XCTestCase
|
|
20
59
|
@end
|
|
21
60
|
|
|
22
61
|
@implementation FBXPathTests
|
|
23
62
|
|
|
63
|
+
- (void)testSerializationVisitsSiblingListAndFrameOnce
|
|
64
|
+
{
|
|
65
|
+
FBTraversalSnapshot *root = [FBTraversalSnapshot new];
|
|
66
|
+
NSMutableArray *children = [NSMutableArray array];
|
|
67
|
+
for (NSUInteger index = 0; index < 64; index++) {
|
|
68
|
+
FBTraversalSnapshot *child = [FBTraversalSnapshot new];
|
|
69
|
+
child.parentSnapshot = root;
|
|
70
|
+
[children addObject:child];
|
|
71
|
+
}
|
|
72
|
+
root.childSnapshots = children;
|
|
73
|
+
NSString *query = @"//*[@index >= 0 and @x=1 and @y=2 and @width=3 and @height=4]";
|
|
74
|
+
xmlDocPtr doc = [self documentForSnapshot:root query:query];
|
|
75
|
+
@try {
|
|
76
|
+
XCTAssertEqual(root.childrenReads, 1u);
|
|
77
|
+
XCTAssertEqual(root.frameReads, 1u);
|
|
78
|
+
for (FBTraversalSnapshot *child in children) {
|
|
79
|
+
XCTAssertEqual(child.frameReads, 1u);
|
|
80
|
+
}
|
|
81
|
+
XCTAssertEqualObjects([self xpathStringResultForQuery:@"count(//*[@index=63])" document:doc], @"1");
|
|
82
|
+
} @finally {
|
|
83
|
+
xmlFreeDoc(doc);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
- (void)testDirectXmlWriterPreservesEscapingAndFormatting
|
|
88
|
+
{
|
|
89
|
+
XCElementSnapshotDouble *snapshot = [XCElementSnapshotDouble new];
|
|
90
|
+
snapshot.label = @"line one\nline two\t& < > \" йоло";
|
|
91
|
+
snapshot.value = @"value";
|
|
92
|
+
NSString *expected = [[self xmlStringWithElement:(id)snapshot xpathQuery:nil excludingAttributes:nil]
|
|
93
|
+
stringByReplacingOccurrencesOfString:@" private_indexPath=\"top\"" withString:@""];
|
|
94
|
+
NSString *actual = [FBXPath xmlStringWithRootElement:(id)snapshot options:nil];
|
|
95
|
+
XCTAssertEqualObjects(actual, expected);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
- (void)testXmlDocumentsAreFreedWhenSnapshotAttributesThrow
|
|
99
|
+
{
|
|
100
|
+
for (NSNumber *isLookup in @[@NO, @YES]) {
|
|
101
|
+
FBReviewCreatedDocs = 0;
|
|
102
|
+
FBReviewFreedDocs = 0;
|
|
103
|
+
FBReviewPreviousRegister = xmlRegisterNodeDefault(FBReviewRegisterNode);
|
|
104
|
+
FBReviewPreviousDeregister = xmlDeregisterNodeDefault(FBReviewDeregisterNode);
|
|
105
|
+
@try {
|
|
106
|
+
id<FBElement> snapshot = (id)[FBThrowingLabelSnapshot new];
|
|
107
|
+
if (isLookup.boolValue) {
|
|
108
|
+
XCTAssertThrowsSpecificNamed([FBXPath matchesWithRootElement:snapshot forQuery:@"//*[@label]"],
|
|
109
|
+
NSException, @"SnapshotAttributeFailure");
|
|
110
|
+
} else {
|
|
111
|
+
XCTAssertThrowsSpecificNamed([FBXPath xmlStringWithRootElement:snapshot options:nil],
|
|
112
|
+
NSException, @"SnapshotAttributeFailure");
|
|
113
|
+
}
|
|
114
|
+
if (isLookup.boolValue) {
|
|
115
|
+
XCTAssertGreaterThan(FBReviewCreatedDocs, 0u);
|
|
116
|
+
}
|
|
117
|
+
XCTAssertEqual(FBReviewCreatedDocs, FBReviewFreedDocs);
|
|
118
|
+
} @finally {
|
|
119
|
+
xmlRegisterNodeDefault(FBReviewPreviousRegister);
|
|
120
|
+
xmlDeregisterNodeDefault(FBReviewPreviousDeregister);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
24
125
|
- (NSString *)xmlStringWithElement:(id<FBXCElementSnapshot>)snapshot
|
|
25
126
|
xpathQuery:(nullable NSString *)query
|
|
26
127
|
excludingAttributes:(nullable NSArray<NSString *> *)excludedAttributes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Hinge angle
|
|
2
|
+
|
|
3
|
+
These endpoints use runtime capability checks on both simulators and physical
|
|
4
|
+
devices, rather than restricting access to a particular device model or to
|
|
5
|
+
Simulator builds. Reading an angle and injecting a simulated angle have separate
|
|
6
|
+
requirements.
|
|
7
|
+
|
|
8
|
+
`POST /session/:sessionId/wda/device/hingeAngle` with `{"angle": 90}` sends an
|
|
9
|
+
event requesting a simulated hinge angle of 90 degrees. The angle
|
|
10
|
+
must be a finite number from `0` (closed) to `180` (fully open); fractional values
|
|
11
|
+
are supported. POST checks for a hinge and the required IOKit serialization,
|
|
12
|
+
event creation, and dispatch APIs. This permits an injection attempt; it does not
|
|
13
|
+
establish that the device accepts this vendor payload. Physical devices may attempt
|
|
14
|
+
injection when those prerequisites exist, but event acceptance and the resulting
|
|
15
|
+
angle/layout changes on a physical Duo remain unverified. Devices without these
|
|
16
|
+
prerequisites, tvOS, and watchOS return `unsupported operation`.
|
|
17
|
+
|
|
18
|
+
The response confirms event dispatch, not that the device applied the angle.
|
|
19
|
+
Wait for the expected app layout before continuing, since folding completes
|
|
20
|
+
asynchronously. The command does not rotate the device or update
|
|
21
|
+
`currentDisplayId`; select the desired display separately.
|
|
22
|
+
|
|
23
|
+
`GET /session/:sessionId/wda/device/hingeAngle` reads the current angle in degrees
|
|
24
|
+
and returns a numeric value, for example `{"value": 90.5}`. It reads CoreMotion
|
|
25
|
+
rather than remembering the last angle sent by WDA, so changes made outside WDA
|
|
26
|
+
are reflected too. The request waits up to five seconds for a valid reading and
|
|
27
|
+
returns an error if none arrives. GET separately checks for a hinge and the
|
|
28
|
+
CoreMotion subscription APIs; it does not depend on the IOKit injection APIs.
|
|
29
|
+
Devices without these reading prerequisites return `unsupported operation`.
|
|
30
|
+
A reading during a fold may reflect an intermediate angle; it does not wait for
|
|
31
|
+
the transition to finish.
|
|
32
|
+
|
|
33
|
+
Both hinge angle endpoints are also available without a session:
|
|
34
|
+
`GET /wda/device/hingeAngle` and `POST /wda/device/hingeAngle`. They operate on
|
|
35
|
+
the device and do not require creating an Appium session first.
|
|
36
|
+
|
|
37
|
+
## Validation and feedback
|
|
38
|
+
|
|
39
|
+
Hinge operations have been tested on the iPhone Duo simulator with Xcode 27.1.
|
|
40
|
+
We have not had access to a physical Duo for testing,
|
|
41
|
+
so hinge readings, acceptance of the private HID events, and the resulting
|
|
42
|
+
display/layout behavior on that hardware remain unverified. Loading CoreMotion
|
|
43
|
+
and finding `CMAngleManager` were verified on a physical iPad mini, which reported
|
|
44
|
+
no available hinge. On the same iPad mini running iPadOS 27.0.1, IOKit loading,
|
|
45
|
+
resolution of all four injection functions, payload serialization, vendor-event
|
|
46
|
+
creation, and HID client creation also succeeded. Event dispatch was not called.
|
|
47
|
+
These checks do not validate hinge readings or event acceptance on physical
|
|
48
|
+
hardware with a hinge.
|
|
49
|
+
|
|
50
|
+
If you have access to a physical device with a hinge, we would appreciate your
|
|
51
|
+
help testing these endpoints and [reporting the observed behavior](https://github.com/appium/WebDriverAgent/issues).
|
|
52
|
+
Please include the device model, iOS and Xcode versions, WDA version, requests and
|
|
53
|
+
responses, and any observed angle or display/layout changes. Reports of unsupported
|
|
54
|
+
operations or successful responses with no observable change are useful too.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Multiple displays
|
|
2
|
+
|
|
3
|
+
On devices such as iPhone Duo, `GET /wda/screens` lists the available displays.
|
|
4
|
+
Set `currentDisplayId` through `POST /session/:sessionId/appium/settings` to select
|
|
5
|
+
one of these IDs. Do not assume that the main display is the currently visible
|
|
6
|
+
one after folding or unfolding the device.
|
|
7
|
+
|
|
8
|
+
The setting selects the display for screenshots and MJPEG frames, and passes the
|
|
9
|
+
selected display ID to newly started XCTest screen recordings (see the
|
|
10
|
+
[simulator recording limitation](#xctest-recording-on-the-duo-simulator) below). MJPEG follows setting changes on the existing
|
|
11
|
+
connection; an in-progress recording keeps the display selected when it started.
|
|
12
|
+
An unavailable display causes screenshots and recording requests to fail; MJPEG
|
|
13
|
+
pauses frame delivery until a valid display is selected. Set `currentDisplayId`
|
|
14
|
+
to `null` to restore the main display. WDA does not automatically switch this
|
|
15
|
+
setting when the device changes pose.
|
|
16
|
+
|
|
17
|
+
`GET /wda/screen` also follows `currentDisplayId`: `displayId` and `scale`
|
|
18
|
+
belong to the selected screen, and `screenSize` is its size in logical pixels,
|
|
19
|
+
adjusted to the active application's orientation. Unavailable selections return
|
|
20
|
+
an error. The status bar must belong to that display and be visible at its top.
|
|
21
|
+
Hidden containers and side-mounted status UI do not define a top-edge crop;
|
|
22
|
+
`statusBarSize` is zero when no such bar is present. Clients should refresh this
|
|
23
|
+
information after display, orientation, or status-bar changes.
|
|
24
|
+
|
|
25
|
+
The XCTest point lookup used by WDA has no display argument, including in the
|
|
26
|
+
Xcode 27.1 interfaces checked for this change. When a secondary display is
|
|
27
|
+
selected, WDA uses its active-app fallback instead of that main-display lookup.
|
|
28
|
+
This is not display-specific app filtering: set `defaultActiveApplication` to the
|
|
29
|
+
app's bundle ID when multiple foreground apps make the result ambiguous. An app
|
|
30
|
+
specified when creating the session retains its existing priority.
|
|
31
|
+
|
|
32
|
+
W3C touch actions target the selected display. Viewport, element, and pointer
|
|
33
|
+
origins can be mixed within one action sequence; element offsets remain relative
|
|
34
|
+
to the element's center. Element origins require `currentDisplayId` to match the
|
|
35
|
+
element's display; they do not override the selected display. Key-only and
|
|
36
|
+
pause-only sequences do not require the selected display to be available.
|
|
37
|
+
For an application-element origin on a secondary display, compute offsets from
|
|
38
|
+
its returned element rect, just as for any other element. XCTest may report that
|
|
39
|
+
rect with dimensions transposed relative to the application's window; WDA uses
|
|
40
|
+
the returned rect's center plus the requested offset as a viewport point and
|
|
41
|
+
applies the selected-display correction. Do not substitute the window's center
|
|
42
|
+
when calculating an offset for an application-element origin. This is separate
|
|
43
|
+
from the older-SDK compatibility issue below. Main-display application origins
|
|
44
|
+
retain their existing behavior.
|
|
45
|
+
Element scrolling uses the visible area in the same
|
|
46
|
+
coordinate space as the element frame, including on rotated main and secondary displays.
|
|
47
|
+
WDA scrolling rejects an unavailable selected display instead of using an
|
|
48
|
+
uncorrected frame. Each normalized scroll keeps its converted frame and vector
|
|
49
|
+
in the same coordinate space across its drags. Keep the device orientation and
|
|
50
|
+
fold state stable until the command completes; change the display setting between
|
|
51
|
+
commands. Scroll-to-visible takes a fresh parent snapshot for each scroll step.
|
|
52
|
+
|
|
53
|
+
Native application-root coordinates on a secondary display require a
|
|
54
|
+
non-empty application window on that display as a coordinate anchor. Edge and
|
|
55
|
+
outside coordinates are preserved, as on the main display; XCTest determines
|
|
56
|
+
how the gesture is delivered. No hit testing or clamping is performed here.
|
|
57
|
+
Requests fail if no such window exists on the selected display.
|
|
58
|
+
|
|
59
|
+
This gesture support builds on [#1269](https://github.com/appium/WebDriverAgent/pull/1269).
|
|
60
|
+
|
|
61
|
+
## Legacy SDK compatibility windows
|
|
62
|
+
|
|
63
|
+
“Legacy SDK” here means the SDK used to build the app: the tested older app was
|
|
64
|
+
built with **iOS SDK 26.5 (Xcode 26.6)** and ran in a compatibility window on
|
|
65
|
+
iPhone Duo. It does not refer to the device's iOS version or the SDK used to build
|
|
66
|
+
WDA. See Apple's [SDK-dependent layout guidance](https://developer.apple.com/videos/play/tech-talks/111461/).
|
|
67
|
+
|
|
68
|
+
On the **iPhone Duo simulator, iOS 27.1 (24A94401), with an Xcode 27.2 beta 2
|
|
69
|
+
UI-test runner**, direct XCTest element and coordinate taps failed to deliver
|
|
70
|
+
input to that app in fully open and some intermediate poses. The same app source
|
|
71
|
+
built with **SDK 27.2** received the taps. The failure reproduced without WDA;
|
|
72
|
+
it is a known limitation observed in the Apple compatibility-layout/XCTest path,
|
|
73
|
+
although the failing internal component has not been identified. Physical devices
|
|
74
|
+
and other SDK/runtime combinations remain unverified.
|
|
75
|
+
|
|
76
|
+
Select the app's `currentDisplayId` explicitly, but be aware that selecting the
|
|
77
|
+
correct display does not resolve this issue. WDA does not apply a compatibility
|
|
78
|
+
coordinate correction. Rebuilding with SDK 27.2 avoided the failure in the tested
|
|
79
|
+
app; for diagnosis, compare direct XCTest taps with app-recorded input.
|
|
80
|
+
|
|
81
|
+
## XCTest recording on the Duo simulator
|
|
82
|
+
|
|
83
|
+
On **iOS 27.1 (24A94401), with Xcode 27.2 beta 2**, the native XCTest
|
|
84
|
+
recording path produced black video for the closed Duo's outer display.
|
|
85
|
+
This reproduced after starting WDA afresh while the device was closed, as well
|
|
86
|
+
as after switching from the inner display back to the outer display.
|
|
87
|
+
|
|
88
|
+
The recorded native `XCTScreenRecordingRequest.screenID` was `1` for the outer
|
|
89
|
+
screen and `3` for the inner screen, matching the selected IDs returned by
|
|
90
|
+
`GET /wda/screens`. Simultaneous outer-display screenshots were correct at
|
|
91
|
+
1398 x 2034 pixels. Nevertheless, each four-second outer recording in the fresh
|
|
92
|
+
runner experiment contained only one black frame at 2006 x 2852 pixels; the
|
|
93
|
+
inner-display recording contained visible content. A successful start/stop
|
|
94
|
+
response is therefore insufficient to verify captured video.
|
|
95
|
+
|
|
96
|
+
The failure occurs downstream of the selected ID being supplied to XCTest;
|
|
97
|
+
this experiment does not identify the failing internal Apple component or
|
|
98
|
+
establish behavior on other runtimes or physical devices. WDA does not remap
|
|
99
|
+
display IDs to work around it. Use the MJPEG/ffmpeg recording path when affected;
|
|
100
|
+
that path was verified across closed, book, and fully open poses with both MJPEG
|
|
101
|
+
and H.264 output. Set `mjpegFixOrientation` to `true` if upright encoded MJPEG
|
|
102
|
+
pixels are required: the default preserves the existing EXIF-oriented output.
|
|
103
|
+
|
|
104
|
+
## Waiting for rotated layouts
|
|
105
|
+
|
|
106
|
+
An application's reported interface orientation can update before its element
|
|
107
|
+
frames settle. On an iPad simulator running iOS 26.5, the gesture fixture's Alerts
|
|
108
|
+
button briefly retained transposed dimensions immediately after rotation;
|
|
109
|
+
resolving an element-origin gesture during that interval missed the button.
|
|
110
|
+
The same failure reproduced on the unmodified WDA baseline. Waiting for the
|
|
111
|
+
fixture's expected landscape geometry resolved it; direct XCTest gesture
|
|
112
|
+
controls also passed. Tests should wait for the application's expected layout
|
|
113
|
+
before resolving coordinates, in addition to keeping its pose stable during a
|
|
114
|
+
command. This observation does not imply that every missed gesture is an
|
|
115
|
+
XCTest defect.
|
|
116
|
+
|
|
117
|
+
## Home button on Duo devices
|
|
118
|
+
|
|
119
|
+
On the Duo simulator running iOS 27.1, XCTest's Home button and its IOHID
|
|
120
|
+
wrapper can return success without leaving the foreground application. WDA
|
|
121
|
+
uses a directly dispatched IOKit Home key-down/key-up pair for Home requests
|
|
122
|
+
on iOS devices with an available hinge sensor, including simulators and physical
|
|
123
|
+
devices. Devices without a hinge retain the XCTest path. The direct path sends button events;
|
|
124
|
+
it does not substitute application activation or change the selected display.
|
|
125
|
+
|
|
126
|
+
The Home key event has been verified on the Duo simulator; physical Duo Home
|
|
127
|
+
behavior remains unverified and feedback is welcome. Availability of IOKit APIs
|
|
128
|
+
and successful event dispatch do not by themselves confirm that the app left
|
|
129
|
+
the foreground.
|
|
130
|
+
|
|
131
|
+
This workaround does not address rotation or input immediately after an
|
|
132
|
+
application is reactivated. See [Appium #22857](https://github.com/appium/appium/issues/22857).
|
|
133
|
+
|
|
134
|
+
## Validation and feedback
|
|
135
|
+
|
|
136
|
+
Duo display behavior has been tested on the iPhone Duo simulator with Xcode 27.1
|
|
137
|
+
and Xcode 27.2 beta 2.
|
|
138
|
+
We have not had access to a physical Duo for testing. If you have access to one,
|
|
139
|
+
we would appreciate [reports of its display and capture behavior](https://github.com/appium/WebDriverAgent/issues),
|
|
140
|
+
including the device model, iOS/Xcode and WDA versions, selected display IDs,
|
|
141
|
+
requests and responses, and observed results.
|
|
142
|
+
|
|
143
|
+
### Rotation on Duo devices
|
|
144
|
+
|
|
145
|
+
On Duo Simulator 27.1, assigning `XCUIDevice.orientation` can update XCTest's
|
|
146
|
+
reported orientation without rotating the application. On Duo simulators with
|
|
147
|
+
an available hinge sensor, WDA directly dispatches the vendor-defined
|
|
148
|
+
`orientation-picker-control` event used by Xcode 27.1's Duo controls, while also
|
|
149
|
+
keeping XCTest's reported device orientation in sync.
|
|
150
|
+
|
|
151
|
+
On physical devices with an available hinge sensor, WDA first uses XCTest and
|
|
152
|
+
checks the application's interface orientation after waiting for stability.
|
|
153
|
+
Only if that check fails does WDA log the transition to the vendor HID fallback
|
|
154
|
+
and attempt it. Devices without a hinge retain the XCTest path.
|
|
155
|
+
|
|
156
|
+
The vendor event has only been verified on the Duo simulator. Its
|
|
157
|
+
`com.apple.Virtualization.VirtualMachines` receiver may not exist on physical
|
|
158
|
+
hardware. Physical-device feedback is welcome; availability of the IOKit APIs
|
|
159
|
+
or successful event submission does not establish that rotation works.
|
|
160
|
+
|
|
161
|
+
Select the active panel with `currentDisplayId` before rotating. The inner
|
|
162
|
+
panel's native orientation is a quarter turn from the cover panel, so WDA
|
|
163
|
+
translates the requested orientation for the selected display. Both orientation
|
|
164
|
+
and numeric rotation commands use the same path. WDA waits up to five seconds
|
|
165
|
+
for the application's interface orientation to match, then waits for stability;
|
|
166
|
+
event submission alone is not success. An app or panel that does not support
|
|
167
|
+
the requested orientation still returns an error. In particular, the tested
|
|
168
|
+
cover panel did not adopt upside-down portrait.
|
|
@@ -4,8 +4,7 @@ import {WebDriverProxy} from '@appium/base-driver';
|
|
|
4
4
|
import {strongbox} from '@appium/strongbox';
|
|
5
5
|
import {fs, util} from '@appium/support';
|
|
6
6
|
import type {AppiumLogger, StringRecord} from '@appium/types';
|
|
7
|
-
import AsyncLock from '
|
|
8
|
-
import {waitForCondition} from 'asyncbox';
|
|
7
|
+
import {AsyncLock, waitForCondition} from 'asyncbox';
|
|
9
8
|
|
|
10
9
|
import {WDA_RUNNER_BUNDLE_ID, WDA_BASE_URL, DEFAULT_TEST_BUNDLE_SUFFIX} from './constants.js';
|
|
11
10
|
import {log as defaultLogger} from './logger.js';
|