blob: f8f8ac1692210069dd45b63bd1d8b23752219024 [file] [log] [blame] [edit]
// Copyright 2018-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 "MDCSheetState.h"
#import "MaterialBottomSheet.h"
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "../../src/private/MDCDraggableView.h"
#import "../../src/private/MDCSheetContainerView.h"
@interface MDCBottomSheetDelegateTest
: UIViewController <MDCBottomSheetPresentationControllerDelegate>
@property(nonatomic, assign) BOOL delegateWasCalled;
@end
@implementation MDCBottomSheetDelegateTest
- (instancetype)init {
self = [super init];
if (self) {
_delegateWasCalled = NO;
}
return self;
}
- (void)bottomSheetDidChangeYOffset:(MDCBottomSheetPresentationController *)bottomSheet
yOffset:(CGFloat)yOffset {
_delegateWasCalled = YES;
}
- (void)bottomSheetWillChangeState:(MDCBottomSheetPresentationController *)bottomSheet
sheetState:(MDCSheetState)sheetState {
_delegateWasCalled = YES;
}
@end
// Exposing internal methods for unit testing
@interface MDCBottomSheetPresentationController (Testing)
@property(nonatomic, strong) MDCSheetContainerView *sheetView;
- (void)updatePreferredSheetHeight;
@end
@interface MDCSheetContainerView (Testing)
@property(nonatomic) MDCDraggableView *sheet;
@property(nonatomic) MDCSheetState sheetState;
- (CGPoint)targetPoint;
- (void)draggableView:(MDCDraggableView *)view didPanToOffset:(CGFloat)offset;
- (BOOL)draggableView:(MDCDraggableView *)view shouldBeginDraggingWithVelocity:(CGPoint)velocity;
- (void)draggableView:(MDCDraggableView *)view draggingEndedWithVelocity:(CGPoint)velocity;
@end
/**
A testing double for @c MDCSheetContainerView that allows setting an explicitly-nonstandardized
@c frame value.
@note Although it is possible to retrieve a non-standardized frame or bounds from a UIView object,
UIView will standardize the CGRect passed to @c setFrame:. To aid in testing, we turn
@c frame into a simple get/set property. We still call up to the super implementation of
@c setFrame: in case there are side-effects in UIView.
*/
@interface FakeSheetView : MDCSheetContainerView
@property(nonatomic, assign) UIEdgeInsets customSafeAreaInsets;
@end
@implementation FakeSheetView {
CGRect _frame;
}
- (CGRect)frame {
return _frame;
}
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
_frame = frame;
}
- (UIEdgeInsets)safeAreaInsets {
return _customSafeAreaInsets;
}
@end
@interface MDCBottomSheetPresentationControllerTests : XCTestCase
@property(nonatomic, strong) FakeSheetView *sheetView;
@property(nonatomic, strong) MDCBottomSheetPresentationController *presentationController;
@property(nonatomic, strong, nullable) MDCBottomSheetDelegateTest *delegateTest;
@end
@implementation MDCBottomSheetPresentationControllerTests
- (void)setUp {
[super setUp];
// The `sheetView` property is both an input and an output to `setPreferredSheetHeight:`. Its
// frame may be used to guess the preferredContentHeight of the sheet. Once calculated, it
// receives an updated value for `preferredSheetHeight`.
self.sheetView = [[FakeSheetView alloc] initWithFrame:CGRectZero
contentView:[[UIView alloc] init]
scrollView:[[UIScrollView alloc] init]
simulateScrollViewBounce:YES];
// Only used as a required `-init` parameters for MDCBottomSheetPresentationController
UIViewController *stubPresentingViewController = [[UIViewController alloc] init];
UIViewController *stubPresentedViewController = [[UIViewController alloc] init];
self.presentationController = [[MDCBottomSheetPresentationController alloc]
initWithPresentedViewController:stubPresentedViewController
presentingViewController:stubPresentingViewController];
self.presentationController.sheetView = self.sheetView;
self.delegateTest = [[MDCBottomSheetDelegateTest alloc] init];
}
- (void)tearDown {
self.presentationController.sheetView = nil;
self.presentationController = nil;
self.sheetView = nil;
self.delegateTest = nil;
[super tearDown];
}
- (void)testUpdatePreferredSheetHeightZeroWhenSheetViewHasStandardizedFrame {
// Given
CGFloat sheetFrameHeight = 80;
self.sheetView.frame = CGRectMake(0, 0, 75, sheetFrameHeight);
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, sheetFrameHeight / 2, 0.001);
}
- (void)testUpdatePreferredSheetHeightZeroWhenSheetViewHasUnstandardizedFrame {
// Given
CGFloat sheetFrameHeight = -80;
self.sheetView.frame = CGRectMake(75, 80, -75, sheetFrameHeight);
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight,
(CGFloat)fabs(sheetFrameHeight / 2), 0.001);
}
- (void)testUpdatePreferredSheetHeightPositiveValue {
// Given
CGFloat preferredSheetHeight = 120;
self.presentationController.presentedViewController.preferredContentSize =
CGSizeMake(100, preferredSheetHeight);
self.sheetView.frame = CGRectMake(0, 0, 75, 80);
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight, 0.001);
}
- (void)testUpdatePreferredSheetHeightNegativeValue {
// Given
CGFloat preferredSheetHeight = -120;
self.presentationController.presentedViewController.preferredContentSize =
CGSizeMake(0, preferredSheetHeight);
self.sheetView.frame = CGRectMake(0, 0, 75, -80);
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight, 0.001);
}
- (void)testUpdatePreferredSheetHeight {
// Given
CGFloat preferredSheetHeight = 100;
self.presentationController.preferredSheetHeight = preferredSheetHeight;
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight, 0.001);
}
- (void)testUpdateNegativePreferredSheetHeight {
// Given
CGFloat preferrredSheetHeight = -100;
self.presentationController.preferredSheetHeight = preferrredSheetHeight;
CGFloat sheetHeight = 250;
self.sheetView.frame = CGRectMake(0, 0, 75, sheetHeight);
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, (CGFloat)(sheetHeight / 2),
0.001);
}
- (void)testUpdatePreferredSheetHeightAndPreferredContentSize {
// Given
CGSize preferredContentSize = CGSizeMake(100, 100);
CGFloat preferredSheetHeight = 200;
self.presentationController.presentedViewController.preferredContentSize = preferredContentSize;
self.presentationController.preferredSheetHeight = preferredSheetHeight;
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight, 0.001);
}
- (void)testPreferredSheetHeightNegativeAndPreferredContentSizePositive {
// Given
CGSize preferredContentSize = CGSizeMake(100, 100);
CGFloat preferredSheetHeight = -200;
self.presentationController.presentedViewController.preferredContentSize = preferredContentSize;
self.presentationController.preferredSheetHeight = preferredSheetHeight;
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredContentSize.height,
0.001);
}
- (void)testPreferredSheetHeightNonZeroThenZero {
// Given
CGFloat preferredSheetHeight = 200;
self.presentationController.preferredSheetHeight = preferredSheetHeight;
CGFloat sheetHeight = 300;
self.sheetView.frame = CGRectMake(0, 0, 200, sheetHeight);
// When
[self.presentationController updatePreferredSheetHeight];
self.presentationController.preferredSheetHeight = 0;
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, (CGFloat)(sheetHeight / 2),
0.001);
}
- (void)testVeryLargePreferredSheetHeightAndSmallContent {
// Given
CGFloat scrollViewHeight = 100;
CGRect smallFrame = CGRectMake(0, 0, 200, scrollViewHeight);
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:smallFrame];
FakeSheetView *sheetView =
[[FakeSheetView alloc] initWithFrame:smallFrame
contentView:[[UIView alloc] initWithFrame:smallFrame]
scrollView:scrollView
simulateScrollViewBounce:YES];
self.presentationController.sheetView = sheetView;
self.presentationController.preferredSheetHeight = 5000;
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(CGRectGetHeight(sheetView.frame), CGRectGetHeight(smallFrame), 0.001);
}
- (void)testDefaultAdjustHeightForSafeAreaInsets {
XCTAssertTrue(self.presentationController.adjustHeightForSafeAreaInsets);
}
- (void)testAdjustHeightForSafeAreaInsetsIsNo {
// Given
CGFloat preferredSheetHeight = 200;
CGFloat inset = 20;
self.sheetView.customSafeAreaInsets = UIEdgeInsetsMake(inset, inset, inset, inset);
self.presentationController.preferredSheetHeight = preferredSheetHeight;
self.presentationController.adjustHeightForSafeAreaInsets = NO;
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight, 0.001);
}
- (void)testAdjustHeightForSafeAreaInsetsSetBeforeHeightIsSet {
// Given
CGFloat preferredSheetHeight = 200;
CGFloat inset = 20;
self.sheetView.customSafeAreaInsets = UIEdgeInsetsMake(inset, inset, inset, inset);
self.presentationController.adjustHeightForSafeAreaInsets = NO;
self.presentationController.preferredSheetHeight = preferredSheetHeight;
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight, 0.001);
}
- (void)testAdjustHeightForSafeAreaInsetsIsYes {
// Given
CGFloat preferredSheetHeight = 200;
CGFloat inset = 20;
self.sheetView.customSafeAreaInsets = UIEdgeInsetsMake(inset, inset, inset, inset);
self.presentationController.preferredSheetHeight = preferredSheetHeight;
self.presentationController.adjustHeightForSafeAreaInsets = YES;
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight + inset,
0.001);
}
- (void)testadjustHeightForSafeAreaInsetsChangesToNo {
// Given
CGFloat preferredSheetHeight = 200;
CGFloat inset = 20;
self.sheetView.customSafeAreaInsets = UIEdgeInsetsMake(inset, inset, inset, inset);
self.presentationController.adjustHeightForSafeAreaInsets = YES;
self.presentationController.preferredSheetHeight = preferredSheetHeight;
self.presentationController.adjustHeightForSafeAreaInsets = NO;
// When
[self.presentationController updatePreferredSheetHeight];
// Then
XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight, 0.001);
}
- (void)testSheetViewFrameMatchesScrollViewFrame {
// Given
CGFloat scrollViewHeight = 100;
CGRect fakeFrame = CGRectMake(0, 0, 200, scrollViewHeight);
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
MDCSheetContainerView *fakeSheet =
[[MDCSheetContainerView alloc] initWithFrame:fakeFrame
contentView:[[UIView alloc] initWithFrame:fakeFrame]
scrollView:scrollView
simulateScrollViewBounce:YES];
// When
[fakeSheet setNeedsLayout];
[fakeSheet layoutIfNeeded];
// Then
CGRect scrollViewFrame = CGRectStandardize(fakeSheet.sheet.frame);
XCTAssertEqualWithAccuracy(scrollViewFrame.size.height, scrollViewHeight, 0.001);
}
- (void)testSheetStateChangeCallback {
// Given
self.presentationController.delegate = self.delegateTest;
self.presentationController.sheetView.delegate =
(id<MDCSheetContainerViewDelegate>)self.presentationController;
// When
self.sheetView.sheetState = MDCSheetStateExtended;
// Then
XCTAssertEqual(self.delegateTest.delegateWasCalled, YES);
}
- (void)testSheetYOffsetChangeCallbackFromTargetPoint {
// Given
self.presentationController.delegate = self.delegateTest;
self.presentationController.sheetView.delegate =
(id<MDCSheetContainerViewDelegate>)self.presentationController;
// When
[self.sheetView targetPoint];
// Then
XCTAssertEqual(self.delegateTest.delegateWasCalled, YES);
}
- (void)testSheetYOffsetChangeCallbackFromDidPanToOffset {
// Given
self.presentationController.delegate = self.delegateTest;
self.presentationController.sheetView.delegate =
(id<MDCSheetContainerViewDelegate>)self.presentationController;
// When
[self.sheetView draggableView:self.sheetView.sheet didPanToOffset:10];
// Then
XCTAssertEqual(self.delegateTest.delegateWasCalled, YES);
}
#pragma mark - dismissOnDraggingDownSheet
- (void)testSettingDismissOnDraggingDownSheetOnPresentationController {
// Given
self.presentationController.dismissOnDraggingDownSheet = NO;
// Then
XCTAssertFalse(self.sheetView.dismissOnDraggingDownSheet);
}
- (void)testSettingDismissOnDraggingDownSheetViewDoesNotBlocksGesture {
// Given
self.sheetView.dismissOnDraggingDownSheet = NO;
// When
BOOL shouldBeginDragging = [self.sheetView draggableView:self.sheetView.sheet
shouldBeginDraggingWithVelocity:CGPointMake(0, 0)];
// Then
XCTAssertTrue(shouldBeginDragging);
}
- (void)testSettingDismissOnDraggingDownSheetToNoViewShouldNotCloseWithDragDown {
// Given
self.sheetView.dismissOnDraggingDownSheet = NO;
self.sheetView.sheetState = MDCSheetStatePreferred;
// When
[self.sheetView draggableView:self.sheetView.sheet draggingEndedWithVelocity:CGPointMake(0, 1)];
// Then
XCTAssertNotEqual(self.sheetView.sheetState, MDCSheetStateClosed);
}
- (void)testSettingDismissOnDraggingDownSheetToNoViewShouldNotCloseWithDragDownWithPreferredHeight {
// Given
self.sheetView.dismissOnDraggingDownSheet = NO;
self.sheetView.sheetState = MDCSheetStatePreferred;
self.sheetView.preferredSheetHeight = 222;
// When
[self.sheetView draggableView:self.sheetView.sheet draggingEndedWithVelocity:CGPointMake(0, 1)];
// Then
XCTAssertNotEqual(self.sheetView.sheetState, MDCSheetStateClosed);
}
- (void)testSettingDismissOnDraggingDownSheetViewToYesShouldCloseWithDragDown {
// Given
self.sheetView.dismissOnDraggingDownSheet = YES;
self.sheetView.sheetState = MDCSheetStatePreferred;
// When
[self.sheetView draggableView:self.sheetView.sheet draggingEndedWithVelocity:CGPointMake(0, 1)];
// Then
XCTAssertEqual(self.sheetView.sheetState, MDCSheetStateClosed);
}
- (void)testSettingDismissOnDraggingDownSheetViewToYesShouldCloseWithDragDownWithPreferredHeight {
// Given
self.sheetView.dismissOnDraggingDownSheet = YES;
self.sheetView.sheetState = MDCSheetStatePreferred;
self.sheetView.preferredSheetHeight = 222;
// When
[self.sheetView draggableView:self.sheetView.sheet draggingEndedWithVelocity:CGPointMake(0, 1)];
// Then
XCTAssertEqual(self.sheetView.sheetState, MDCSheetStateClosed);
}
- (void)testSettingDismissOnDraggingDownSheetToNoViewShouldNotCloseWithDragUp {
// Given
self.sheetView.dismissOnDraggingDownSheet = NO;
self.sheetView.sheetState = MDCSheetStatePreferred;
// When
[self.sheetView draggableView:self.sheetView.sheet draggingEndedWithVelocity:CGPointMake(0, -1)];
// Then
XCTAssertNotEqual(self.sheetView.sheetState, MDCSheetStateClosed);
}
- (void)testSettingDismissOnDraggingDownSheetToNoViewShouldNotCloseWithDragUpWithPreferredHeight {
// Given
self.sheetView.dismissOnDraggingDownSheet = NO;
self.sheetView.sheetState = MDCSheetStatePreferred;
self.sheetView.preferredSheetHeight = 222;
// When
[self.sheetView draggableView:self.sheetView.sheet draggingEndedWithVelocity:CGPointMake(0, -1)];
// Then
XCTAssertNotEqual(self.sheetView.sheetState, MDCSheetStateClosed);
}
- (void)testSettingDismissOnDraggingDownSheetViewToYesShouldCloseWithDragUp {
// Given
self.sheetView.dismissOnDraggingDownSheet = YES;
self.sheetView.sheetState = MDCSheetStatePreferred;
// When
[self.sheetView draggableView:self.sheetView.sheet draggingEndedWithVelocity:CGPointMake(0, -1)];
// Then
XCTAssertEqual(self.sheetView.sheetState, MDCSheetStatePreferred);
}
- (void)testSettingDismissOnDraggingDownSheetViewToYesShouldCloseWithDragUpWithPreferredHeight {
// Given
self.sheetView.dismissOnDraggingDownSheet = YES;
self.sheetView.sheetState = MDCSheetStatePreferred;
self.sheetView.preferredSheetHeight = 222;
// When
[self.sheetView draggableView:self.sheetView.sheet draggingEndedWithVelocity:CGPointMake(0, -1)];
// Then
XCTAssertEqual(self.sheetView.sheetState, MDCSheetStatePreferred);
}
@end