Skip to content

Commit e615621

Browse files
feat: Expose native isAccessibilityElement (#1146)
* feat: expose native isAccessibilityElement * refactor: rename nativeAccessible to nativeAccessibilityElement Address review feedback to use the full word for clarity: - protocol property wdNativeAccessible -> wdNativeAccessibilityElement - JSON key isNativeAccessible -> isNativeAccessibilityElement - XML attribute nativeAccessible -> nativeAccessibilityElement - setting includeNativeAccessibleInPageSource -> includeNativeAccessibilityElementInPageSource Co-authored-by: Cursor <cursoragent@cursor.com> * Unify JSON attribute key prefixing * Remove empty-key guard from FBJsonPrefixedAttributeKey --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 5fe4d83 commit e615621

15 files changed

Lines changed: 95 additions & 3 deletions

File tree

WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,21 @@
4444
static NSString* const FBExclusionAttributeEnabled = @"enabled";
4545
static NSString* const FBExclusionAttributeVisible = @"visible";
4646
static NSString* const FBExclusionAttributeAccessible = @"accessible";
47+
static NSString* const FBExclusionAttributeNativeAccessibilityElement = @"nativeAccessibilityElement";
4748
static NSString* const FBExclusionAttributeFocused = @"focused";
4849
static NSString* const FBExclusionAttributePlaceholderValue = @"placeholderValue";
4950
static NSString* const FBExclusionAttributeNativeFrame = @"nativeFrame";
5051
static NSString* const FBExclusionAttributeTraits = @"traits";
5152
static NSString* const FBExclusionAttributeMinValue = @"minValue";
5253
static NSString* const FBExclusionAttributeMaxValue = @"maxValue";
5354

55+
static NSString *FBJsonPrefixedAttributeKey(NSString *key)
56+
{
57+
return [NSString stringWithFormat:@"is%@%@",
58+
[[key substringToIndex:1] uppercaseString],
59+
[key substringFromIndex:1]];
60+
}
61+
5462
_Nullable id extractIssueProperty(id issue, NSString *propertyName) {
5563
SEL selector = NSSelectorFromString(propertyName);
5664
NSMethodSignature *methodSignature = [issue methodSignatureForSelector:selector];
@@ -223,7 +231,7 @@ + (NSDictionary *)dictionaryForElement:(id<FBXCElementSnapshot>)snapshot
223231
if ([nonPrefixedKeys containsObject:key]) {
224232
info[key] = value;
225233
} else {
226-
info[[NSString stringWithFormat:@"is%@", [key capitalizedString]]] = value;
234+
info[FBJsonPrefixedAttributeKey(key)] = value;
227235
}
228236
}
229237
}
@@ -268,6 +276,9 @@ + (NSDictionary *)dictionaryForElement:(id<FBXCElementSnapshot>)snapshot
268276
},
269277
FBExclusionAttributeAccessible: ^{
270278
return [@([wrappedSnapshot isWDAccessible]) stringValue];
279+
},
280+
FBExclusionAttributeNativeAccessibilityElement: ^{
281+
return [@([wrappedSnapshot isWDNativeAccessibilityElement]) stringValue];
271282
},
272283
FBExclusionAttributeFocused: ^{
273284
return [@([wrappedSnapshot isWDFocused]) stringValue];

WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ - (BOOL)isWDFocused
202202
return self.hasFocus;
203203
}
204204

205+
- (BOOL)isWDNativeAccessibilityElement
206+
{
207+
return self.fb_isAccessibilityElement;
208+
}
209+
205210
- (BOOL)isWDAccessible
206211
{
207212
XCUIElementType elementType = self.elementType;

WebDriverAgentLib/Routing/FBElement.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ NS_ASSUME_NONNULL_BEGIN
5555
/*! Whether element is accessible */
5656
@property (nonatomic, readonly, getter = isWDAccessible) BOOL wdAccessible;
5757

58+
/*! The raw, native `isAccessibilityElement` value reported by the accessibility framework, without WebDriverAgent's custom computation applied by `wdAccessible` */
59+
@property (nonatomic, readonly, getter = isWDNativeAccessibilityElement) BOOL wdNativeAccessibilityElement;
60+
5861
/*! Whether element is an accessibility container (contains children of any depth that are accessible) */
5962
@property (nonatomic, readonly, getter = isWDAccessibilityContainer) BOOL wdAccessibilityContainer;
6063

WebDriverAgentLib/Utilities/FBConfiguration.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,24 @@ typedef NS_ENUM(NSInteger, FBConfigurationKeyboardPreference) {
366366
+ (void)setIncludeNativeFrameInPageSource:(BOOL)enabled;
367367
+ (BOOL)includeNativeFrameInPageSource;
368368

369+
/**
370+
* Whether to include the `nativeAccessibilityElement` attribute in the XML page source.
371+
*
372+
* When enabled, the XML representation will contain the raw, native
373+
* `isAccessibilityElement` value as reported by the accessibility framework,
374+
* without the custom computation that WebDriverAgent applies to the
375+
* `accessible` attribute (cell/text field special cases and parent absorption).
376+
*
377+
* This is useful for consumers that need to reason about the unmodified
378+
* accessibility flag alongside the computed `accessible` value.
379+
*
380+
* The value is disabled by default to keep the default page source stable.
381+
*
382+
* @param enabled Either YES or NO
383+
*/
384+
+ (void)setIncludeNativeAccessibilityElementInPageSource:(BOOL)enabled;
385+
+ (BOOL)includeNativeAccessibilityElementInPageSource;
386+
369387
/**
370388
* Whether to include `minValue`/`maxValue` attributes in the page source.
371389
* These attributes are retrieved from native element snapshots and represent

WebDriverAgentLib/Utilities/FBConfiguration.m

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#endif
6262
static BOOL FBShouldIncludeHittableInPageSource = NO;
6363
static BOOL FBShouldIncludeNativeFrameInPageSource = NO;
64+
static BOOL FBShouldIncludeNativeAccessibilityElementInPageSource = NO;
6465
static BOOL FBShouldIncludeMinMaxValueInPageSource = NO;
6566
static BOOL FBShouldIncludeCustomActionsInPageSource = NO;
6667
static BOOL FBShouldEnforceCustomSnapshots = NO;
@@ -665,6 +666,16 @@ + (BOOL)includeNativeFrameInPageSource
665666
return FBShouldIncludeNativeFrameInPageSource;
666667
}
667668

669+
+ (void)setIncludeNativeAccessibilityElementInPageSource:(BOOL)enabled
670+
{
671+
FBShouldIncludeNativeAccessibilityElementInPageSource = enabled;
672+
}
673+
674+
+ (BOOL)includeNativeAccessibilityElementInPageSource
675+
{
676+
return FBShouldIncludeNativeAccessibilityElementInPageSource;
677+
}
678+
668679
+ (void)setIncludeMinMaxValueInPageSource:(BOOL)enabled
669680
{
670681
FBShouldIncludeMinMaxValueInPageSource = enabled;

WebDriverAgentLib/Utilities/FBSettings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ extern NSString* const FB_SETTING_LIMIT_XPATH_CONTEXT_SCOPE;
4141
extern NSString* const FB_SETTING_AUTO_CLICK_ALERT_SELECTOR;
4242
extern NSString *const FB_SETTING_INCLUDE_HITTABLE_IN_PAGE_SOURCE;
4343
extern NSString *const FB_SETTING_INCLUDE_NATIVE_FRAME_IN_PAGE_SOURCE;
44+
extern NSString *const FB_SETTING_INCLUDE_NATIVE_ACCESSIBILITY_ELEMENT_IN_PAGE_SOURCE;
4445
extern NSString *const FB_SETTING_INCLUDE_MIN_MAX_VALUE_IN_PAGE_SOURCE;
4546
extern NSString *const FB_SETTING_INCLUDE_CUSTOM_ACTIONS_IN_PAGE_SOURCE;
4647
extern NSString *const FB_SETTING_ENFORCE_CUSTOM_SNAPSHOTS;

WebDriverAgentLib/Utilities/FBSettings.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
NSString* const FB_SETTING_AUTO_CLICK_ALERT_SELECTOR = @"autoClickAlertSelector";
3838
NSString* const FB_SETTING_INCLUDE_HITTABLE_IN_PAGE_SOURCE = @"includeHittableInPageSource";
3939
NSString* const FB_SETTING_INCLUDE_NATIVE_FRAME_IN_PAGE_SOURCE = @"includeNativeFrameInPageSource";
40+
NSString* const FB_SETTING_INCLUDE_NATIVE_ACCESSIBILITY_ELEMENT_IN_PAGE_SOURCE = @"includeNativeAccessibilityElementInPageSource";
4041
NSString* const FB_SETTING_INCLUDE_MIN_MAX_VALUE_IN_PAGE_SOURCE = @"includeMinMaxValueInPageSource";
4142
NSString* const FB_SETTING_INCLUDE_CUSTOM_ACTIONS_IN_PAGE_SOURCE = @"includeCustomActionsInPageSource";
4243
NSString* const FB_SETTING_ENFORCE_CUSTOM_SNAPSHOTS = @"enforceCustomSnapshots";

WebDriverAgentLib/Utilities/FBSettingsHandler.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ @implementation FBSettingsHandler
162162
[FBConfiguration setIncludeNativeFrameInPageSource:[value boolValue]];
163163
return nil;
164164
};
165+
map[FB_SETTING_INCLUDE_NATIVE_ACCESSIBILITY_ELEMENT_IN_PAGE_SOURCE] = ^FBCommandStatus *(FBSession *session, id value) {
166+
[FBConfiguration setIncludeNativeAccessibilityElementInPageSource:[value boolValue]];
167+
return nil;
168+
};
165169
map[FB_SETTING_INCLUDE_MIN_MAX_VALUE_IN_PAGE_SOURCE] = ^FBCommandStatus *(FBSession *session, id value) {
166170
[FBConfiguration setIncludeMinMaxValueInPageSource:[value boolValue]];
167171
return nil;
@@ -280,6 +284,9 @@ @implementation FBSettingsHandler
280284
map[FB_SETTING_INCLUDE_NATIVE_FRAME_IN_PAGE_SOURCE] = ^id(FBSession *session) {
281285
return @([FBConfiguration includeNativeFrameInPageSource]);
282286
};
287+
map[FB_SETTING_INCLUDE_NATIVE_ACCESSIBILITY_ELEMENT_IN_PAGE_SOURCE] = ^id(FBSession *session) {
288+
return @([FBConfiguration includeNativeAccessibilityElementInPageSource]);
289+
};
283290
map[FB_SETTING_INCLUDE_MIN_MAX_VALUE_IN_PAGE_SOURCE] = ^id(FBSession *session) {
284291
return @([FBConfiguration includeMinMaxValueInPageSource]);
285292
};

WebDriverAgentLib/Utilities/FBXPath.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ @interface FBNativeFrameAttribute : FBElementAttribute
120120

121121
@end
122122

123+
@interface FBNativeAccessibilityElementAttribute : FBElementAttribute
124+
125+
@end
126+
123127
@interface FBTraitsAttribute : FBElementAttribute
124128

125129
@end
@@ -409,6 +413,10 @@ + (int)xmlRepresentationWithRootElement:(id<FBXCElementSnapshot>)root
409413
// Include nativeFrame only when requested
410414
[includedAttributes removeObject:FBNativeFrameAttribute.class];
411415
}
416+
if (!FBConfiguration.includeNativeAccessibilityElementInPageSource) {
417+
// Include the raw native accessibility flag only when requested
418+
[includedAttributes removeObject:FBNativeAccessibilityElementAttribute.class];
419+
}
412420
if (!FBConfiguration.includeMinMaxValueInPageSource) {
413421
// minValue/maxValue are retrieved from private APIs and may be slow on deep trees
414422
[includedAttributes removeObject:FBMinValueAttribute.class];
@@ -686,6 +694,7 @@ + (int)recordWithWriter:(xmlTextWriterPtr)writer forValue:(nullable NSString *)v
686694
FBEnabledAttribute.class,
687695
FBVisibleAttribute.class,
688696
FBAccessibleAttribute.class,
697+
FBNativeAccessibilityElementAttribute.class,
689698
#if TARGET_OS_TV
690699
FBFocusedAttribute.class,
691700
#endif
@@ -954,6 +963,19 @@ + (NSString *)valueForElement:(id<FBElement>)element
954963
}
955964
@end
956965

966+
@implementation FBNativeAccessibilityElementAttribute
967+
968+
+ (NSString *)name
969+
{
970+
return @"nativeAccessibilityElement";
971+
}
972+
973+
+ (NSString *)valueForElement:(id<FBElement>)element
974+
{
975+
return FBBoolToString(element.wdNativeAccessibilityElement);
976+
}
977+
@end
978+
957979
@implementation FBTraitsAttribute
958980

959981
+ (NSString *)name

WebDriverAgentTests/IntegrationTests/FBElementAttributeTests.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ - (void)testElementAccessibilityAttributes
4141
XCTAssertFalse(buttonElement.isWDAccessibilityContainer);
4242
}
4343

44+
- (void)testNativeAccessibilityElementAttribute
45+
{
46+
// wdNativeAccessibilityElement must expose the raw, native isAccessibilityElement flag
47+
// without WebDriverAgent's custom computation applied by wdAccessible
48+
XCUIElement *buttonElement = self.testedApplication.buttons[@"Button"];
49+
XCTAssertTrue(buttonElement.exists);
50+
XCTAssertEqual(buttonElement.wdNativeAccessibilityElement, buttonElement.fb_isAccessibilityElement);
51+
}
52+
4453
- (void)testContainerAccessibilityAttributes
4554
{
4655
// "not_accessible" isn't accessibility element, but contains accessibility elements, so it is accessibility container

0 commit comments

Comments
 (0)