Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,16 @@ - (BOOL)fb_deactivateWithDuration:(NSTimeInterval)duration error:(NSError **)err
}
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:MAX(duration, .0)]];
[self activate];
#if !TARGET_OS_TV && !TARGET_OS_WATCH
Comment thread
mykola-mokhnach marked this conversation as resolved.
Outdated
// With Xcode 27.1, reproduced on Duo/iOS 27.1 and iPhone 17e/iOS 27.0:
// the app can be idle while SpringBoard's resume transition is still running.
// Wait before the next command resolves element frames; waiting inside tap
// synthesis can be too late. See appium/appium#22857. The affected version
// range is unknown. Remove when testInputAfterDeactivate passes without this
// on supported environments. This wait does not prove app-specific readiness.
[XCUIApplication.fb_systemApplication fb_waitUntilStableWithTimeout:
FBConfiguration.sharedInstance.animationCoolOffTimeout];
#endif
return YES;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#import "XCUIApplication+FBHelpers.h"
#import "XCUIElement+FBIsVisible.h"
#import "FBXCodeCompatibility.h"
#import "FBConfiguration.h"
#import "FBScreen.h"
#import "XCUIDevice+FBHinge.h"

void calculateMaxTreeDepth(NSDictionary *tree, NSNumber *currentDepth, NSNumber** maxDepth) {
if (nil == maxDepth) {
Expand Down Expand Up @@ -83,6 +86,48 @@ - (void)testDeactivateApplication
XCTAssertTrue(self.testedApplication.buttons[@"Alerts"].exists);
}

- (void)testInputAfterDeactivate
{
XCUIDevice *device = XCUIDevice.sharedDevice;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we skip this test in CI?

BOOL canFold = NO;
#if TARGET_OS_SIMULATOR
canFold = device.fb_canAttemptSimulatedHingeAngleInjection;
#endif
NSNumber *originalAngle = canFold ? [device fb_getSimulatedHingeAngle:nil] : nil;
NSNumber *originalDisplay = FBConfiguration.sharedInstance.currentDisplayId;
if (canFold) {
XCTAssertNotNil(originalAngle);
}
@try {
for (NSNumber *angle in (canFold ? @[@0, @90, @180] : @[@0])) {
if (canFold) {
XCTAssertTrue([device fb_setSimulatedHingeAngle:angle.doubleValue error:nil]);
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
Comment thread
KazuCocoa marked this conversation as resolved.
Outdated
for (NSDictionary *screen in [FBScreen screensWithError:nil]) {
if ([screen[@"isMain"] boolValue] == (angle.intValue == 0)) {
FBConfiguration.sharedInstance.currentDisplayId = screen[@"displayId"];
}
}
}
for (NSNumber *duration in @[@0, @2]) {
[self launchApplication];
NSError *error = nil;
XCTAssertTrue([self.testedApplication fb_deactivateWithDuration:duration.doubleValue error:&error]);
XCTAssertNil(error);
// Existence/foreground checks alone miss lost input. Do not add a
// delay or retry: this first tap must actually open the alerts page.
[self.testedApplication.buttons[@"Alerts"] tap];
XCTAssertTrue([self.testedApplication.buttons[FBShowAlertButtonName] waitForExistenceWithTimeout:2]);
}
}
} @finally {
if (nil != originalAngle) {
[device fb_setSimulatedHingeAngle:originalAngle.doubleValue error:nil];
}
FBConfiguration.sharedInstance.currentDisplayId = originalDisplay;
}
}

- (void)testActiveApplication
{
XCUIApplication *systemApp = XCUIApplication.fb_systemApplication;
Expand Down
17 changes: 17 additions & 0 deletions packages/webdriveragent/docs/multiple-displays.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,23 @@ before resolving coordinates, in addition to keeping its pose stable during a
command. This observation does not imply that every missed gesture is an
XCTest defect.

## Input after background/resume on Duo

On the Duo simulator with Xcode 27.1 / iOS 27.1, an immediate tap after
backgrounding and reactivating an app can return successfully without reaching
the app. Foreground state, element existence, and `hittable` do not reliably
establish input readiness. Waiting for the app itself to idle was insufficient.

The same delivery failure also reproduced on an iPhone 17e / iOS 27.0 simulator.
For iOS background-app commands, WDA waits for SpringBoard to become idle,
including animations, after reactivation.
This happens before a subsequent command resolves its target element; waiting
later during tap synthesis did not prevent the reproduced missed taps. The wait
uses `animationCoolOffTimeout` and is skipped when that setting is zero. This
workaround is verified on the simulator; physical-device behavior remains untested.
It does not retry taps or restart the app, and does not cover independent calls
to `activateApp`. See [Appium #22857](https://github.com/appium/appium/issues/22857).

## Validation and feedback

Duo display behavior has been tested on the iPhone Duo simulator with Xcode 27.1
Expand Down
Loading