From f62afc372c123bdd8dd7bb493f653bb128144d24 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Mon, 13 Jan 2025 08:34:09 +0100 Subject: [PATCH] chore: Exclude element visibility and accessibility info from the accessibility audit details (#968) --- .../Categories/XCUIApplication+FBHelpers.m | 64 +++++++++++++++---- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m index 6cdae945b..2672e3272 100644 --- a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +++ b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m @@ -40,6 +40,13 @@ static NSString* const FBUnknownBundleId = @"unknown"; +static NSString* const FBExclusionAttributeFrame = @"frame"; +static NSString* const FBExclusionAttributeEnabled = @"enabled"; +static NSString* const FBExclusionAttributeVisible = @"visible"; +static NSString* const FBExclusionAttributeAccessible = @"accessible"; +static NSString* const FBExclusionAttributeFocused = @"focused"; + + _Nullable id extractIssueProperty(id issue, NSString *propertyName) { SEL selector = NSSelectorFromString(propertyName); NSMethodSignature *methodSignature = [issue methodSignatureForSelector:selector]; @@ -88,6 +95,17 @@ _Nullable id extractIssueProperty(id issue, NSString *propertyName) { return result; } +NSDictionary *customExclusionAttributesMap(void) { + static dispatch_once_t onceToken; + static NSDictionary *result; + dispatch_once(&onceToken, ^{ + result = @{ + FBExclusionAttributeVisible: FB_XCAXAIsVisibleAttributeName, + FBExclusionAttributeAccessible: FB_XCAXAIsElementAttributeName, + }; + }); + return result; +} @implementation XCUIApplication (FBHelpers) @@ -156,12 +174,26 @@ - (NSDictionary *)fb_tree return [self fb_tree:nil]; } -- (NSDictionary *)fb_tree:(nullable NSSet *) excludedAttributes +- (NSDictionary *)fb_tree:(nullable NSSet *)excludedAttributes { - id snapshot = self.fb_isResolvedFromCache.boolValue - ? self.lastSnapshot - : [self fb_snapshotWithAllAttributesAndMaxDepth:nil]; - return [self.class dictionaryForElement:snapshot recursive:YES excludedAttributes:excludedAttributes]; + // This set includes XCTest-specific internal attribute names, + // while the `excludedAttributes` arg contains human-readable ones + NSMutableSet* includedAttributeNames = [NSMutableSet setWithArray:FBCustomAttributeNames()]; + [includedAttributeNames addObjectsFromArray:FBStandardAttributeNames()]; + if (nil != excludedAttributes) { + for (NSString *attr in excludedAttributes) { + NSString *mappedName = [customExclusionAttributesMap() objectForKey:attr]; + if (nil != mappedName) { + [includedAttributeNames removeObject:attr]; + } + } + } + id snapshot = nil == excludedAttributes + ? [self fb_snapshotWithAllAttributesAndMaxDepth:nil] + : [self fb_snapshotWithAttributes:[includedAttributeNames allObjects] maxDepth:nil]; + return [self.class dictionaryForElement:snapshot + recursive:YES + excludedAttributes:excludedAttributes]; } - (NSDictionary *)fb_accessibilityTree @@ -174,7 +206,7 @@ - (NSDictionary *)fb_accessibilityTree + (NSDictionary *)dictionaryForElement:(id)snapshot recursive:(BOOL)recursive - excludedAttributes:(nullable NSSet *) excludedAttributes + excludedAttributes:(nullable NSSet *)excludedAttributes { NSMutableDictionary *info = [[NSMutableDictionary alloc] init]; info[@"type"] = [FBElementTypeTransformer shortStringWithElementType:snapshot.elementType]; @@ -186,19 +218,19 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot info[@"rect"] = wrappedSnapshot.wdRect; NSDictionary *attributeBlocks = @{ - @"frame": ^{ + FBExclusionAttributeFrame: ^{ return NSStringFromCGRect(wrappedSnapshot.wdFrame); }, - @"enabled": ^{ + FBExclusionAttributeEnabled: ^{ return [@([wrappedSnapshot isWDEnabled]) stringValue]; }, - @"visible": ^{ + FBExclusionAttributeVisible: ^{ return [@([wrappedSnapshot isWDVisible]) stringValue]; }, - @"accessible": ^{ + FBExclusionAttributeAccessible: ^{ return [@([wrappedSnapshot isWDAccessible]) stringValue]; }, - @"focused": ^{ + FBExclusionAttributeFocused: ^{ return [@([wrappedSnapshot isWDFocused]) stringValue]; } }; @@ -206,7 +238,7 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot for (NSString *key in attributeBlocks) { if (excludedAttributes == nil || ![excludedAttributes containsObject:key]) { NSString *value = ((NSString * (^)(void))attributeBlocks[key])(); - if ([key isEqualToString:@"frame"]) { + if ([key isEqualToString:FBExclusionAttributeFrame]) { info[key] = value; } else { info[[NSString stringWithFormat:@"is%@", [key capitalizedString]]] = value; @@ -396,6 +428,8 @@ - (BOOL)fb_dismissKeyboardWithKeyNames:(nullable NSArray *)keyNames return nil; } + // These custom attributes could take too long to fetch, thus excluded + NSSet *customAttributesToExclude = [NSSet setWithArray:[customExclusionAttributesMap() allKeys]]; NSMutableArray *resultArray = [NSMutableArray array]; NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; @@ -411,9 +445,11 @@ - (BOOL)fb_dismissKeyboardWithKeyNames:(nullable NSArray *)keyNames id extractedElement = extractIssueProperty(issue, @"element"); - id elementSnapshot = [extractedElement fb_takeSnapshot]; + id elementSnapshot = [extractedElement fb_cachedSnapshot] ?: [extractedElement fb_takeSnapshot]; NSDictionary *elementAttributes = elementSnapshot - ? [self.class dictionaryForElement:elementSnapshot recursive:NO excludedAttributes:nil] + ? [self.class dictionaryForElement:elementSnapshot + recursive:NO + excludedAttributes:customAttributesToExclude] : @{}; [resultArray addObject:@{