blob: 912411a8a65182611245dba3b979cbfa3c84ff10 [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 XCTest
import MaterialComponents.MaterialAppBar
private class MockAppBarNavigationControllerDelegate:
NSObject, MDCAppBarNavigationControllerDelegate
{
var trackingScrollView: UIScrollView?
func appBarNavigationController(
_ navigationController: MDCAppBarNavigationController,
trackingScrollViewFor trackingScrollViewForViewController: UIViewController,
suggestedTrackingScrollView: UIScrollView?
) -> UIScrollView? {
return trackingScrollView
}
}
class AppBarNavigationControllerTests: XCTestCase {
var navigationController: MDCAppBarNavigationController!
override func setUp() {
super.setUp()
navigationController = MDCAppBarNavigationController()
}
override func tearDown() {
navigationController = nil
super.tearDown()
}
// MARK: - AppBar injection
func testInitializingWithRootViewControllerInjectsAnAppBar() {
// Given
let viewController = UIViewController()
// When
let navigationController = MDCAppBarNavigationController(rootViewController: viewController)
// Then
XCTAssertEqual(
viewController.children.count, 1,
"Expected there to be exactly one child view controller added to the view"
+ " controller.")
XCTAssertEqual(
navigationController.topViewController, viewController,
"The navigation controller's top view controller is supposed to be the pushed"
+ " view controller, but it is \(viewController).")
XCTAssertTrue(
viewController.children.first is MDCFlexibleHeaderViewController,
"The injected view controller is not a flexible header view controller, it is"
+ "\(String(describing: viewController.children.first)) instead.")
if let headerViewController = viewController.children.first as? MDCFlexibleHeaderViewController
{
XCTAssertEqual(
headerViewController.headerView.frame.height,
headerViewController.headerView.maximumHeight)
}
}
func testPushingAViewControllerInjectsAnAppBar() {
// Given
let viewController = UIViewController()
// When
navigationController.pushViewController(viewController, animated: false)
// Then
XCTAssertEqual(
viewController.children.count, 1,
"Expected there to be exactly one child view controller added to the view"
+ " controller.")
XCTAssertEqual(
navigationController.topViewController, viewController,
"The navigation controller's top view controller is supposed to be the pushed"
+ " view controller, but it is \(viewController).")
XCTAssertTrue(
viewController.children.first is MDCFlexibleHeaderViewController,
"The injected view controller is not a flexible header view controller, it is"
+ "\(String(describing: viewController.children.first)) instead.")
if let headerViewController = viewController.children.first as? MDCFlexibleHeaderViewController
{
XCTAssertEqual(
headerViewController.headerView.frame.height,
headerViewController.headerView.maximumHeight)
}
}
func testPushingAnAppBarContainerViewControllerDoesNotInjectAnAppBar() {
// Given
let viewController = UIViewController()
let container = MDCAppBarContainerViewController(contentViewController: viewController)
// When
navigationController.pushViewController(container, animated: false)
// Then
XCTAssertEqual(
container.children.count, 2,
"An App Bar container view controller should have exactly two child view"
+ " controllers. A failure of this assertion implies that the navigation"
+ " controller may have injected another App Bar.")
}
func testPushingAContainedAppBarContainerViewControllerDoesNotInjectAnAppBar() {
// Given
let viewController = UIViewController()
let container = MDCAppBarContainerViewController(contentViewController: viewController)
let nestedContainer = UIViewController()
nestedContainer.addChild(container)
nestedContainer.view.addSubview(container.view)
container.didMove(toParent: nestedContainer)
// When
navigationController.pushViewController(nestedContainer, animated: false)
// Then
XCTAssertEqual(
nestedContainer.children.count, 1,
"The view controller hierarchy already has one app bar view controller, but it"
+ " appears to have possibly added another.")
}
func testPushingAViewControllerWithAFlexibleHeaderDoesNotInjectAnAppBar() {
// Given
let viewController = UIViewController()
let fhvc = MDCFlexibleHeaderViewController()
viewController.addChild(fhvc)
viewController.view.addSubview(fhvc.view)
fhvc.didMove(toParent: viewController)
// When
navigationController.pushViewController(viewController, animated: false)
// Then
XCTAssertEqual(
viewController.children.count, 1,
"The navigation controller may have injected another App Bar when it shouldn't"
+ " have.")
}
// MARK: - traitCollectionDidChangeBlock support
func testInitializingWithRootViewControllerDoesNotSetTraitCollectionDidChangeBlock() {
// Given
let viewController = UIViewController()
// When
let _ = MDCAppBarNavigationController(rootViewController: viewController)
// Then
let injectedAppBarViewController = viewController.children.first as! MDCAppBarViewController
XCTAssertNotNil(injectedAppBarViewController)
XCTAssertNil(injectedAppBarViewController.traitCollectionDidChangeBlock)
}
func testPushingAViewControllerAssignsTraitCollectionDidChangeBlock() {
// Given
let viewController = UIViewController()
let block: ((MDCFlexibleHeaderViewController, UITraitCollection?) -> Void)? = { _, _ in }
navigationController.traitCollectionDidChangeBlockForAppBarController = block
// When
navigationController.pushViewController(viewController, animated: false)
// Then
let injectedAppBarViewController = viewController.children.first as! MDCAppBarViewController
XCTAssertNotNil(injectedAppBarViewController)
XCTAssertNotNil(injectedAppBarViewController.traitCollectionDidChangeBlock)
}
func testPushingAnAppBarContainerViewControllerDoesNotAssignTraitCollectionDidChangeBlock() {
// Given
let viewController = UIViewController()
let container = MDCAppBarContainerViewController(contentViewController: viewController)
var blockSemaphore = false
let block: ((MDCFlexibleHeaderViewController, UITraitCollection?) -> Void)? =
{ _, _ in
blockSemaphore = true
}
container.appBarViewController.traitCollectionDidChangeBlock = block
// When
navigationController.pushViewController(container, animated: false)
container.appBarViewController.traitCollectionDidChange(nil)
// Then
XCTAssertTrue(blockSemaphore)
}
func
testPushingAContainedAppBarContainerViewControllerDoesNotAssignTraitCollectionDidChangeBlock()
{
// Given
let viewController = UIViewController()
let container = MDCAppBarContainerViewController(contentViewController: viewController)
let nestedContainer = UIViewController()
nestedContainer.addChild(container)
nestedContainer.view.addSubview(container.view)
container.didMove(toParent: nestedContainer)
var blockSemaphore = false
let block: ((MDCFlexibleHeaderViewController, UITraitCollection?) -> Void)? =
{ _, _ in
blockSemaphore = true
}
container.appBarViewController.traitCollectionDidChangeBlock = block
// When
navigationController.pushViewController(nestedContainer, animated: false)
container.appBarViewController.traitCollectionDidChange(nil)
// Then
XCTAssertTrue(blockSemaphore)
}
// MARK: - The rest
func testStatusBarStyleIsFetchedFromFlexibleHeaderViewController() {
// Given
let viewController = UIViewController()
// When
navigationController.pushViewController(viewController, animated: false)
// Then
let appBar = navigationController.appBar(for: viewController)
XCTAssertNotNil(appBar, "Could not retrieve the injected App Bar.")
if let appBar = appBar {
XCTAssertEqual(
navigationController.childForStatusBarStyle,
appBar.headerViewController,
"The navigation controller should be using the injected app bar's flexible"
+ "header view controller for status bar style updates.")
}
}
func testInfersFirstTrackingScrollViewByDefault() {
// Given
let viewController = UIViewController()
let scrollView1 = UIScrollView()
viewController.view.addSubview(scrollView1)
let scrollView2 = UIScrollView()
viewController.view.addSubview(scrollView2)
// When
navigationController.pushViewController(viewController, animated: false)
// Then
guard let appBarViewController = navigationController.appBarViewController(for: viewController)
else {
XCTFail("No app bar view controller found.")
return
}
XCTAssertEqual(appBarViewController.headerView.trackingScrollView, scrollView1)
}
func testDelegateCanReturnNilTrackingScrollView() {
// Given
let viewController = UIViewController()
let scrollView1 = UIScrollView()
viewController.view.addSubview(scrollView1)
let scrollView2 = UIScrollView()
viewController.view.addSubview(scrollView2)
let delegate = MockAppBarNavigationControllerDelegate()
navigationController.delegate = delegate
// When
delegate.trackingScrollView = nil
navigationController.pushViewController(viewController, animated: false)
// Then
guard let appBarViewController = navigationController.appBarViewController(for: viewController)
else {
XCTFail("No app bar view controller found.")
return
}
XCTAssertNil(appBarViewController.headerView.trackingScrollView)
}
func testDelegateCanPickDifferentTrackingScrollView() {
// Given
let viewController = UIViewController()
let scrollView1 = UIScrollView()
viewController.view.addSubview(scrollView1)
let scrollView2 = UIScrollView()
viewController.view.addSubview(scrollView2)
let delegate = MockAppBarNavigationControllerDelegate()
navigationController.delegate = delegate
// When
delegate.trackingScrollView = scrollView2
navigationController.pushViewController(viewController, animated: false)
// Then
guard let appBarViewController = navigationController.appBarViewController(for: viewController)
else {
XCTFail("No app bar view controller found.")
return
}
XCTAssertEqual(appBarViewController.headerView.trackingScrollView, scrollView2)
}
}