Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- (void)_XCT_requestElementAtPoint:(CGPoint)arg1 reply:(void (^)(id/*XCAccessibilityElement*/, NSError *))arg2;
- (void)_XCT_fetchParameterizedAttributeForElement:(id/*XCAccessibilityElement*/)arg1 attributes:(NSNumber *)arg2 parameter:(id)arg3 reply:(void (^)(id, NSError *))arg4;
- (void)_XCT_setAttribute:(NSNumber *)arg1 value:(id)arg2 element:(id/*XCAccessibilityElement*/)arg3 reply:(void (^)(BOOL, NSError *))arg4;
- (void)_XCT_fetchAttributes:(id)attributes forElement:(id)element reply:(void (^)(NSDictionary *, NSError *))reply;
- (void)_XCT_fetchAttributesForElement:(id/*XCAccessibilityElement*/)arg1 attributes:(NSArray *)arg2 reply:(void (^)(NSDictionary *, NSError *))arg3;
- (void)_XCT_terminateApplicationWithBundleID:(NSString *)arg1 completion:(void (^)(NSError *))arg2;
- (void)_XCT_performAccessibilityAction:(int)arg1 onElement:(id/*XCAccessibilityElement*/)arg2 withValue:(id)arg3 reply:(void (^)(NSError *))arg4;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,14 @@ NS_ASSUME_NONNULL_BEGIN

@param attribute attribute's accessibility identifier. Can be one of
`XC_kAXXCAttribute`-prefixed attribute names.
@return value for given accessibility property identifier
@param timeout The maximum time is flota seconds to wait until XCTest/Accessbility framework
returns the value of the requested attribute
@param error Error instance in case of a failure
@return value for given accessibility property identifier or nil in case of failure
*/
- (nullable id)fb_attributeValue:(NSString *)attribute;
- (nullable id)fb_attributeValue:(NSString *)attribute
timeout:(NSTimeInterval)timeout
error:(NSError **)error;

/**
Method used to determine whether given element matches receiver by comparing it's parameters except frame.
Expand Down
46 changes: 40 additions & 6 deletions WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@
#import "FBXCElementSnapshotWrapper+Helpers.h"

#import "FBFindElementCommands.h"
#import "FBErrorBuilder.h"
#import "FBRunLoopSpinner.h"
#import "FBLogger.h"
#import "FBXCElementSnapshot.h"
#import "FBXCTestDaemonsProxy.h"
#import "FBXCAXClientProxy.h"
#import "XCTestDriver.h"
#import "XCTestPrivateSymbols.h"
#import "XCUIElement.h"
#import "XCUIElement+FBWebDriverAttributes.h"
#import "XCUIHitPointResult.h"

#define VisibleFrameFetchTimeout 0.3

inline static BOOL isSnapshotTypeAmongstGivenTypes(id<FBXCElementSnapshot> snapshot,
NSArray<NSNumber *> *types);

Expand Down Expand Up @@ -65,10 +69,38 @@ - (NSString *)fb_description
}

- (id)fb_attributeValue:(NSString *)attribute
{
NSDictionary *result = [FBXCAXClientProxy.sharedClient attributesForElement:[self accessibilityElement]
attributes:@[attribute]];
return result[attribute];
timeout:(NSTimeInterval)timeout
error:(NSError **)error
{
id<XCTestManager_ManagerInterface> proxy = [FBXCTestDaemonsProxy testRunnerProxy];
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
__block NSDictionary *result = nil;
__block NSError *blockError;
[proxy _XCT_fetchAttributes:@[attribute]
forElement:[self accessibilityElement]
reply:^(NSDictionary *innerResult, NSError *innerError) {
if (nil == innerError) {
result = innerResult;
} else {
blockError = innerError;
}
dispatch_semaphore_signal(sem);
}];
if (0 != dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)))) {
NSString *timeoutMsg = [NSString stringWithFormat:@"Cannot fetch %@ attribute of '%@' within %@s timeout",
attribute, self.fb_description, @(timeout)];
[[[FBErrorBuilder builder]
withDescription:timeoutMsg]
buildError:error];
return nil;
}
if (nil != result) {
return [result objectForKey:attribute];
}
if (error) {
*error = blockError;
}
return nil;
}

inline static BOOL areValuesEqual(id value1, id value2);
Expand Down Expand Up @@ -146,8 +178,10 @@ - (CGRect)fb_visibleFrameWithFallback
if (!CGRectIsEmpty(thisVisibleFrame)) {
return thisVisibleFrame;
}

NSDictionary *visibleFrameDict = (NSDictionary*)[self fb_attributeValue:@"XC_kAXXCAttributeVisibleFrame"];

NSDictionary *visibleFrameDict = [self fb_attributeValue:FB_XCAXAVisibleFrameAttributeName
timeout:VisibleFrameFetchTimeout
error:nil];
if (visibleFrameDict == nil) {
return thisVisibleFrame;
}
Expand Down
4 changes: 2 additions & 2 deletions WebDriverAgentLib/Categories/XCUIApplication+FBAlert.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ - (nullable XCUIElement *)fb_alertElementFromSafariWithScrollView:(XCUIElement *
// and conatins at least one text view
__block NSUInteger buttonsCount = 0;
__block NSUInteger textViewsCount = 0;
id<FBXCElementSnapshot> snapshot = candidate.fb_cachedSnapshot ?: candidate.fb_takeSnapshot;
id<FBXCElementSnapshot> snapshot = candidate.fb_cachedSnapshot ?: [candidate fb_takeSnapshot:YES];
[snapshot enumerateDescendantsUsingBlock:^(id<FBXCElementSnapshot> descendant) {
XCUIElementType curType = descendant.elementType;
if (curType == XCUIElementTypeButton) {
Expand All @@ -73,7 +73,7 @@ - (XCUIElement *)fb_alertElement
if (nil == alert) {
return nil;
}
id<FBXCElementSnapshot> alertSnapshot = alert.fb_cachedSnapshot ?: alert.fb_takeSnapshot;
id<FBXCElementSnapshot> alertSnapshot = alert.fb_cachedSnapshot ?: [alert fb_takeSnapshot:YES];

if (alertSnapshot.elementType == XCUIElementTypeAlert) {
return alert;
Expand Down
24 changes: 4 additions & 20 deletions WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m
Original file line number Diff line number Diff line change
Expand Up @@ -176,31 +176,15 @@ - (NSDictionary *)fb_tree

- (NSDictionary *)fb_tree:(nullable NSSet<NSString *> *)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<FBXCElementSnapshot> snapshot = nil == excludedAttributes
? [self fb_snapshotWithAllAttributesAndMaxDepth:nil]
: [self fb_snapshotWithAttributes:[includedAttributeNames allObjects] maxDepth:nil];
id<FBXCElementSnapshot> snapshot = [self fb_takeSnapshot:YES];
return [self.class dictionaryForElement:snapshot
recursive:YES
excludedAttributes:excludedAttributes];
}

- (NSDictionary *)fb_accessibilityTree
{
id<FBXCElementSnapshot> snapshot = self.fb_isResolvedFromCache.boolValue
? self.lastSnapshot
: [self fb_snapshotWithAllAttributesAndMaxDepth:nil];
id<FBXCElementSnapshot> snapshot = [self fb_takeSnapshot:YES];
return [self.class accessibilityInfoForElement:snapshot];
}

Expand Down Expand Up @@ -445,8 +429,8 @@ - (BOOL)fb_dismissKeyboardWithKeyNames:(nullable NSArray<NSString *> *)keyNames

id extractedElement = extractIssueProperty(issue, @"element");

id<FBXCElementSnapshot> elementSnapshot = [extractedElement fb_cachedSnapshot] ?: [extractedElement fb_takeSnapshot];
NSDictionary *elementAttributes = elementSnapshot
id<FBXCElementSnapshot> elementSnapshot = [extractedElement fb_cachedSnapshot] ?: [extractedElement fb_takeSnapshot:NO];
NSDictionary *elementAttributes = elementSnapshot
? [self.class dictionaryForElement:elementSnapshot
recursive:NO
excludedAttributes:customAttributesToExclude]
Expand Down
19 changes: 15 additions & 4 deletions WebDriverAgentLib/Categories/XCUIElement+FBAccessibility.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
#import "XCUIElement+FBUtilities.h"
#import "FBXCElementSnapshotWrapper+Helpers.h"

#define AX_FETCH_TIMEOUT 0.3

@implementation XCUIElement (FBAccessibility)

- (BOOL)fb_isAccessibilityElement
{
id<FBXCElementSnapshot> snapshot = [self fb_snapshotWithAttributes:@[FB_XCAXAIsElementAttributeName]
maxDepth:@1];
id<FBXCElementSnapshot> snapshot = [self fb_takeSnapshot:NO];
return [FBXCElementSnapshotWrapper ensureWrapped:snapshot].fb_isAccessibilityElement;
}

Expand All @@ -33,8 +34,18 @@ - (BOOL)fb_isAccessibilityElement
if (nil != isAccessibilityElement) {
return isAccessibilityElement.boolValue;
}

return [(NSNumber *)[self fb_attributeValue:FB_XCAXAIsElementAttributeName] boolValue];

NSError *error;
NSNumber *attributeValue = [self fb_attributeValue:FB_XCAXAIsElementAttributeName
timeout:AX_FETCH_TIMEOUT
error:&error];
if (nil != attributeValue) {
return [attributeValue boolValue];
}

NSLog(@"Cannot determine '%@' accessibility natively: %@. Defaulting to: %@",
self.fb_description, error.description, @(NO));
return NO;
}

@end
3 changes: 0 additions & 3 deletions WebDriverAgentLib/Categories/XCUIElement+FBCaching.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ NS_ASSUME_NONNULL_BEGIN

@interface XCUIElement (FBCaching)

/*! This property is set to YES if the given element has been resolved from the cache, so it is safe to use the `lastSnapshot` property */
@property (nullable, nonatomic) NSNumber *fb_isResolvedFromCache;

@property (nonatomic, readonly) NSString *fb_cacheId;

@end
Expand Down
23 changes: 1 addition & 22 deletions WebDriverAgentLib/Categories/XCUIElement+FBCaching.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,6 @@

@implementation XCUIElement (FBCaching)

static char XCUIELEMENT_IS_RESOLVED_FROM_CACHE_KEY;

@dynamic fb_isResolvedFromCache;

- (void)setFb_isResolvedFromCache:(NSNumber *)isResolvedFromCache
{
objc_setAssociatedObject(self, &XCUIELEMENT_IS_RESOLVED_FROM_CACHE_KEY, isResolvedFromCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSNumber *)fb_isResolvedFromCache
{
return (NSNumber *)objc_getAssociatedObject(self, &XCUIELEMENT_IS_RESOLVED_FROM_CACHE_KEY);
}

static char XCUIELEMENT_CACHE_ID_KEY;

@dynamic fb_cacheId;
Expand All @@ -43,14 +29,7 @@ - (NSString *)fb_cacheId
return (NSString *)result;
}

NSString *uid;
if ([self isKindOfClass:XCUIApplication.class]) {
uid = self.fb_uid;
} else {
id<FBXCElementSnapshot> snapshot = self.fb_cachedSnapshot ?: self.fb_takeSnapshot;
uid = [FBXCElementSnapshotWrapper wdUIDWithSnapshot:snapshot];
}

NSString *uid = self.fb_uid;
objc_setAssociatedObject(self, &XCUIELEMENT_CACHE_ID_KEY, uid, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return uid;
}
Expand Down
2 changes: 1 addition & 1 deletion WebDriverAgentLib/Categories/XCUIElement+FBFind.m
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ @implementation XCUIElement (FBFind)
matchingSnapshots = @[snapshot];
}
return [self fb_filterDescendantsWithSnapshots:matchingSnapshots
selfUID:[FBXCElementSnapshotWrapper wdUIDWithSnapshot:self.lastSnapshot]
selfUID:self.fb_uid
onlyChildren:NO];
}

Expand Down
Loading
Loading