diff --git a/Integration Tests/Tests/SLActionSheetTest.m b/Integration Tests/Tests/SLActionSheetTest.m new file mode 100644 index 0000000..27994fe --- /dev/null +++ b/Integration Tests/Tests/SLActionSheetTest.m @@ -0,0 +1,82 @@ +// +// SLActionSheetTest.m +// Subliminal +// +// For details and documentation: +// http://github.com/inkling/Subliminal +// +// Copyright 2013-2014 Inkling Systems, 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 "SLIntegrationTest.h" + +// Popover action sheets are only supported on the iPad. +@interface SLActionSheetTest_iPad : SLIntegrationTest + +@end + +@implementation SLActionSheetTest_iPad + ++ (NSString *)testCaseViewControllerClassName { + return @"SLActionSheetTestViewController"; +} + +- (void)tearDownTestCaseWithSelector:(SEL)testCaseSelector { + SLAskApp(hideActionSheet); + + [super tearDownTestCaseWithSelector:testCaseSelector]; +} + +// this test isn't very specific but we can't confirm +// any other attributes of the popover independent of UIAutomation +// testDismiss shows that we are actually manipulating the popover +- (void)testCanMatchActionSheet { + SLAssertFalse([UIAElement([SLActionSheet currentActionSheet]) isValid], + @"There should not currently be any action sheets."); + + SLAskApp(showActionSheet); + + SLAssertTrue([UIAElement([SLActionSheet currentActionSheet]) isValid], + @"There should be an action sheet."); + BOOL expectedActionSheetVisibility = SLAskAppYesNo(isActionSheetVisible); + BOOL actualActionSheetVisibility = [UIAElement([SLActionSheet currentActionSheet]) isVisible]; + SLAssertTrue(expectedActionSheetVisibility == actualActionSheetVisibility, + @"The current action sheet had a mismatched visibility."); +} + +- (void)testDismiss { + SLAskApp(showActionSheet); + + SLAssertTrueWithTimeout(SLAskAppYesNo(isActionSheetVisible), 2.0, @"Action sheet should have become visible."); + [UIAElement([SLActionSheet currentActionSheet]) dismiss]; + SLAssertFalse(SLAskAppYesNo(isActionSheetVisible), @"Action sheet should have been dismissed."); +} + +- (void)testClickButtonAtIndex { + SLAskApp(showActionSheet); + + SLAssertTrueWithTimeout(SLAskAppYesNo(isActionSheetVisible), 2.0, @"Action sheet should have become visible."); + + SLActionSheet *actionSheet = [SLActionSheet currentActionSheet]; + + SLAssertTrue([UIAElement(actionSheet) isValid], + @"There should be an action sheet."); + + [UIAElement(actionSheet) clickButtonWithAccessibilityLabel:@"OK"]; + + SLAssertFalse(SLAskAppYesNo(isActionSheetVisible), @"Action sheet should have been dismissed."); +} + +@end diff --git a/Integration Tests/Tests/SLActionSheetTestViewController.m b/Integration Tests/Tests/SLActionSheetTestViewController.m new file mode 100644 index 0000000..f54655a --- /dev/null +++ b/Integration Tests/Tests/SLActionSheetTestViewController.m @@ -0,0 +1,110 @@ +// +// SLActionSheetTestViewController.m +// Subliminal +// +// For details and documentation: +// http://github.com/inkling/Subliminal +// +// Copyright 2013-2014 Inkling Systems, 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 "SLTestCaseViewController.h" + +#import + +@interface SLActionSheetTestViewController : SLTestCaseViewController + +@end + +@implementation SLActionSheetTestViewController { + UILabel *_label; + UIActionSheet *_actionSheet; + BOOL _actionSheetVisible; +} + +- (void)loadViewForTestCase:(SEL)testCase { + // Since we're testing popovers in this test, + // we don't need any particular view. + UIView *view = [[UIView alloc] initWithFrame:(CGRect){CGPointZero, CGSizeMake(200.0f, 200.0f)}]; + view.backgroundColor = [UIColor whiteColor]; + + UIFont *nothingToShowHereFont = [UIFont systemFontOfSize:18.0f]; + NSString *nothingToShowHereText = @"Nothing to show here."; + CGSize nothingToShowHereSize = [nothingToShowHereText sizeWithFont:nothingToShowHereFont + constrainedToSize:CGSizeMake(3 * CGRectGetWidth(view.bounds) / 4.0f, CGFLOAT_MAX)]; + _label = [[UILabel alloc] initWithFrame:(CGRect){CGPointZero, nothingToShowHereSize}]; + _label.backgroundColor = view.backgroundColor; + _label.font = nothingToShowHereFont; + _label.numberOfLines = 0; + _label.text = nothingToShowHereText; + _label.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin; + + [view addSubview:_label]; + _label.center = CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds)); + + self.view = view; +} + +- (instancetype)initWithTestCaseWithSelector:(SEL)testCase { + self = [super initWithTestCaseWithSelector:testCase]; + if (self) { + [[SLTestController sharedTestController] registerTarget:self forAction:@selector(showActionSheet)]; + [[SLTestController sharedTestController] registerTarget:self forAction:@selector(isActionSheetVisible)]; + [[SLTestController sharedTestController] registerTarget:self forAction:@selector(hideActionSheet)]; + } + return self; +} + +- (void)dealloc { + [[SLTestController sharedTestController] deregisterTarget:self]; +} + +#pragma mark - App hooks + +- (void)showActionSheet { + // Pass in nil for the cancelButtonTitle if displaying on an iPad + _actionSheet = [[UIActionSheet alloc] initWithTitle:@"Action Sheet" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:@"OK", nil]; + + [_actionSheet showFromRect:_label.frame inView:self.view animated:NO]; +} + +- (NSNumber *)isActionSheetVisible { + return @(_actionSheetVisible); +} + +- (void)hideActionSheet { + [_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:NO]; + _actionSheetVisible = NO; +} + + +#pragma mark - UIActionSheetDelegate + +- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex +{ + _actionSheetVisible = NO; +} + +- (void)didPresentActionSheet:(UIActionSheet *)actionSheet +{ + _actionSheetVisible = YES; +} + +- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex +{ + _actionSheetVisible = NO; +} + +@end diff --git a/Sources/Classes/UIAutomation/User Interface Elements/SLActionSheet.h b/Sources/Classes/UIAutomation/User Interface Elements/SLActionSheet.h new file mode 100644 index 0000000..5166aaa --- /dev/null +++ b/Sources/Classes/UIAutomation/User Interface Elements/SLActionSheet.h @@ -0,0 +1,54 @@ +// +// SLActionSheet.h +// Subliminal +// +// For details and documentation: +// http://github.com/inkling/Subliminal +// +// Copyright 2013-2014 Inkling Systems, 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 "SLStaticElement.h" + +/** + SLActionSheet provides methods for accessing and manipulating the current action sheet. + */ +@interface SLActionSheet : SLStaticElement + +/** + Returns an element representing the action sheet currently shown by the application, + if any. + + This element will be (valid)[-isValid] if and only if the application + is currently showing a action sheet. + */ ++ (instancetype)currentActionSheet; + +/** + Dismisses the specified action sheet by tapping outside the action sheet + and within the region defined for dismissal. + + This method will block until the action sheet has been fully dismissed. + */ +- (void)dismiss; + +/** + Dismisses the specified action sheet by clicking on the button with the specified label. + + This method will block until the action sheet has been fully dismissed. + */ +- (void)clickButtonWithAccessibilityLabel:(NSString *)label; + +@end diff --git a/Sources/Classes/UIAutomation/User Interface Elements/SLActionSheet.m b/Sources/Classes/UIAutomation/User Interface Elements/SLActionSheet.m new file mode 100644 index 0000000..bebdd16 --- /dev/null +++ b/Sources/Classes/UIAutomation/User Interface Elements/SLActionSheet.m @@ -0,0 +1,52 @@ +// +// SLActionSheet.m +// Subliminal +// +// For details and documentation: +// http://github.com/inkling/Subliminal +// +// Copyright 2013-2014 Inkling Systems, 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 "SLActionSheet.h" +#import "SLUIAElement+Subclassing.h" +#import "SLPopover.h" + +@implementation SLActionSheet + ++ (instancetype)currentActionSheet { + return [[SLActionSheet alloc] initWithUIARepresentation:@"UIATarget.localTarget().frontMostApp().mainWindow().popover().actionSheet()"]; +} + +- (void)dismiss { + /* + This action sheet is inside the current popover, so dismiss that popover + */ + [[SLPopover currentPopover] dismiss]; +} + +- (void)clickButtonWithAccessibilityLabel:(NSString *)label +{ + if ([[[UIDevice currentDevice] systemVersion] compare:@"8.0.0" options:NSNumericSearch] != NSOrderedAscending) { + [self waitUntilTappable:NO thenSendMessage:@"collectionViews()[0].cells()[\"%@\"].buttons()[\"%@\"].tap()", label, label]; + } else { + [self waitUntilTappable:NO thenSendMessage:@"buttons()[\"%@\"].tap()", label]; + } + + // wait for the dismissal animation to finish + [NSThread sleepForTimeInterval:0.5]; +} + +@end diff --git a/Sources/Subliminal.h b/Sources/Subliminal.h index 33fcb34..dd893fd 100644 --- a/Sources/Subliminal.h +++ b/Sources/Subliminal.h @@ -30,6 +30,7 @@ #import "NSObject+SLAccessibilityDescription.h" #import "NSObject+SLAccessibilityHierarchy.h" #import "SLStaticElement.h" +#import "SLActionSheet.h" #import "SLAlert.h" #import "SLButton.h" #import "SLDatePicker.h" diff --git a/Subliminal.xcodeproj/project.pbxproj b/Subliminal.xcodeproj/project.pbxproj index cb21be7..23aead5 100644 --- a/Subliminal.xcodeproj/project.pbxproj +++ b/Subliminal.xcodeproj/project.pbxproj @@ -66,6 +66,10 @@ CAC388061641CD7500F995F9 /* SLStringUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = CAC388041641CD7500F995F9 /* SLStringUtilities.m */; }; CAC3883F1643503C00F995F9 /* NSObject+SLAccessibilityHierarchy.h in Headers */ = {isa = PBXBuildFile; fileRef = CAC3883D1643503C00F995F9 /* NSObject+SLAccessibilityHierarchy.h */; settings = {ATTRIBUTES = (Public, ); }; }; CAC388401643503C00F995F9 /* NSObject+SLAccessibilityHierarchy.m in Sources */ = {isa = PBXBuildFile; fileRef = CAC3883E1643503C00F995F9 /* NSObject+SLAccessibilityHierarchy.m */; }; + D98DA60C1A2FD4F0000A11BB /* SLActionSheetTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D98DA60A1A2FD4ED000A11BB /* SLActionSheetTest.m */; }; + D98DA60E1A2FD539000A11BB /* SLActionSheetTestViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D98DA60D1A2FD538000A11BB /* SLActionSheetTestViewController.m */; }; + D9D37ED21A2F9F4500CCEC70 /* SLActionSheet.h in Headers */ = {isa = PBXBuildFile; fileRef = D9D37ED01A2F9F4500CCEC70 /* SLActionSheet.h */; }; + D9D37ED31A2F9F4500CCEC70 /* SLActionSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = D9D37ED11A2F9F4500CCEC70 /* SLActionSheet.m */; }; DB2627DE17B96727009DA3A6 /* SLStatusBarTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DB2627DC17B96727009DA3A6 /* SLStatusBarTest.m */; }; DB2627DF17B96727009DA3A6 /* SLStatusBarTestViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DB2627DD17B96727009DA3A6 /* SLStatusBarTestViewController.m */; }; DB2627E117B96974009DA3A6 /* SLStatusBarTestViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DB2627E017B96974009DA3A6 /* SLStatusBarTestViewController.xib */; }; @@ -319,6 +323,10 @@ CAC388041641CD7500F995F9 /* SLStringUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLStringUtilities.m; sourceTree = ""; }; CAC3883D1643503C00F995F9 /* NSObject+SLAccessibilityHierarchy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+SLAccessibilityHierarchy.h"; sourceTree = ""; }; CAC3883E1643503C00F995F9 /* NSObject+SLAccessibilityHierarchy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+SLAccessibilityHierarchy.m"; sourceTree = ""; }; + D98DA60A1A2FD4ED000A11BB /* SLActionSheetTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLActionSheetTest.m; sourceTree = ""; }; + D98DA60D1A2FD538000A11BB /* SLActionSheetTestViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLActionSheetTestViewController.m; sourceTree = ""; }; + D9D37ED01A2F9F4500CCEC70 /* SLActionSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLActionSheet.h; sourceTree = ""; }; + D9D37ED11A2F9F4500CCEC70 /* SLActionSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLActionSheet.m; sourceTree = ""; }; DB2627DC17B96727009DA3A6 /* SLStatusBarTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLStatusBarTest.m; sourceTree = ""; }; DB2627DD17B96727009DA3A6 /* SLStatusBarTestViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLStatusBarTestViewController.m; sourceTree = ""; }; DB2627E017B96974009DA3A6 /* SLStatusBarTestViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SLStatusBarTestViewController.xib; sourceTree = ""; }; @@ -606,6 +614,15 @@ path = Internal; sourceTree = ""; }; + D98DA6061A2FD4BF000A11BB /* SLActionSheet Tests */ = { + isa = PBXGroup; + children = ( + D98DA60A1A2FD4ED000A11BB /* SLActionSheetTest.m */, + D98DA60D1A2FD538000A11BB /* SLActionSheetTestViewController.m */, + ); + name = "SLActionSheet Tests"; + sourceTree = ""; + }; DB2627D817B96704009DA3A6 /* SLStatusBar Tests */ = { isa = PBXGroup; children = ( @@ -853,6 +870,8 @@ CAC3883E1643503C00F995F9 /* NSObject+SLAccessibilityHierarchy.m */, F089F98417445D9A00DF1F25 /* SLStaticElement.h */, F089F98517445D9A00DF1F25 /* SLStaticElement.m */, + D9D37ED01A2F9F4500CCEC70 /* SLActionSheet.h */, + D9D37ED11A2F9F4500CCEC70 /* SLActionSheet.m */, F0C07A361703F95B00C93F93 /* SLAlert.h */, F0C07A371703F95B00C93F93 /* SLAlert.m */, F0C07A451703FEF600C93F93 /* SLButton.h */, @@ -979,6 +998,7 @@ children = ( DB2627D817B96704009DA3A6 /* SLStatusBar Tests */, 50A59BD317848CEE002A863A /* SLGeometry Tests */, + D98DA6061A2FD4BF000A11BB /* SLActionSheet Tests */, F07DA31F16E439B7004C2282 /* SLAlert Tests */, F00800BF174B2CB0001927AC /* SLButton Tests */, 622DA0AD194CCA7500EFFE05 /* SLDatePicker Tests */, @@ -1112,6 +1132,7 @@ F05C51E5171C8AE000A381BC /* SLMainThreadRef.h in Headers */, F0A04E1D1749F70F002C7520 /* SLElement.h in Headers */, 2CE9AA4C17E3A747007EF0B5 /* SLSwitch.h in Headers */, + D9D37ED21A2F9F4500CCEC70 /* SLActionSheet.h in Headers */, F089F98617445D9A00DF1F25 /* SLStaticElement.h in Headers */, F02DF30817EC064F00BE28BF /* UIScrollView+SLProgrammaticScrolling.h in Headers */, F00800CE174C1C64001927AC /* SLPopover.h in Headers */, @@ -1384,6 +1405,7 @@ 2CE9AA4D17E3A747007EF0B5 /* SLSwitch.m in Sources */, 62E7A634193EF84C00CB11AB /* SLStaticText.m in Sources */, 50F3E18E1783A60100C6BD1B /* SLGeometry.m in Sources */, + D9D37ED31A2F9F4500CCEC70 /* SLActionSheet.m in Sources */, F0695DE4160138DF000B05D0 /* SLUIAElement.m in Sources */, F0695DE5160138DF000B05D0 /* SLLogger.m in Sources */, F0695DE7160138DF000B05D0 /* SLTerminal.m in Sources */, @@ -1425,6 +1447,7 @@ F0AC80BD16BB50FF00C5D5C0 /* SLTestsViewController.m in Sources */, 6256A2D519476E1F00C6507F /* SLStaticTextTestViewController.m in Sources */, F0AC80C416BC355D00C5D5C0 /* SLTerminalTest.m in Sources */, + D98DA60C1A2FD4F0000A11BB /* SLActionSheetTest.m in Sources */, F0AC80C116BB559F00C5D5C0 /* SLIntegrationTest.m in Sources */, F0AC80C816BC367E00C5D5C0 /* SLTestViewController.m in Sources */, F0AC80E616BCF91800C5D5C0 /* SLTestCaseViewController.m in Sources */, @@ -1460,6 +1483,7 @@ F089F99517458BA300DF1F25 /* SLWindowTestViewController.m in Sources */, F089F9E21745FB7E00DF1F25 /* SLKeyboardTest.m in Sources */, F089F9E31745FB7E00DF1F25 /* SLKeyboardTestViewController.m in Sources */, + D98DA60E1A2FD539000A11BB /* SLActionSheetTestViewController.m in Sources */, F05D2B061746B55C0089DB9E /* SLStaticElementTest.m in Sources */, F05D2B071746B55C0089DB9E /* SLStaticElementTestViewController.m in Sources */, F00800C7174B3349001927AC /* SLButtonTest.m in Sources */,