Skip to content

Commit

Permalink
Fix multiple elements matched when dealing with nested scrollviews. (#…
Browse files Browse the repository at this point in the history
…896)

* Fix multiple elements matched when dealing with scrollviews.

* Remove unneeded matcher creator

* Fix oops + style

* Further refine matchers to prevent false positives in the native world.

Closes #164
  • Loading branch information
LeoNatan authored Aug 22, 2018
1 parent 98af972 commit 5ec797f
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 29 deletions.
90 changes: 64 additions & 26 deletions detox/ios/Detox/GREYMatchers+Detox.m
Original file line number Diff line number Diff line change
Expand Up @@ -48,41 +48,79 @@ @implementation GREYMatchers (Detox)

}

+ (id<GREYMatcher>)detoxMatcherForScrollChildOfMatcher:(id<GREYMatcher>)matcher
id<GREYMatcher> detox_grey_parent(id<GREYMatcher> ancestorMatcher)
{
// find scroll views in a more robust way, either the original matcher already points to a UIScrollView
// and if it isn't look for a child under it that is a UIScrollView
return grey_anyOf(grey_allOf(grey_anyOf(grey_kindOfClass([UIScrollView class]),
grey_kindOfClass([UIWebView class]),
nil),
matcher,
nil),
grey_allOf(grey_kindOfClass([UIScrollView class]),
grey_ancestor(matcher),
MatchesBlock matches = ^BOOL(id element)
{
id parent = nil;

if ([element isKindOfClass:[UIView class]])
{
parent = [element superview];
} else {
parent = [element accessibilityContainer];
}

if (parent && [ancestorMatcher matches:parent])
{
return YES;
}

return NO;
};
DescribeToBlock describe = ^void(id<GREYDescription> description)
{
[description appendText:[NSString stringWithFormat:@"parentThatMatches(%@)",
ancestorMatcher]];
};
return grey_allOf(grey_anyOf(grey_kindOfClass([UIView class]),
grey_respondsToSelector(@selector(accessibilityContainer)),
nil),
[[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches descriptionBlock:describe],
nil);
}

+ (id<GREYMatcher>)detoxMatcherForScrollChildOfMatcher:(id<GREYMatcher>)matcher
{
//No RN—Life is always good.
Class RN_RCTScrollView = NSClassFromString(@"RCTScrollView");
if (!RN_RCTScrollView)
{
return matcher;
}

//Either take scroll views or web views that match the provided matcher, or take views whose parent is a RCTScrollView subclass and the parent matches the provided matcher.
return grey_anyOf(grey_allOf(
grey_anyOf(grey_kindOfClass([UIScrollView class]),
grey_kindOfClass([UIWebView class]),
nil),
matcher,
nil),
grey_allOf(detox_grey_parent(grey_kindOfClass(RN_RCTScrollView)),
grey_kindOfClass([UIScrollView class]),
detox_grey_parent(matcher),
nil),
nil);
}

+ (id<GREYMatcher>)detoxMatcherAvoidingProblematicReactNativeElements:(id<GREYMatcher>)matcher
{
Class RN_RCTScrollView = NSClassFromString(@"RCTScrollView");
if (!RN_RCTScrollView)
{
return matcher;
}
// RCTScrollView is problematic because EarlGrey's visibility matcher adds a subview and this causes a RN assertion
// solution: if we match RCTScrollView, switch over to matching its contained UIScrollView
return grey_anyOf(grey_allOf(matcher,
grey_not(grey_kindOfClass(RN_RCTScrollView)),
//No RN—Life is always good.
Class RN_RCTScrollView = NSClassFromString(@"RCTScrollView");
if (!RN_RCTScrollView)
{
return matcher;
}
//Either take items which are not RCTScrollView subclasses and match the provided matcher, or take items whose parent is RCTScrollView and the parent matches the provided matcher.
return grey_anyOf(
grey_allOf(grey_not(grey_kindOfClass(RN_RCTScrollView)),
matcher,
nil),
grey_allOf(grey_kindOfClass([UIScrollView class]),
grey_ancestor(grey_allOf(matcher,
grey_kindOfClass(RN_RCTScrollView),
nil)),
grey_allOf(detox_grey_parent(grey_kindOfClass(RN_RCTScrollView)),
detox_grey_parent(matcher),
nil),
nil);
nil);
}

+ (id<GREYMatcher>)detoxMatcherForBoth:(id<GREYMatcher>)firstMatcher and:(id<GREYMatcher>)secondMatcher
Expand Down
6 changes: 3 additions & 3 deletions detox/test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,18 @@
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/example.app",
"build": "set -o pipefail && xcodebuild -project ios/example.xcodeproj -scheme example_ci -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build | xcpretty",
"type": "ios.simulator",
"name": "iPhone 8 Plus"
"name": "iPhone X"
},
"ios.sim.release": {
"binaryPath": "ios/build/Build/Products/Release-iphonesimulator/example.app",
"build": "set -o pipefail && export CODE_SIGNING_REQUIRED=NO && export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -project ios/example.xcodeproj -scheme example_ci -configuration Release -sdk iphonesimulator -derivedDataPath ios/build | xcpretty",
"type": "ios.simulator",
"name": "iPhone 8 Plus"
"name": "iPhone X"
},
"ios.none": {
"binaryPath": "ios",
"type": "ios.none",
"name": "iPhone 8 Plus",
"name": "iPhone X",
"session": {
"server": "ws://localhost:8099",
"sessionId": "test"
Expand Down

0 comments on commit 5ec797f

Please sign in to comment.