Skip to content

Commit

Permalink
Merge pull request #243 from inkling/jeff/action_sheets_and_nav_bars
Browse files Browse the repository at this point in the history
Add support for manipulating action sheets and nav bars.
  • Loading branch information
Axlle committed Sep 5, 2014
2 parents c0962b2 + de1ff9f commit e49a5f3
Show file tree
Hide file tree
Showing 25 changed files with 1,010 additions and 110 deletions.
25 changes: 25 additions & 0 deletions Integration Tests/SLTestCaseViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@
This method should create a view hierarchy and then assign the root view
of the hierarchy to the [view](-[UIView view]) property.
Subclasses can use the methods in the `ConvenienceViews` category
to load views for standard test scenarios.
The default implementation of this method is a no-op.
Expand All @@ -93,3 +96,25 @@
- (instancetype)initWithTestCaseWithSelector:(SEL)testCase;

@end


/**
The methods in the `SLTestCaseViewController (ConvenienceViews)` category
may be used to load views for certain standard test scenarios.
A subclass of `SLTestCaseViewController` would call one of these methods
from within its implementation of `-loadViewForTestCase:`.
*/
@interface SLTestCaseViewController (ConvenienceViews)

/**
Creates a generic view.
This method is to be used by test controllers that don't need to
display any particular interface, perhaps because they're testing
a system modal view/view controller presented in front of their view
or because they're testing some aspect of Subliminal unrelated to their view.
*/
- (void)loadGenericView;

@end
26 changes: 26 additions & 0 deletions Integration Tests/SLTestCaseViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,29 @@ - (UIRectEdge)edgesForExtendedLayout {
}

@end


@implementation SLTestCaseViewController (ConvenienceViews)

- (void)loadGenericView {
UIView *view = [[UIView alloc] initWithFrame:self.navigationController.view.bounds];
view.backgroundColor = [UIColor whiteColor];

UIFont *nothingToShowHereFont = [UIFont systemFontOfSize:18.0f];
NSString *nothingToShowHereText = @"Nothing to show here.";
CGRect nothingToShowHereBounds = CGRectIntegral((CGRect){ .size = [nothingToShowHereText sizeWithFont:nothingToShowHereFont
constrainedToSize:CGSizeMake(3 * CGRectGetWidth(view.bounds) / 4.0f, CGFLOAT_MAX)] });
UILabel *nothingToShowHereLabel = [[UILabel alloc] initWithFrame:nothingToShowHereBounds];
nothingToShowHereLabel.backgroundColor = view.backgroundColor;
nothingToShowHereLabel.font = nothingToShowHereFont;
nothingToShowHereLabel.numberOfLines = 0;
nothingToShowHereLabel.text = nothingToShowHereText;
nothingToShowHereLabel.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin;

[view addSubview:nothingToShowHereLabel];
nothingToShowHereLabel.center = CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds));

self.view = view;
}

@end
112 changes: 112 additions & 0 deletions Integration Tests/Tests/SLActionSheetTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//
// SLActionSheetTest.m
// Subliminal
//
// Created by Jeffrey Wear on 5/26/14.
// Copyright (c) 2014 Inkling. All rights reserved.
//

#import "SLIntegrationTest.h"

@interface SLActionSheetTest : SLIntegrationTest

@end

@implementation SLActionSheetTest

+ (NSString *)testCaseViewControllerClassName {
return @"SLActionSheetTestViewController";
}

- (void)tearDownTestCaseWithSelector:(SEL)testCaseSelector {
SLAskApp(dismissActionSheet);

if (([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) &&
((testCaseSelector == @selector(testButtonsIncludesTheCancelButtonIfPresent)) ||
(testCaseSelector == @selector(testCanMatchCancelButton)))) {
SLAskApp(dismissPopover);
}

[super tearDownTestCaseWithSelector:testCaseSelector];
}

- (void)testCanMatchActionSheet {
SLAskApp1(showActionSheetWithInfo:, @{ @"title": @"Action!" });

CGRect actionSheetRect, expectedActionSheetRect = [SLAskApp(actionSheetFrameValue) CGRectValue];
SLAssertNoThrow(actionSheetRect = [[SLActionSheet currentActionSheet] rect],
@"An action sheet should exist.");
SLAssertTrue(CGRectEqualToRect(actionSheetRect, expectedActionSheetRect),
@"The action sheet's frame does not match the expected frame.");
}

- (void)testCanReadTitle {
NSString *actualTitle, *expectedTitle = @"Action!";
SLAskApp1(showActionSheetWithInfo:, @{ @"title": expectedTitle });

SLAssertNoThrow(actualTitle = [[SLActionSheet currentActionSheet] title],
@"Should have been able to read the action sheet's title.");
SLAssertTrue([actualTitle isEqualToString:expectedTitle],
@"The action sheet's title was not equal to the expected value.");
}

- (void)testCanMatchButtons {
NSArray *actualButtonTitles, *expectedButtonTitles = @[ @"Other Button", @"Other Other Button" ];
// Note: the action sheet should not be presented with a cancel button here,
// for contrast with `-testThatButtonsIncludesTheCancelButtonIfPresent`.
SLAskApp1(showActionSheetWithInfo:, (@{
@"otherButtonTitle1": expectedButtonTitles[0],
@"otherButtonTitle2": expectedButtonTitles[1]
}));

SLAssertNoThrow(actualButtonTitles = [[[SLActionSheet currentActionSheet] buttons] valueForKey:@"label"],
@"Should have been able to retrieve the titles of the action sheet buttons.");
SLAssertTrue([actualButtonTitles isEqualToArray:expectedButtonTitles],
@"The titles of the action sheet buttons were not read as expected: %@", actualButtonTitles);
}

- (void)testButtonsIncludesTheCancelButtonIfPresent {
NSArray *actualButtonTitles, *expectedButtonTitles = @[ @"Other Button", @"Other Other Button", @"Cancel" ];
// `-testCanMatchButtons` verifies that the array will not include an invalid
// cancel button element if the cancel button isn't present.
SLAskApp1(showActionSheetWithInfo:, (@{
@"otherButtonTitle1": expectedButtonTitles[0],
@"otherButtonTitle2": expectedButtonTitles[1],
@"cancelButtonTitle": expectedButtonTitles[2],
// On the iPad, `UIActionSheet` will not show a cancel button unless it is shown in a popover.
@"showInPopover": @([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
}));

SLAssertNoThrow(actualButtonTitles = [[[SLActionSheet currentActionSheet] buttons] valueForKey:@"label"],
@"Should have been able to retrieve the titles of the action sheet buttons.");
SLAssertTrue([actualButtonTitles isEqualToArray:expectedButtonTitles],
@"The titles of the action sheet buttons were not read as expected: %@", actualButtonTitles);
}

- (void)testCanMatchCancelButton {
NSString *actualCancelButtonTitle, *expectedCancelButtonTitle = @"Get Out of Here";
SLAskApp1(showActionSheetWithInfo:, (@{
@"cancelButtonTitle": expectedCancelButtonTitle,
// On the iPad, `UIActionSheet` will not show a cancel button unless it is shown in a popover.
@"showInPopover": @([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
}));
[self wait:5.0];

SLAssertNoThrow(actualCancelButtonTitle = [[[SLActionSheet currentActionSheet] cancelButton] label],
@"Should have been able to retrieve the title of the action sheet's cancel button.");
SLAssertTrue([actualCancelButtonTitle isEqualToString:expectedCancelButtonTitle],
@"The action sheet's cancel button did not have the expected title.");
}

- (void)testCancelButtonIsInvalidIfThereIsNoCancelButton {
SLAskApp1(showActionSheetWithInfo:, @{ @"title": @"Action!" });

BOOL cancelButtonIsValid = NO;
SLAssertNoThrow(cancelButtonIsValid = [[[SLActionSheet currentActionSheet] cancelButton] isValid],
@"It should have been safe to access the action sheet's cancel button even though the button doesn't exist.");
SLAssertFalse(cancelButtonIsValid,
@"The action sheet's cancel button should be invalid.");

}

@end
81 changes: 81 additions & 0 deletions Integration Tests/Tests/SLActionSheetTestViewController.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// SLActionSheetTestViewController.m
// Subliminal
//
// Created by Jeffrey Wear on 5/26/14.
// Copyright (c) 2014 Inkling. All rights reserved.
//

#import "SLTestCaseViewController.h"

#import <Subliminal/SLTestController+AppHooks.h>

@interface SLActionSheetTestViewController : SLTestCaseViewController

@end

@implementation SLActionSheetTestViewController {
UIActionSheet *_actionSheet;
UIPopoverController *_popoverController;
}

- (void)loadViewForTestCase:(SEL)testCase {
[self loadGenericView];
}

- (instancetype)initWithTestCaseWithSelector:(SEL)testCase {
self = [super initWithTestCaseWithSelector:testCase];
if (self) {
SLTestController *testController = [SLTestController sharedTestController];
[testController registerTarget:self forAction:@selector(showActionSheetWithInfo:)];
[testController registerTarget:self forAction:@selector(dismissActionSheet)];
[testController registerTarget:self forAction:@selector(actionSheetFrameValue)];
}
return self;
}

- (void)dealloc {
[[SLTestController sharedTestController] deregisterTarget:self];
}

#pragma mark - App hooks

- (void)showActionSheetWithInfo:(NSDictionary *)info {
NSAssert(!_actionSheet, @"An action sheet is already showing.");

_actionSheet = [[UIActionSheet alloc] initWithTitle:info[@"title"]
delegate:nil
cancelButtonTitle:info[@"cancelButtonTitle"]
destructiveButtonTitle:nil
otherButtonTitles:info[@"otherButtonTitle1"], info[@"otherButtonTitle2"], nil];

if ([info[@"showInPopover"] boolValue]) {
SLActionSheetTestViewController *contentViewController = [[SLActionSheetTestViewController alloc] initWithTestCaseWithSelector:self.testCase];
_popoverController = [[UIPopoverController alloc] initWithContentViewController:contentViewController];
_popoverController.popoverContentSize = CGSizeMake(320.0f, 480.0f);
[_popoverController presentPopoverFromRect:CGRectInset((CGRect){ .origin = self.view.center }, -10.0f, -10.0f)
inView:self.view.superview
permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];

// register this here vs. in init so the controller we just presented doesn't steal it
[[SLTestController sharedTestController] registerTarget:self forAction:@selector(dismissPopover)];
[_actionSheet showInView:_popoverController.contentViewController.view];
} else {
[_actionSheet showInView:self.view];
}
}

- (void)dismissActionSheet {
[_actionSheet dismissWithClickedButtonIndex:0 animated:NO];
_actionSheet = nil;
}

- (NSValue *)actionSheetFrameValue {
return [NSValue valueWithCGRect:_actionSheet.accessibilityFrame];
}

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

@end
19 changes: 1 addition & 18 deletions Integration Tests/Tests/SLAlertTestViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,7 @@ @implementation SLAlertTestViewController {
- (void)loadViewForTestCase:(SEL)testCase {
// Since we're testing UIAlertViews in this test,
// we don't need any particular view.
UIView *view = [[UIView alloc] initWithFrame:self.navigationController.view.bounds];
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)];
UILabel *nothingToShowHereLabel = [[UILabel alloc] initWithFrame:(CGRect){CGPointZero, nothingToShowHereSize}];
nothingToShowHereLabel.backgroundColor = view.backgroundColor;
nothingToShowHereLabel.font = nothingToShowHereFont;
nothingToShowHereLabel.numberOfLines = 0;
nothingToShowHereLabel.text = nothingToShowHereText;
nothingToShowHereLabel.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin;

[view addSubview:nothingToShowHereLabel];
nothingToShowHereLabel.center = CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds));

self.view = view;
[self loadGenericView];
}

- (instancetype)initWithTestCaseWithSelector:(SEL)testCase {
Expand Down
60 changes: 52 additions & 8 deletions Integration Tests/Tests/SLGeometryTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
// Copyright (c) 2013 Inkling. All rights reserved.
//

#import <Subliminal/Subliminal.h>
#import "SLIntegrationTest.h"
#import "SLGeometry.h"
#import "SLTerminal.h"

#import <Subliminal/SLGeometry.h>
#import <Subliminal/SLTerminal+ConvenienceFunctions.h>

@interface SLGeometryTest : SLIntegrationTest

Expand All @@ -21,12 +21,56 @@ + (NSString *)testCaseViewControllerClassName {
return @"SLGeometryTestViewController";
}

- (void)testSLCGRectFromUIARectConvertsCorrectly
{
const CGRect UIARect = SLCGRectFromUIARect(@"UIATarget.localTarget().frontMostApp().navigationBar().rect()");
const CGRect UIKitRect = [SLAskApp(navigationBarFrameValue) CGRectValue];
- (void)testSLUIARectFromCGRect {
NSString *const expectedRect = @"{ origin: { x: 5.0, y: 10.0 }, size: { width: 50.0, height: 100.0 } }";
NSString *const actualRect = SLUIARectFromCGRect(CGRectMake(5.0, 10.0, 50.0, 100.0));

SLAssertTrue([[[SLTerminal sharedTerminal] evalFunctionWithName:SLUIARectEqualToRectFunctionName()
withArgs:(@[ actualRect, expectedRect ])] boolValue],
@"`SLUIARectFromCGRect` did not return the expected value.");
}

- (void)testSLCGRectFromUIARect {
const CGRect expectedRect = CGRectMake(5.0, 10.0, 50.0, 100.0);
const CGRect actualRect = SLCGRectFromUIARect(@"{ origin: { x: 5.0, y: 10.0 }, size: { width: 50.0, height: 100.0 } }");

SLAssertTrue(CGRectEqualToRect(UIARect, UIKitRect), @"The frame of the main window should be the same when coming from UIAutomation or UIKit");
SLAssertTrue(CGRectEqualToRect(actualRect, expectedRect),
@"`SLCGRectFromUIARect` did not return the expected value.");
}

- (void)testSLUIARectEqualToRect {
NSString *const rect1 = @"{ origin: { x: 5.0, y: 10.0 }, size: { width: 50.0, height: 100.0 } }";
NSString *const rect2 = @"{ origin: { x: 5.0, y: 10.0 }, size: { width: 50.0, height: 115.0 } }";

SLAssertTrue([[[SLTerminal sharedTerminal] evalFunctionWithName:SLUIARectEqualToRectFunctionName()
withArgs:(@[ rect1, rect1 ])] boolValue],
@"Two identical rects should be equal.");
SLAssertTrue([[[SLTerminal sharedTerminal] evalFunctionWithName:SLUIARectEqualToRectFunctionName()
withArgs:(@[ @"null", @"null" ])] boolValue],
@"Two null rects should be equal.");
SLAssertFalse([[[SLTerminal sharedTerminal] evalFunctionWithName:SLUIARectEqualToRectFunctionName()
withArgs:(@[ rect1, rect2 ])] boolValue],
@"Two different rects should not be equal.");
}

- (void)testSLUIARectContainsRect {
NSString *const containerRect = @"{ origin: { x: 5.0, y: 10.0 }, size: { width: 50.0, height: 100.0 } }";
NSString *const containedWithinRect = @"{ origin: { x: 10.0, y: 15.0 }, size: { width: 30.0, height: 50.0 } }";
NSString *const intersectingRect = @"{ origin: { x: 10.0, y: 15.0 }, size: { width: 50.0, height: 100.0 } }";
NSString *const nonIntersectingRect = @"{ origin: { x: 65.0, y: 120.0 }, size: { width: 50.0, height: 100.0 } }";

SLAssertTrue([[[SLTerminal sharedTerminal] evalFunctionWithName:SLUIARectContainsRectFunctionName()
withArgs:(@[ containerRect, containerRect ])] boolValue],
@"A rect should contain itself.");
SLAssertTrue([[[SLTerminal sharedTerminal] evalFunctionWithName:SLUIARectContainsRectFunctionName()
withArgs:(@[ containerRect, containedWithinRect ])] boolValue],
@"A rect should contain a rect within itself.");
SLAssertFalse([[[SLTerminal sharedTerminal] evalFunctionWithName:SLUIARectContainsRectFunctionName()
withArgs:(@[ containerRect, intersectingRect])] boolValue],
@"A rect should not contain a partially-intersecting rect.");
SLAssertFalse([[[SLTerminal sharedTerminal] evalFunctionWithName:SLUIARectContainsRectFunctionName()
withArgs:(@[ containerRect, nonIntersectingRect])] boolValue],
@"A rect should not contain a non-intersecting rect.");
}

@end
Loading

0 comments on commit e49a5f3

Please sign in to comment.