Skip to content
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

Add support for manipulating action sheets, nav bars, and mail compose views. #222

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
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