featherless 2181084272
[automated] Standardize our open source license stanza to what Xcode generates. (#4985)
Removes the need to copy-paste stanzas from other files anymore as we'll rely on #4478 to generate the correct stanza for us instead.

This was an automated change generated by running a find-and-replace regular expression:

```
/\*
 Copyright ([0-9]+)-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\.
 \*/
```

```
/\*
Copyright ([0-9]+)-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\.
\*/
```

```
/\*
 Copyright ([0-9]+)-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\.
 \*/
```

```
// Copyright $1-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.
```
2018-08-31 12:13:07 -04:00

825 lines
34 KiB
Objective-C

// Copyright 2017-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"
static UIImage *fakeImage(void) {
CGSize imageSize = CGSizeMake(24, 24);
UIGraphicsBeginImageContext(imageSize);
[UIColor.whiteColor setFill];
UIRectFill(CGRectMake(0, 0, imageSize.width, imageSize.height));
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
@interface FakeFloatingButton : MDCFloatingButton
@property(nonatomic, assign) BOOL intrinsicContentSizeCalled;
@property(nonatomic, assign) BOOL invalidateIntrinsicContentSizeCalled;
@property(nonatomic, assign) BOOL sizeThatFitsCalled;
@property(nonatomic, assign) BOOL layoutSubviewsCalled;
- (void)reset;
@end
@implementation FakeFloatingButton
- (void)reset {
self.intrinsicContentSizeCalled = NO;
self.invalidateIntrinsicContentSizeCalled = NO;
self.layoutSubviewsCalled = NO;
self.sizeThatFitsCalled = NO;
}
- (CGSize)intrinsicContentSize {
self.intrinsicContentSizeCalled = YES;
return [super intrinsicContentSize];
}
- (void)invalidateIntrinsicContentSize {
self.invalidateIntrinsicContentSizeCalled = YES;
[super invalidateIntrinsicContentSize];
}
- (CGSize)sizeThatFits:(CGSize)size {
self.sizeThatFitsCalled = YES;
return [super sizeThatFits:size];
}
- (void)layoutSubviews {
self.layoutSubviewsCalled = YES;
[super layoutSubviews];
}
@end
@interface FloatingButtonsTests : XCTestCase
@end
@implementation FloatingButtonsTests
#pragma mark - setHitAreaInsets:forShape:inMode:
- (void)testDefaultHitAreaInsetsValues {
// Given
MDCFloatingButton *defaultButtonNormal =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeDefault];
MDCFloatingButton *defaultButtonExpanded =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeDefault];
defaultButtonExpanded.mode = MDCFloatingButtonModeExpanded;
MDCFloatingButton *miniButtonNormal =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
MDCFloatingButton *miniButtonExpanded =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
miniButtonExpanded.mode = MDCFloatingButtonModeExpanded;
// Then
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, defaultButtonNormal.hitAreaInsets));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero,
defaultButtonExpanded.hitAreaInsets));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(-4, -4, -4, -4),
miniButtonNormal.hitAreaInsets));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, miniButtonExpanded.hitAreaInsets));
}
- (void)testSetHitAreaInsetsForShapeInNormalMode {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init]; // Default shape
defaultButton.mode = MDCFloatingButtonModeExpanded;
MDCFloatingButton *miniButton =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
miniButton.mode = MDCFloatingButtonModeExpanded;
// When
[defaultButton setHitAreaInsets:UIEdgeInsetsMake(1, 2, 3, 4)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeNormal];
defaultButton.mode = MDCFloatingButtonModeNormal;
[miniButton setHitAreaInsets:UIEdgeInsetsMake(9, 8, 7, 6)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeNormal];
miniButton.mode = MDCFloatingButtonModeNormal;
// Then
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(1, 2, 3, 4),
defaultButton.hitAreaInsets));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(9, 8, 7, 6),
miniButton.hitAreaInsets));
}
- (void)testSetHitAreaInsetsForShapeInExpandedMode {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init]; // Default shape
MDCFloatingButton *miniButton =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
// When
[defaultButton setHitAreaInsets:UIEdgeInsetsMake(1, 2, 3, 4)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeExpanded];
defaultButton.mode = MDCFloatingButtonModeExpanded;
[miniButton setHitAreaInsets:UIEdgeInsetsMake(9, 8, 7, 6)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeExpanded];
miniButton.mode = MDCFloatingButtonModeExpanded;
// Then
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(1, 2, 3, 4),
defaultButton.hitAreaInsets));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(9, 8, 7, 6),
miniButton.hitAreaInsets));
}
- (void)testChangingHitAreaInsetsWontTriggerResizing {
// Given
FakeFloatingButton *button = [[FakeFloatingButton alloc] init]; // Default shape
[button reset];
// When
[button setHitAreaInsets:UIEdgeInsetsMake(17, 21, 25, 29)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeNormal];
// Then
XCTAssertFalse(button.intrinsicContentSizeCalled);
XCTAssertFalse(button.invalidateIntrinsicContentSizeCalled);
XCTAssertFalse(button.sizeThatFitsCalled);
XCTAssertFalse(button.layoutSubviewsCalled);
}
#pragma mark - setContentEdgeInsets:forShape:inMode:
- (void)testDefaultContentEdgeInsetsValues {
// Given
MDCFloatingButton *defaultButtonNormal =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeDefault];
MDCFloatingButton *defaultButtonExpanded =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeDefault];
defaultButtonExpanded.mode = MDCFloatingButtonModeExpanded;
MDCFloatingButton *miniButtonNormal =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
MDCFloatingButton *miniButtonExpanded =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
miniButtonExpanded.mode = MDCFloatingButtonModeExpanded;
// Then
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero,
defaultButtonNormal.contentEdgeInsets));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero,
defaultButtonExpanded.contentEdgeInsets));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(8, 8, 8, 8),
miniButtonNormal.contentEdgeInsets));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero,
miniButtonExpanded.contentEdgeInsets));
}
- (void)testSetContentEdgeInsetsForShapeInNormalMode {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init]; // Default shape
defaultButton.mode = MDCFloatingButtonModeExpanded;
MDCFloatingButton *miniButton =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
miniButton.mode = MDCFloatingButtonModeExpanded;
// When
[defaultButton setContentEdgeInsets:UIEdgeInsetsMake(1, 2, 3, 4)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeNormal];
defaultButton.mode = MDCFloatingButtonModeNormal;
[miniButton setContentEdgeInsets:UIEdgeInsetsMake(9, 8, 7, 6)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeNormal];
miniButton.mode = MDCFloatingButtonModeNormal;
// Then
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(1, 2, 3, 4),
defaultButton.contentEdgeInsets));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(9, 8, 7, 6),
miniButton.contentEdgeInsets));
}
- (void)testSetContentEdgeInsetsForShapeInExpandedMode {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init]; // Default shape
MDCFloatingButton *miniButton =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
// When
[defaultButton setContentEdgeInsets:UIEdgeInsetsMake(1, 2, 3, 4)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeExpanded];
defaultButton.mode = MDCFloatingButtonModeExpanded;
[miniButton setContentEdgeInsets:UIEdgeInsetsMake(9, 8, 7, 6)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeExpanded];
miniButton.mode = MDCFloatingButtonModeExpanded;
// Then
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(1, 2, 3, 4),
defaultButton.contentEdgeInsets));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(9, 8, 7, 6),
miniButton.contentEdgeInsets));
}
- (void)testSettingContentEdgeInsetsInvalidatesIntrinsicContentSize {
// Given
FakeFloatingButton *button = [[FakeFloatingButton alloc] init]; // Default shape
[button reset];
// When
[button setContentEdgeInsets:UIEdgeInsetsMake(9, 8, 7, 7)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeNormal];
// Then
XCTAssertFalse(button.intrinsicContentSizeCalled);
XCTAssertTrue(button.invalidateIntrinsicContentSizeCalled);
XCTAssertFalse(button.sizeThatFitsCalled);
XCTAssertFalse(button.layoutSubviewsCalled);
}
#pragma mark - setMaximumSize:forShape:inMode:
- (void)testDefaultMaximumSizeForShapeInNormalModeSizeToFit {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init]; // Default shape
[defaultButton setTitle:@"a very long title" forState:UIControlStateNormal];
MDCFloatingButton *miniButton =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
[miniButton setTitle:@"a very long title" forState:UIControlStateNormal];
// When
[defaultButton sizeToFit];
[miniButton sizeToFit];
// Then
XCTAssertLessThanOrEqual(defaultButton.bounds.size.width, [MDCFloatingButton defaultDimension]);
XCTAssertLessThanOrEqual(defaultButton.bounds.size.height, [MDCFloatingButton defaultDimension]);
XCTAssertLessThanOrEqual(miniButton.bounds.size.width, [MDCFloatingButton miniDimension]);
XCTAssertLessThanOrEqual(miniButton.bounds.size.height, [MDCFloatingButton miniDimension]);
}
- (void)testDefaultMaximumSizeForShapeInExpandedModeSizeToFit {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init];
[defaultButton setTitle:@"An even longer title that should require more than 328 points to render"
forState:UIControlStateNormal];
defaultButton.mode = MDCFloatingButtonModeExpanded;
// When
[defaultButton sizeToFit];
// Then
XCTAssertLessThanOrEqual(defaultButton.bounds.size.width, 328);
}
- (void)testSetMaximumSizeForShapeInModeNormal {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init]; // Default shape
[defaultButton setTitle:@"a very long title" forState:UIControlStateNormal];
defaultButton.mode = MDCFloatingButtonModeExpanded;
MDCFloatingButton *miniButton =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
[miniButton setTitle:@"a very long title" forState:UIControlStateNormal];
miniButton.mode = MDCFloatingButtonModeExpanded;
// When
[defaultButton setMaximumSize:CGSizeMake(100, 50)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeNormal];
defaultButton.mode = MDCFloatingButtonModeNormal;
[miniButton setMaximumSize:CGSizeMake(100, 50)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeNormal];
miniButton.mode = MDCFloatingButtonModeNormal;
// Then
XCTAssertLessThanOrEqual(defaultButton.bounds.size.width, 100);
XCTAssertLessThanOrEqual(defaultButton.bounds.size.height, 50);
XCTAssertLessThanOrEqual(miniButton.bounds.size.width, 100);
XCTAssertLessThanOrEqual(miniButton.bounds.size.height, 50);
}
- (void)testSetMaximumSizeForShapeInModeExpanded {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init]; // Default shape
[defaultButton setTitle:@"An even longer title that should require more than 328 points to render"
forState:UIControlStateNormal];
MDCFloatingButton *miniButton =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
[miniButton setTitle:@"An even longer title that should require more than 328 points to render"
forState:UIControlStateNormal];
// When
[defaultButton setMaximumSize:CGSizeMake(50, 100)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeExpanded];
defaultButton.mode = MDCFloatingButtonModeExpanded;
[miniButton setMaximumSize:CGSizeMake(50, 100)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeExpanded];
miniButton.mode = MDCFloatingButtonModeExpanded;
// Then
XCTAssertLessThanOrEqual(defaultButton.bounds.size.width, 50);
XCTAssertLessThanOrEqual(defaultButton.bounds.size.height, 100);
XCTAssertLessThanOrEqual(miniButton.bounds.size.width, 50);
XCTAssertLessThanOrEqual(miniButton.bounds.size.height, 100);
}
- (void)testSettingMaximumSizeInvalidatesIntrinsicContentSize {
// Given
FakeFloatingButton *button = [[FakeFloatingButton alloc] init]; // Default shape
[button reset];
// When
[button setMaximumSize:CGSizeMake(7, 7)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeNormal];
// Then
XCTAssertFalse(button.intrinsicContentSizeCalled);
XCTAssertTrue(button.invalidateIntrinsicContentSizeCalled);
XCTAssertFalse(button.sizeThatFitsCalled);
XCTAssertFalse(button.layoutSubviewsCalled);
}
#pragma mark - setMinimumSize:forShape:inMode:
- (void)testDefaultMinimumSizeForShapeInNormalModeSizeToFit {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init];
MDCFloatingButton *miniButton =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
// When
[defaultButton sizeToFit];
[miniButton sizeToFit];
// Then
XCTAssertGreaterThanOrEqual(defaultButton.bounds.size.width,
[MDCFloatingButton defaultDimension]);
XCTAssertGreaterThanOrEqual(defaultButton.bounds.size.height,
[MDCFloatingButton defaultDimension]);
XCTAssertGreaterThanOrEqual(miniButton.bounds.size.width, [MDCFloatingButton miniDimension]);
XCTAssertGreaterThanOrEqual(miniButton.bounds.size.height, [MDCFloatingButton miniDimension]);
}
- (void)testDefaultMinimumSizeForShapeInExpandedModeSizeToFit {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init];
defaultButton.mode = MDCFloatingButtonModeExpanded;
// When
[defaultButton sizeToFit];
// Then
XCTAssertGreaterThanOrEqual(defaultButton.bounds.size.height, 48);
}
- (void)testSetMinimumSizeForShapeInModeNormal {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init]; // Default shape
defaultButton.mode = MDCFloatingButtonModeExpanded;
MDCFloatingButton *miniButton =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
miniButton.mode = MDCFloatingButtonModeExpanded;
// When
[defaultButton setMinimumSize:CGSizeMake(100, 50)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeNormal];
[defaultButton setMaximumSize:CGSizeZero
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeNormal];
defaultButton.mode = MDCFloatingButtonModeNormal;
[defaultButton sizeToFit];
[miniButton setMinimumSize:CGSizeMake(100, 50)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeNormal];
[miniButton setMaximumSize:CGSizeZero
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeNormal];
miniButton.mode = MDCFloatingButtonModeNormal;
[miniButton sizeToFit];
// Then
XCTAssertGreaterThanOrEqual(defaultButton.bounds.size.width, 100);
XCTAssertGreaterThanOrEqual(defaultButton.bounds.size.height, 50);
XCTAssertGreaterThanOrEqual(miniButton.bounds.size.width, 100);
XCTAssertGreaterThanOrEqual(miniButton.bounds.size.height, 50);
}
- (void)testSetMinimumSizeForShapeInModeExpanded {
// Given
MDCFloatingButton *defaultButton = [[MDCFloatingButton alloc] init]; // Default shape
MDCFloatingButton *miniButton =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeMini];
// When
[defaultButton setMinimumSize:CGSizeMake(50, 100)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeExpanded];
defaultButton.mode = MDCFloatingButtonModeExpanded;
[defaultButton sizeToFit];
[miniButton setMinimumSize:CGSizeMake(50, 100)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeExpanded];
miniButton.mode = MDCFloatingButtonModeExpanded;
[miniButton sizeToFit];
// Then
XCTAssertGreaterThanOrEqual(defaultButton.bounds.size.width, 50);
XCTAssertGreaterThanOrEqual(defaultButton.bounds.size.height, 100);
XCTAssertGreaterThanOrEqual(miniButton.bounds.size.width, 50);
XCTAssertGreaterThanOrEqual(miniButton.bounds.size.height, 100);
}
- (void)testSettingMinimumSizeInvalidatesIntrinsicContentSize {
// Given
FakeFloatingButton *button = [[FakeFloatingButton alloc] init]; // Default shape
[button reset];
// When
[button setMinimumSize:CGSizeMake(12, 12)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeNormal];
// Then
XCTAssertFalse(button.intrinsicContentSizeCalled);
XCTAssertTrue(button.invalidateIntrinsicContentSizeCalled);
XCTAssertFalse(button.layoutSubviewsCalled);
XCTAssertFalse(button.layoutSubviewsCalled);
}
#pragma mark - layoutSubviews
- (void)testExpandedLayout {
// Given
MDCFloatingButton *button =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeDefault];
button.mode = MDCFloatingButtonModeExpanded;
[button setTitle:@"A very long title that can never fit" forState:UIControlStateNormal];
[button setImage:fakeImage() forState:UIControlStateNormal];
[button setMaximumSize:CGSizeMake(100, 48)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeExpanded];
button.imageTitleSpace = 12;
// When
[button sizeToFit];
// Then
CGRect buttonBounds = CGRectStandardize(button.bounds);
CGRect imageFrame = CGRectStandardize(button.imageView.frame);
CGRect titleFrame = CGRectStandardize(button.titleLabel.frame);
XCTAssertEqualWithAccuracy(imageFrame.origin.x, 16, 1);
XCTAssertEqualWithAccuracy(CGRectGetMaxX(titleFrame), buttonBounds.size.width - 24, 1);
XCTAssertEqualWithAccuracy(titleFrame.origin.x,
CGRectGetMaxX(imageFrame) + button.imageTitleSpace, 1);
}
- (void)testExpandedLayoutShortTitle {
// Given
MDCFloatingButton *button =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeDefault];
button.mode = MDCFloatingButtonModeExpanded;
[button setTitle:@"A" forState:UIControlStateNormal];
[button setImage:fakeImage() forState:UIControlStateNormal];
[button setMaximumSize:CGSizeMake(100, 48)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeExpanded];
button.imageTitleSpace = 12;
// When
[button sizeToFit];
// Then
CGRect buttonBounds = CGRectStandardize(button.bounds);
CGRect imageFrame = CGRectStandardize(button.imageView.frame);
CGRect titleFrame = CGRectStandardize(button.titleLabel.frame);
XCTAssertEqualWithAccuracy(imageFrame.origin.x, 16, 1);
XCTAssertEqualWithAccuracy(CGRectGetMaxX(titleFrame), buttonBounds.size.width - 24, 1);
XCTAssertEqualWithAccuracy(titleFrame.origin.x,
CGRectGetMaxX(imageFrame) + button.imageTitleSpace, 1);
}
- (void)testExpandedLayoutWithImageLocationTrailing {
// Given
MDCFloatingButton *button =
[[MDCFloatingButton alloc] initWithFrame:CGRectZero shape:MDCFloatingButtonShapeDefault];
button.mode = MDCFloatingButtonModeExpanded;
button.imageLocation = MDCFloatingButtonImageLocationTrailing;
[button setTitle:@"A very long title that can never fit" forState:UIControlStateNormal];
[button setImage:fakeImage() forState:UIControlStateNormal];
[button setMaximumSize:CGSizeMake(100, 48)
forShape:MDCFloatingButtonShapeDefault
inMode:MDCFloatingButtonModeExpanded];
button.imageTitleSpace = 12;
// When
[button sizeToFit];
// Then
CGRect imageFrame = button.imageView.frame;
CGRect titleFrame = button.titleLabel.frame;
XCTAssertEqualWithAccuracy(CGRectGetMaxX(imageFrame), CGRectGetWidth(button.bounds)-16, 1);
XCTAssertEqualWithAccuracy(CGRectGetMinX(titleFrame), 24, 1);
XCTAssertEqualWithAccuracy(CGRectGetMinX(imageFrame),
CGRectGetMaxX(titleFrame) + button.imageTitleSpace, 1);
}
- (void)testExpandedLayoutWithNonZeroContentEdgeInsets {
// Given
MDCFloatingButton *button = [[MDCFloatingButton alloc] initWithFrame:CGRectZero
shape:MDCFloatingButtonShapeMini];
button.mode = MDCFloatingButtonModeExpanded;
[button setTitle:@"A very long title that can never fit" forState:UIControlStateNormal];
[button setImage:fakeImage() forState:UIControlStateNormal];
[button setMinimumSize:CGSizeMake(100, 48)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeExpanded];
[button setMaximumSize:CGSizeMake(100, 48)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeExpanded];
[button setContentEdgeInsets:UIEdgeInsetsMake(4, -4, 8, -8)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeExpanded];
// When
[button sizeToFit];
// Then
CGRect buttonBounds = CGRectStandardize(button.bounds);
CGRect imageFrame = CGRectStandardize(button.imageView.frame);
CGRect titleFrame = CGRectStandardize(button.titleLabel.frame);
XCTAssertEqualWithAccuracy(imageFrame.origin.x, 12, 1);
XCTAssertEqualWithAccuracy(CGRectGetMaxX(titleFrame), buttonBounds.size.width - 16, 1);
XCTAssertEqualWithAccuracy(titleFrame.origin.x,
CGRectGetMaxX(imageFrame) + button.imageTitleSpace, 1);
}
- (void)testExpandedLayoutBehaviorIsIdempotent {
// Given
MDCFloatingButton *button = [[MDCFloatingButton alloc] init]; // Default shape
button.mode = MDCFloatingButtonModeExpanded;
[button setTitle:@"Title" forState:UIControlStateNormal];
[button setImage:fakeImage() forState:UIControlStateNormal];
[button sizeToFit];
CGRect originalImageFrame = button.imageView.frame;
CGRect originalTitleFrame = button.titleLabel.frame;
// When
[button layoutIfNeeded];
[button sizeToFit];
[button layoutIfNeeded];
[button layoutIfNeeded];
// Then
XCTAssertTrue(CGRectEqualToRect(originalImageFrame, button.imageView.frame),
@"Image frames are not equal.\nExpected: %@\nReceived: %@",
NSStringFromCGRect(originalImageFrame), NSStringFromCGRect(button.imageView.frame));
XCTAssertTrue(CGRectEqualToRect(originalTitleFrame, button.titleLabel.frame),
@"Title frames are not equal.\nExpected: %@\nReceived: %@",
NSStringFromCGRect(originalTitleFrame),
NSStringFromCGRect(button.titleLabel.frame));
}
#pragma mark - Encoding/decoding
- (void)testEncoding {
// Given
MDCFloatingButton *button = [[MDCFloatingButton alloc] initWithFrame:CGRectMake(1, 2, 3, 4)
shape:MDCFloatingButtonShapeMini];
button.mode = MDCFloatingButtonModeExpanded;
button.imageLocation = MDCFloatingButtonImageLocationTrailing;
[button setImage:fakeImage() forState:UIControlStateNormal];
[button setTitle:@"Title" forState:UIControlStateNormal];
[button setMinimumSize:CGSizeMake(75, 37)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeExpanded];
[button setMaximumSize:CGSizeMake(99, 65)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeExpanded];
[button setContentEdgeInsets:UIEdgeInsetsMake(5, 3, 1, 7)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeExpanded];
[button setHitAreaInsets:UIEdgeInsetsMake(8, 6, 4, 2)
forShape:MDCFloatingButtonShapeMini
inMode:MDCFloatingButtonModeExpanded];
// When
NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:button];
MDCFloatingButton *unarchivedButton = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData];
[button sizeToFit];
[button layoutIfNeeded];
[unarchivedButton sizeToFit];
[unarchivedButton layoutIfNeeded];
// Then
XCTAssertTrue(CGRectEqualToRect(button.bounds, unarchivedButton.bounds),
@"Unarchived bounds did not match original button's bounds.\nExpected: %@\n"
"Received: %@",NSStringFromCGRect(button.bounds),
NSStringFromCGRect(unarchivedButton.bounds));
XCTAssertEqual(button.mode, unarchivedButton.mode);
XCTAssertEqual(button.imageLocation, unarchivedButton.imageLocation);
XCTAssertEqualObjects(button.currentTitle, unarchivedButton.currentTitle);
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(button.contentEdgeInsets,
unarchivedButton.contentEdgeInsets));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(button.hitAreaInsets,
unarchivedButton.hitAreaInsets));
XCTAssertTrue(CGSizeEqualToSize(button.minimumSize, unarchivedButton.minimumSize));
XCTAssertTrue(CGSizeEqualToSize(button.maximumSize, unarchivedButton.maximumSize));
}
- (void)testShapeMigrationFromLargeIcon {
// Given
MDCFloatingButton *button = [[MDCFloatingButton alloc] initWithFrame:CGRectZero
shape:MDCFloatingButtonShapeMini];
[button setValue:@(2) forKey:@"shape"];
[button sizeToFit];
// When
NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:button];
MDCFloatingButton *unarchivedButton = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData];
[unarchivedButton sizeToFit];
// Then
CGFloat dimension = [MDCFloatingButton defaultDimension];
XCTAssertTrue(CGRectEqualToRect(unarchivedButton.bounds, CGRectMake(0, 0, dimension, dimension)));
XCTAssertEqualObjects([unarchivedButton valueForKey:@"shape"], @(0));
}
- (void)testDefaultElevationsForState {
// Given
MDCFloatingButton *button = [MDCFloatingButton appearance];
// Then
XCTAssertEqual([button elevationForState:UIControlStateNormal], MDCShadowElevationFABResting);
XCTAssertEqual([button elevationForState:UIControlStateHighlighted],
MDCShadowElevationFABPressed);
XCTAssertEqual([button elevationForState:UIControlStateDisabled], MDCShadowElevationNone);
}
#pragma mark - Animations
- (void)testCollapseExpandRestoresIdentityTransform {
// Given
MDCFloatingButton *button = [[MDCFloatingButton alloc] init];
CGAffineTransform transform = CGAffineTransformIdentity;
button.transform = transform;
// When
[button collapse:NO completion:nil];
[button expand:NO completion:nil];
// Then
XCTAssertTrue(
CGAffineTransformEqualToTransform(button.transform, transform),
@"Collapse and expand did not restore the original transform.\nExpected: %@\nReceived: %@",
NSStringFromCGAffineTransform(transform), NSStringFromCGAffineTransform(button.transform));
}
- (void)testCollapseExpandRestoresTransform {
// Given
MDCFloatingButton *button = [[MDCFloatingButton alloc] init];
CGAffineTransform transform = CGAffineTransformRotate(button.transform, (CGFloat)M_PI_2);
button.transform = transform;
// When
[button collapse:NO completion:nil];
[button expand:NO completion:nil];
// Then
XCTAssertTrue(
CGAffineTransformEqualToTransform(button.transform, transform),
@"Collapse and expand did not restore the original transform.\nExpected: %@\nReceived: %@",
NSStringFromCGAffineTransform(transform), NSStringFromCGAffineTransform(button.transform));
}
// This test is flaky.
// <unknown>:0: error: -[FloatingButtonsTests testCollapseExpandAnimatedRestoresTransform] : Asynchronous wait failed: Exceeded timeout of 1 seconds, with unfulfilled expectations: "Expand animation complete".
- (void)disabled_testCollapseExpandAnimatedRestoresTransform {
// Given
MDCFloatingButton *button = [[MDCFloatingButton alloc] init];
CGAffineTransform transform = CGAffineTransformMakeTranslation(10, (CGFloat)-77.1);
button.transform = transform;
XCTestExpectation *expectation = [self expectationWithDescription:@"Collapse animation complete"];
// When
[button collapse:YES
completion:^{
[expectation fulfill];
}];
// Then
// Collapse should take around 200ms in total
[self waitForExpectationsWithTimeout:1 handler:nil];
XCTAssertFalse(CGAffineTransformEqualToTransform(button.transform, transform),
@"Collapse did not modify the original transform.\nOriginal: %@\nReceived: %@",
NSStringFromCGAffineTransform(transform),
NSStringFromCGAffineTransform(button.transform));
expectation = [self expectationWithDescription:@"Expand animation complete"];
[button expand:YES
completion:^{
[expectation fulfill];
}];
// Expand should take around 200ms in total
[self waitForExpectationsWithTimeout:1 handler:nil];
XCTAssertTrue(
CGAffineTransformEqualToTransform(button.transform, transform),
@"Collapse and expand did not restore the original transform.\nExpected: %@\nReceived: %@",
NSStringFromCGAffineTransform(transform), NSStringFromCGAffineTransform(button.transform));
}
#pragma mark - Sizing methods
- (void)testSizeThatFits {
// Given
MDCFloatingButton *miniButton =
[MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeMini];
MDCFloatingButton *defaultButton =
[MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeDefault];
// When
CGSize miniSize = [miniButton sizeThatFits:CGSizeZero];
CGSize defaultSize = [defaultButton sizeThatFits:CGSizeZero];
// Then
CGFloat miniDimension = [MDCFloatingButton miniDimension];
CGFloat defaultDimension = [MDCFloatingButton defaultDimension];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(miniDimension, miniDimension), miniSize));
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(defaultDimension, defaultDimension), defaultSize));
}
- (void)testIntrinsicContentSize {
// Given
MDCFloatingButton *miniButton =
[MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeMini];
MDCFloatingButton *defaultButton =
[MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeDefault];
// When
CGSize miniSize = [miniButton intrinsicContentSize];
CGSize defaultSize = [defaultButton intrinsicContentSize];
// Then
CGFloat miniDimension = [MDCFloatingButton miniDimension];
CGFloat defaultDimension = [MDCFloatingButton defaultDimension];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(miniDimension, miniDimension), miniSize));
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(defaultDimension, defaultDimension), defaultSize));
}
- (void)testHitAreaInsetsSurviveSizeThatFits {
// Given
MDCFloatingButton *defaultButton =
[MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeDefault];
MDCFloatingButton *miniButton =
[MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeMini];
// When
[defaultButton sizeThatFits:CGSizeZero];
[miniButton sizeThatFits:CGSizeZero];
// Then
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, defaultButton.hitAreaInsets));
XCTAssertTrue(
UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(-4, -4, -4, -4), miniButton.hitAreaInsets));
}
- (void)testCornerRadius {
// Given
MDCFloatingButton *button =
[MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeDefault];
[button sizeToFit];
[button layoutIfNeeded];
// Then
XCTAssertNotEqualWithAccuracy(CGRectGetHeight(button.bounds), 0, 0.0001);
XCTAssertEqualWithAccuracy(button.layer.cornerRadius, CGRectGetHeight(button.bounds) / 2, 0.0001);
}
@end