Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add precise swipe actions for swipes from a start to an end point #675

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
8 changes: 8 additions & 0 deletions EarlGrey.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
C5EFB20120117AF00010D067 /* EarlGreyImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = C5EFB1FF20117AF00010D067 /* EarlGreyImpl.m */; };
D210ED8D1E6F47D100978B9E /* GREYMultiFingerSwipeAction.h in Headers */ = {isa = PBXBuildFile; fileRef = D210ED8B1E6F47D100978B9E /* GREYMultiFingerSwipeAction.h */; settings = {ATTRIBUTES = (Private, ); }; };
D210ED8E1E6F47D100978B9E /* GREYMultiFingerSwipeAction.m in Sources */ = {isa = PBXBuildFile; fileRef = D210ED8C1E6F47D100978B9E /* GREYMultiFingerSwipeAction.m */; };
F892EDF820164F12008DB046 /* GREYPreciseSwipeAction.h in Headers */ = {isa = PBXBuildFile; fileRef = F892EDF620164F11008DB046 /* GREYPreciseSwipeAction.h */; settings = {ATTRIBUTES = (Private, ); }; };
F892EDF920164F12008DB046 /* GREYPreciseSwipeAction.m in Sources */ = {isa = PBXBuildFile; fileRef = F892EDF720164F12008DB046 /* GREYPreciseSwipeAction.m */; };
FD1001B01C5B46C200B2DB0A /* GREYAction.h in Headers */ = {isa = PBXBuildFile; fileRef = FD1001011C5B46C100B2DB0A /* GREYAction.h */; settings = {ATTRIBUTES = (Public, ); }; };
FD1001B11C5B46C200B2DB0A /* GREYActionBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = FD1001021C5B46C100B2DB0A /* GREYActionBlock.h */; settings = {ATTRIBUTES = (Public, ); }; };
FD1001B21C5B46C200B2DB0A /* GREYActionBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = FD1001031C5B46C100B2DB0A /* GREYActionBlock.m */; };
Expand Down Expand Up @@ -302,6 +304,8 @@
C5EFB1FF20117AF00010D067 /* EarlGreyImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EarlGreyImpl.m; sourceTree = "<group>"; };
D210ED8B1E6F47D100978B9E /* GREYMultiFingerSwipeAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GREYMultiFingerSwipeAction.h; sourceTree = "<group>"; };
D210ED8C1E6F47D100978B9E /* GREYMultiFingerSwipeAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GREYMultiFingerSwipeAction.m; sourceTree = "<group>"; };
F892EDF620164F11008DB046 /* GREYPreciseSwipeAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GREYPreciseSwipeAction.h; sourceTree = "<group>"; };
F892EDF720164F12008DB046 /* GREYPreciseSwipeAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GREYPreciseSwipeAction.m; sourceTree = "<group>"; };
FD06C66B1BECAD8B009032A5 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
FD06C66C1BECAD8B009032A5 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
FD06C66F1BECAD9B009032A5 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -595,6 +599,8 @@
FD1001151C5B46C100B2DB0A /* GREYSlideAction.m */,
FD1001161C5B46C100B2DB0A /* GREYSwipeAction.h */,
FD1001171C5B46C100B2DB0A /* GREYSwipeAction.m */,
F892EDF620164F11008DB046 /* GREYPreciseSwipeAction.h */,
F892EDF720164F12008DB046 /* GREYPreciseSwipeAction.m */,
FD1001181C5B46C100B2DB0A /* GREYTapAction.h */,
FD1001191C5B46C100B2DB0A /* GREYTapAction.m */,
FD10011A1C5B46C100B2DB0A /* GREYTapper.h */,
Expand Down Expand Up @@ -985,6 +991,7 @@
FDCB29941E2467F60001557E /* GREYActions+Internal.h in Headers */,
597E02E81D55AD6D0052A8D1 /* NSURL+GREYAdditions.h in Headers */,
597E02ED1D55AD6D0052A8D1 /* UITouch+GREYAdditions.h in Headers */,
F892EDF820164F12008DB046 /* GREYPreciseSwipeAction.h in Headers */,
597E02EA1D55AD6D0052A8D1 /* UIAnimation+GREYAdditions.h in Headers */,
3F5122141EE1D37E0000CC56 /* GREYTraversal.h in Headers */,
597E02EC1D55AD6D0052A8D1 /* UIScrollView+GREYAdditions.h in Headers */,
Expand Down Expand Up @@ -1150,6 +1157,7 @@
7CCBEBA71DCD2F0500CC01B8 /* GREYError.m in Sources */,
FD1002061C5B46C200B2DB0A /* GREYConstants.m in Sources */,
FD10021B1C5B46C200B2DB0A /* GREYCAAnimationDelegate.m in Sources */,
F892EDF920164F12008DB046 /* GREYPreciseSwipeAction.m in Sources */,
FD1001B41C5B46C200B2DB0A /* GREYActions.m in Sources */,
FD6D0B991C6D49400001EA75 /* GREYBaseAction.m in Sources */,
FD10025C1C5B489A00B2DB0A /* fishhook.c in Sources */,
Expand Down
40 changes: 40 additions & 0 deletions EarlGrey/Action/GREYActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,32 @@ NS_ASSUME_NONNULL_BEGIN
xOriginStartPercentage:(CGFloat)xOriginStartPercentage
yOriginStartPercentage:(CGFloat)yOriginStartPercentage;

/**
* Returns an action that swipes through the view from a given @c startPoint to a given
* @c endPoint.
*
* @param startPoint The point where the swipe should begin. Relative to the matched view's origin.
* @param endPoint The point where the swipe should end. Relative to the matched view's origin.
*
* @return A GREYAction that performs a fast swipe through a view from a specifc start point
* to a specific end point.
steviki marked this conversation as resolved.
Show resolved Hide resolved
*/
+ (id<GREYAction>)actionForSwipeFastWithStartPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint;

/**
* Returns an action that swipes through the view from a given @c startPoint to a given
* @c endPoint.
*
* @param startPoint The point where the swipe should begin.
* @param endPoint The point where the swipe should end.
*
* @return A GREYAction that performs a slow swipe through a view from a specifc start point
steviki marked this conversation as resolved.
Show resolved Hide resolved
* to a specific end point.
*/
+ (id<GREYAction>)actionForSwipeSlowWithStartPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint;

/**
* Returns an action that performs a multi-finger slow swipe through the view in the given
* @c direction.
Expand Down Expand Up @@ -458,6 +484,20 @@ GREY_EXPORT id<GREYAction> grey_swipeSlowInDirectionWithStartPoint(GREYDirection
CGFloat xOriginStartPercentage,
CGFloat yOriginStartPercentage);

/**
* Shorthand macro for
* GREYActions::actionForSwipeFastWithStartPoint:endPoint:.
*/
GREY_EXPORT id<GREYAction> grey_swipeFastFromStartToEndPoint(CGPoint startPoint,
CGPoint endPoint);

/**
* Shorthand macro for
* GREYActions::actionForSwipeSlowWithStartPoint:endPoint:.
*/
GREY_EXPORT id<GREYAction> grey_swipeSlowFromStartToEndPoint(CGPoint startPoint,
CGPoint endPoint);

/**
* Shorthand macro for
* GREYActions::actionForMultiFingerSwipeSlowInDirection:numberOfFingers:.
Expand Down
27 changes: 27 additions & 0 deletions EarlGrey/Action/GREYActions.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#import "Action/GREYScrollToContentEdgeAction.h"
#import "Action/GREYSlideAction.h"
#import "Action/GREYSwipeAction.h"
#import "Action/GREYPreciseSwipeAction.h"
#import "Action/GREYTapAction.h"
#import "Additions/NSError+GREYAdditions.h"
#import "Additions/NSObject+GREYAdditions.h"
Expand Down Expand Up @@ -88,6 +89,20 @@ + (void)initialize {
yOriginStartPercentage)];
}

+ (id<GREYAction>)actionForSwipeFastWithStartPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint {
return [[GREYPreciseSwipeAction alloc] initWithStartPoint:startPoint
endPoint:endPoint
duration:kGREYSwipeFastDuration];
}

+ (id<GREYAction>)actionForSwipeSlowWithStartPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint {
return [[GREYPreciseSwipeAction alloc] initWithStartPoint:startPoint
endPoint:endPoint
duration:kGREYSwipeSlowDuration];
}

+ (id<GREYAction>)actionForMultiFingerSwipeSlowInDirection:(GREYDirection)direction
numberOfFingers:(NSUInteger)numberOfFingers {
return [[GREYMultiFingerSwipeAction alloc] initWithDirection:direction
Expand Down Expand Up @@ -673,6 +688,18 @@ + (BOOL)grey_disableAutoCorrectForDelegateAndTypeText:(NSString *)text
yOriginStartPercentage:yOriginStartPercentage];
}

id<GREYAction> grey_swipeFastFromStartToEndPoint(CGPoint startPoint,
CGPoint endPoint) {
return [GREYActions actionForSwipeFastWithStartPoint:startPoint
endPoint:endPoint];
}

id<GREYAction> grey_swipeSlowFromStartToEndPoint(CGPoint startPoint,
CGPoint endPoint) {
return [GREYActions actionForSwipeSlowWithStartPoint:startPoint
endPoint:endPoint];
}

id<GREYAction> grey_multiFingerSwipeSlowInDirection(GREYDirection direction,
NSUInteger numberOfFingers) {
return [GREYActions actionForMultiFingerSwipeSlowInDirection:direction
Expand Down
54 changes: 54 additions & 0 deletions EarlGrey/Action/GREYPreciseSwipeAction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#import <EarlGrey/GREYBaseAction.h>
#import <EarlGrey/GREYConstants.h>

NS_ASSUME_NONNULL_BEGIN

/**
* A GREYAction that swipes/flicks the matched element from a concrete start point to an end point.
*/
@interface GREYPreciseSwipeAction : GREYBaseAction

/**
* @remark init is not an available initializer. Use the other initializers.
*/
- (instancetype)init NS_UNAVAILABLE;

/**
* @remark initWithName:constraints: is overridden from its superclass.
*/
- (instancetype)initWithName:(NSString *)name
constraints:(id<GREYMatcher>)constraints NS_UNAVAILABLE;

/**
* Performs a swipe from the given @c startPoint to the given @c endPoint.
*
* @param startPoint The point where the swipe should begin. Relative to the matched view's origin.
* @param endPoint The point where the swipe should end. Relative to the matched view's origin.
* @param duration The time interval for which the swipe takes place.
*
* @return An instance of GREYPreciseSwipeAction, initialized with the provided start point,
* end point and duration.
*/
- (instancetype)initWithStartPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint
duration:(CFTimeInterval)duration;

@end

NS_ASSUME_NONNULL_END
120 changes: 120 additions & 0 deletions EarlGrey/Action/GREYPreciseSwipeAction.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#import "Action/GREYPreciseSwipeAction.h"

#import "Action/GREYPathGestureUtils.h"
#import "Additions/NSError+GREYAdditions.h"
#import "Additions/NSObject+GREYAdditions.h"
#import "Additions/NSString+GREYAdditions.h"
#import "Assertion/GREYAssertionDefines.h"
#import "Assertion/GREYAssertions+Internal.h"
#import "Common/GREYError.h"
#import "Common/GREYThrowDefines.h"
#import "Event/GREYSyntheticEvents.h"
#import "Matcher/GREYAllOf.h"
#import "Matcher/GREYMatcher.h"
#import "Matcher/GREYMatchers.h"
#import "Matcher/GREYNot.h"

@implementation GREYPreciseSwipeAction {
/**
* The point where the swipe should begin.
*/
CGPoint _startPoint;
/**
* The point where the swipe should end.
*/
CGPoint _endPoint;
/**
* The duration within which the swipe action must be complete.
*/
CFTimeInterval _duration;
}

- (instancetype)initWithStartPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint
duration:(CFTimeInterval)duration {

NSString *name =
[NSString stringWithFormat:@"Precise swipe from %@ to %@ for duration %g",
NSStringFromCGPoint(startPoint),
NSStringFromCGPoint(endPoint),
duration];
self = [super initWithName:name
constraints:grey_allOf(grey_interactable(),
grey_not(grey_systemAlertViewShown()),
grey_kindOfClass([UIView class]),
grey_respondsToSelector(@selector(accessibilityFrame)),
nil)];
if (self) {
_startPoint = startPoint;
_endPoint = endPoint;
_duration = duration;
}
return self;
}

#pragma mark - GREYAction

- (BOOL)perform:(id)element error:(__strong NSError **)errorOrNil {
if (![self satisfiesConstraintsForElement:element error:errorOrNil]) {
return NO;
}
CGRect accessibilityFrame = [element accessibilityFrame];
CGPoint startPoint =
CGPointMake(_startPoint.x + accessibilityFrame.origin.x,
_startPoint.y + accessibilityFrame.origin.y);
CGPoint endPoint =
CGPointMake(_endPoint.x + accessibilityFrame.origin.x,
_endPoint.y + accessibilityFrame.origin.y);

UIWindow *window = [element window];
if (!window) {
if ([element isKindOfClass:[UIWindow class]]) {
window = (UIWindow *)element;
} else {
NSString *errorDescription =
[NSString stringWithFormat:@"Cannot swipe on view [V], as it has no window and "
@"it isn't a window itself."];
NSDictionary *glossary = @{ @"V" : [element grey_description] };
GREYError *error;
error = GREYErrorMake(kGREYSyntheticEventInjectionErrorDomain,
kGREYOrientationChangeFailedErrorCode,
errorDescription);
error.descriptionGlossary = glossary;
if (errorOrNil) {
*errorOrNil = error;
} else {
[GREYAssertions grey_raiseExceptionNamed:kGREYGenericFailureException
exceptionDetails:@""
withError:error];
}
return NO;
}
}
NSArray *touchPath = [GREYPathGestureUtils touchPathForDragGestureWithStartPoint:startPoint
endPoint:endPoint
cancelInertia:NO];

[GREYSyntheticEvents touchAlongPath:touchPath
relativeToWindow:window
forDuration:_duration
expendable:YES];
return YES;
}

@end
59 changes: 59 additions & 0 deletions Tests/FunctionalTests/Sources/FTRGestureTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,65 @@ - (void)testSwipeWithLocationForAllDirections {
assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testPreciseSwipe {
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Grey Box")]
performAction:grey_swipeFastFromStartToEndPoint(CGPointMake(55.f, 5.f), CGPointMake(55.f, 100.f))];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"swipe down")]
assertWithMatcher:grey_sufficientlyVisible()];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"x:55.0 - y:5.0")]
assertWithMatcher:grey_sufficientlyVisible()];

[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Grey Box")]
performAction:grey_swipeFastFromStartToEndPoint(CGPointMake(2.f, 30.f), CGPointMake(80.f, 38.f))];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"swipe right")]
assertWithMatcher:grey_sufficientlyVisible()];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"x:2.0 - y:30.0")]
assertWithMatcher:grey_sufficientlyVisible()];

[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Grey Box")]
performAction:grey_swipeSlowFromStartToEndPoint(CGPointMake(150.f, 80.f), CGPointMake(50.f, 80.f))];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"swipe left")]
assertWithMatcher:grey_sufficientlyVisible()];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"x:150.0 - y:80.0")]
assertWithMatcher:grey_sufficientlyVisible()];

[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Grey Box")]
performAction:grey_swipeSlowFromStartToEndPoint(CGPointMake(40.f, 100.f), CGPointMake(40.f, 30.f))];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"swipe up")]
assertWithMatcher:grey_sufficientlyVisible()];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"x:40.0 - y:100.0")]
assertWithMatcher:grey_sufficientlyVisible()];

[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Grey Box")]
performAction:grey_swipeFastFromStartToEndPoint(CGPointMake(40.f, 100.f), CGPointMake(40.f, -50.f))];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"swipe up")]
assertWithMatcher:grey_sufficientlyVisible()];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"x:40.0 - y:100.0")]
assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testPreciseSwipeOnWindow {
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Window swipes start here")]
performAction:grey_swipeFastFromStartToEndPoint(CGPointMake(55.f, 5.f), CGPointMake(55.f, 100.f))];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"swipe down on window")]
assertWithMatcher:grey_sufficientlyVisible()];

[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Window swipes start here")]
performAction:grey_swipeFastFromStartToEndPoint(CGPointMake(2.f, 30.f), CGPointMake(80.f, 38.f))];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"swipe right on window")]
assertWithMatcher:grey_sufficientlyVisible()];

[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Window swipes start here")]
performAction:grey_swipeSlowFromStartToEndPoint(CGPointMake(150.f, 80.f), CGPointMake(50.f, 80.f))];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"swipe left on window")]
assertWithMatcher:grey_sufficientlyVisible()];

[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Window swipes start here")]
performAction:grey_swipeSlowFromStartToEndPoint(CGPointMake(40.f, 100.f), CGPointMake(40.f, 30.f))];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"swipe up on window")]
assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testPinchWorksInAllDirectionsInPortraitMode {
[self ftr_assertPinchWorksInAllDirections];
}
Expand Down
Loading