-
Notifications
You must be signed in to change notification settings - Fork 918
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
Widen firstResponder
searches to all windows
#1143
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -220,7 +220,7 @@ - (void)waitForAnimationsToFinishWithTimeout:(NSTimeInterval)timeout stabilizati | |
[self waitForTimeInterval:maximumWaitingTimeInterval relativeToAnimationSpeed:YES]; | ||
} | ||
} else { | ||
|
||
// Wait for the view to stabilize and give them a chance to start animations before we wait for them. | ||
[self waitForTimeInterval:stabilizationTime relativeToAnimationSpeed:YES]; | ||
maximumWaitingTimeInterval -= stabilizationTime; | ||
|
@@ -468,24 +468,38 @@ - (void)enterTextIntoCurrentFirstResponder:(NSString *)text fallbackView:(UIView | |
[text enumerateSubstringsInRange:NSMakeRange(0, text.length) | ||
options:NSStringEnumerationByComposedCharacterSequences | ||
usingBlock: ^(NSString *characterString,NSRange substringRange,NSRange enclosingRange,BOOL * stop) | ||
{ | ||
{ | ||
if (![KIFTypist enterCharacter:characterString]) { | ||
NSLog(@"KIF: Unable to find keyboard key for %@. Will attempt to insert manually.", characterString); | ||
|
||
// Attempt to cheat if we couldn't find the character | ||
UIView * fallback = fallbackView; | ||
if (!fallback) { | ||
UIResponder *firstResponder = [[[UIApplication sharedApplication] keyWindow] firstResponder]; | ||
NSMutableArray *fallbackViews = [NSMutableArray array]; | ||
|
||
if ([firstResponder isKindOfClass:[UIView class]]) { | ||
fallback = (UIView *)firstResponder; | ||
if (fallbackView) { | ||
[fallbackViews addObject:fallbackView]; | ||
} else { | ||
[fallbackViews addObjectsFromArray:[[UIApplication sharedApplication] firstResponders]]; | ||
} | ||
|
||
for (id fallback in [fallbackViews copy]) { | ||
if (![fallback isKindOfClass:[UITextField class]] && | ||
![fallback isKindOfClass:[UITextView class]] && | ||
![fallback isKindOfClass:[UISearchBar class]]) { | ||
[fallbackViews removeObject:fallback]; | ||
} | ||
} | ||
|
||
if ([fallback isKindOfClass:[UITextField class]] || [fallback isKindOfClass:[UITextView class]] || [fallback isKindOfClass:[UISearchBar class]]) { | ||
NSLog(@"KIF: Unable to find keyboard key for %@. Inserting manually.", characterString); | ||
[(UITextField *)fallback setText:[[(UITextField *)fallback text] stringByAppendingString:characterString]]; | ||
} else { | ||
[self failWithError:[NSError KIFErrorWithFormat:@"Failed to find key for character \"%@\"", characterString] stopTest:YES]; | ||
UITextField *fallbackTextView = fallbackViews.firstObject; | ||
if (fallbackTextView) { | ||
if (fallbackViews.count > 1) { | ||
NSLog(@"KIF: Found multiple possible fallback views for entering text: %@. Will use: %@", fallbackViews, fallbackTextView); | ||
} | ||
|
||
[fallbackTextView setText:[[fallbackTextView text] stringByAppendingString:characterString]]; | ||
return; | ||
} | ||
|
||
[self failWithError:[NSError KIFErrorWithFormat:@"Failed to find key for character \"%@\"", characterString] stopTest:YES]; | ||
} | ||
}]; | ||
|
||
|
@@ -551,9 +565,17 @@ - (void)expectView:(UIView *)view toContainText:(NSString *)expectedResult | |
- (void)clearTextFromFirstResponder | ||
{ | ||
@autoreleasepool { | ||
UIView *firstResponder = (id)[[[UIApplication sharedApplication] keyWindow] firstResponder]; | ||
if ([firstResponder isKindOfClass:[UIView class]]) { | ||
[self clearTextFromElement:(UIAccessibilityElement *)firstResponder inView:firstResponder]; | ||
NSArray *firstResponders = [[UIApplication sharedApplication] firstResponders]; | ||
|
||
for (UIResponder *firstResponder in firstResponders) { | ||
if ([firstResponder isKindOfClass:[UIView class]]) { | ||
if (firstResponders.count > 1) { | ||
NSLog(@"KIF: Found multiple first responders while attempting to clear text: %@. Will use: %@.", firstResponders, firstResponder); | ||
} | ||
|
||
[self clearTextFromElement:(UIAccessibilityElement *)firstResponder inView:(UIView *)firstResponder]; | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -976,7 +998,7 @@ - (void)choosePhotoInAlbum:(NSString *)albumName atRow:(NSInteger)row column:(NS | |
|
||
// Tap the desired photo in the grid | ||
// TODO: This currently only works for the first page of photos. It should scroll appropriately at some point. | ||
UIAccessibilityElement *headerElt = [[UIApplication sharedApplication] accessibilityElementMatchingBlock:^(UIAccessibilityElement *element) { | ||
UIAccessibilityElement *headerElt = [[UIApplication sharedApplication] accessibilityElementMatchingBlock:^(UIAccessibilityElement *element) { | ||
return [NSStringFromClass(element.class) isEqual:@"UINavigationItemButtonView"]; | ||
}]; | ||
UIView* headerView = [UIAccessibilityElement viewContainingAccessibilityElement:headerElt]; | ||
|
@@ -1095,56 +1117,56 @@ - (void)swipeViewWithAccessibilityLabel:(NSString *)label value:(NSString *)valu | |
- (void)swipeAccessibilityElement:(UIAccessibilityElement *)element inView:(UIView *)viewToSwipe inDirection:(KIFSwipeDirection)direction | ||
{ | ||
// The original version of this came from http://groups.google.com/group/kif-framework/browse_thread/thread/df3f47eff9f5ac8c | ||
|
||
const NSUInteger kNumberOfPointsInSwipePath = 20; | ||
|
||
// Within this method, all geometry is done in the coordinate system of the view to swipe. | ||
CGRect elementFrame = [self elementFrameForElement:element andView:viewToSwipe]; | ||
|
||
CGPoint swipeStart = CGPointCenteredInRect(elementFrame); | ||
|
||
KIFDisplacement swipeDisplacement = [self _displacementForSwipingInDirection:direction]; | ||
|
||
[viewToSwipe dragFromPoint:swipeStart displacement:swipeDisplacement steps:kNumberOfPointsInSwipePath]; | ||
} | ||
|
||
- (void)pullToRefreshViewWithAccessibilityLabel:(NSString *)label | ||
{ | ||
[self pullToRefreshViewWithAccessibilityLabel:label value:nil pullDownDuration:0 traits:UIAccessibilityTraitNone]; | ||
[self pullToRefreshViewWithAccessibilityLabel:label value:nil pullDownDuration:0 traits:UIAccessibilityTraitNone]; | ||
} | ||
|
||
- (void)pullToRefreshViewWithAccessibilityLabel:(NSString *)label pullDownDuration:(KIFPullToRefreshTiming) pullDownDuration | ||
{ | ||
[self pullToRefreshViewWithAccessibilityLabel:label value:nil pullDownDuration:pullDownDuration traits:UIAccessibilityTraitNone]; | ||
[self pullToRefreshViewWithAccessibilityLabel:label value:nil pullDownDuration:pullDownDuration traits:UIAccessibilityTraitNone]; | ||
} | ||
|
||
- (void)pullToRefreshViewWithAccessibilityLabel:(NSString *)label value:(NSString *)value | ||
{ | ||
[self pullToRefreshViewWithAccessibilityLabel:label value:value pullDownDuration:0 traits:UIAccessibilityTraitNone]; | ||
[self pullToRefreshViewWithAccessibilityLabel:label value:value pullDownDuration:0 traits:UIAccessibilityTraitNone]; | ||
} | ||
|
||
- (void)pullToRefreshViewWithAccessibilityLabel:(NSString *)label value:(NSString *)value pullDownDuration:(KIFPullToRefreshTiming) pullDownDuration traits:(UIAccessibilityTraits)traits | ||
{ | ||
UIView *viewToSwipe = nil; | ||
UIAccessibilityElement *element = nil; | ||
UIView *viewToSwipe = nil; | ||
UIAccessibilityElement *element = nil; | ||
|
||
[self waitForAccessibilityElement:&element view:&viewToSwipe withLabel:label value:value traits:traits tappable:YES]; | ||
[self waitForAccessibilityElement:&element view:&viewToSwipe withLabel:label value:value traits:traits tappable:YES]; | ||
|
||
[self pullToRefreshAccessibilityElement:element inView:viewToSwipe pullDownDuration:pullDownDuration]; | ||
[self pullToRefreshAccessibilityElement:element inView:viewToSwipe pullDownDuration:pullDownDuration]; | ||
} | ||
|
||
- (void)pullToRefreshAccessibilityElement:(UIAccessibilityElement *)element inView:(UIView *)viewToSwipe pullDownDuration:(KIFPullToRefreshTiming) pullDownDuration | ||
{ | ||
//Based on swipeAccessibilityElement | ||
//Based on swipeAccessibilityElement | ||
|
||
const NSUInteger kNumberOfPointsInSwipePath = pullDownDuration ? pullDownDuration : KIFPullToRefreshInAboutAHalfSecond; | ||
const NSUInteger kNumberOfPointsInSwipePath = pullDownDuration ? pullDownDuration : KIFPullToRefreshInAboutAHalfSecond; | ||
|
||
// Can handle only the touchable space. | ||
CGRect elementFrame = [viewToSwipe convertRect:viewToSwipe.bounds toView:[UIApplication sharedApplication].keyWindow.rootViewController.view]; | ||
CGPoint swipeStart = CGPointCenteredInRect(elementFrame); | ||
CGPoint swipeDisplacement = CGPointMake(CGRectGetMidX(elementFrame), CGRectGetMaxY(elementFrame)); | ||
CGPoint swipeDisplacement = CGPointMake(CGRectGetMidX(elementFrame), CGRectGetMaxY(elementFrame)); | ||
|
||
[viewToSwipe dragFromPoint:swipeStart displacement:swipeDisplacement steps:kNumberOfPointsInSwipePath]; | ||
[viewToSwipe dragFromPoint:swipeStart displacement:swipeDisplacement steps:kNumberOfPointsInSwipePath]; | ||
} | ||
|
||
- (void)scrollViewWithAccessibilityLabel:(NSString *)label byFractionOfSizeHorizontal:(CGFloat)horizontalFraction vertical:(CGFloat)verticalFraction | ||
|
@@ -1182,13 +1204,24 @@ - (void)scrollAccessibilityElement:(UIAccessibilityElement *)element inView:(UIV | |
- (void)waitForFirstResponderWithAccessibilityLabel:(NSString *)label | ||
{ | ||
[self runBlock:^KIFTestStepResult(NSError **error) { | ||
UIResponder *firstResponder = [[[UIApplication sharedApplication] keyWindow] firstResponder]; | ||
if ([firstResponder isKindOfClass:NSClassFromString(@"UISearchBarTextField")]) { | ||
do { | ||
firstResponder = [(UIView *)firstResponder superview]; | ||
} while (firstResponder && ![firstResponder isKindOfClass:[UISearchBar class]]); | ||
BOOL didMatch = NO; | ||
NSArray *firstResponders = [[UIApplication sharedApplication] firstResponders]; | ||
|
||
for (UIResponder *firstResponder in firstResponders) { | ||
UIResponder *foundResponder = firstResponder; | ||
if ([foundResponder isKindOfClass:NSClassFromString(@"UISearchBarTextField")]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :eyeroll: at this special case being here, but not for the other method w/ traits matching below... (no action for you, just noticing this while reviewing) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I noticed a lot of inconsistencies around There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general, |
||
do { | ||
foundResponder = [(UIView *)foundResponder superview]; | ||
} while (foundResponder && ![foundResponder isKindOfClass:[UISearchBar class]]); | ||
} | ||
|
||
if (foundResponder.accessibilityLabel == label || [foundResponder.accessibilityLabel isEqualToString:label]) { | ||
didMatch = YES; | ||
break; | ||
} | ||
} | ||
KIFTestWaitCondition([[firstResponder accessibilityLabel] isEqualToString:label], error, @"Expected accessibility label for first responder to be '%@', got '%@'", label, [firstResponder accessibilityLabel]); | ||
|
||
KIFTestWaitCondition(didMatch, error, @"Expected to find a first responder with the accessibility label '%@', got: %@", label, firstResponders); | ||
|
||
return KIFTestStepResultSuccess; | ||
}]; | ||
|
@@ -1197,13 +1230,26 @@ - (void)waitForFirstResponderWithAccessibilityLabel:(NSString *)label | |
- (void)waitForFirstResponderWithAccessibilityLabel:(NSString *)label traits:(UIAccessibilityTraits)traits | ||
{ | ||
[self runBlock:^KIFTestStepResult(NSError **error) { | ||
UIResponder *firstResponder = [[[UIApplication sharedApplication] keyWindow] firstResponder]; | ||
|
||
NSString *foundLabel = firstResponder.accessibilityLabel; | ||
BOOL didMatchLabel = NO; | ||
BOOL didMatchTraits = NO; | ||
NSArray *firstResponders = [[UIApplication sharedApplication] firstResponders]; | ||
|
||
for (UIResponder *firstResponder in firstResponders) { | ||
if (firstResponder.accessibilityLabel == label || [firstResponder.accessibilityLabel isEqualToString:label]) { | ||
didMatchLabel = YES; | ||
} | ||
if (firstResponder.accessibilityTraits & traits) { | ||
didMatchTraits = YES; | ||
} | ||
|
||
if (didMatchLabel && didMatchTraits) { | ||
break; | ||
} | ||
} | ||
|
||
// foundLabel == label checks for the case where both are nil. | ||
KIFTestWaitCondition(foundLabel == label || [foundLabel isEqualToString:label], error, @"Expected accessibility label for first responder to be '%@', got '%@'", label, foundLabel); | ||
KIFTestWaitCondition(firstResponder.accessibilityTraits & traits, error, @"Found first responder with accessibility label, but not traits. First responder: %@", firstResponder); | ||
KIFTestWaitCondition(didMatchLabel, error, @"Expected to find a first responder with the accessibility label '%@', got: %@", label, firstResponders); | ||
KIFTestWaitCondition(didMatchTraits, error, @"Expected to find a first responder with accessibility traits, got: %@", firstResponders); | ||
|
||
return KIFTestStepResultSuccess; | ||
}]; | ||
|
@@ -1392,43 +1438,43 @@ - (void)deactivateAppForDuration:(NSTimeInterval)duration { | |
|
||
-(void) tapStepperWithAccessibilityLabel: (NSString *)accessibilityLabel increment: (KIFStepperDirection) stepperDirection | ||
{ | ||
@autoreleasepool { | ||
UIView *view = nil; | ||
UIAccessibilityElement *element = nil; | ||
[self waitForAccessibilityElement:&element view:&view withLabel:accessibilityLabel value:nil traits:UIAccessibilityTraitNone tappable:YES]; | ||
[self tapStepperWithAccessibilityElement:element increment:stepperDirection inView:view]; | ||
} | ||
@autoreleasepool { | ||
UIView *view = nil; | ||
UIAccessibilityElement *element = nil; | ||
[self waitForAccessibilityElement:&element view:&view withLabel:accessibilityLabel value:nil traits:UIAccessibilityTraitNone tappable:YES]; | ||
[self tapStepperWithAccessibilityElement:element increment:stepperDirection inView:view]; | ||
} | ||
} | ||
|
||
//inspired by http://www.raywenderlich.com/61419/ios-ui-testing-with-kif | ||
- (void)tapStepperWithAccessibilityElement:(UIAccessibilityElement *)element increment: (KIFStepperDirection) stepperDirection inView:(UIView *)view | ||
{ | ||
[self runBlock:^KIFTestStepResult(NSError **error) { | ||
[self runBlock:^KIFTestStepResult(NSError **error) { | ||
|
||
KIFTestWaitCondition(view.isUserInteractionActuallyEnabled, error, @"View is not enabled for interaction: %@", view); | ||
KIFTestWaitCondition(view.isUserInteractionActuallyEnabled, error, @"View is not enabled for interaction: %@", view); | ||
|
||
CGPoint stepperPointToTap = [self tappablePointInElement:element andView:view]; | ||
|
||
switch (stepperDirection) | ||
{ | ||
case KIFStepperDirectionIncrement: | ||
stepperPointToTap.x += CGRectGetWidth(view.frame) / 4; | ||
break; | ||
case KIFStepperDirectionDecrement: | ||
stepperPointToTap.x -= CGRectGetWidth(view.frame) / 4; | ||
break; | ||
} | ||
switch (stepperDirection) | ||
{ | ||
case KIFStepperDirectionIncrement: | ||
stepperPointToTap.x += CGRectGetWidth(view.frame) / 4; | ||
break; | ||
case KIFStepperDirectionDecrement: | ||
stepperPointToTap.x -= CGRectGetWidth(view.frame) / 4; | ||
break; | ||
} | ||
|
||
// This is mostly redundant of the test in _accessibilityElementWithLabel: | ||
KIFTestWaitCondition(!isnan(stepperPointToTap.x), error, @"View is not tappable: %@", view); | ||
[view tapAtPoint:stepperPointToTap]; | ||
// This is mostly redundant of the test in _accessibilityElementWithLabel: | ||
KIFTestWaitCondition(!isnan(stepperPointToTap.x), error, @"View is not tappable: %@", view); | ||
[view tapAtPoint:stepperPointToTap]; | ||
|
||
KIFTestCondition(![view canBecomeFirstResponder] || [view isDescendantOfFirstResponder], error, @"Failed to make the view into the first responder: %@", view); | ||
KIFTestCondition(![view canBecomeFirstResponder] || [view isDescendantOfFirstResponder], error, @"Failed to make the view into the first responder: %@", view); | ||
|
||
return KIFTestStepResultSuccess; | ||
}]; | ||
return KIFTestStepResultSuccess; | ||
}]; | ||
|
||
[self waitForAnimationsToFinish]; | ||
[self waitForAnimationsToFinish]; | ||
} | ||
|
||
- (CGRect) elementFrameForElement:(UIAccessibilityElement *)element andView:(UIView *)view | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably should only do this from one element and not multiple.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean that I should revert this portion so it continues to only check
UIApplication.keyWindow
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess what I mean is to bail from the loop after clearing the text from some first responder. Probably would want to do the same as above and output a warning if there are multiple matching fields and we're only clearing the contents of one of them.