blob: 22b61d418c67b5579ef7eea811529c140db8c985 [file]
/*
Copyright 2016-present the Material Components for iOS authors. All Rights Reserved.
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 <XCTest/XCTest.h>
#import "MaterialButtons.h"
#import "MaterialShadowElevations.h"
#import "MaterialShapes.h"
#import "MaterialShadowLayer.h"
#import "MaterialTypography.h"
static const CGFloat kEpsilonAccuracy = 0.001f;
// A value greater than the largest value created by combining normal values of UIControlState.
// This is a complete hack, but UIControlState doesn't expose anything useful here.
// This assumes that UIControlState is actually a set of bitfields and ignores application-specific
// values.
static const UIControlState kNumUIControlStates = 2 * UIControlStateSelected - 1;
static const UIControlState kUIControlStateDisabledHighlighted =
UIControlStateHighlighted | UIControlStateDisabled;
static CGFloat randomNumber() {
return arc4random_uniform(100) / (CGFloat)10;
}
static CGFloat randomNumberNotEqualTo(const CGFloat targetNumber) {
while (1) {
CGFloat number = randomNumber();
if (number != targetNumber) {
return number;
}
}
}
static UIColor *randomColor() {
switch (arc4random_uniform(5)) {
case 0:
return [UIColor colorWithRed:1 green:1 blue:1 alpha:1];
break;
case 1:
return [UIColor colorWithRed:0 green:0 blue:0 alpha:1];
break;
case 2:
return [UIColor redColor];
break;
case 3:
return [UIColor orangeColor];
break;
case 4:
return [UIColor greenColor];
break;
default:
return [UIColor blueColor];
break;
}
}
static NSString *controlStateDescription(UIControlState controlState) {
if (controlState == UIControlStateNormal) {
return @"Normal";
}
NSMutableString *string = [NSMutableString string];
if ((UIControlStateHighlighted & controlState) == UIControlStateHighlighted) {
[string appendString:@"Highlighted "];
}
if ((UIControlStateDisabled & controlState) == UIControlStateDisabled) {
[string appendString:@"Disabled "];
}
if ((UIControlStateSelected & controlState) == UIControlStateSelected) {
[string appendString:@"Selected "];
}
return [string copy];
}
@interface FakeShadowLayer : MDCShapedShadowLayer
@property(nonatomic, assign) NSInteger elevationAssignmentCount;
@end
@implementation FakeShadowLayer
- (void)setElevation:(MDCShadowElevation)elevation {
++self.elevationAssignmentCount;
[super setElevation:elevation];
}
@end
@interface TestButton : MDCButton
@property(nonatomic, strong) FakeShadowLayer *shadowLayer;
@end
@implementation TestButton
+ (Class)layerClass {
return [FakeShadowLayer class];
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_shadowLayer = (FakeShadowLayer *)self.layer;
}
return self;
}
@end
@interface ButtonsTests : XCTestCase
@end
@implementation ButtonsTests
- (void)testUppercaseTitleYes {
// Given
MDCButton *button = [[MDCButton alloc] init];
NSString *originalTitle = @"some Text";
// When
button.uppercaseTitle = YES;
[button setTitle:originalTitle forState:UIControlStateNormal];
// Then
XCTAssertEqualObjects(button.currentTitle,
[originalTitle uppercaseStringWithLocale:[NSLocale currentLocale]]);
}
- (void)testUppercaseTitleNo {
// Given
MDCButton *button = [[MDCButton alloc] init];
NSString *originalTitle = @"some Text";
// When
button.uppercaseTitle = NO;
[button setTitle:originalTitle forState:UIControlStateNormal];
// Then
XCTAssertEqualObjects(button.currentTitle, originalTitle);
}
- (void)testUppercaseTitleNoChangedToYes {
// Given
MDCButton *button = [[MDCButton alloc] init];
NSString *originalTitle = @"some Text";
// When
button.uppercaseTitle = NO;
[button setTitle:originalTitle forState:UIControlStateNormal];
[button setTitle:originalTitle forState:UIControlStateHighlighted];
[button setTitle:originalTitle forState:UIControlStateDisabled];
button.uppercaseTitle = YES;
// Then
XCTAssertEqualObjects(button.currentTitle,
[originalTitle uppercaseStringWithLocale:[NSLocale currentLocale]]);
}
- (void)testUppercaseTitleYesChangedToNo {
// Given
MDCButton *button = [[MDCButton alloc] init];
NSString *originalTitle = @"some Text";
// When
button.uppercaseTitle = YES;
[button setTitle:originalTitle forState:UIControlStateNormal];
[button setTitle:originalTitle forState:UIControlStateHighlighted];
[button setTitle:originalTitle forState:UIControlStateDisabled];
button.uppercaseTitle = NO;
// Then
XCTAssertEqualObjects(button.currentTitle, originalTitle);
}
- (void)testSetEnabledAnimated {
// Given
MDCButton *button = [[MDCButton alloc] init];
NSArray *boolValues = @[ @YES, @NO ];
for (id enabled in boolValues) {
for (id animated in boolValues) {
// When
[button setEnabled:[enabled boolValue] animated:[animated boolValue]];
// Then
XCTAssertEqual(button.enabled, [enabled boolValue]);
}
}
}
- (void)testElevationForState {
// Given
MDCButton *button = [[MDCButton alloc] init];
for (NSUInteger controlState = 0; controlState < kNumUIControlStates; ++controlState) {
// And given
CGFloat elevation = randomNumber();
// When
[button setElevation:elevation forState:controlState];
// Then
XCTAssertEqual([button elevationForState:controlState], elevation);
}
}
- (void)testSetElevationOnlyUpdatesCurrentState {
// Given
TestButton *selectedButton = [[TestButton alloc] init];
TestButton *highlightedButton = [[TestButton alloc] init];
XCTAssertEqualWithAccuracy([selectedButton elevationForState:UIControlStateNormal],
[selectedButton elevationForState:UIControlStateHighlighted],
0.0001,
@"This test assumes that .normal and .highlighted start with the same "
"elevation values.");
[selectedButton setSelected:YES];
[highlightedButton setHighlighted:YES];
NSInteger selectedButtonElevationCount = selectedButton.shadowLayer.elevationAssignmentCount;
NSInteger highlightedButtonElevationCount =
highlightedButton.shadowLayer.elevationAssignmentCount;
// When
[selectedButton setElevation:77 forState:UIControlStateHighlighted];
[highlightedButton setElevation:75 forState:UIControlStateNormal];
// Then
XCTAssertEqual(selectedButton.shadowLayer.elevationAssignmentCount,
selectedButtonElevationCount,
@"Updating an unrelated elevation should not update the layer elevation.");
XCTAssertEqual(highlightedButtonElevationCount + 1,
highlightedButton.shadowLayer.elevationAssignmentCount,
@"Updating the current elevation should cause one elevation update.");
}
- (void)testElevationNormal {
// Given
MDCButton *button = [[MDCButton alloc] init];
CGFloat normalElevation = randomNumberNotEqualTo(0);
// When
[button setElevation:normalElevation forState:UIControlStateNormal];
// Then
XCTAssertEqual([button elevationForState:UIControlStateNormal], normalElevation);
XCTAssertEqual([button elevationForState:UIControlStateHighlighted], normalElevation);
XCTAssertEqual([button elevationForState:UIControlStateDisabled], normalElevation);
XCTAssertEqual([button elevationForState:UIControlStateSelected], normalElevation);
}
- (void)testElevationNormalZeroElevation {
// Given
MDCButton *button = [[MDCButton alloc] init];
// When
[button setElevation:0 forState:UIControlStateNormal];
// Then
XCTAssertEqual([button elevationForState:UIControlStateNormal], 0);
}
- (void)testBackgroundColorForState {
// Given
MDCButton *button = [[MDCButton alloc] init];
for (NSUInteger controlState = 0; controlState < kNumUIControlStates; ++controlState) {
// And given
UIColor *color = randomColor();
// When
[button setBackgroundColor:color forState:controlState];
// Then
XCTAssertEqualObjects([button backgroundColorForState:controlState], color);
}
}
#pragma mark - shadowColor:forState:
- (void)testRemovedShadowColorForState {
// Given
MDCButton *button = [[MDCButton alloc] init];
// When
[button setShadowColor:nil forState:UIControlStateNormal];
// Then
XCTAssertNil([button shadowColorForState:UIControlStateNormal]);
XCTAssertNil([button shadowColorForState:UIControlStateHighlighted]);
}
- (void)testDefaultShadowColorForState {
// Given
MDCButton *button = [[MDCButton alloc] init];
// Then
XCTAssertNotNil([button shadowColorForState:UIControlStateSelected]);
}
- (void)testShadowColorForUnspecifiedStateEqualsNormalState {
// Given
MDCButton *button = [[MDCButton alloc] init];
UIColor *color = randomColor();
// When
[button setShadowColor:color forState:UIControlStateNormal];
XCTAssertEqual([button shadowColorForState:UIControlStateHighlighted], color);
}
- (void)testShadowColorForState {
// Given
MDCButton *button = [[MDCButton alloc] init];
for (NSUInteger controlState = 0; controlState < kNumUIControlStates; ++controlState) {
// And given
UIColor *color = randomColor();
// When
[button setShadowColor:color forState:controlState];
// Then
XCTAssertEqualObjects([button shadowColorForState:controlState], color);
}
}
- (void)testLayerShadowColorForState {
// Given
MDCButton *button = [[MDCButton alloc] init];
for (NSUInteger controlState = 0; controlState <= kNumUIControlStates; ++controlState) {
NSUInteger effectiveControlState = controlState;
// And given
UIColor *color = randomColor();
// When
[button setShadowColor:color forState:controlState];
if ((effectiveControlState & UIControlStateHighlighted) == UIControlStateHighlighted) {
button.highlighted = YES;
} else {
button.highlighted = NO;
}
if ((effectiveControlState & UIControlStateDisabled) == UIControlStateDisabled) {
button.enabled = NO;
// Disabling a button turns off "highlighted"
effectiveControlState = (effectiveControlState & ~UIControlStateHighlighted);
} else {
button.enabled = YES;
}
if ((effectiveControlState & UIControlStateSelected) == UIControlStateSelected) {
button.selected = YES;
} else {
button.selected = NO;
}
// Then
UIColor *layerShadowColor = [UIColor colorWithCGColor:button.layer.shadowColor];
XCTAssertEqualObjects([button shadowColorForState:effectiveControlState], layerShadowColor);
}
}
#pragma mark - imageTintColor:forState:
- (void)testRemovedImageTintColorForState {
// Given
MDCButton *button = [[MDCButton alloc] init];
// When
[button setImageTintColor:nil forState:UIControlStateNormal];
// Then
XCTAssertNil([button imageTintColorForState:UIControlStateNormal]);
XCTAssertNil([button imageTintColorForState:UIControlStateHighlighted]);
}
- (void)testDefaultImageTintColorForState {
// Given
MDCButton *button = [[MDCButton alloc] init];
// Then
XCTAssertNil([button imageTintColorForState:UIControlStateSelected]);
}
- (void)testImageTintForUnspecifiedStateEqualsNormalState {
// Given
MDCButton *button = [[MDCButton alloc] init];
UIColor *color = randomColor();
// When
[button setImageTintColor:color forState:UIControlStateNormal];
XCTAssertEqual([button imageTintColorForState:UIControlStateHighlighted], color);
}
- (void)testImageTintColorForState {
// Given
MDCButton *button = [[MDCButton alloc] init];
for (NSUInteger controlState = 0; controlState < kNumUIControlStates; ++controlState) {
// And given
UIColor *color = randomColor();
// When
[button setImageTintColor:color forState:controlState];
// Then
XCTAssertEqualObjects([button imageTintColorForState:controlState], color);
}
}
- (void)testImageTintColorForStateFallsBackToDefault {
// Given
MDCButton *button = [[MDCButton alloc] init];
UIColor *normalTint = [UIColor yellowColor];
UIColor *selectedTint = [UIColor redColor];
// When
[button setImageTintColor:normalTint forState:UIControlStateNormal];
[button setImageTintColor:selectedTint forState:UIControlStateSelected];
// Then
XCTAssertEqualObjects([button imageTintColorForState:UIControlStateNormal], normalTint);
XCTAssertEqualObjects([button imageTintColorForState:UIControlStateSelected], selectedTint);
XCTAssertEqualObjects([button imageTintColorForState:UIControlStateHighlighted], normalTint);
}
- (void)testImageTintColorForStateSetsImageViewTintColor {
// Given
MDCButton *button = [[MDCButton alloc] init];
UIColor *normalTint = [UIColor yellowColor];
UIColor *selectedTint = [UIColor redColor];
// When
[button setImageTintColor:normalTint forState:UIControlStateNormal];
[button setImageTintColor:selectedTint forState:UIControlStateSelected];
// Then
XCTAssertEqualObjects(button.imageView.tintColor, normalTint);
// When
button.selected = YES;
// Then
XCTAssertEqualObjects(button.imageView.tintColor, selectedTint);
}
#pragma mark - backgroundColor:forState:
- (void)testCurrentBackgroundColorNormal {
// Given
MDCButton *button = [[MDCButton alloc] init];
UIColor *normalColor = [UIColor redColor];
[button setBackgroundColor:normalColor forState:UIControlStateNormal];
// Then
XCTAssertEqualObjects([button backgroundColor], normalColor);
}
- (void)testCurrentBackgroundColorHighlighted {
// Given
MDCButton *button = [[MDCButton alloc] init];
UIColor *normalColor = [UIColor redColor];
UIColor *color = [UIColor orangeColor];
[button setBackgroundColor:normalColor forState:UIControlStateNormal];
[button setBackgroundColor:color forState:UIControlStateHighlighted];
// When
button.highlighted = YES;
// Then
XCTAssertEqualObjects([button backgroundColor], color);
}
- (void)testCurrentBackgroundColorDisabled {
// Given
MDCButton *button = [[MDCButton alloc] init];
UIColor *normalColor = [UIColor redColor];
UIColor *color = [UIColor orangeColor];
[button setBackgroundColor:normalColor forState:UIControlStateNormal];
[button setBackgroundColor:color forState:UIControlStateDisabled];
// When
button.enabled = NO;
// Then
XCTAssertEqualObjects([button backgroundColor], color);
}
- (void)testCurrentBackgroundColorSelected {
// Given
MDCButton *button = [[MDCButton alloc] init];
UIColor *normalColor = [UIColor redColor];
UIColor *color = [UIColor orangeColor];
[button setBackgroundColor:normalColor forState:UIControlStateNormal];
[button setBackgroundColor:color forState:UIControlStateSelected];
// When
button.selected = YES;
// Then
XCTAssertEqualObjects([button backgroundColor], color);
}
#pragma mark - elevation:forState:
- (void)testCurrentElevationNormal {
// Given
MDCButton *button = [[MDCButton alloc] init];
CGFloat normalElevation = 10;
[button setElevation:normalElevation forState:UIControlStateNormal];
// Then
XCTAssertEqualWithAccuracy([button elevationForState:button.state],
normalElevation,
kEpsilonAccuracy);
}
- (void)testCurrentElevationHighlighted {
// Given
MDCButton *button = [[MDCButton alloc] init];
CGFloat normalElevation = 10;
CGFloat elevation = 40;
[button setElevation:normalElevation forState:UIControlStateNormal];
[button setElevation:elevation forState:UIControlStateHighlighted];
// When
button.highlighted = YES;
// Then
XCTAssertEqualWithAccuracy([button elevationForState:button.state], elevation, kEpsilonAccuracy);
}
- (void)testCurrentElevationDisabled {
// Given
MDCButton *button = [[MDCButton alloc] init];
CGFloat normalElevation = 10;
CGFloat elevation = 40;
[button setElevation:normalElevation forState:UIControlStateNormal];
[button setElevation:elevation forState:UIControlStateDisabled];
// When
button.enabled = NO;
// Then
XCTAssertEqualWithAccuracy([button elevationForState:button.state], elevation, kEpsilonAccuracy);
}
- (void)testCurrentElevationSelected {
// Given
MDCButton *button = [[MDCButton alloc] init];
CGFloat normalElevation = 10;
CGFloat elevation = 40;
[button setElevation:normalElevation forState:UIControlStateNormal];
[button setElevation:elevation forState:UIControlStateSelected];
// When
button.selected = YES;
// Then
XCTAssertEqualWithAccuracy([button elevationForState:button.state], elevation, kEpsilonAccuracy);
}
#pragma mark - Ink Color
- (void)testInkColors {
// Given
MDCButton *button = [[MDCButton alloc] init];
UIColor *color = randomColor();
// When
button.inkColor = color;
// Then
XCTAssertEqualObjects(button.inkColor, color);
}
/*
TODO: things to unit test
(should these even be a thing?)
- hitAreaInset
- underlyingColor (text color)
*/
- (void)testAlphaRestoredWhenReenabled {
// Given
MDCButton *button = [[MDCButton alloc] initWithFrame:CGRectMake(0, 0, 80, 48)];
CGFloat alpha = (CGFloat)0.5;
// When
button.alpha = alpha;
button.enabled = NO;
button.enabled = YES;
// Then
XCTAssertEqualWithAccuracy(alpha, button.alpha, 0.0001);
}
- (void)testEnabledAlphaNotSetWhileDisabled {
// Given
MDCButton *button = [[MDCButton alloc] initWithFrame:CGRectMake(0, 0, 80, 48)];
CGFloat alpha = (CGFloat)0.2;
// When
button.alpha = alpha;
button.enabled = NO;
button.alpha = 1 - alpha;
button.enabled = YES;
// Then
XCTAssertEqualWithAccuracy(alpha, button.alpha, (CGFloat)0.0001);
}
- (void)testDisabledAlpha {
// Given
MDCButton *button = [[MDCButton alloc] initWithFrame:CGRectMake(0, 0, 80, 48)];
CGFloat alpha = 0.5;
// When
[button setDisabledAlpha:alpha];
button.enabled = NO;
// Then
XCTAssertEqualWithAccuracy(alpha, button.alpha, (CGFloat)0.0001);
}
- (void)testEncode {
// Given
MDCButton *button = [[MDCButton alloc] init];
button.inkStyle = arc4random_uniform(2) ? MDCInkStyleBounded : MDCInkStyleUnbounded;
button.inkMaxRippleRadius = randomNumber();
button.uppercaseTitle = (BOOL)arc4random_uniform(2) ? YES : NO;
button.hitAreaInsets = UIEdgeInsetsMake(10, 10, 10, 10);
button.inkColor = randomColor();
button.underlyingColorHint = randomColor();
button.minimumSize = CGSizeMake(15, 33);
button.maximumSize = CGSizeMake(17, 41);
CGFloat buttonAlpha = (CGFloat)0.5;
button.alpha = buttonAlpha;
button.enabled = NO;
for (NSUInteger controlState = 0; controlState < kNumUIControlStates; ++controlState) {
[button setBackgroundColor:randomColor() forState:controlState];
[button setElevation:randomNumber() forState:controlState];
[button setShadowColor:randomColor() forState:controlState];
}
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:button];
// When
MDCButton *unarchivedButton = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// Then
XCTAssertEqualObjects(unarchivedButton.inkColor, button.inkColor);
XCTAssertEqual(unarchivedButton.uppercaseTitle, button.uppercaseTitle);
XCTAssertEqual(unarchivedButton.inkStyle, button.inkStyle);
XCTAssertEqualWithAccuracy(unarchivedButton.inkMaxRippleRadius,
button.inkMaxRippleRadius,
kEpsilonAccuracy);
XCTAssertEqualWithAccuracy(unarchivedButton.hitAreaInsets.bottom,
button.hitAreaInsets.bottom,
kEpsilonAccuracy);
XCTAssertEqualWithAccuracy(unarchivedButton.hitAreaInsets.top,
button.hitAreaInsets.top,
kEpsilonAccuracy);
XCTAssertEqualWithAccuracy(unarchivedButton.hitAreaInsets.right,
button.hitAreaInsets.right,
kEpsilonAccuracy);
XCTAssertEqualWithAccuracy(unarchivedButton.hitAreaInsets.left,
button.hitAreaInsets.left,
kEpsilonAccuracy);
XCTAssertEqualObjects(unarchivedButton.underlyingColorHint, button.underlyingColorHint);
XCTAssertTrue(CGSizeEqualToSize(unarchivedButton.minimumSize, button.minimumSize));
XCTAssertTrue(CGSizeEqualToSize(unarchivedButton.maximumSize, button.maximumSize));
XCTAssertEqual(unarchivedButton.enabled, button.enabled);
XCTAssertEqualWithAccuracy(unarchivedButton.alpha, button.alpha, (CGFloat)0.0001);
unarchivedButton.enabled = YES;
XCTAssertEqualWithAccuracy(unarchivedButton.alpha, buttonAlpha, (CGFloat)0.0001);
XCTAssertEqualObjects(unarchivedButton.titleLabel.font, button.titleLabel.font);
for (NSUInteger controlState = 0; controlState < kNumUIControlStates; ++controlState) {
XCTAssertEqualWithAccuracy([unarchivedButton elevationForState:controlState],
[button elevationForState:controlState],
kEpsilonAccuracy);
XCTAssertEqualObjects([unarchivedButton backgroundColorForState:controlState],
[button backgroundColorForState:controlState]);
XCTAssertEqualObjects([unarchivedButton shadowColorForState:controlState],
[button shadowColorForState:controlState]);
XCTAssertEqualObjects([unarchivedButton titleFontForState:controlState],
[button titleFontForState:controlState]);
}
}
- (void)testDecodeOnUpgradeFromVersionWithoutShadowColors {
// Given
MDCButton *button = [[MDCButton alloc] init];
for (NSUInteger controlState = 0; controlState < kNumUIControlStates; ++controlState) {
[button setShadowColor:randomColor() forState:controlState];
}
// When
id shadowColorsValue = [button valueForKey:@"_shadowColors"];
[button setValue:nil forKey:@"_shadowColors"];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:button];
[button setValue:shadowColorsValue forKey:@"_shadowColors"];
MDCButton *unarchivedButton = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// Then
UIColor *normalStateShadowColor = [unarchivedButton shadowColorForState:UIControlStateNormal];
XCTAssertNotNil(normalStateShadowColor);
for (NSUInteger controlState = 0; controlState < kNumUIControlStates; ++controlState) {
XCTAssertEqualObjects(normalStateShadowColor,
[unarchivedButton shadowColorForState:controlState]);
}
}
- (void)testPointInsideWithoutHitAreaInsets {
// Given
MDCButton *button = [[MDCButton alloc] initWithFrame:CGRectMake(0, 0, 80, 50)];
CGPoint touchPointInsideBoundsTopLeft = CGPointMake(0, 0);
CGPoint touchPointInsideBoundsTopRight = CGPointMake((CGFloat)79.9, 0);
CGPoint touchPointInsideBoundsBottomRight = CGPointMake((CGFloat)79.9, (CGFloat)49.9);
CGPoint touchPointInsideBoundsBottomLeft = CGPointMake(0, (CGFloat)49.9);
CGPoint touchPointOutsideBoundsTopLeft = CGPointMake(0, (CGFloat)-0.1);
CGPoint touchPointOutsideBoundsTopRight = CGPointMake(80, 0);
CGPoint touchPointOutsideBoundsBottomRight = CGPointMake(80, 50);
CGPoint touchPointOutsideBoundsBottomLeft = CGPointMake(0, 50);
// Then
XCTAssertTrue([button pointInside:touchPointInsideBoundsTopLeft withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideBoundsTopRight withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideBoundsBottomRight withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideBoundsBottomLeft withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideBoundsTopLeft withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideBoundsTopRight withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideBoundsBottomRight withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideBoundsBottomLeft withEvent:nil]);
}
- (void)testPointInsideWithoutHitAreaInsetsTooSmall {
// Given
MDCButton *button = [[MDCButton alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
CGPoint touchPointInsideBoundsTopLeft = CGPointMake(0, 0);
CGPoint touchPointInsideBoundsTopRight = CGPointMake((CGFloat)9.9, 0);
CGPoint touchPointInsideBoundsBottomRight = CGPointMake((CGFloat)9.9, (CGFloat)9.9);
CGPoint touchPointInsideBoundsBottomLeft = CGPointMake(0, (CGFloat)9.9);
CGPoint touchPointOutsideBoundsTopLeft = CGPointMake(0, (CGFloat)-0.1);
CGPoint touchPointOutsideBoundsTopRight = CGPointMake(10, 0);
CGPoint touchPointOutsideBoundsBottomRight = CGPointMake(10, 10);
CGPoint touchPointOutsideBoundsBottomLeft = CGPointMake(0, 10);
// Then
XCTAssertTrue([button pointInside:touchPointInsideBoundsTopLeft withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideBoundsTopRight withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideBoundsBottomRight withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideBoundsBottomLeft withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideBoundsTopLeft withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideBoundsTopRight withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideBoundsBottomRight withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideBoundsBottomLeft withEvent:nil]);
}
- (void)testPointInsideWithCustomHitAreaInsets {
// Given
MDCButton *button = [[MDCButton alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
CGPoint touchPointInsideHitAreaTopLeft = CGPointMake(-5, -5);
CGPoint touchPointInsideHitAreaTopRight = CGPointMake(-5, (CGFloat)14.9);
CGPoint touchPointInsideHitAreaBottomRight = CGPointMake((CGFloat)14.9, (CGFloat)14.9);
CGPoint touchPointInsideHitAreaBottomLeft = CGPointMake((CGFloat)14.9, -5);
CGPoint touchPointOutsideHitAreaTopLeft = CGPointMake((CGFloat)-5.1, -5);
CGPoint touchPointOutsideHitAreaTopRight = CGPointMake(-5, 15);
CGPoint touchPointOutsideHitAreaBottomRight = CGPointMake(15, 15);
CGPoint touchPointOutsideHitAreaBottomLeft = CGPointMake(15, -5);
// When
button.hitAreaInsets = UIEdgeInsetsMake(-5, -5, -5, -5);
// Then
XCTAssertTrue([button pointInside:touchPointInsideHitAreaTopLeft withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideHitAreaTopRight withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideHitAreaBottomRight withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideHitAreaBottomLeft withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideHitAreaTopLeft withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideHitAreaTopRight withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideHitAreaBottomRight withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideHitAreaBottomLeft withEvent:nil]);
}
- (void)testPointInsideWithNonStandardizedBounds {
// Given
MDCButton *button = [[MDCButton alloc] initWithFrame:CGRectZero];
// This is (-10, -10, 20, 20) in standardized form
CGRect bounds = CGRectMake(10, 10, -20, -20);
// Once applied, these insets should increase the hitArea to (-15, -20, 30, 40)
UIEdgeInsets insets = UIEdgeInsetsMake(-10, -5, -10, -5);
CGPoint touchPointInsideHitAreaTopLeft = CGPointMake(-15, -20);
CGPoint touchPointInsideHitAreaTopRight = CGPointMake((CGFloat)14.9, -20);
CGPoint touchPointInsideHitAreaBottomRight = CGPointMake((CGFloat)14.9, (CGFloat)19.9);
CGPoint touchPointInsideHitAreaBottomLeft = CGPointMake(-15, (CGFloat)19.9);
CGPoint touchPointOutsideHitAreaTopLeft = CGPointMake((CGFloat)-15.1, -20);
CGPoint touchPointOutsideHitAreaTopRight = CGPointMake(20, -20);
CGPoint touchPointOutsideHitAreaBottomRight = CGPointMake(15, 20);
CGPoint touchPointOutsideHitAreaBottomLeft = CGPointMake(-15, 20);
// When
button.bounds = bounds;
button.hitAreaInsets = insets;
// Then
XCTAssertTrue([button pointInside:touchPointInsideHitAreaTopLeft withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideHitAreaTopRight withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideHitAreaBottomRight withEvent:nil]);
XCTAssertTrue([button pointInside:touchPointInsideHitAreaBottomLeft withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideHitAreaTopLeft withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideHitAreaTopRight withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideHitAreaBottomRight withEvent:nil]);
XCTAssertFalse([button pointInside:touchPointOutsideHitAreaBottomLeft withEvent:nil]);
}
#pragma mark - UIButton strangeness
- (void)testTitleColorForState {
for (NSUInteger controlState = 0; controlState < kNumUIControlStates; ++controlState) {
if (controlState & kUIControlStateDisabledHighlighted) {
// We skip the Disabled Highlighted state because UIButton titleColorForState ignores it.
continue;
}
// Given
MDCButton *button = [[MDCButton alloc] init];
UIColor *color = [UIColor blueColor];
// When
[button setTitleColor:color forState:controlState];
// Then
XCTAssertEqualObjects([button titleColorForState:controlState],
color,
@"for control state:%@ ",
controlStateDescription(controlState));
}
}
- (void)testTitleColorForStateDisabledHighlight {
// This is strange that setting the color for a state does not return the value of that state.
// It turns out that it returns the value set to the normal state.
// Given
UIControlState controlState = kUIControlStateDisabledHighlighted;
MDCButton *button = [[MDCButton alloc] init];
UIColor *color = [UIColor blueColor];
UIColor *normalColor = [UIColor greenColor];
[button setTitleColor:normalColor forState:UIControlStateNormal];
// When
[button setTitleColor:color forState:controlState];
// Then
XCTAssertEqualObjects([button titleColorForState:controlState],
normalColor,
@"for control state:%@ ",
controlStateDescription(controlState));
XCTAssertNotEqualObjects([button titleColorForState:controlState],
color,
@"for control state:%@ ",
controlStateDescription(controlState));
}
#pragma mark - UIButton state changes
- (void)testEnabled {
// Given
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.highlighted = (BOOL)arc4random_uniform(2);
button.selected = (BOOL)arc4random_uniform(2);
button.enabled = (BOOL)arc4random_uniform(2);
// When
button.enabled = YES;
// Then
XCTAssertTrue(button.enabled);
XCTAssertFalse(button.state & UIControlStateDisabled);
}
- (void)testDisabled {
// Given
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.highlighted = (BOOL)arc4random_uniform(2);
button.selected = (BOOL)arc4random_uniform(2);
button.enabled = (BOOL)arc4random_uniform(2);
// When
button.enabled = NO;
// Then
XCTAssertFalse(button.enabled);
XCTAssertTrue(button.state & UIControlStateDisabled);
}
- (void)testHighlighted {
// Given
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.highlighted = NO;
button.selected = (BOOL)arc4random_uniform(2);
// For some reason we can only set the highlighted state to YES if its enabled is also YES.
button.enabled = YES;
UIControlState oldState = button.state;
XCTAssertFalse(button.highlighted);
// When
button.highlighted = YES;
// Then
XCTAssertTrue(button.highlighted);
XCTAssertTrue(button.state & UIControlStateHighlighted);
XCTAssertEqual(button.state, (oldState | UIControlStateHighlighted));
}
- (void)testUnhighlighted {
// Given
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.highlighted = YES;
button.selected = (BOOL)arc4random_uniform(2);
button.enabled = (BOOL)arc4random_uniform(2);
UIControlState oldState = button.state;
XCTAssertTrue(button.highlighted);
// When
button.highlighted = NO;
// Then
XCTAssertFalse(button.highlighted);
XCTAssertFalse(button.state & UIControlStateHighlighted);
XCTAssertEqual(button.state, (oldState & ~UIControlStateHighlighted));
}
- (void)testSelected {
// Given
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.highlighted = (BOOL)arc4random_uniform(2);
button.selected = NO;
button.enabled = (BOOL)arc4random_uniform(2);
UIControlState oldState = button.state;
// When
button.selected = YES;
// Then
XCTAssertTrue(button.selected);
XCTAssertTrue(button.state & UIControlStateSelected);
XCTAssertEqual(button.state, (oldState | UIControlStateSelected));
}
- (void)testUnselected {
// Given
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.highlighted = (BOOL)arc4random_uniform(2);
button.selected = YES;
button.enabled = (BOOL)arc4random_uniform(2);
// When
button.selected = NO;
// Then
XCTAssertFalse(button.selected);
XCTAssertFalse(button.state & UIControlStateSelected);
}
- (void)testDefaultFont {
// Given
MDCButton *button = [[MDCButton alloc] init];
// Then
XCTAssertEqualObjects(button.titleLabel.font, [MDCTypography buttonFont]);
}
- (void)testDefaultAdjustsFontProperty {
// Given
MDCButton *button = [[MDCButton alloc] init];
// Then
XCTAssertFalse(button.mdc_adjustsFontForContentSizeCategory);
}
- (void)testAdjustsFontProperty {
// Given
MDCButton *button = [[MDCButton alloc] init];
UIFont *preferredFont = [UIFont mdc_preferredFontForMaterialTextStyle:MDCFontTextStyleButton];
// When
button.mdc_adjustsFontForContentSizeCategory = YES;
// Then
XCTAssertTrue(button.mdc_adjustsFontForContentSizeCategory);
XCTAssertEqualWithAccuracy(button.titleLabel.font.pointSize,
preferredFont.pointSize,
kEpsilonAccuracy,
@"Font size should be equal to MDCFontTextStyleButton's.");
}
#pragma mark - Size-related tests
- (void)testSizeThatFitsWithMinimumOnly {
// Given
MDCButton *button = [[MDCButton alloc] initWithFrame:CGRectZero];
[button sizeToFit];
CGRect expectedFrame = CGRectMake(0, 0,
CGRectGetWidth(button.frame) + 15,
CGRectGetHeight(button.frame) + 21);
// When
button.minimumSize = expectedFrame.size;
[button sizeToFit];
// Then
XCTAssertTrue(CGRectEqualToRect(expectedFrame, button.frame),
@"\nE: %@\nA: %@",
NSStringFromCGRect(expectedFrame),
NSStringFromCGRect(button.frame));
}
- (void)testSizeThatFitsWithMaximumOnly {
// Given
MDCButton *button = [[MDCButton alloc] initWithFrame:CGRectZero];
[button sizeToFit];
CGRect expectedFrame = CGRectMake(0, 0,
CGRectGetWidth(button.frame) - 7,
CGRectGetHeight(button.frame) - 3);
// When
button.maximumSize = expectedFrame.size;
[button sizeToFit];
// Then
XCTAssertTrue(CGRectEqualToRect(expectedFrame, button.frame),
@"\nE: %@\nA: %@",
NSStringFromCGRect(expectedFrame),
NSStringFromCGRect(button.frame));
}
- (void)testSizeThatFitsWithMinimumAndMaximum {
// Given
MDCButton *button = [[MDCButton alloc] initWithFrame:CGRectZero];
[button sizeToFit];
CGRect expectedFrame = CGRectMake(0, 0,
CGRectGetWidth(button.frame) + 21,
CGRectGetHeight(button.frame) - 4);
// When
button.maximumSize = CGSizeMake(0, CGRectGetHeight(expectedFrame)); // Only bound max height
button.minimumSize = CGSizeMake(CGRectGetWidth(expectedFrame), 0); // Only bound min width
[button sizeToFit];
// Then
XCTAssertTrue(CGRectEqualToRect(expectedFrame, button.frame),
@"\nE: %@\nA: %@",
NSStringFromCGRect(expectedFrame),
NSStringFromCGRect(button.frame));
}
@end