From e7d088534a1622dcee63ee4ad07b9552c983ed68 Mon Sep 17 00:00:00 2001 From: Ilia Kharebashvili Date: Thu, 15 May 2025 21:37:15 +0300 Subject: [PATCH 1/5] Include placeholderValue in /source endpoint response --- WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m index 84f697405..d8eeae8c1 100644 --- a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +++ b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m @@ -45,6 +45,7 @@ static NSString* const FBExclusionAttributeVisible = @"visible"; static NSString* const FBExclusionAttributeAccessible = @"accessible"; static NSString* const FBExclusionAttributeFocused = @"focused"; +static NSString* const FBExclusionAttributePlaceholderValue = @"placeholderValue"; _Nullable id extractIssueProperty(id issue, NSString *propertyName) { @@ -216,13 +217,18 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot }, FBExclusionAttributeFocused: ^{ return [@([wrappedSnapshot isWDFocused]) stringValue]; + }, + FBExclusionAttributePlaceholderValue: ^{ + return FBValueOrNull(wrappedSnapshot.wdPlaceholderValue); } }; + NSSet *nonPrefixedKeys = [NSSet setWithObjects:FBExclusionAttributeFrame, FBExclusionAttributePlaceholderValue, nil]; + for (NSString *key in attributeBlocks) { if (excludedAttributes == nil || ![excludedAttributes containsObject:key]) { NSString *value = ((NSString * (^)(void))attributeBlocks[key])(); - if ([key isEqualToString:FBExclusionAttributeFrame]) { + if ([nonPrefixedKeys containsObject:key]) { info[key] = value; } else { info[[NSString stringWithFormat:@"is%@", [key capitalizedString]]] = value; From 5a492c70c776346e7baebfa9a542ec0f9f0706bf Mon Sep 17 00:00:00 2001 From: Ilia Kharebashvili Date: Fri, 16 May 2025 16:37:02 +0300 Subject: [PATCH 2/5] feat: add placeholderValue to /source for text input elements only --- .../Categories/XCUIApplication+FBHelpers.m | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m index d8eeae8c1..dbda97248 100644 --- a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +++ b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m @@ -202,7 +202,8 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot info[@"label"] = FBValueOrNull(wrappedSnapshot.wdLabel); info[@"rect"] = wrappedSnapshot.wdRect; - NSDictionary *attributeBlocks = @{ + // Define the base attribute blocks that apply to all elements. + NSDictionary *baseAttributeBlocks = @{ FBExclusionAttributeFrame: ^{ return NSStringFromCGRect(wrappedSnapshot.wdFrame); }, @@ -217,11 +218,28 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot }, FBExclusionAttributeFocused: ^{ return [@([wrappedSnapshot isWDFocused]) stringValue]; - }, - FBExclusionAttributePlaceholderValue: ^{ - return FBValueOrNull(wrappedSnapshot.wdPlaceholderValue); } }; + + NSMutableDictionary *attributeBlocks; + + // Add placeholderValue only for elements where it is meaningful (e.g., text input fields). + switch (snapshot.elementType) { + case XCUIElementTypeTextField: + case XCUIElementTypeSecureTextField: + case XCUIElementTypeSearchField: { + // Copy base attributes to a mutable dictionary so we can add placeholderValue. + attributeBlocks = [baseAttributeBlocks mutableCopy]; + attributeBlocks[FBExclusionAttributePlaceholderValue] = ^{ + return (NSString *)FBValueOrNull(wrappedSnapshot.wdPlaceholderValue); + }; + break; + } + default: + // Use the base attributes as-is if placeholderValue is not applicable. + attributeBlocks = [baseAttributeBlocks copy]; + break; + } NSSet *nonPrefixedKeys = [NSSet setWithObjects:FBExclusionAttributeFrame, FBExclusionAttributePlaceholderValue, nil]; From 94a6f66a0c1429be45170a7c45d829b2973ea308 Mon Sep 17 00:00:00 2001 From: Ilia Kharebashvili Date: Sat, 17 May 2025 19:30:26 +0300 Subject: [PATCH 3/5] refactor: extract attribute block building logic into helper method Moved the logic for constructing attribute blocks into a separate method for clarity and future extensibility. Also replaced direct type checks with a reusable fb_supportsPlaceholder helper on FBXCElementSnapshotWrapper. --- .../FBXCElementSnapshotWrapper+Helpers.h | 5 ++ .../FBXCElementSnapshotWrapper+Helpers.m | 7 ++ .../Categories/XCUIApplication+FBHelpers.m | 78 +++++++++---------- 3 files changed, 51 insertions(+), 39 deletions(-) diff --git a/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.h b/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.h index a4ebdcaea..c224d280d 100644 --- a/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.h +++ b/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.h @@ -96,6 +96,11 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable NSValue *)fb_hitPoint; +/** + @return YES, if the element type is one that supports placeholder text + */ +- (BOOL)fb_supportsPlaceholder; + @end NS_ASSUME_NONNULL_END diff --git a/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.m b/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.m index c3a70f1f9..4d7232e3e 100644 --- a/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.m +++ b/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.m @@ -162,6 +162,13 @@ - (NSValue *)fb_hitPoint return [NSValue valueWithCGPoint:result.hitPoint]; } +- (BOOL)fb_supportsPlaceholder { + XCUIElementType elementType = self.elementType; + return elementType == XCUIElementTypeTextField + || elementType == XCUIElementTypeSearchField + || elementType == XCUIElementTypeSecureTextField; +} + @end inline static BOOL isSnapshotTypeAmongstGivenTypes(id snapshot, NSArray *types) diff --git a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m index dbda97248..7e901d904 100644 --- a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +++ b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m @@ -202,46 +202,11 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot info[@"label"] = FBValueOrNull(wrappedSnapshot.wdLabel); info[@"rect"] = wrappedSnapshot.wdRect; - // Define the base attribute blocks that apply to all elements. - NSDictionary *baseAttributeBlocks = @{ - FBExclusionAttributeFrame: ^{ - return NSStringFromCGRect(wrappedSnapshot.wdFrame); - }, - FBExclusionAttributeEnabled: ^{ - return [@([wrappedSnapshot isWDEnabled]) stringValue]; - }, - FBExclusionAttributeVisible: ^{ - return [@([wrappedSnapshot isWDVisible]) stringValue]; - }, - FBExclusionAttributeAccessible: ^{ - return [@([wrappedSnapshot isWDAccessible]) stringValue]; - }, - FBExclusionAttributeFocused: ^{ - return [@([wrappedSnapshot isWDFocused]) stringValue]; - } - }; - - NSMutableDictionary *attributeBlocks; - - // Add placeholderValue only for elements where it is meaningful (e.g., text input fields). - switch (snapshot.elementType) { - case XCUIElementTypeTextField: - case XCUIElementTypeSecureTextField: - case XCUIElementTypeSearchField: { - // Copy base attributes to a mutable dictionary so we can add placeholderValue. - attributeBlocks = [baseAttributeBlocks mutableCopy]; - attributeBlocks[FBExclusionAttributePlaceholderValue] = ^{ - return (NSString *)FBValueOrNull(wrappedSnapshot.wdPlaceholderValue); - }; - break; - } - default: - // Use the base attributes as-is if placeholderValue is not applicable. - attributeBlocks = [baseAttributeBlocks copy]; - break; - } + NSMutableDictionary *attributeBlocks = [self attributeBlockMapForSnapshot:snapshot + wrappedSnapshot:wrappedSnapshot]; - NSSet *nonPrefixedKeys = [NSSet setWithObjects:FBExclusionAttributeFrame, FBExclusionAttributePlaceholderValue, nil]; + NSSet *nonPrefixedKeys = [NSSet setWithObjects:FBExclusionAttributeFrame, + FBExclusionAttributePlaceholderValue, nil]; for (NSString *key in attributeBlocks) { if (excludedAttributes == nil || ![excludedAttributes containsObject:key]) { @@ -272,6 +237,41 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot return info; } +// Private helper that builds all attribute blocks for a given snapshot. +// Includes both base attributes and any element-specific ones (e.g. placeholder for text inputs, etc.). ++ (NSMutableDictionary *)attributeBlockMapForSnapshot:(id)snapshot + wrappedSnapshot:(FBXCElementSnapshotWrapper *)wrappedSnapshot +{ + // Base attributes common to every element + NSMutableDictionary *blocks = + [@{ + FBExclusionAttributeFrame: ^{ + return NSStringFromCGRect(wrappedSnapshot.wdFrame); + }, + FBExclusionAttributeEnabled: ^{ + return [@([wrappedSnapshot isWDEnabled]) stringValue]; + }, + FBExclusionAttributeVisible: ^{ + return [@([wrappedSnapshot isWDVisible]) stringValue]; + }, + FBExclusionAttributeAccessible: ^{ + return [@([wrappedSnapshot isWDAccessible]) stringValue]; + }, + FBExclusionAttributeFocused: ^{ + return [@([wrappedSnapshot isWDFocused]) stringValue]; + } + } mutableCopy]; + + // Text-input placeholder (only for elements that support inner text) + if ([wrappedSnapshot fb_supportsPlaceholder]) { + blocks[FBExclusionAttributePlaceholderValue] = ^{ + return (NSString *)FBValueOrNull(wrappedSnapshot.wdPlaceholderValue); + }; + } + + return blocks; +} + + (NSDictionary *)accessibilityInfoForElement:(id)snapshot { FBXCElementSnapshotWrapper *wrappedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:snapshot]; From 7e9dbb494ba1c2140dd97a28f367951e197d2d96 Mon Sep 17 00:00:00 2001 From: Ilia Kharebashvili Date: Mon, 19 May 2025 14:40:10 +0300 Subject: [PATCH 4/5] refactor: use FBDoesElementSupportInnerText and clarify helper method comment - Replaced custom placeholder support check with FBDoesElementSupportInnerText, as introduced in upstream PR - Updated comment for fb_attributeBlockMapForSnapshot to clarify usage context and avoid confusion about method placement --- .../FBXCElementSnapshotWrapper+Helpers.h | 5 ----- .../FBXCElementSnapshotWrapper+Helpers.m | 7 ------- .../Categories/XCUIApplication+FBHelpers.m | 15 +++++++++------ 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.h b/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.h index c224d280d..a4ebdcaea 100644 --- a/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.h +++ b/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.h @@ -96,11 +96,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable NSValue *)fb_hitPoint; -/** - @return YES, if the element type is one that supports placeholder text - */ -- (BOOL)fb_supportsPlaceholder; - @end NS_ASSUME_NONNULL_END diff --git a/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.m b/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.m index 4d7232e3e..c3a70f1f9 100644 --- a/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.m +++ b/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.m @@ -162,13 +162,6 @@ - (NSValue *)fb_hitPoint return [NSValue valueWithCGPoint:result.hitPoint]; } -- (BOOL)fb_supportsPlaceholder { - XCUIElementType elementType = self.elementType; - return elementType == XCUIElementTypeTextField - || elementType == XCUIElementTypeSearchField - || elementType == XCUIElementTypeSecureTextField; -} - @end inline static BOOL isSnapshotTypeAmongstGivenTypes(id snapshot, NSArray *types) diff --git a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m index 7e901d904..34da35aa7 100644 --- a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +++ b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m @@ -37,6 +37,7 @@ #import "XCUIElement+FBUtilities.h" #import "XCUIElement+FBWebDriverAttributes.h" #import "XCUIElementQuery.h" +#import "FBElementHelpers.h" static NSString* const FBUnknownBundleId = @"unknown"; @@ -202,7 +203,7 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot info[@"label"] = FBValueOrNull(wrappedSnapshot.wdLabel); info[@"rect"] = wrappedSnapshot.wdRect; - NSMutableDictionary *attributeBlocks = [self attributeBlockMapForSnapshot:snapshot + NSDictionary *attributeBlocks = [self fb_attributeBlockMapForSnapshot:snapshot wrappedSnapshot:wrappedSnapshot]; NSSet *nonPrefixedKeys = [NSSet setWithObjects:FBExclusionAttributeFrame, @@ -237,9 +238,9 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot return info; } -// Private helper that builds all attribute blocks for a given snapshot. -// Includes both base attributes and any element-specific ones (e.g. placeholder for text inputs, etc.). -+ (NSMutableDictionary *)attributeBlockMapForSnapshot:(id)snapshot +// Helper used by `dictionaryForElement:` to assemble attribute value blocks, +// including both common attributes and conditionally included ones like placeholderValue. ++ (NSDictionary *)fb_attributeBlockMapForSnapshot:(id)snapshot wrappedSnapshot:(FBXCElementSnapshotWrapper *)wrappedSnapshot { // Base attributes common to every element @@ -262,14 +263,16 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot } } mutableCopy]; + XCUIElementType elementType = wrappedSnapshot.elementType; + // Text-input placeholder (only for elements that support inner text) - if ([wrappedSnapshot fb_supportsPlaceholder]) { + if (FBDoesElementSupportInnerText(elementType)) { blocks[FBExclusionAttributePlaceholderValue] = ^{ return (NSString *)FBValueOrNull(wrappedSnapshot.wdPlaceholderValue); }; } - return blocks; + return [blocks copy]; } + (NSDictionary *)accessibilityInfoForElement:(id)snapshot From 49921ca0a7406cc16d01b44f0ede19c43a03b0ec Mon Sep 17 00:00:00 2001 From: Ilia Kharebashvili Date: Tue, 20 May 2025 11:09:33 +0300 Subject: [PATCH 5/5] refactor: remove extra parameter from the fb_attributeBlockMapForWrappedSnapshot method --- WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m index 34da35aa7..1c788cd54 100644 --- a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +++ b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m @@ -203,8 +203,7 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot info[@"label"] = FBValueOrNull(wrappedSnapshot.wdLabel); info[@"rect"] = wrappedSnapshot.wdRect; - NSDictionary *attributeBlocks = [self fb_attributeBlockMapForSnapshot:snapshot - wrappedSnapshot:wrappedSnapshot]; + NSDictionary *attributeBlocks = [self fb_attributeBlockMapForWrappedSnapshot:wrappedSnapshot]; NSSet *nonPrefixedKeys = [NSSet setWithObjects:FBExclusionAttributeFrame, FBExclusionAttributePlaceholderValue, nil]; @@ -240,8 +239,8 @@ + (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_attributeBlockMapForSnapshot:(id)snapshot - wrappedSnapshot:(FBXCElementSnapshotWrapper *)wrappedSnapshot ++ (NSDictionary *)fb_attributeBlockMapForWrappedSnapshot:(FBXCElementSnapshotWrapper *)wrappedSnapshot + { // Base attributes common to every element NSMutableDictionary *blocks =