XCUIElement+FBFind.m 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /**
  2. * Copyright (c) 2015-present, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. */
  9. #import "XCUIElement+FBFind.h"
  10. #import "FBMacros.h"
  11. #import "FBElementTypeTransformer.h"
  12. #import "NSPredicate+FBFormat.h"
  13. #import "FBXCElementSnapshotWrapper+Helpers.h"
  14. #import "FBXCodeCompatibility.h"
  15. #import "XCUIElement+FBCaching.h"
  16. #import "XCUIElement+FBUID.h"
  17. #import "XCUIElement+FBUtilities.h"
  18. #import "XCUIElement+FBWebDriverAttributes.h"
  19. #import "XCUIElementQuery.h"
  20. #import "FBElementUtils.h"
  21. #import "FBXCodeCompatibility.h"
  22. #import "FBXPath.h"
  23. @implementation XCUIElement (FBFind)
  24. + (NSArray<XCUIElement *> *)fb_extractMatchingElementsFromQuery:(XCUIElementQuery *)query
  25. shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch
  26. {
  27. if (!shouldReturnAfterFirstMatch) {
  28. return query.fb_allMatches;
  29. }
  30. XCUIElement *matchedElement = query.fb_firstMatch;
  31. return matchedElement ? @[matchedElement] : @[];
  32. }
  33. - (id<FBXCElementSnapshot>)fb_cachedSnapshotWithQuery:(XCUIElementQuery *)query
  34. {
  35. return [self isKindOfClass:XCUIApplication.class] ? query.rootElementSnapshot : self.fb_cachedSnapshot;
  36. }
  37. #pragma mark - Search by ClassName
  38. - (NSArray<XCUIElement *> *)fb_descendantsMatchingClassName:(NSString *)className
  39. shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch
  40. {
  41. XCUIElementType type = [FBElementTypeTransformer elementTypeWithTypeName:className];
  42. XCUIElementQuery *query = [self.fb_query descendantsMatchingType:type];
  43. NSMutableArray *result = [NSMutableArray array];
  44. [result addObjectsFromArray:[self.class fb_extractMatchingElementsFromQuery:query
  45. shouldReturnAfterFirstMatch:shouldReturnAfterFirstMatch]];
  46. id<FBXCElementSnapshot> cachedSnapshot = [self fb_cachedSnapshotWithQuery:query];
  47. if (type == XCUIElementTypeAny || cachedSnapshot.elementType == type) {
  48. if (shouldReturnAfterFirstMatch || result.count == 0) {
  49. return @[self];
  50. }
  51. [result insertObject:self atIndex:0];
  52. }
  53. return result.copy;
  54. }
  55. #pragma mark - Search by property value
  56. - (NSArray<XCUIElement *> *)fb_descendantsMatchingProperty:(NSString *)property
  57. value:(NSString *)value
  58. partialSearch:(BOOL)partialSearch
  59. {
  60. NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:(partialSearch ? @"%K CONTAINS %@" : @"%K == %@"), property, value];
  61. return [self fb_descendantsMatchingPredicate:searchPredicate shouldReturnAfterFirstMatch:NO];
  62. }
  63. #pragma mark - Search by Predicate String
  64. - (NSArray<XCUIElement *> *)fb_descendantsMatchingPredicate:(NSPredicate *)predicate
  65. shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch
  66. {
  67. NSPredicate *formattedPredicate = [NSPredicate fb_snapshotBlockPredicateWithPredicate:predicate];
  68. XCUIElementQuery *query = [[self.fb_query descendantsMatchingType:XCUIElementTypeAny] matchingPredicate:formattedPredicate];
  69. NSMutableArray<XCUIElement *> *result = [NSMutableArray array];
  70. [result addObjectsFromArray:[self.class fb_extractMatchingElementsFromQuery:query
  71. shouldReturnAfterFirstMatch:shouldReturnAfterFirstMatch]];
  72. id<FBXCElementSnapshot> cachedSnapshot = [self fb_cachedSnapshotWithQuery:query];
  73. // Include self element into predicate search
  74. if ([formattedPredicate evaluateWithObject:cachedSnapshot]) {
  75. if (shouldReturnAfterFirstMatch || result.count == 0) {
  76. return @[self];
  77. }
  78. [result insertObject:self atIndex:0];
  79. }
  80. return result.copy;
  81. }
  82. #pragma mark - Search by xpath
  83. - (NSArray<XCUIElement *> *)fb_descendantsMatchingXPathQuery:(NSString *)xpathQuery
  84. shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch
  85. {
  86. // XPath will try to match elements only class name, so requesting elements by XCUIElementTypeAny will not work. We should use '*' instead.
  87. xpathQuery = [xpathQuery stringByReplacingOccurrencesOfString:@"XCUIElementTypeAny" withString:@"*"];
  88. NSArray<id<FBXCElementSnapshot>> *matchingSnapshots = [FBXPath matchesWithRootElement:self forQuery:xpathQuery];
  89. if (0 == [matchingSnapshots count]) {
  90. return @[];
  91. }
  92. if (shouldReturnAfterFirstMatch) {
  93. id<FBXCElementSnapshot> snapshot = matchingSnapshots.firstObject;
  94. matchingSnapshots = @[snapshot];
  95. }
  96. return [self fb_filterDescendantsWithSnapshots:matchingSnapshots
  97. selfUID:[FBXCElementSnapshotWrapper wdUIDWithSnapshot:self.lastSnapshot]
  98. onlyChildren:NO];
  99. }
  100. #pragma mark - Search by Accessibility Id
  101. - (NSArray<XCUIElement *> *)fb_descendantsMatchingIdentifier:(NSString *)accessibilityId
  102. shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch
  103. {
  104. NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id<FBXCElementSnapshot> snapshot,
  105. NSDictionary<NSString *,id> * _Nullable bindings) {
  106. return [[FBXCElementSnapshotWrapper wdNameWithSnapshot:snapshot] isEqualToString:accessibilityId];
  107. }];
  108. return [self fb_descendantsMatchingPredicate:predicate
  109. shouldReturnAfterFirstMatch:shouldReturnAfterFirstMatch];
  110. }
  111. @end