@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.
Files changed (118) hide show
  1. package/README.md +30 -3
  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,6 +9,9 @@
9
9
  #import <XCTest/XCTest.h>
10
10
 
11
11
  #import "FBIntegrationTestCase.h"
12
+ #import "FBConfiguration.h"
13
+ #import "FBMathUtils.h"
14
+ #import "FBScreen.h"
12
15
 
13
16
  #import "XCUIElement.h"
14
17
  #import "XCUIDevice.h"
@@ -21,6 +24,7 @@
21
24
  #import "XCSynthesizedEventRecord.h"
22
25
  #import "XCPointerEventPath.h"
23
26
  #import "XCPointerEvent.h"
27
+ #import "XCUIScreen.h"
24
28
 
25
29
  @interface FBW3CTouchActionsIntegrationTestsPart1 : FBIntegrationTestCase
26
30
  @end
@@ -32,9 +36,36 @@
32
36
 
33
37
  @implementation FBW3CTouchActionsIntegrationTestsPart1
34
38
 
39
+ - (void)testDisplayIndependentActionsAfterSelectedDisplayDisappears
40
+ {
41
+ // setUp already opens the Alerts fixture; relaunching would reset that page
42
+ // and break the subsequent touch tests that share it.
43
+ NSNumber *previous = FBConfiguration.sharedInstance.currentDisplayId;
44
+ [self addTeardownBlock:^{ FBConfiguration.sharedInstance.currentDisplayId = previous; }];
45
+ NSArray *sequences = @[
46
+ @{@"id": @"keyboard", @"type": @"key", @"actions": @[@{@"type": @"keyDown", @"value": @"a"}, @{@"type": @"keyUp", @"value": @"a"}]},
47
+ @{@"id": @"finger", @"type": @"pointer", @"parameters": @{@"pointerType": @"touch"}, @"actions": @[@{@"type": @"pause", @"duration": @10}]}
48
+ ];
49
+ for (NSDictionary *sequence in sequences) {
50
+ for (NSNumber *display in @[@1, @987654]) {
51
+ FBConfiguration.sharedInstance.currentDisplayId = display;
52
+ NSError *error = nil;
53
+ FBW3CActionsSynthesizer *synthesizer = [[FBW3CActionsSynthesizer alloc] initWithActions:@[sequence] forApplication:self.testedApplication elementCache:nil error:&error];
54
+ XCSynthesizedEventRecord *record = [synthesizer synthesizeWithError:&error];
55
+ XCTAssertNotNil(record, @"Display-independent %@ should succeed: %@", sequence[@"type"], error);
56
+ }
57
+ }
58
+ }
59
+
35
60
  - (void)verifyGesture:(NSArray<NSDictionary<NSString *, id> *> *)gesture orientation:(UIDeviceOrientation)orientation
36
61
  {
37
62
  [[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:orientation];
63
+ // XCTest may report the new interface orientation while the Alerts button
64
+ // still has transposed geometry from the rotation transition. The fixture's
65
+ // button is wider than it is tall in every settled orientation. Wait for that
66
+ // layout before resolving the W3C action's element-origin coordinates.
67
+ XCUIElement *button = self.testedApplication.buttons[FBShowAlertButtonName];
68
+ FBAssertWaitTillBecomesTrue(CGRectGetWidth(button.frame) > CGRectGetHeight(button.frame));
38
69
  NSError *error;
39
70
  XCTAssertTrue([self.testedApplication fb_performW3CActions:gesture elementCache:nil error:&error]);
40
71
  FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count > 0);
@@ -53,11 +84,106 @@
53
84
 
54
85
  - (void)tearDown
55
86
  {
87
+ FBConfiguration.sharedInstance.currentDisplayId = nil;
56
88
  [self clearAlert];
57
89
  [self resetOrientation];
58
90
  [super tearDown];
59
91
  }
60
92
 
93
+ - (nullable XCSynthesizedEventRecord *)tapRecordWithError:(NSError **)error
94
+ {
95
+ NSArray *gesture = @[@{
96
+ @"type": @"pointer", @"id": @"finger", @"parameters": @{@"pointerType": @"touch"},
97
+ @"actions": @[
98
+ @{@"type": @"pointerMove", @"x": @100, @"y": @100, @"duration": @0},
99
+ @{@"type": @"pointerDown", @"button": @0},
100
+ @{@"type": @"pointerUp", @"button": @0},
101
+ ],
102
+ }];
103
+ FBW3CActionsSynthesizer *synthesizer = [[FBW3CActionsSynthesizer alloc] initWithActions:gesture
104
+ forApplication:self.testedApplication
105
+ elementCache:nil
106
+ error:error];
107
+ return [synthesizer synthesizeWithError:error];
108
+ }
109
+
110
+ - (NSArray<XCPointerEvent *> *)pointerEventsOfRecord:(XCSynthesizedEventRecord *)record
111
+ {
112
+ XCPointerEventPath *eventPath = record.eventPaths.firstObject;
113
+ return eventPath.pointerEvents;
114
+ }
115
+
116
+ - (void)testMainDisplaySettingKeepsDefaultCoordinates
117
+ {
118
+ for (NSNumber *orientation in @[@(UIDeviceOrientationPortrait),
119
+ @(UIDeviceOrientationLandscapeLeft),
120
+ @(UIDeviceOrientationLandscapeRight)]) {
121
+ [[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:orientation.integerValue];
122
+ NSError *error = nil;
123
+ FBConfiguration.sharedInstance.currentDisplayId = nil;
124
+ XCSynthesizedEventRecord *baseline = [self tapRecordWithError:&error];
125
+ FBConfiguration.sharedInstance.currentDisplayId = @([FBScreen displayID]);
126
+ XCSynthesizedEventRecord *selected = [self tapRecordWithError:&error];
127
+ XCTAssertNotNil(baseline);
128
+ XCTAssertNotNil(selected);
129
+ XCTAssertNil(error);
130
+ NSArray<XCPointerEvent *> *baselineEvents = [self pointerEventsOfRecord:baseline];
131
+ NSArray<XCPointerEvent *> *selectedEvents = [self pointerEventsOfRecord:selected];
132
+ XCTAssertEqual(baselineEvents.count, selectedEvents.count);
133
+ for (NSUInteger i = 0; i < baselineEvents.count; i++) {
134
+ XCTAssertTrue(CGPointEqualToPoint(baselineEvents[i].coordinate, selectedEvents[i].coordinate));
135
+ }
136
+ }
137
+ }
138
+
139
+ - (void)testUnavailableDisplaySettingIsRejected
140
+ {
141
+ long long maxID = 0;
142
+ for (NSDictionary<NSString *, id> *screen in [FBScreen screensWithError:nil]) {
143
+ maxID = MAX(maxID, [screen[@"displayId"] longLongValue]);
144
+ }
145
+ FBConfiguration.sharedInstance.currentDisplayId = @(maxID + 1);
146
+ NSError *error = nil;
147
+ XCTAssertNil([self tapRecordWithError:&error]);
148
+ XCTAssertNotNil(error);
149
+ }
150
+
151
+ - (void)testSecondaryDisplaySetting
152
+ {
153
+ XCUIScreen *secondary = nil;
154
+ NSError *error = nil;
155
+ NSArray<NSDictionary<NSString *, id> *> *screens = [FBScreen screensWithError:&error];
156
+ XCTAssertNotNil(screens);
157
+ XCTAssertNil(error);
158
+ for (NSDictionary<NSString *, id> *screen in screens) {
159
+ long long displayID = [screen[@"displayId"] longLongValue];
160
+ if (displayID != [FBScreen displayID]) {
161
+ secondary = [FBScreen screenWithDisplayID:displayID error:&error];
162
+ XCTAssertNotNil(secondary);
163
+ XCTAssertNil(error);
164
+ break;
165
+ }
166
+ }
167
+ if (nil == secondary) {
168
+ XCTSkip(@"The device has no secondary display");
169
+ }
170
+ XCSynthesizedEventRecord *baseline = [self tapRecordWithError:&error];
171
+ FBConfiguration.sharedInstance.currentDisplayId = @(secondary.displayID);
172
+ XCSynthesizedEventRecord *selected = [self tapRecordWithError:&error];
173
+ XCTAssertNotNil(selected);
174
+ XCTAssertNil(error);
175
+ XCTAssertEqual(selected.displayID, (unsigned long long)secondary.displayID);
176
+ XCUIScreen *main = XCUIScreen.mainScreen;
177
+ CGVector offset = FBDisplayCoordinateOffset(
178
+ CGSizeMake(main.bounds.size.width / main.scale, main.bounds.size.height / main.scale),
179
+ CGSizeMake(secondary.bounds.size.width / secondary.scale, secondary.bounds.size.height / secondary.scale),
180
+ selected.interfaceOrientation);
181
+ CGPoint expected = [self pointerEventsOfRecord:baseline].firstObject.coordinate;
182
+ CGPoint actual = [self pointerEventsOfRecord:selected].firstObject.coordinate;
183
+ XCTAssertEqual(actual.x, expected.x + offset.dx);
184
+ XCTAssertEqual(actual.y, expected.y + offset.dy);
185
+ }
186
+
61
187
  - (void)testErroneousGestures
62
188
  {
63
189
  NSArray<NSArray<NSDictionary<NSString *, id> *> *> *invalidGestures =
@@ -9,15 +9,29 @@
9
9
  #import <XCTest/XCTest.h>
10
10
 
11
11
  #import <mach/mach_time.h>
12
+ #import <math.h>
12
13
 
13
14
  #import "FBIntegrationTestCase.h"
14
15
  #import "FBElement.h"
16
+ #import "FBSession.h"
17
+ #import "FBSessionCommands.h"
15
18
  #import "FBMacros.h"
16
19
  #import "FBTestMacros.h"
17
20
  #import "XCUIApplication.h"
18
21
  #import "XCUIApplication+FBHelpers.h"
19
22
  #import "XCUIElement+FBIsVisible.h"
20
23
  #import "FBXCodeCompatibility.h"
24
+ #import "FBConfiguration.h"
25
+ #import "FBScreen.h"
26
+ #import "FBRunLoopSpinner.h"
27
+ #import "XCUIDevice+FBHinge.h"
28
+
29
+ // Exercise the session-start resume path without creating a second session.
30
+ @interface FBSessionCommands (FBActivationTests)
31
+ + (id)activateBackgroundApplication:(XCUIApplication *)app
32
+ bundleID:(NSString *)bundleID
33
+ initialUrl:(nullable NSString *)initialUrl;
34
+ @end
21
35
 
22
36
  void calculateMaxTreeDepth(NSDictionary *tree, NSNumber *currentDepth, NSNumber** maxDepth) {
23
37
  if (nil == maxDepth) {
@@ -83,6 +97,103 @@ void calculateMaxTreeDepth(NSDictionary *tree, NSNumber *currentDepth, NSNumber*
83
97
  XCTAssertTrue(self.testedApplication.buttons[@"Alerts"].exists);
84
98
  }
85
99
 
100
+ - (void)testInputAfterDeactivate
101
+ {
102
+ [self verifyInputAfterBackgroundWithMode:0];
103
+ }
104
+
105
+ - (void)testInputAfterSessionActivate
106
+ {
107
+ [self verifyInputAfterBackgroundWithMode:1];
108
+ }
109
+
110
+ - (void)testInputAfterSessionLaunch
111
+ {
112
+ [self verifyInputAfterBackgroundWithMode:2];
113
+ }
114
+
115
+ - (void)testInputAfterSessionStartActivate
116
+ {
117
+ [self verifyInputAfterBackgroundWithMode:3];
118
+ }
119
+
120
+ - (void)verifyInputAfterBackgroundWithMode:(NSUInteger)mode
121
+ {
122
+ XCTSkipIf(self.class.isRunningInCI, @"Immediate input after activation is unreliable on slow CI hosts");
123
+ XCUIDevice *device = XCUIDevice.sharedDevice;
124
+ BOOL canFold = NO;
125
+ #if TARGET_OS_SIMULATOR
126
+ canFold = device.fb_canAttemptSimulatedHingeAngleInjection;
127
+ #endif
128
+ NSNumber *originalAngle = canFold ? [device fb_getSimulatedHingeAngle:nil] : nil;
129
+ NSNumber *originalDisplay = FBConfiguration.sharedInstance.currentDisplayId;
130
+ if (canFold) {
131
+ XCTAssertNotNil(originalAngle);
132
+ }
133
+ BOOL (^setAngleAndWait)(NSNumber *) = ^BOOL(NSNumber *angle) {
134
+ if (![device fb_setSimulatedHingeAngle:angle.doubleValue error:nil]) {
135
+ return NO;
136
+ }
137
+ return [[[FBRunLoopSpinner new] timeout:5] spinUntilTrue:^BOOL{
138
+ NSNumber *reading = [device fb_getSimulatedHingeAngle:nil];
139
+ return nil != reading && fabs(reading.doubleValue - angle.doubleValue) < 1;
140
+ }];
141
+ };
142
+ FBSession *session = mode != 0 ? [FBSession initWithApplication:self.testedApplication] : nil;
143
+ @try {
144
+ for (NSNumber *angle in (canFold ? @[@0, @90, @180] : @[@0])) {
145
+ if (canFold) {
146
+ XCTAssertTrue(setAngleAndWait(angle), @"Hinge did not reach %@ degrees", angle);
147
+ for (NSDictionary *screen in [FBScreen screensWithError:nil]) {
148
+ if ([screen[@"isMain"] boolValue] == (angle.intValue == 0)) {
149
+ FBConfiguration.sharedInstance.currentDisplayId = screen[@"displayId"];
150
+ }
151
+ }
152
+ }
153
+ for (NSNumber *duration in @[@0, @2]) {
154
+ [self launchApplication];
155
+ NSError *error = nil;
156
+ if (mode != 0) {
157
+ XCTAssertTrue([device fb_goToHomescreenWithError:&error]);
158
+ XCTAssertTrue([[[FBRunLoopSpinner new] timeout:5] spinUntilTrue:^BOOL{
159
+ XCUIApplicationState state = self.testedApplication.state;
160
+ return state == XCUIApplicationStateRunningBackground
161
+ || state == XCUIApplicationStateRunningBackgroundSuspended;
162
+ }]);
163
+ [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:duration.doubleValue]];
164
+ XCUIApplicationState state = self.testedApplication.state;
165
+ XCTAssertTrue(state == XCUIApplicationStateRunningBackground
166
+ || state == XCUIApplicationStateRunningBackgroundSuspended,
167
+ @"Expected a background app before activation (angle %@, duration %@), got %lu",
168
+ angle, duration, (unsigned long)state);
169
+ if (mode == 1) {
170
+ [session activateApplicationWithBundleId:self.testedApplication.bundleID];
171
+ } else if (mode == 2) {
172
+ [session launchApplicationWithBundleId:self.testedApplication.bundleID
173
+ shouldWaitForQuiescence:nil arguments:nil environment:nil];
174
+ } else {
175
+ XCTAssertNil([FBSessionCommands activateBackgroundApplication:self.testedApplication
176
+ bundleID:self.testedApplication.bundleID
177
+ initialUrl:nil]);
178
+ }
179
+ } else {
180
+ XCTAssertTrue([self.testedApplication fb_deactivateWithDuration:duration.doubleValue error:&error]);
181
+ }
182
+ XCTAssertNil(error);
183
+ // Existence/foreground checks alone miss lost input. Do not add a
184
+ // delay or retry: this first tap must actually open the alerts page.
185
+ [self.testedApplication.buttons[@"Alerts"] tap];
186
+ XCTAssertTrue([self.testedApplication.buttons[FBShowAlertButtonName] waitForExistenceWithTimeout:2]);
187
+ }
188
+ }
189
+ } @finally {
190
+ [session kill];
191
+ BOOL restored = nil == originalAngle || setAngleAndWait(originalAngle);
192
+ FBConfiguration.sharedInstance.currentDisplayId = originalDisplay;
193
+ XCTAssertTrue(restored, @"Hinge did not return to %@ degrees", originalAngle);
194
+ }
195
+ }
196
+
86
197
  - (void)testActiveApplication
87
198
  {
88
199
  XCUIApplication *systemApp = XCUIApplication.fb_systemApplication;
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  #import <XCTest/XCTest.h>
10
+ #import <math.h>
10
11
 
11
12
  #import "FBIntegrationTestCase.h"
12
13
  #import "FBImageUtils.h"
@@ -15,6 +16,7 @@
15
16
  #import "XCUIApplication.h"
16
17
  #import "XCUIApplication+FBHelpers.h"
17
18
  #import "XCUIDevice+FBHelpers.h"
19
+ #import "XCUIDevice+FBHinge.h"
18
20
  #import "XCUIDevice+FBRotation.h"
19
21
  #import "XCUIScreen.h"
20
22
 
@@ -104,6 +106,53 @@
104
106
  FBAssertWaitTillBecomesTrue([XCUIApplication fb_activeApplication].icons[@"Safari"].exists);
105
107
  }
106
108
 
109
+ - (void)testHomeButtonBackgroundsApplication
110
+ {
111
+ XCUIDevice *device = XCUIDevice.sharedDevice;
112
+ BOOL hasHinge = NO;
113
+ #if TARGET_OS_SIMULATOR
114
+ hasHinge = device.fb_canAttemptSimulatedHingeAngleInjection;
115
+ #endif
116
+ NSNumber *originalAngle = hasHinge ? [device fb_getSimulatedHingeAngle:nil] : nil;
117
+ if (hasHinge && nil == originalAngle) {
118
+ XCTFail(@"Cannot read the original hinge angle");
119
+ return;
120
+ }
121
+ BOOL (^setAngleAndWait)(NSNumber *) = ^BOOL(NSNumber *angle) {
122
+ return [device fb_setSimulatedHingeAngle:angle.doubleValue error:nil]
123
+ && [[[FBRunLoopSpinner new] timeout:5] spinUntilTrue:^BOOL{
124
+ NSNumber *reading = [device fb_getSimulatedHingeAngle:nil];
125
+ return nil != reading && fabs(reading.doubleValue - angle.doubleValue) < 1;
126
+ }];
127
+ };
128
+ @try {
129
+ for (NSNumber *angle in (hasHinge ? @[@0, @180] : @[@0])) {
130
+ if (hasHinge) {
131
+ if (!setAngleAndWait(angle)) {
132
+ XCTFail(@"Hinge angle did not reach %@", angle);
133
+ return;
134
+ }
135
+ }
136
+ for (NSUInteger attempt = 0; attempt < 2; attempt++) {
137
+ [self.testedApplication activate];
138
+ XCTAssertEqual(self.testedApplication.state, XCUIApplicationStateRunningForeground);
139
+ NSError *error = nil;
140
+ XCTAssertTrue([device fb_pressButton:@"home" forDuration:nil error:&error]);
141
+ XCTAssertNil(error);
142
+ XCTAssertTrue([[[FBRunLoopSpinner new] timeout:5] spinUntilTrue:^BOOL{
143
+ return self.testedApplication.state == XCUIApplicationStateRunningBackground
144
+ || self.testedApplication.state == XCUIApplicationStateRunningBackgroundSuspended;
145
+ }]);
146
+ XCTAssertTrue([XCUIApplication.fb_activeApplication.bundleID isEqualToString:@"com.apple.springboard"]);
147
+ }
148
+ }
149
+ } @finally {
150
+ if (nil != originalAngle) {
151
+ XCTAssertTrue(setAngleAndWait(originalAngle));
152
+ }
153
+ }
154
+ }
155
+
107
156
  - (void)testLockUnlockScreen
108
157
  {
109
158
  XCTAssertFalse([[XCUIDevice sharedDevice] fb_isScreenLocked]);
@@ -116,6 +165,54 @@
116
165
  XCTAssertNil(error);
117
166
  }
118
167
 
168
+ - (void)testRepeatedLockUnlockAcrossHingePoses
169
+ {
170
+ XCTSkipIf(self.class.isRunningInCI, @"Repeated lock/unlock is unreliable on slow CI hosts");
171
+ XCUIDevice *device = XCUIDevice.sharedDevice;
172
+ BOOL hasHinge = NO;
173
+ #if TARGET_OS_SIMULATOR
174
+ hasHinge = device.fb_canAttemptSimulatedHingeAngleInjection;
175
+ #endif
176
+ NSNumber *originalAngle = hasHinge ? [device fb_getSimulatedHingeAngle:nil] : nil;
177
+ if (hasHinge) {
178
+ XCTAssertNotNil(originalAngle);
179
+ }
180
+ BOOL (^setAngleAndWait)(NSNumber *) = ^BOOL(NSNumber *angle) {
181
+ return [device fb_setSimulatedHingeAngle:angle.doubleValue error:nil]
182
+ && [[[FBRunLoopSpinner new] timeout:5] spinUntilTrue:^BOOL{
183
+ NSNumber *reading = [device fb_getSimulatedHingeAngle:nil];
184
+ return nil != reading && fabs(reading.doubleValue - angle.doubleValue) < 1;
185
+ }];
186
+ };
187
+ @try {
188
+ for (NSNumber *angle in (hasHinge ? @[@0, @90, @180] : @[@0])) {
189
+ if (hasHinge) {
190
+ XCTAssertTrue(setAngleAndWait(angle));
191
+ }
192
+ for (NSUInteger attempt = 0; attempt < 3; attempt++) {
193
+ XCTAssertFalse(device.fb_isScreenLocked);
194
+ NSError *error = nil;
195
+ XCTAssertTrue([device fb_lockScreen:&error]);
196
+ XCTAssertNil(error);
197
+ XCTAssertTrue(device.fb_isScreenLocked);
198
+ // Repeating lock must not toggle the screen back to unlocked.
199
+ XCTAssertTrue([device fb_lockScreen:&error]);
200
+ XCTAssertTrue(device.fb_isScreenLocked);
201
+ XCTAssertTrue([device fb_unlockScreen:&error]);
202
+ XCTAssertNil(error);
203
+ XCTAssertFalse(device.fb_isScreenLocked);
204
+ XCTAssertTrue([device fb_unlockScreen:&error]);
205
+ XCTAssertFalse(device.fb_isScreenLocked);
206
+ }
207
+ }
208
+ } @finally {
209
+ [device fb_unlockScreen:nil];
210
+ if (nil != originalAngle) {
211
+ XCTAssertTrue(setAngleAndWait(originalAngle));
212
+ }
213
+ }
214
+ }
215
+
119
216
  - (void)testUrlSchemeActivation
120
217
  {
121
218
  if (SYSTEM_VERSION_LESS_THAN(@"16.4")) {
@@ -10,8 +10,13 @@
10
10
  #import <UIKit/UIKit.h>
11
11
  #import "FBIntegrationTestCase.h"
12
12
  #import "XCUIDevice+FBRotation.h"
13
+ #import "XCUIDevice+FBHinge.h"
14
+ #import "FBConfiguration.h"
15
+ #import "FBScreen.h"
16
+ #import "FBSession.h"
13
17
 
14
18
  @interface XCUIDeviceRotationTests : FBIntegrationTestCase
19
+ @property (nonatomic, strong) FBSession *session;
15
20
 
16
21
  @end
17
22
 
@@ -21,11 +26,13 @@
21
26
  {
22
27
  [super setUp];
23
28
  [self launchApplication];
29
+ self.session = [FBSession initWithApplication:self.testedApplication];
24
30
  }
25
31
 
26
32
  - (void)tearDown
27
33
  {
28
34
  [self resetOrientation];
35
+ [self.session kill];
29
36
  [super tearDown];
30
37
  }
31
38
 
@@ -102,4 +109,50 @@
102
109
  XCTAssertEqual(currentRotation, [XCUIDevice sharedDevice].orientation, @"Device doesnt support tilt, should be at previous orientation");
103
110
  }
104
111
 
112
+ - (void)testRotationOnSelectedDuoDisplay
113
+ {
114
+ #if TARGET_OS_SIMULATOR
115
+ XCUIDevice *device = XCUIDevice.sharedDevice;
116
+ XCTSkipIf(!device.fb_supportsHingeAngleReading || !device.fb_canAttemptSimulatedHingeAngleInjection,
117
+ @"Requires hinge angle reading and injection support");
118
+ NSNumber *originalDisplay = FBConfiguration.sharedInstance.currentDisplayId;
119
+ NSNumber *originalAngle = [device fb_getSimulatedHingeAngle:nil];
120
+ XCTAssertNotNil(originalAngle);
121
+ @try {
122
+ for (NSNumber *angle in @[@0, @90, @180]) {
123
+ XCTAssertTrue([device fb_setSimulatedHingeAngle:angle.doubleValue error:nil]);
124
+ [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
125
+ for (NSDictionary *screen in [FBScreen screensWithError:nil]) {
126
+ if ([screen[@"isMain"] boolValue] == (angle.intValue == 0)) {
127
+ FBConfiguration.sharedInstance.currentDisplayId = screen[@"displayId"];
128
+ }
129
+ }
130
+ [self.testedApplication activate];
131
+ NSArray *orientations = @[@(UIDeviceOrientationLandscapeLeft),
132
+ @(UIDeviceOrientationLandscapeRight),
133
+ @(UIDeviceOrientationPortrait)];
134
+ NSArray *labels = @[@"LandscapeRight", @"LandscapeLeft", @"Portrait"];
135
+ for (NSUInteger i = 0; i < orientations.count; i++) {
136
+ UIDeviceOrientation orientation = [orientations[i] integerValue];
137
+ XCTAssertTrue([device fb_setDeviceInterfaceOrientation:orientation]);
138
+ XCTAssertTrue([self.testedApplication.staticTexts[labels[i]] waitForExistenceWithTimeout:5]);
139
+ XCTAssertEqual(device.orientation, orientation);
140
+ }
141
+ BOOL rotated = [device fb_setDeviceRotation:@{@"x": @0, @"y": @0, @"z": @270}];
142
+ XCTAssertTrue(rotated);
143
+ XCTAssertTrue(self.testedApplication.staticTexts[@"LandscapeRight"].exists);
144
+ }
145
+ } @finally {
146
+ [device fb_setSimulatedHingeAngle:0 error:nil];
147
+ [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
148
+ FBConfiguration.sharedInstance.currentDisplayId = nil;
149
+ [device fb_setDeviceInterfaceOrientation:UIDeviceOrientationPortrait];
150
+ if (nil != originalAngle) {
151
+ [device fb_setSimulatedHingeAngle:originalAngle.doubleValue error:nil];
152
+ }
153
+ FBConfiguration.sharedInstance.currentDisplayId = originalDisplay;
154
+ }
155
+ #endif
156
+ }
157
+
105
158
  @end
@@ -9,6 +9,7 @@
9
9
  #import <XCTest/XCTest.h>
10
10
 
11
11
  #import "FBIntegrationTestCase.h"
12
+ #import "FBConfiguration.h"
12
13
  #import "FBElementUtils.h"
13
14
  #import "FBExceptions.h"
14
15
  #import "FBTestMacros.h"
@@ -558,3 +559,115 @@
558
559
  }
559
560
 
560
561
  @end
562
+
563
+
564
+ @interface XCUIElementFBFindTests_IdentifierSemantics : FBIntegrationTestCase
565
+ @end
566
+
567
+ @implementation XCUIElementFBFindTests_IdentifierSemantics
568
+
569
+ - (void)setUp
570
+ {
571
+ [super setUp];
572
+ self.testedApplication.launchArguments = @[@"--identifier-lookup-test"];
573
+ [self.testedApplication launch];
574
+ XCTAssertTrue([self.testedApplication.otherElements[@"IdentifierLookupFixture"] waitForExistenceWithTimeout:5]);
575
+ }
576
+
577
+ - (void)tearDown
578
+ {
579
+ [self.testedApplication terminate];
580
+ [super tearDown];
581
+ }
582
+
583
+ - (void)testIdentifierTakesPrecedenceOverLabel
584
+ {
585
+ NSArray<XCUIElement *> *matches = [self.testedApplication fb_descendantsMatchingIdentifier:@"something"
586
+ shouldReturnAfterFirstMatch:NO];
587
+ XCTAssertEqual(matches.count, 4);
588
+ XCTAssertEqualObjects([matches valueForKey:@"label"], (@[@"other label", @"something", @"something", @"something"]));
589
+ XCTAssertEqual([self.testedApplication fb_descendantsMatchingIdentifier:@"different" shouldReturnAfterFirstMatch:NO].count, 1);
590
+ // A matching label must not override a different, nonempty identifier.
591
+ XCTAssertEqual([self.testedApplication fb_descendantsMatchingIdentifier:@"other label" shouldReturnAfterFirstMatch:NO].count, 0);
592
+ }
593
+
594
+ - (void)testEmptyIdentifierDoesNotMatchEmptyAccessibilityId
595
+ {
596
+ // The fixture has an empty identifier with a nonempty label at index 2,
597
+ // and an empty identifier with an empty label at index 5.
598
+ XCUIElement *labelFallback = [self.testedApplication.staticTexts elementBoundByIndex:2];
599
+ XCUIElement *unnamed = [self.testedApplication.staticTexts elementBoundByIndex:5];
600
+ XCTAssertEqualObjects(labelFallback.identifier, @"");
601
+ XCTAssertEqualObjects(labelFallback.label, @"something");
602
+ XCTAssertEqualObjects(unnamed.identifier, @"");
603
+ XCTAssertEqualObjects(unnamed.label, @"");
604
+
605
+ // Regression guard for "identifier != nil AND identifier != ''": the old
606
+ // wdName lookup used the nonempty label, or nil when both fields were empty.
607
+ // Thus an empty query matched neither element. Checking only identifier != nil
608
+ // would instead let identifier == @"" match both, changing existing behavior.
609
+ BOOL previousFirstMatch = FBConfiguration.sharedInstance.useFirstMatch;
610
+ @try {
611
+ for (NSNumber *useFirstMatch in @[@NO, @YES]) {
612
+ FBConfiguration.sharedInstance.useFirstMatch = useFirstMatch.boolValue;
613
+ for (NSNumber *firstMatch in @[@NO, @YES]) {
614
+ XCTAssertEqual([self.testedApplication fb_descendantsMatchingIdentifier:@""
615
+ shouldReturnAfterFirstMatch:firstMatch.boolValue].count, 0,
616
+ @"An empty accessibility ID must not match empty identifiers, even when they are non-nil");
617
+ }
618
+ }
619
+ } @finally {
620
+ FBConfiguration.sharedInstance.useFirstMatch = previousFirstMatch;
621
+ }
622
+ }
623
+
624
+ - (void)testMissingQuotedAndUnicodeIdentifiers
625
+ {
626
+ for (NSNumber *firstMatch in @[@NO, @YES]) {
627
+ XCTAssertEqual([self.testedApplication fb_descendantsMatchingIdentifier:@"missing-target" shouldReturnAfterFirstMatch:firstMatch.boolValue].count, 0);
628
+ XCTAssertEqual([self.testedApplication fb_descendantsMatchingIdentifier:@"quote'\"é" shouldReturnAfterFirstMatch:firstMatch.boolValue].count, 1);
629
+ XCTAssertEqual([self.testedApplication fb_descendantsMatchingIdentifier:@"日本語" shouldReturnAfterFirstMatch:firstMatch.boolValue].count, 1);
630
+ }
631
+ }
632
+
633
+ - (void)testFirstMatchAndParentScopeWithBothBindings
634
+ {
635
+ BOOL previousBinding = FBConfiguration.sharedInstance.boundElementsByIndex;
636
+ @try {
637
+ for (NSNumber *indexed in @[@NO, @YES]) {
638
+ FBConfiguration.sharedInstance.boundElementsByIndex = indexed.boolValue;
639
+ // Use the element returned by WDA lookup, as the element-scoped endpoint does.
640
+ XCUIElement *parent = [self.testedApplication fb_descendantsMatchingIdentifier:@"IdentifierLookupFixture"
641
+ shouldReturnAfterFirstMatch:YES].firstObject;
642
+ XCTAssertNotNil(parent);
643
+ // The HTTP endpoint resolves the parent when registering its element ID.
644
+ XCTAssertNotNil(parent.fb_uid);
645
+ NSArray<XCUIElement *> *matches = [parent fb_descendantsMatchingIdentifier:@"something" shouldReturnAfterFirstMatch:NO];
646
+ NSArray<XCUIElement *> *first = [parent fb_descendantsMatchingIdentifier:@"something" shouldReturnAfterFirstMatch:YES];
647
+ XCTAssertEqual(matches.count, 4);
648
+ XCTAssertEqual(first.count, 1);
649
+ XCTAssertEqualObjects(first.firstObject.fb_uid, matches.firstObject.fb_uid);
650
+ NSArray<XCUIElement *> *selfMatches = [parent fb_descendantsMatchingIdentifier:@"IdentifierLookupFixture" shouldReturnAfterFirstMatch:NO];
651
+ XCTAssertEqual(selfMatches.count, 1);
652
+ XCTAssertEqualObjects(selfMatches.firstObject.fb_uid, parent.fb_uid);
653
+ }
654
+ } @finally {
655
+ FBConfiguration.sharedInstance.boundElementsByIndex = previousBinding;
656
+ }
657
+ }
658
+
659
+ - (void)testIdentifierChangesAreObserved
660
+ {
661
+ NSArray<XCUIElement *> *before = [self.testedApplication fb_descendantsMatchingIdentifier:@"something" shouldReturnAfterFirstMatch:NO];
662
+ XCTAssertEqual(before.count, 4);
663
+ NSArray<NSString *> *beforeIds = [before valueForKey:@"fb_uid"];
664
+ [self.testedApplication.buttons[@"mutateIdentifier"] tap];
665
+ NSArray<XCUIElement *> *after = [self.testedApplication fb_descendantsMatchingIdentifier:@"something" shouldReturnAfterFirstMatch:NO];
666
+ NSArray<XCUIElement *> *changed = [self.testedApplication fb_descendantsMatchingIdentifier:@"changed" shouldReturnAfterFirstMatch:NO];
667
+ XCTAssertEqual(after.count, 3);
668
+ XCTAssertEqual(changed.count, 1);
669
+ XCTAssertEqualObjects([after valueForKey:@"fb_uid"], [beforeIds subarrayWithRange:NSMakeRange(1, 3)]);
670
+ XCTAssertEqualObjects(changed.firstObject.fb_uid, beforeIds.firstObject);
671
+ }
672
+
673
+ @end
@@ -7,6 +7,8 @@
7
7
  */
8
8
 
9
9
  #import <XCTest/XCTest.h>
10
+ #import "XCUIApplication+FBHelpers.h"
11
+ #import "XCUIDevice+FBHelpers.h"
10
12
 
11
13
  #if TARGET_OS_TV
12
14
 
@@ -79,6 +81,25 @@
79
81
  XCTAssertFalse(disabled.hasFocus);
80
82
  }
81
83
 
84
+ - (void)testHomeAndInputAfterDeactivate
85
+ {
86
+ NSError *error = nil;
87
+ XCTAssertTrue([XCUIDevice.sharedDevice fb_goToHomescreenWithError:&error], @"%@", error);
88
+ XCTAssertNotEqual(self.app.state, XCUIApplicationStateRunningForeground);
89
+ XCTAssertEqual(XCUIApplication.fb_systemApplication.state, XCUIApplicationStateRunningForeground);
90
+ [self.app activate];
91
+ for (NSNumber *duration in @[@0, @2]) {
92
+ [self.app launch];
93
+ XCTAssertTrue([self.app fb_deactivateWithDuration:duration.doubleValue error:&error], @"%@", error);
94
+ XCTAssertEqual(self.app.state, XCUIApplicationStateRunningForeground);
95
+ XCTAssertTrue([self.app.buttons[@"cell_1_2"] fb_selectWithError:&error], @"%@", error);
96
+ XCTNSPredicateExpectation *updated = [[XCTNSPredicateExpectation alloc]
97
+ initWithPredicate:[NSPredicate predicateWithFormat:@"label == %@", @"cell_1_2"]
98
+ object:self.app.staticTexts[@"lastSelectedLabel"]];
99
+ [self waitForExpectations:@[updated] timeout:5];
100
+ }
101
+ }
102
+
82
103
  @end
83
104
 
84
105
  #endif
@@ -34,4 +34,16 @@ final class WDAAppLifecycleIntegrationTests: WDAWatchInProcessTestCase {
34
34
  app.activate()
35
35
  XCTAssertEqual(app.state, .runningForeground)
36
36
  }
37
+ func testHomeAndDeactivate() throws {
38
+ try XCUIDevice.shared.fb_goToHomescreen()
39
+ XCTAssertNotEqual(app.state, .runningForeground)
40
+ try XCUIDevice.shared.fb_goToHomescreen()
41
+ XCTAssertNotEqual(app.state, .runningForeground)
42
+ for duration in [0.0, 2.0] {
43
+ app.activate()
44
+ try app.fb_deactivate(withDuration: duration)
45
+ XCTAssertEqual(app.state, .runningForeground)
46
+ }
47
+ }
48
+
37
49
  }