diff --git a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h index 3ef66423b..54d4bd281 100644 --- a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +++ b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h @@ -31,6 +31,12 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSDictionary *)fb_tree; +/** + @param excludedAttributes Set of possible attributes to be excluded i.e frame, enabled, visible, accessible, focused. If set to nil or an empty array then no attributes will be excluded from the resulting JSON + @return application elements tree in form of nested dictionaries + */ +- (NSDictionary *)fb_tree:(nullable NSSet *) excludedAttributes; + /** Return application elements accessibility tree in form of nested dictionaries */ diff --git a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m index c3c99e6f5..6cdae945b 100644 --- a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +++ b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m @@ -152,11 +152,16 @@ - (BOOL)fb_deactivateWithDuration:(NSTimeInterval)duration error:(NSError **)err } - (NSDictionary *)fb_tree +{ + return [self fb_tree:nil]; +} + +- (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]; + return [self.class dictionaryForElement:snapshot recursive:YES excludedAttributes:excludedAttributes]; } - (NSDictionary *)fb_accessibilityTree @@ -167,7 +172,9 @@ - (NSDictionary *)fb_accessibilityTree return [self.class accessibilityInfoForElement:snapshot]; } -+ (NSDictionary *)dictionaryForElement:(id)snapshot recursive:(BOOL)recursive ++ (NSDictionary *)dictionaryForElement:(id)snapshot + recursive:(BOOL)recursive + excludedAttributes:(nullable NSSet *) excludedAttributes { NSMutableDictionary *info = [[NSMutableDictionary alloc] init]; info[@"type"] = [FBElementTypeTransformer shortStringWithElementType:snapshot.elementType]; @@ -177,11 +184,35 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot recursi info[@"value"] = FBValueOrNull(wrappedSnapshot.wdValue); info[@"label"] = FBValueOrNull(wrappedSnapshot.wdLabel); info[@"rect"] = wrappedSnapshot.wdRect; - info[@"frame"] = NSStringFromCGRect(wrappedSnapshot.wdFrame); - info[@"isEnabled"] = [@([wrappedSnapshot isWDEnabled]) stringValue]; - info[@"isVisible"] = [@([wrappedSnapshot isWDVisible]) stringValue]; - info[@"isAccessible"] = [@([wrappedSnapshot isWDAccessible]) stringValue]; - info[@"isFocused"] = [@([wrappedSnapshot isWDFocused]) stringValue]; + + NSDictionary *attributeBlocks = @{ + @"frame": ^{ + return NSStringFromCGRect(wrappedSnapshot.wdFrame); + }, + @"enabled": ^{ + return [@([wrappedSnapshot isWDEnabled]) stringValue]; + }, + @"visible": ^{ + return [@([wrappedSnapshot isWDVisible]) stringValue]; + }, + @"accessible": ^{ + return [@([wrappedSnapshot isWDAccessible]) stringValue]; + }, + @"focused": ^{ + return [@([wrappedSnapshot isWDFocused]) stringValue]; + } + }; + + for (NSString *key in attributeBlocks) { + if (excludedAttributes == nil || ![excludedAttributes containsObject:key]) { + NSString *value = ((NSString * (^)(void))attributeBlocks[key])(); + if ([key isEqualToString:@"frame"]) { + info[key] = value; + } else { + info[[NSString stringWithFormat:@"is%@", [key capitalizedString]]] = value; + } + } + } if (!recursive) { return info.copy; @@ -191,7 +222,9 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot recursi if ([childElements count]) { info[@"children"] = [[NSMutableArray alloc] init]; for (id childSnapshot in childElements) { - [info[@"children"] addObject:[self dictionaryForElement:childSnapshot recursive:YES]]; + [info[@"children"] addObject:[self dictionaryForElement:childSnapshot + recursive:YES + excludedAttributes:excludedAttributes]]; } } return info; @@ -379,7 +412,9 @@ - (BOOL)fb_dismissKeyboardWithKeyNames:(nullable NSArray *)keyNames id extractedElement = extractIssueProperty(issue, @"element"); id elementSnapshot = [extractedElement fb_takeSnapshot]; - NSDictionary *elementAttributes = elementSnapshot ? [self.class dictionaryForElement:elementSnapshot recursive:NO] : @{}; + NSDictionary *elementAttributes = elementSnapshot + ? [self.class dictionaryForElement:elementSnapshot recursive:NO excludedAttributes:nil] + : @{}; [resultArray addObject:@{ @"detailedDescription": extractIssueProperty(issue, @"detailedDescription") ?: @"", diff --git a/WebDriverAgentLib/Commands/FBDebugCommands.m b/WebDriverAgentLib/Commands/FBDebugCommands.m index dc96958d0..c3f9a7816 100644 --- a/WebDriverAgentLib/Commands/FBDebugCommands.m +++ b/WebDriverAgentLib/Commands/FBDebugCommands.m @@ -54,7 +54,12 @@ + (NSArray *)routes withExcludedAttributes:excludedAttributes] withScope:sourceScope]]; } else if ([sourceType caseInsensitiveCompare:SOURCE_FORMAT_JSON] == NSOrderedSame) { - result = application.fb_tree; + NSString *excludedAttributesString = request.parameters[@"excluded_attributes"]; + NSSet *excludedAttributes = (excludedAttributesString == nil) + ? nil + : [NSSet setWithArray:[excludedAttributesString componentsSeparatedByString:@","]]; + + result = [application fb_tree:excludedAttributes]; } else if ([sourceType caseInsensitiveCompare:SOURCE_FORMAT_DESCRIPTION] == NSOrderedSame) { result = application.fb_descriptionRepresentation; } else { diff --git a/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m b/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m index 15b906db7..794be2192 100644 --- a/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m +++ b/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m @@ -44,6 +44,13 @@ - (void)testApplicationTree XCTAssertNotNil(self.testedApplication.fb_accessibilityTree); } +- (void)testApplicationTreeAttributesFiltering +{ + NSDictionary *applicationTree = [self.testedApplication fb_tree:[NSSet setWithArray:@[@"visible"]]]; + XCTAssertNotNil(applicationTree); + XCTAssertNil([applicationTree objectForKey:@"isVisible"], @"'isVisible' key should not be present in the application tree"); +} + - (void)testDeactivateApplication { NSError *error;