chromium/ios/chrome/browser/shared/ui/util/uiview_window_coordinates_unittest.mm

// Copyright 2022 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/shared/ui/util/util_swift.h"

#import <UIKit/UIKit.h>

#import "base/test/ios/wait_util.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/platform_test.h"

using base::test::ios::kWaitForUIElementTimeout;
using base::test::ios::WaitUntilConditionOrTimeout;

// Sets up a window and a view.
class UIViewWindowCoordinatesTest : public PlatformTest {
 protected:
  UIViewWindowCoordinatesTest()
      : window_([[UIWindow alloc] init]), view_([[UIView alloc] init]) {}

  UIWindow* window_;
  UIView* view_;
};

// Checks that the callback is called when the view is added to a window.
TEST_F(UIViewWindowCoordinatesTest, SetWindow) {
  __block BOOL callback_called = NO;
  UIView* retained_view = view_;
  view_.cr_onWindowCoordinatesChanged = ^(UIView* view) {
    callback_called = YES;
    EXPECT_EQ(view, retained_view);
  };

  [window_ addSubview:view_];

  EXPECT_TRUE(callback_called);
}

// Checks that the initial callback is not called when the callback is reset and
// the view is added to a window.
TEST_F(UIViewWindowCoordinatesTest, Reset) {
  view_.cr_onWindowCoordinatesChanged = ^(UIView* view) {
    FAIL() << "Callback should not have been called after being unset.";
  };
  view_.cr_onWindowCoordinatesChanged = nil;

  [window_ addSubview:view_];
}

// Checks the callback is called when the view's frame changes.
TEST_F(UIViewWindowCoordinatesTest, ChangeFrameAsDirectSubview) {
  [window_ addSubview:view_];
  __block BOOL callback_called = NO;
  view_.cr_onWindowCoordinatesChanged = ^(UIView* view) {
    callback_called = YES;
  };
  // The callback is called when set if the view is in a window. Now reset it to
  // check that it's called again when changing view_'s frame.
  EXPECT_TRUE(callback_called);
  callback_called = NO;

  view_.frame = CGRectMake(10, 20, 30, 40);
  [window_ setNeedsLayout];
  [window_ layoutIfNeeded];

  // Wait until the expected handler is called.
  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{
    return callback_called;
  }));
}

// Checks the callback is called when any parent view's frame changes (even
// though the view's frame itself doesn't change in its parent).
TEST_F(UIViewWindowCoordinatesTest, ChangeFrameOfParentView) {
  UIView* parent_view = [[UIView alloc] init];
  [parent_view addSubview:view_];
  [window_ addSubview:parent_view];
  __block BOOL callback_called = NO;
  view_.cr_onWindowCoordinatesChanged = ^(UIView* view) {
    callback_called = YES;
  };
  // The callback is called when set if the view is in a window. Now reset it to
  // check that it's called again when changing view_'s frame.
  EXPECT_TRUE(callback_called);
  callback_called = NO;

  parent_view.frame = CGRectMake(10, 20, 30, 40);
  [window_ setNeedsLayout];
  [window_ layoutIfNeeded];

  // Wait until the expected handler is called.
  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{
    return callback_called;
  }));
}