Skip to content

Commit

Permalink
Merge pull request #47 from inkling/jeff/identify_actionsheets
Browse files Browse the repository at this point in the history
Ensure that action sheets are included in accessibility paths. (refs #46...
  • Loading branch information
aegolden committed Jul 22, 2013
2 parents 79c2fe5 + 46f8918 commit 450f37c
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 9 deletions.
29 changes: 28 additions & 1 deletion Integration Tests/Tests/SLElementMatchingTest.m
Expand Up @@ -46,8 +46,12 @@ - (void)setUpTestCaseWithSelector:(SEL)testCaseSelector {
}

- (void)tearDownTestCaseWithSelector:(SEL)testCaseSelector {
if (testCaseSelector == @selector(testMatchingPopoverChildElement_iPad)) {
// popovers must be hidden before they are deallocated or else will raise an exception
if ((testCaseSelector == @selector(testMatchingPopoverChildElement_iPad)) ||
(testCaseSelector == @selector(testMatchingButtonsOfActionSheetsInPopovers_iPad))){
SLAskApp(hidePopover);
} else if (testCaseSelector == @selector(testMatchingActionSheetButtons)) {
SLAskApp(hideActionSheet);
}
[super tearDownTestCaseWithSelector:testCaseSelector];
}
Expand Down Expand Up @@ -307,6 +311,29 @@ - (void)testMatchingTabBarButtons {
SLAssertTrue([actualLabel isEqualToString:expectedLabel], @"Did not match button as expected.");
}

#pragma mark - Action sheets

- (void)testMatchingActionSheetButtons {
SLAskApp(showActionSheet);

NSString *actualLabel, *expectedLabel = @"Cancel";
SLButton *cancelButton = [SLButton elementWithAccessibilityLabel:expectedLabel];
SLAssertNoThrow(actualLabel = [UIAElement(cancelButton) label], @"Could not retrieve button's label.");
SLAssertTrue([actualLabel isEqualToString:expectedLabel], @"Did not match button as expected.");
}

// Somewhat of an internal test--when a popover shows an action sheet,
// that changes the popover's accessibility structure in a way that
// once caused Subliminal to misidentify the action sheet
- (void)testMatchingButtonsOfActionSheetsInPopovers_iPad {
SLAskApp(showPopoverWithActionSheet);

NSString *actualLabel, *expectedLabel = @"Popover Cancel";
SLButton *cancelButton = [SLButton elementWithAccessibilityLabel:expectedLabel];
SLAssertNoThrow(actualLabel = [UIAElement(cancelButton) label], @"Could not retrieve button's label.");
SLAssertTrue([actualLabel isEqualToString:expectedLabel], @"Did not match button as expected.");
}

#pragma mark - Internal tests

// Subliminal replaces the accessibility identifiers of objects in the accessibility
Expand Down
42 changes: 40 additions & 2 deletions Integration Tests/Tests/SLElementMatchingTestViewController.m
Expand Up @@ -114,6 +114,7 @@ @interface SLElementMatchingTestViewController () <UITableViewDataSource, UITabl
@property (weak, nonatomic) IBOutlet UIButton *barButton;
@property (weak, nonatomic) IBOutlet UISearchBar *searchBar;
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet UITabBar *tabBar;

@end

Expand All @@ -127,6 +128,8 @@ @implementation SLElementMatchingTestViewController {
BOOL _webViewDidFinishLoad;

UIPopoverController *_popoverController;

UIActionSheet *_actionSheet;
}

+ (NSString *)nibNameForTestCase:(SEL)testCase {
Expand All @@ -143,7 +146,9 @@ + (NSString *)nibNameForTestCase:(SEL)testCase {
(testCase == @selector(testSubliminalRestoresAccessibilityIdentifiersAfterMatching)) ||
(testCase == @selector(testSubliminalRestoresAccessibilityIdentifiersAfterMatchingEvenIfActionThrows)) ||
(testCase == @selector(testMatchingPopoverChildElement_iPad)) ||
(testCase == @selector(testMatchingTabBarButtons))) {
(testCase == @selector(testMatchingTabBarButtons)) ||
(testCase == @selector(testMatchingActionSheetButtons)) ||
(testCase == @selector(testMatchingButtonsOfActionSheetsInPopovers_iPad))) {
return @"SLElementMatchingTestViewController";
} else if ((testCase == @selector(testMatchingTableViewCellTextLabel)) ||
(testCase == @selector(testMatchingTableViewCellWithCombinedLabel)) ||
Expand All @@ -168,6 +173,9 @@ - (instancetype)initWithTestCaseWithSelector:(SEL)testCase {
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(barButtonIdentifier)];
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(webViewDidFinishLoad)];
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(showPopover)];
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(showPopoverWithActionSheet)];
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(showActionSheet)];
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(hideActionSheet)];
}
return self;
}
Expand Down Expand Up @@ -362,7 +370,7 @@ - (NSNumber *)webViewDidFinishLoad {
return @(_webViewDidFinishLoad);
}

- (void)showPopover {
- (void)showPopoverWithActionSheet:(BOOL)showActionSheet {
// Inception!
SLElementMatchingTestViewController *contentViewController = [[SLElementMatchingTestViewController alloc] initWithTestCaseWithSelector:self.testCase];

Expand All @@ -375,10 +383,40 @@ - (void)showPopover {

// register this here vs. in init so the controller we just presented doesn't steal it
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(hidePopover)];

if (showActionSheet) {
UIActionSheet *testSheet = [[UIActionSheet alloc] initWithTitle:@"Test Sheet"
delegate:nil
cancelButtonTitle:@"Popover Cancel"
destructiveButtonTitle:@"Destruct"
otherButtonTitles:nil];
[testSheet showInView:_popoverController.contentViewController.view];
}
}

- (void)showPopover {
[self showPopoverWithActionSheet:NO];
}

- (void)showPopoverWithActionSheet {
[self showPopoverWithActionSheet:YES];
}

- (void)hidePopover {
[_popoverController dismissPopoverAnimated:NO];
}

- (void)showActionSheet {
_actionSheet = [[UIActionSheet alloc] initWithTitle:@"Test Sheet"
delegate:nil
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:@"Destruct"
otherButtonTitles:nil];
[_actionSheet showFromTabBar:self.tabBar];
}

- (void)hideActionSheet {
[_actionSheet dismissWithClickedButtonIndex:0 animated:NO];
}

@end
18 changes: 16 additions & 2 deletions Integration Tests/Tests/SLElementMatchingTestViewController.xib
Expand Up @@ -94,7 +94,7 @@
<string key="NSFrame">{{20, 167}, {280, 44}}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
<reference key="NSNextKeyView" ref="416575555"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
Expand All @@ -117,6 +117,7 @@
<string key="NSFrame">{{0, 455}, {320, 49}}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
Expand Down Expand Up @@ -206,6 +207,14 @@
</object>
<int key="connectionID">37</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">tabBar</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="416575555"/>
</object>
<int key="connectionID">51</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
Expand Down Expand Up @@ -297,7 +306,7 @@
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">50</int>
<int key="maxID">51</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
Expand All @@ -308,6 +317,7 @@
<string key="barButton">UIButton</string>
<string key="fooButton">UIButton</string>
<string key="searchBar">UISearchBar</string>
<string key="tabBar">UITabBar</string>
<string key="tableView">UITableView</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
Expand All @@ -323,6 +333,10 @@
<string key="name">searchBar</string>
<string key="candidateClassName">UISearchBar</string>
</object>
<object class="IBToOneOutletInfo" key="tabBar">
<string key="name">tabBar</string>
<string key="candidateClassName">UITabBar</string>
</object>
<object class="IBToOneOutletInfo" key="tableView">
<string key="name">tableView</string>
<string key="candidateClassName">UITableView</string>
Expand Down
Expand Up @@ -211,10 +211,6 @@ - (BOOL)classForcesPresenceInAccessibilityHierarchy {
}
if (isTableViewSectionElement) return YES;

// _UIPopoverView is identified by its parent's label.
BOOL isPopover = [[parent accessibilityLabel] isEqualToString:@"dismiss popup"];
if (isPopover) return YES;

return NO;
}

Expand Down Expand Up @@ -242,6 +238,17 @@ - (NSArray *)slChildAccessibilityElementsFavoringSubviews:(BOOL)favoringSubviews
}
}

- (BOOL)classForcesPresenceInAccessibilityHierarchy {
if ([super classForcesPresenceInAccessibilityHierarchy]) return YES;

// Identify _UIPopoverView by its first subview being a popover background view.
BOOL isPopover = NO;
if ([self.subviews count]) {
isPopover = [self.subviews[0] isKindOfClass:[UIPopoverBackgroundView class]];
}
return isPopover;
}

// An object is a mock view if its accessibilityIdentifier tracks
// the accessibilityIdentifier of the view.
+ (BOOL)elementObject:(id)elementObject isMockingViewObject:(id)viewObject {
Expand Down Expand Up @@ -330,6 +337,13 @@ - (BOOL)classForcesPresenceInAccessibilityHierarchy {
@end


@implementation UIActionSheet (SLAccessibilityHierarchy)
- (BOOL)classForcesPresenceInAccessibilityHierarchy {
return YES;
}
@end


@implementation UINavigationBar (SLAccessibilityHierarchy)
- (BOOL)classForcesPresenceInAccessibilityHierarchy {
return YES;
Expand Down

0 comments on commit 450f37c

Please sign in to comment.