// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/overlays/ui_bundled/overlay_presentation_context_view_controller.h"
#import "base/test/ios/wait_util.h"
#import "build/branding_buildflags.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request.h"
#import "ios/chrome/browser/overlays/model/public/test_modality/test_presented_overlay_request_config.h"
#import "ios/chrome/browser/overlays/model/public/test_modality/test_resizing_presented_overlay_request_config.h"
#import "ios/chrome/browser/overlays/ui_bundled/overlay_presentation_context_impl.h"
#import "ios/chrome/browser/overlays/ui_bundled/test/fake_overlay_request_coordinator_delegate.h"
#import "ios/chrome/browser/overlays/ui_bundled/test_modality/test_presented_overlay_coordinator.h"
#import "ios/chrome/browser/overlays/ui_bundled/test_modality/test_resizing_presented_overlay_coordinator.h"
#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/test/scoped_key_window.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/platform_test.h"
using base::test::ios::WaitUntilConditionOrTimeout;
using base::test::ios::kWaitForUIElementTimeout;
namespace {
// The frame of resizable presened overlay UI.
const CGRect kWindowFrame = {{100.0, 100.0}, {100.0, 100.0}};
} // namespace
// Test fixture for OverlayPresentationContextViewController.
class OverlayPresentationContextViewControllerTest : public PlatformTest {
public:
OverlayPresentationContextViewControllerTest() {
browser_state_ = TestChromeBrowserState::Builder().Build();
browser_ = std::make_unique<TestBrowser>(browser_state_.get());
root_view_controller_ = [[UIViewController alloc] init];
view_controller_ = [[OverlayPresentationContextViewController alloc] init];
root_view_controller_.definesPresentationContext = YES;
scoped_window_.Get().rootViewController = root_view_controller_;
// Present `view_controller_` over `root_view_controller_` without animation
// and wait for the presentation to finish.
view_controller_.modalPresentationStyle =
UIModalPresentationOverCurrentContext;
__block bool presentation_finished = NO;
[root_view_controller_ presentViewController:view_controller_
animated:NO
completion:^{
presentation_finished = YES;
}];
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
return presentation_finished;
}));
}
~OverlayPresentationContextViewControllerTest() override {
// Dismisses `view_controller_` and waits for the dismissal to finish.
__block bool dismissal_finished = NO;
[root_view_controller_ dismissViewControllerAnimated:NO
completion:^{
dismissal_finished = YES;
}];
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
return dismissal_finished;
}));
}
protected:
web::WebTaskEnvironment task_environment_;
std::unique_ptr<TestChromeBrowserState> browser_state_;
std::unique_ptr<TestBrowser> browser_;
FakeOverlayRequestCoordinatorDelegate delegate_;
ScopedKeyWindow scoped_window_;
UIViewController* root_view_controller_ = nil;
OverlayPresentationContextViewController* view_controller_ = nil;
};
// Tests that `view_controller_`'s frame is CGRectZero when there is no overlay
// UI presented upon it.
TEST_F(OverlayPresentationContextViewControllerTest, NoPresentedUI) {
CGRect container_view_frame =
view_controller_.presentationController.containerView.frame;
EXPECT_TRUE(CGRectEqualToRect(container_view_frame, CGRectZero));
CGRect frame = view_controller_.view.frame;
EXPECT_TRUE(CGRectEqualToRect(frame, CGRectZero));
}
// Tests that `view_controller_`'s frame is the same as its presenter while
// showing overlay UI presented over its context.
TEST_F(OverlayPresentationContextViewControllerTest,
PresentedOverCurrentContext) {
if (@available(iOS 15.7.1, *)) {
if (@available(iOS 15.7.2, *)) {
} else {
// TODO(crbug.com/40254110): Failing on a few 15.7.1 devices.
return;
}
}
// Create a fake overlay coordinator that presents its UI over
// `view_controller_`.
std::unique_ptr<OverlayRequest> request =
OverlayRequest::CreateWithConfig<TestPresentedOverlay>();
TestPresentedOverlayCoordinator* coordinator =
[[TestPresentedOverlayCoordinator alloc]
initWithBaseViewController:view_controller_
browser:browser_.get()
request:request.get()
delegate:&delegate_];
// Start the coordinator and wait for its presentation to finish.
[coordinator startAnimated:NO];
UIViewController* overlay_view_controller = coordinator.viewController;
ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
return overlay_view_controller.presentingViewController &&
!overlay_view_controller.beingPresented;
}));
// Verify that the presentation context is resized to
// `root_view_controller_`'s view.
UIView* root_view = root_view_controller_.view;
CGRect root_window_frame = [root_view convertRect:root_view.bounds
toView:nil];
UIView* container_view =
view_controller_.presentationController.containerView;
EXPECT_TRUE(CGRectEqualToRect(
[container_view convertRect:container_view.bounds toView:nil],
root_window_frame));
UIView* view = view_controller_.view;
EXPECT_TRUE(CGRectEqualToRect([view convertRect:view.bounds toView:nil],
root_window_frame));
// Stop the coordinator, wait for its dismissal to finish, and make sure the
// layout is updated.
[coordinator stopAnimated:NO];
ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
return !overlay_view_controller.presentingViewController;
}));
[container_view layoutIfNeeded];
[view layoutIfNeeded];
// Verify that the views are resized to CGRectZero when nothing is presented
// upon it.
EXPECT_TRUE(CGRectEqualToRect(container_view.frame, CGRectZero));
EXPECT_TRUE(CGRectEqualToRect(view.frame, CGRectZero));
}
// Tests that `view_controller_`'s frame is the same as its presented view's
// container view if it is shown using custom UIViewController presentation that
// resizes the contianer view.
TEST_F(OverlayPresentationContextViewControllerTest, ResizingPresentedOverlay) {
if (@available(iOS 15.7.1, *)) {
if (@available(iOS 15.7.2, *)) {
} else {
// TODO(crbug.com/40254110): Failing on a few 15.7.1 devices.
return;
}
}
// Create a fake overlay coordinator that presents its UI over
// `view_controller_` and resizes its presentation container view to
// kWindowFrame.
std::unique_ptr<OverlayRequest> request =
OverlayRequest::CreateWithConfig<TestResizingPresentedOverlay>(
kWindowFrame);
TestResizingPresentedOverlayCoordinator* coordinator =
[[TestResizingPresentedOverlayCoordinator alloc]
initWithBaseViewController:view_controller_
browser:browser_.get()
request:request.get()
delegate:&delegate_];
// Start the coordinator and wait for its presentation to finish.
[coordinator startAnimated:NO];
UIViewController* overlay_view_controller = coordinator.viewController;
ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
return overlay_view_controller.presentingViewController &&
!overlay_view_controller.beingPresented;
}));
// Verify that the presentation context is resized kWindowFrame.
UIView* container_view =
view_controller_.presentationController.containerView;
EXPECT_TRUE(CGRectEqualToRect(
[container_view convertRect:container_view.bounds toView:nil],
kWindowFrame));
UIView* view = view_controller_.view;
EXPECT_TRUE(CGRectEqualToRect([view convertRect:view.bounds toView:nil],
kWindowFrame));
// Verify that resizing the presentation context container doesn't affect the
// layout of the overlay container.
UIView* overlay_container_view =
overlay_view_controller.presentationController.containerView;
EXPECT_TRUE(CGRectEqualToRect([overlay_container_view convertRect:view.bounds
toView:nil],
kWindowFrame));
// Stop the coordinator, wait for its dismissal to finish, and make sure the
// layout is updated.
[coordinator stopAnimated:NO];
ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
return !overlay_view_controller.presentingViewController;
}));
[container_view layoutIfNeeded];
[view layoutIfNeeded];
// Verify that the views are resized to CGRectZero when nothing is presented
// upon it.
EXPECT_TRUE(CGRectEqualToRect(container_view.frame, CGRectZero));
EXPECT_TRUE(CGRectEqualToRect(view.frame, CGRectZero));
}