diff --git a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m index 1737ff7af..677178964 100644 --- a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +++ b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m @@ -48,6 +48,7 @@ static NSString* const FBExclusionAttributeFocused = @"focused"; static NSString* const FBExclusionAttributePlaceholderValue = @"placeholderValue"; static NSString* const FBExclusionAttributeNativeFrame = @"nativeFrame"; +static NSString* const FBExclusionAttributeHittable = @"hittable"; _Nullable id extractIssueProperty(id issue, NSString *propertyName) { SEL selector = NSSelectorFromString(propertyName); @@ -205,6 +206,7 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot NSDictionary *attributeBlocks = [self fb_attributeBlockMapForWrappedSnapshot:wrappedSnapshot]; + NSSet *nonPrefixedKeys = [NSSet setWithObjects: FBExclusionAttributeFrame, FBExclusionAttributePlaceholderValue, @@ -243,7 +245,6 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot // Helper used by `dictionaryForElement:` to assemble attribute value blocks, // including both common attributes and conditionally included ones like placeholderValue. + (NSDictionary *)fb_attributeBlockMapForWrappedSnapshot:(FBXCElementSnapshotWrapper *)wrappedSnapshot - { // Base attributes common to every element NSMutableDictionary *blocks = @@ -277,6 +278,10 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot }; } + blocks[FBExclusionAttributeHittable] = ^{ + return [@([wrappedSnapshot isWDResolvedHittable]) stringValue]; + }; + return [blocks copy]; } diff --git a/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m b/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m index d5860113a..612ae1806 100644 --- a/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m +++ b/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m @@ -22,6 +22,7 @@ #import "FBElementUtils.h" #import "XCTestPrivateSymbols.h" #import "XCUIHitPointResult.h" +#import "XCUIApplication+FBHelpers.h" #define BROKEN_RECT CGRectMake(-1, -1, 0, 0) @@ -242,6 +243,14 @@ - (BOOL)isWDHittable return nil == result ? NO : result.hittable; } +- (BOOL)isWDResolvedHittable +{ + // Snapshot-based estimation of XCUIElement.hittable using accessibility, visibility, and hit point. + // Does not rely on live XCUIElement resolution. + // See discussion: https://github.com/appium/WebDriverAgent/pull/1021 + return self.isWDAccessible && self.isWDHittable && self.isWDVisible; +} + - (NSDictionary *)wdRect { CGRect frame = self.wdFrame; diff --git a/WebDriverAgentLib/Routing/FBElement.h b/WebDriverAgentLib/Routing/FBElement.h index eed55b8b7..a418cac86 100644 --- a/WebDriverAgentLib/Routing/FBElement.h +++ b/WebDriverAgentLib/Routing/FBElement.h @@ -59,9 +59,15 @@ NS_ASSUME_NONNULL_BEGIN /*! Whether element is focused */ @property (nonatomic, readonly, getter = isWDFocused) BOOL wdFocused; -/*! Whether element is hittable */ +/*! Whether the element is considered hittable based on snapshot hit point */ @property (nonatomic, readonly, getter = isWDHittable) BOOL wdHittable; +/*! + * Returns a snapshot-based estimation of hittability + * using accessibility, visibility, and hit point heuristics. + */ +@property (nonatomic, readonly, getter = isWDResolvedHittable) BOOL wdResolvedHittable; + /*! Element's index relatively to its parent. Starts from zero */ @property (nonatomic, readonly) NSUInteger wdIndex; diff --git a/WebDriverAgentTests/IntegrationTests/FBElementAttributeTests.m b/WebDriverAgentTests/IntegrationTests/FBElementAttributeTests.m index 1591d12af..5375d644a 100644 --- a/WebDriverAgentTests/IntegrationTests/FBElementAttributeTests.m +++ b/WebDriverAgentTests/IntegrationTests/FBElementAttributeTests.m @@ -192,4 +192,15 @@ - (void)testTextViewAttributes XCTAssertEqualObjects(element.wdValue, @"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901"); } +- (void)testNativeHittableAttribute +{ + XCUIElement *button = self.testedApplication.buttons[@"Button"]; + XCTAssertTrue(button.exists); + + id snapshot = [button fb_standardSnapshot]; + FBXCElementSnapshotWrapper *wrapped = [FBXCElementSnapshotWrapper ensureWrapped:snapshot]; + + XCTAssertEqual([wrapped isWDResolvedHittable], button.hittable); +} + @end