Skip to content

Commit

Permalink
Merge pull request #61 from inkling/jeff/tap_at_activation_point
Browse files Browse the repository at this point in the history
It is now possible to tap at an element's accessibility activation point...
  • Loading branch information
aegolden committed Aug 14, 2013
2 parents b738b23 + 5f7c7c9 commit fc1dee1
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 0 deletions.
15 changes: 15 additions & 0 deletions Integration Tests/Tests/SLElementStateTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ - (void)setUpTest {
_testElement = [SLElement elementWithAccessibilityLabel:@"Test Element"];
}

- (void)setUpTestCaseWithSelector:(SEL)testCaseSelector {
[super setUpTestCaseWithSelector:testCaseSelector];
if (testCaseSelector == @selector(testHitpointDoesNotReturnAccessibilityActivationPoint)) {
SLAskApp(modifyActivationPoint);
}
}

- (void)testLabel {
NSString *expectedLabel = SLAskApp(elementLabel);
NSString *label = [UIAElement(_testElement) label];
Expand Down Expand Up @@ -94,6 +101,14 @@ - (void)testHitpointReturnsNullPointIfElementIsCovered {
SLAssertTrue(SLCGPointIsNull(hitpoint), @"-hitpoint did not return expected value.");
}

// This test case justifies the development of `-[SLElement tapAtActivationPoint]`.
- (void)testHitpointDoesNotReturnAccessibilityActivationPoint {
CGPoint hitpoint = [UIAElement(_testElement) hitpoint];
CGPoint activationPoint = [SLAskApp(activationPoint) CGPointValue];
SLAssertFalse(CGPointEqualToPoint(hitpoint, activationPoint),
@"-hitpoint should not have returned the button's modified activation point.");
}

- (void)testElementIsTappableIfItHasANonNullHitpoint {
CGPoint hitpoint = [UIAElement(_testElement) hitpoint];
SLAssertTrue(SLCGPointIsNull(hitpoint), @"-hitpoint did not return expected value.");
Expand Down
14 changes: 14 additions & 0 deletions Integration Tests/Tests/SLElementStateTestViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ - (void)loadViewForTestCase:(SEL)testCase {
testCase == @selector(testValue) ||
testCase == @selector(testIsEnabledMirrorsUIControlIsEnabledWhenMatchingObjectIsUIControl) ||
testCase == @selector(testHitpointReturnsRectMidpointByDefault) ||
testCase == @selector(testHitpointDoesNotReturnAccessibilityActivationPoint) ||
testCase == @selector(testRect)) {
UIView *view = [[UIView alloc] initWithFrame:self.navigationController.view.bounds];
view.backgroundColor = [UIColor whiteColor];
Expand Down Expand Up @@ -119,6 +120,8 @@ - (instancetype)initWithTestCaseWithSelector:(SEL)testCase {
[testController registerTarget:self forAction:@selector(elementValue)];
[testController registerTarget:self forAction:@selector(disableElement)];
[testController registerTarget:self forAction:@selector(enableElement)];
[testController registerTarget:self forAction:@selector(modifyActivationPoint)];
[testController registerTarget:self forAction:@selector(activationPoint)];
[testController registerTarget:self forAction:@selector(uncoverTestView)];
[testController registerTarget:self forAction:@selector(makeTextFieldFirstResponder)];
[testController registerTarget:self forAction:@selector(elementRect)];
Expand Down Expand Up @@ -148,6 +151,17 @@ - (void)enableElement {
_button.enabled = YES;
}

- (void)modifyActivationPoint {
_button.accessibilityActivationPoint = (CGPoint){
.x = _button.accessibilityActivationPoint.x - 15.0f,
.y = _button.accessibilityActivationPoint.y - 15.0f
};
}

- (NSValue *)activationPoint {
return [NSValue valueWithCGPoint:_button.accessibilityActivationPoint];
}

- (void)uncoverTestView {
_coveringView.hidden = YES;
}
Expand Down
11 changes: 11 additions & 0 deletions Integration Tests/Tests/SLElementTapTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ - (void)setUpTestCaseWithSelector:(SEL)testCaseSelector {
testCaseSelector == @selector(testWaitUntilTappableYESThenPerformActionWithUIARepresentationWaitsUntilTappable) ||
testCaseSelector == @selector(testWaitUntilTappableYESThenPerformActionWithUIARepresentationThrowsIfElementIsNotTappableAtEndOfTimeout)) {
SLAskApp(hideTestView); // to make the test view not tappable
} else if (testCaseSelector == @selector(testTapAtActivationPointOccursAtActivationPoint)) {
SLAskApp(modifyActivationPoint); // to make sure that we're not just tapping at the hitpoint
}
}

Expand Down Expand Up @@ -95,6 +97,15 @@ - (void)testTapOccursAtHitpoint {
NSStringFromCGPoint(tapPoint), NSStringFromCGPoint(expectedTapPoint));
}

- (void)testTapAtActivationPointOccursAtActivationPoint {
[UIAElement(_testElement) tapAtActivationPoint];
CGPoint tapPoint = [SLAskApp(tapPoint) CGPointValue];
CGPoint expectedTapPoint = [SLAskApp(activationPoint) CGPointValue];
SLAssertTrue(CGPointEqualToPoint(tapPoint, expectedTapPoint),
@"-tapAtActivationPoint tapped the element at %@, not at %@ as expected.",
NSStringFromCGPoint(tapPoint), NSStringFromCGPoint(expectedTapPoint));
}

#pragma mark - Internal tests

// If UIAutomation is asked to simulate user interaction with an untappable element,
Expand Down
13 changes: 13 additions & 0 deletions Integration Tests/Tests/SLElementTapTestViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ - (instancetype)initWithTestCaseWithSelector:(SEL)testCase {
self = [super initWithTestCaseWithSelector:testCase];
if (self) {
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(tapPoint)];
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(modifyActivationPoint)];
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(activationPoint)];
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(scrollViewButtonWasTapped)];
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(resetTapRecognition)];
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(hideTestView)];
Expand Down Expand Up @@ -121,6 +123,17 @@ - (NSValue *)tapPoint {
else return [NSValue valueWithCGPoint:_tapPoint];
}

- (void)modifyActivationPoint {
_testView.accessibilityActivationPoint = (CGPoint){
.x = _testView.accessibilityActivationPoint.x - 25.0f,
.y = _testView.accessibilityActivationPoint.y - 25.0f
};
}

- (NSValue *)activationPoint {
return [NSValue valueWithCGPoint:_testView.accessibilityActivationPoint];
}

- (NSNumber *)scrollViewButtonWasTapped {
return @(_scrollViewButtonWasTapped);
}
Expand Down
16 changes: 16 additions & 0 deletions Sources/Classes/UIAutomation/User Interface Elements/SLElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,22 @@
*/
+ (instancetype)anyElement;

#pragma mark - Gestures and Actions
/// ------------------------------------------
/// @name Gestures and Actions
/// ------------------------------------------

/**
Taps the specified element at its activation point.
The activation point is by default the midpoint of the accessibility element's
frame (`-rect`), same as the element's hitpoint; but unlike the hitpoint,
the activation point may be modified, to direct VoiceOver to tap on a certain
region of the element. See `-[NSObject (UIAccessibility) accessibilityActivationPoint]`
for more information and examples.
*/
- (void)tapAtActivationPoint;

@end


Expand Down
17 changes: 17 additions & 0 deletions Sources/Classes/UIAutomation/User Interface Elements/SLElement.m
Original file line number Diff line number Diff line change
Expand Up @@ -274,4 +274,21 @@ - (BOOL)isVisible {
return isVisible;
}

#pragma mark -

- (void)tapAtActivationPoint {
__block CGPoint activationPoint;
__block CGRect accessibilityFrame;
[self examineMatchingObject:^(NSObject *object) {
activationPoint = [object accessibilityActivationPoint];
accessibilityFrame = [object accessibilityFrame];
}];

CGPoint activationOffset = (CGPoint){
.x = (activationPoint.x - CGRectGetMinX(accessibilityFrame)) / CGRectGetWidth(accessibilityFrame),
.y = (activationPoint.y - CGRectGetMinY(accessibilityFrame)) / CGRectGetHeight(accessibilityFrame)
};
[self waitUntilTappable:YES thenSendMessage:@"tapWithOptions({tapOffset:{x:%g, y:%g}})", activationOffset.x, activationOffset.y];
}

@end

0 comments on commit fc1dee1

Please sign in to comment.