chromium/ash/system/time/calendar_view_unittest.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/system/time/calendar_view.h"

#include <climits>
#include <memory>

#include "ash/calendar/calendar_client.h"
#include "ash/calendar/calendar_controller.h"
#include "ash/constants/ash_features.h"
#include "ash/glanceables/common/glanceables_view_id.h"
#include "ash/public/cpp/ash_view_ids.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/icon_button.h"
#include "ash/system/notification_center/views/notification_center_view.h"
#include "ash/system/time/calendar_event_list_view.h"
#include "ash/system/time/calendar_list_model.h"
#include "ash/system/time/calendar_model.h"
#include "ash/system/time/calendar_month_view.h"
#include "ash/system/time/calendar_unittest_utils.h"
#include "ash/system/time/calendar_utils.h"
#include "ash/system/time/calendar_view_controller.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/system/unified/unified_system_tray_bubble.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/time/time_override.h"
#include "base/types/cxx23_to_underlying.h"
#include "chromeos/ash/components/settings/scoped_timezone_settings.h"
#include "components/account_id/account_id.h"
#include "google_apis/calendar/calendar_api_requests.h"
#include "google_apis/common/api_error_codes.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/test/layer_animation_stopped_waiter.h"
#include "ui/events/base_event_utils.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/test/ax_event_counter.h"
#include "ui/views/view.h"

namespace ash {

namespace {

using ::google_apis::calendar::CalendarEvent;
using ::google_apis::calendar::EventList;
using ::google_apis::calendar::SingleCalendar;

constexpr char kTestUser[] = "user@test";

const char* kCalendarId1 = "[email protected]";
const char* kCalendarSummary1 = "[email protected]";
const char* kCalendarColorId1 = "12";
bool kCalendarSelected1 = true;
bool kCalendarPrimary1 = true;

}  // namespace

class CalendarViewControllerTestObserver
    : public CalendarViewController::Observer {
 public:
  explicit CalendarViewControllerTestObserver(
      base::OnceCallback<void(void)> callback)
      : callback_(std::move(callback)) {}

  CalendarViewControllerTestObserver(
      const CalendarViewControllerTestObserver&) = delete;
  CalendarViewControllerTestObserver& operator=(
      const CalendarViewControllerTestObserver&) = delete;

  ~CalendarViewControllerTestObserver() override = default;

  void OnCalendarLoaded() override { std::move(callback_).Run(); }

 private:
  base::OnceCallback<void(void)> callback_;
};

class CalendarViewTest : public AshTestBase {
 public:
  CalendarViewTest() {
    features_.InitWithFeatures(
        /*enabled_features=*/{},
        /*disabled_features=*/{features::kGlanceablesTimeManagementTasksView});
  }
  CalendarViewTest(const CalendarViewTest&) = delete;
  CalendarViewTest& operator=(const CalendarViewTest&) = delete;
  ~CalendarViewTest() override = default;

  void SetUp() override {
    AshTestBase::SetUp();

    Shell::Get()->calendar_controller()->SetActiveUserAccountIdForTesting(
        account_id_);
    Shell::Get()->calendar_controller()->RegisterClientForUser(account_id_,
                                                               &client_);

    widget_ = CreateFramelessTestWidget();
    widget_->SetFullscreen(true);
  }

  void TearDown() override {
    DestroyCalendarViewWidget();
    Shell::Get()->calendar_controller()->RegisterClientForUser(account_id_,
                                                               nullptr);
    AshTestBase::TearDown();
  }

  calendar_test_utils::CalendarClientTestImpl* client() { return &client_; }

  // Gets date cell of a given CalendarMonthView and numerical `day`.
  const views::LabelButton* GetDateCell(CalendarMonthView* month,
                                        std::u16string day) {
    const views::LabelButton* date_cell = nullptr;
    for (const views::View* child_view : month->children()) {
      auto* current_date_cell =
          static_cast<const views::LabelButton*>(child_view);
      if (day != current_date_cell->GetText()) {
        continue;
      }

      date_cell = current_date_cell;
      break;
    }
    return date_cell;
  }

  // Clicks on a given `date_cell`.
  void ClickDateCell(const views::LabelButton* date_cell) {
    auto* event_generator = GetEventGenerator();
    event_generator->MoveMouseTo(date_cell->GetBoundsInScreen().CenterPoint());
    event_generator->ClickLeftButton();
  }

  void CreateCalendarView() {
    if (!widget_) {
      widget_ = CreateFramelessTestWidget();
      widget_->SetFullscreen(true);
    }
    AccountId user_account = AccountId::FromUserEmail(kTestUser);
    GetSessionControllerClient()->SwitchActiveUser(user_account);

    auto calendar_view = std::make_unique<CalendarView>(
        /*use_glanceables_container_style=*/false);

    // This awkward-looking thing is to ensure that calendar_view_ (a raw_ptr)
    // doesn't outlive the calendar view it points to, which is otherwise
    // destroyed as part of Widget::SetContentsView().
    calendar_view_ = nullptr;
    calendar_view_ = widget_->SetContentsView(std::move(calendar_view));
  }

  void CloseEventList() { calendar_view_->CloseEventList(); }

  void DestroyCalendarViewWidget() {
    calendar_view_ = nullptr;
    widget_.reset();
  }

  // Calendar has some arbitrary delays to allow itself to load, otherwise the
  // test assertions run too early and fail. We hook into the `OnCalendarLoaded`
  // callback here to pause the test until the Calendar has finished loading.
  void WaitForCalendarToCompleteLoading() {
    base::RunLoop run_loop;
    auto callback = [](base::RunLoop* run_loop) {
      run_loop->QuitClosure().Run();
    };

    auto observer = std::make_unique<CalendarViewControllerTestObserver>(
        base::BindOnce(callback, &run_loop));
    AddCalendarViewControllerObserver(observer.get());

    run_loop.Run();

    RemoveCalendarViewControllerObserver(observer.get());
  }

  CalendarView* calendar_view() { return calendar_view_; }
  views::ScrollView* scroll_view() { return calendar_view_->scroll_view_; }

  views::View* previous_label() { return calendar_view_->previous_label_; }
  views::View* current_label() { return calendar_view_->current_label_; }
  views::View* next_label() { return calendar_view_->next_label_; }
  views::View* next_next_label() { return calendar_view_->next_next_label_; }

  views::ScrollView::ScrollBarMode GetScrollBarMode() {
    return scroll_view()->GetVerticalScrollBarMode();
  }

  // The position of the `next_month_`.
  int NextMonthPosition() {
    return previous_label()->GetPreferredSize().height() +
           calendar_view_->previous_month_->GetPreferredSize().height() +
           current_label()->GetPreferredSize().height() +
           calendar_view_->current_month_->GetPreferredSize().height() +
           next_label()->GetPreferredSize().height();
  }

  std::u16string GetPreviousLabelText() {
    return static_cast<views::Label*>(previous_label()->children()[0])
        ->GetText();
  }
  std::u16string GetCurrentLabelText() {
    return static_cast<views::Label*>(current_label()->children()[0])
        ->GetText();
  }
  std::u16string GetNextLabelText() {
    return static_cast<views::Label*>(next_label()->children()[0])->GetText();
  }
  std::u16string GetNextNextLabelText() {
    return static_cast<views::Label*>(next_next_label()->children()[0])
        ->GetText();
  }
  CalendarMonthView* previous_month() {
    return calendar_view_->previous_month_;
  }
  CalendarMonthView* current_month() { return calendar_view_->current_month_; }
  CalendarMonthView* next_month() { return calendar_view_->next_month_; }
  CalendarMonthView* next_next_month() {
    return calendar_view_->next_next_month_;
  }

  views::Label* month_header() { return calendar_view_->header_->header_; }
  views::Label* header_year() { return calendar_view_->header_->header_year_; }

  views::Button* reset_to_today_button() {
    return calendar_view_->reset_to_today_button_;
  }
  views::Button* settings_button() { return calendar_view_->settings_button_; }

  views::Button* managed_button() { return calendar_view_->managed_button_; }
  IconButton* up_button() { return calendar_view_->up_button_; }
  IconButton* down_button() { return calendar_view_->down_button_; }
  views::View* close_button() {
    return calendar_view_->event_list_view_->close_button_container_
        ->children()[0];
  }
  views::View* event_list_view() { return calendar_view_->event_list_view_; }

  std::optional<base::Time> selected_date() {
    return calendar_view_->event_list_view_->calendar_view_controller_
        ->selected_date_;
  }

  CalendarUpNextView* up_next_view() { return calendar_view_->up_next_view_; }

  views::View* up_next_scroll_contents() {
    return calendar_view_->up_next_view_->content_view_;
  }

  // Executes the given task immediately rather than waiting for the timer.
  void run_upcoming_events_timer_task() {
    calendar_view_->check_upcoming_events_timer_.user_task().Run();
  }

  views::View* calendar_sliding_surface_view() {
    return calendar_view_->calendar_sliding_surface_;
  }

  views::View* up_next_todays_events_button() {
    return calendar_view_->up_next_view_->todays_events_button_container_
        ->children()[0];
  }

  void ScrollUpOneMonth() {
    calendar_view_->ScrollOneMonthAndAutoScroll(/*scroll_up=*/true);
  }
  void ScrollDownOneMonth() {
    calendar_view_->ScrollOneMonthAndAutoScroll(/*scroll_up=*/false);
  }
  void ScrollUpOneMonthWithAnimation() {
    calendar_view_->ScrollOneMonthWithAnimation(/*scroll_up=*/true);
  }
  void ScrollDownOneMonthWithAnimation() {
    calendar_view_->ScrollOneMonthWithAnimation(/*scroll_up=*/false);
  }
  void ResetToToday() { calendar_view_->ResetToToday(); }

  void RequestFocusForEventListCloseButton() {
    calendar_view_->RequestFocusForEventListCloseButton();
  }

  void PressTab() {
    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
    generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EF_NONE);
  }

  void PressShiftTab() {
    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
    generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EF_SHIFT_DOWN);
  }

  void PressEnter() {
    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
    generator.PressKey(ui::KeyboardCode::VKEY_RETURN, ui::EF_NONE);
  }

  void PressUp() {
    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
    generator.PressKey(ui::KeyboardCode::VKEY_UP, ui::EF_NONE);
  }

  void PressDown() {
    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
    generator.PressKey(ui::KeyboardCode::VKEY_DOWN, ui::EF_NONE);
  }

  void PressLeft() {
    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
    generator.PressKey(ui::KeyboardCode::VKEY_LEFT, ui::EF_NONE);
  }

  void PressRight() {
    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
    generator.PressKey(ui::KeyboardCode::VKEY_RIGHT, ui::EF_NONE);
  }

  void AddCalendarViewControllerObserver(
      CalendarViewController::Observer* observer) {
    calendar_view_->calendar_view_controller_->AddObserver(observer);
  }

  void RemoveCalendarViewControllerObserver(
      CalendarViewController::Observer* observer) {
    calendar_view_->calendar_view_controller_->RemoveObserver(observer);
  }

  static base::Time FakeTimeNow() { return fake_time_; }
  static void SetFakeNow(base::Time fake_now) { fake_time_ = fake_now; }

 private:
  base::test::ScopedFeatureList features_;
  const AccountId account_id_ = AccountId::FromUserEmail("[email protected]");
  calendar_test_utils::CalendarClientTestImpl client_;
  std::unique_ptr<views::Widget> widget_;
  // Owned by `widget_`.
  raw_ptr<CalendarView> calendar_view_ = nullptr;
  std::unique_ptr<CalendarEventListView> event_list_view_;
  static base::Time fake_time_;
};

base::Time CalendarViewTest::fake_time_;

// Test the init view of the `CalendarView`.
TEST_F(CalendarViewTest, Init) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("24 Aug 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  EXPECT_EQ(u"July", GetPreviousLabelText());
  EXPECT_EQ(u"August", GetCurrentLabelText());
  EXPECT_EQ(u"September", GetNextLabelText());
  EXPECT_EQ(u"October", GetNextNextLabelText());
  EXPECT_EQ(u"August", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  EXPECT_EQ(u"27",
            static_cast<views::LabelButton*>(previous_month()->children()[0])
                ->GetText());
  EXPECT_EQ(u"1",
            static_cast<views::LabelButton*>(current_month()->children()[0])
                ->GetText());
  EXPECT_EQ(
      u"29",
      static_cast<views::LabelButton*>(next_month()->children()[0])->GetText());
  EXPECT_EQ(u"26",
            static_cast<views::LabelButton*>(next_next_month()->children()[0])
                ->GetText());
}

// Test the init view of the `CalendarView` starting with December.
TEST_F(CalendarViewTest, InitDec) {
  base::Time dec_date;
  ASSERT_TRUE(base::Time::FromString("24 Dec 2021 10:00 GMT", &dec_date));

  // Set time override.
  SetFakeNow(dec_date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  EXPECT_EQ(u"November", GetPreviousLabelText());
  EXPECT_EQ(u"December", GetCurrentLabelText());
  EXPECT_EQ(u"January", GetNextLabelText());
  EXPECT_EQ(u"February", GetNextNextLabelText());
  EXPECT_EQ(u"December", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  EXPECT_EQ(u"31",
            static_cast<views::LabelButton*>(previous_month()->children()[0])
                ->GetText());
  EXPECT_EQ(u"28",
            static_cast<views::LabelButton*>(current_month()->children()[0])
                ->GetText());
  EXPECT_EQ(u"30",
            static_cast<views::LabelButton*>(next_next_month()->children()[0])
                ->GetText());
}

// TODO(b/285280977): Remove when CalendarView is out of TrayDetailedView.
TEST_F(CalendarViewTest, NoBackButton) {
  CreateCalendarView();

  // No back button should be shown.
  EXPECT_FALSE(
      calendar_view()->GetViewByID(VIEW_ID_QS_DETAILED_VIEW_BACK_BUTTON));
}

TEST_F(CalendarViewTest, Scroll) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("24 Oct 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  EXPECT_EQ(u"September", GetPreviousLabelText());
  EXPECT_EQ(u"October", GetCurrentLabelText());
  EXPECT_EQ(u"November", GetNextLabelText());
  EXPECT_EQ(u"December", GetNextNextLabelText());
  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  // Scrolls to the next month.
  scroll_view()->ScrollToPosition(scroll_view()->vertical_scroll_bar(),
                                  NextMonthPosition());

  EXPECT_EQ(u"October", GetPreviousLabelText());
  EXPECT_EQ(u"November", GetCurrentLabelText());
  EXPECT_EQ(u"December", GetNextLabelText());
  EXPECT_EQ(u"January", GetNextNextLabelText());
  EXPECT_EQ(u"November", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  scroll_view()->ScrollToPosition(scroll_view()->vertical_scroll_bar(),
                                  NextMonthPosition());

  EXPECT_EQ(u"November", GetPreviousLabelText());
  EXPECT_EQ(u"December", GetCurrentLabelText());
  EXPECT_EQ(u"January", GetNextLabelText());
  EXPECT_EQ(u"February", GetNextNextLabelText());
  EXPECT_EQ(u"December", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  scroll_view()->ScrollToPosition(scroll_view()->vertical_scroll_bar(),
                                  NextMonthPosition());

  EXPECT_EQ(u"December", GetPreviousLabelText());
  EXPECT_EQ(u"January", GetCurrentLabelText());
  EXPECT_EQ(u"February", GetNextLabelText());
  EXPECT_EQ(u"March", GetNextNextLabelText());
  EXPECT_EQ(u"January", month_header()->GetText());
  EXPECT_EQ(u"2022", header_year()->GetText());
}

// Tests the up, down, and reset_to_today button callback functions.
TEST_F(CalendarViewTest, ButtonFunctions) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("24 Oct 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  EXPECT_EQ(u"September", GetPreviousLabelText());
  EXPECT_EQ(u"October", GetCurrentLabelText());
  EXPECT_EQ(u"November", GetNextLabelText());
  EXPECT_EQ(u"December", GetNextNextLabelText());
  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  ScrollDownOneMonth();

  EXPECT_EQ(u"October", GetPreviousLabelText());
  EXPECT_EQ(u"November", GetCurrentLabelText());
  EXPECT_EQ(u"December", GetNextLabelText());
  EXPECT_EQ(u"January", GetNextNextLabelText());
  EXPECT_EQ(u"November", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  ScrollDownOneMonth();

  EXPECT_EQ(u"November", GetPreviousLabelText());
  EXPECT_EQ(u"December", GetCurrentLabelText());
  EXPECT_EQ(u"January", GetNextLabelText());
  EXPECT_EQ(u"February", GetNextNextLabelText());
  EXPECT_EQ(u"December", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  ScrollDownOneMonth();

  EXPECT_EQ(u"December", GetPreviousLabelText());
  EXPECT_EQ(u"January", GetCurrentLabelText());
  EXPECT_EQ(u"February", GetNextLabelText());
  EXPECT_EQ(u"January", month_header()->GetText());
  EXPECT_EQ(u"2022", header_year()->GetText());

  ScrollUpOneMonth();

  EXPECT_EQ(u"November", GetPreviousLabelText());
  EXPECT_EQ(u"December", GetCurrentLabelText());
  EXPECT_EQ(u"January", GetNextLabelText());
  EXPECT_EQ(u"December", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  ScrollDownOneMonth();

  EXPECT_EQ(u"December", GetPreviousLabelText());
  EXPECT_EQ(u"January", GetCurrentLabelText());
  EXPECT_EQ(u"February", GetNextLabelText());
  EXPECT_EQ(u"March", GetNextNextLabelText());
  EXPECT_EQ(u"January", month_header()->GetText());
  EXPECT_EQ(u"2022", header_year()->GetText());

  // Goes back to the landing view.
  ResetToToday();

  EXPECT_EQ(u"September", GetPreviousLabelText());
  EXPECT_EQ(u"October", GetCurrentLabelText());
  EXPECT_EQ(u"November", GetNextLabelText());
  EXPECT_EQ(u"December", GetNextNextLabelText());
  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  // Multiple clicking on the up/down buttons.
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  EXPECT_EQ(u"May", GetPreviousLabelText());
  EXPECT_EQ(u"June", GetCurrentLabelText());
  EXPECT_EQ(u"July", GetNextLabelText());
  EXPECT_EQ(u"August", GetNextNextLabelText());
  EXPECT_EQ(u"June", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  ScrollDownOneMonth();
  ScrollDownOneMonth();
  ScrollDownOneMonth();
  ScrollDownOneMonth();
  EXPECT_EQ(u"September", GetPreviousLabelText());
  EXPECT_EQ(u"October", GetCurrentLabelText());
  EXPECT_EQ(u"November", GetNextLabelText());
  EXPECT_EQ(u"December", GetNextNextLabelText());
  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());
}

// For all the Focusing tests below, Jun, 2021 is used.
// 30, 31, 1 , 2 , 3 , 4 , 5
// 6 , 7 , 8 , 9 , 10, 11, 12
// 13, 14, 15, 16, 17, 18, 19
// 20, 21, 22, 23, 24, 25, 26
// 27, 28, 29, 30, 1 , 2 , 3
TEST_F(CalendarViewTest, HeaderFocusing) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  auto* focus_manager = calendar_view()->GetFocusManager();
  // Todays DateCellView should be focused on open.
  EXPECT_STREQ(focus_manager->GetFocusedView()->GetClassName(),
               "CalendarDateCellView");
  EXPECT_EQ(
      static_cast<const views::LabelButton*>(focus_manager->GetFocusedView())
          ->GetText(),
      u"7");

  // Moves to the next focusable view. Today's button.
  PressTab();
  EXPECT_EQ(reset_to_today_button(), focus_manager->GetFocusedView());

  // Moves to settings button.
  PressTab();
  EXPECT_EQ(settings_button(), focus_manager->GetFocusedView());

  // Moves to up button.
  PressTab();
  EXPECT_EQ(up_button(), focus_manager->GetFocusedView());

  // Moves to down button.
  PressTab();
  EXPECT_EQ(down_button(), focus_manager->GetFocusedView());

  // Moves to today's cell.
  PressTab();
  EXPECT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  // Moves to down button.
  PressShiftTab();
  EXPECT_EQ(down_button(), focus_manager->GetFocusedView());

  // Moves to up button.
  PressShiftTab();
  EXPECT_EQ(up_button(), focus_manager->GetFocusedView());

  // Moves to settings button.
  PressShiftTab();
  EXPECT_EQ(settings_button(), focus_manager->GetFocusedView());

  // Moves to "Go back to today" button.
  PressShiftTab();
  EXPECT_EQ(reset_to_today_button(), focus_manager->GetFocusedView());
}

// Tests the focus loop between the back button, today's button, settings
// button, and the todays date in the month.
TEST_F(CalendarViewTest, FocusingToDateCell) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  auto* focus_manager = calendar_view()->GetFocusManager();

  // Focus should start on todays CalendarDateCellView.
  EXPECT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  PressTab();  // Moves to Today's button.
  EXPECT_EQ(reset_to_today_button(), focus_manager->GetFocusedView());

  PressTab();  // Moves to settings button.
  PressTab();  // Moves to down button.
  PressTab();  // Moves to up button.

  // Moves to the the 7th date cell, which is the date of "today".
  PressTab();
  EXPECT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
}

// Tests the Ash.Calendar.MaxDistanceBrowsed metric only records once in
// CalendarViews lifetime.
TEST_F(CalendarViewTest, MaxDistanceBrowsedRecordsOncePerLifetime) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("24 Oct 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  auto histogram_tester = std::make_unique<base::HistogramTester>();

  CreateCalendarView();
  DestroyCalendarViewWidget();

  // The metric should log once.
  histogram_tester->ExpectTotalCount("Ash.Calendar.MaxDistanceBrowsed", 1);

  // Create the CalendarView again, and scroll once. The metric should record
  // once, but only once the widget has been destroyed.
  histogram_tester = std::make_unique<base::HistogramTester>();
  CreateCalendarView();

  ScrollDownOneMonth();
  histogram_tester->ExpectTotalCount("Ash.Calendar.MaxDistanceBrowsed", 0);
  DestroyCalendarViewWidget();

  histogram_tester->ExpectTotalCount("Ash.Calendar.MaxDistanceBrowsed", 1);

  // Create the CalendarView again, scroll a few more times. Still the metric
  // should only record once.
  histogram_tester = std::make_unique<base::HistogramTester>();
  CreateCalendarView();

  ScrollDownOneMonth();
  ScrollDownOneMonth();
  ScrollDownOneMonth();
  ScrollUpOneMonth();

  DestroyCalendarViewWidget();
  histogram_tester->ExpectTotalCount("Ash.Calendar.MaxDistanceBrowsed", 1);
}

// Tests the Ash.Calendar.MaxDistanceBrowsed metric records max distance
// traveled from today.
TEST_F(CalendarViewTest,
       MaxDistanceBrowsedRecordsAbsoluteValueOfDistanceTraveled) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("24 Oct 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  auto histogram_tester = base::HistogramTester();

  CreateCalendarView();
  const int scroll_up_count = 10;
  const int scroll_down_count = scroll_up_count - 1;
  // Scroll up.
  for (int i = 0; i < scroll_up_count; ++i) {
    ScrollUpOneMonth();
  }
  // Return to today.
  for (int i = 0; i < scroll_up_count; ++i) {
    ScrollDownOneMonth();
  }
  // Scroll down from today.
  for (int i = 0; i < scroll_down_count; ++i) {
    ScrollDownOneMonth();
  }

  DestroyCalendarViewWidget();

  // `scroll_up_count` is the furthest traveled.
  histogram_tester.ExpectBucketCount("Ash.Calendar.MaxDistanceBrowsed",
                                     scroll_up_count, 1);
}

// Used to determine whether focus goes directly to the proper CalendarDateCell
// prior to moving on to the EventListView.
class DateCellFocusChangeListener : public views::FocusChangeListener {
 public:
  DateCellFocusChangeListener(views::FocusManager* focus_manager,
                              std::u16string looking_for,
                              int steps_to_find)
      : focus_manager_(focus_manager),
        looking_for_(looking_for),
        steps_to_find_(steps_to_find) {
    focus_manager_->AddFocusChangeListener(this);
  }
  DateCellFocusChangeListener(const DateCellFocusChangeListener& other) =
      delete;
  DateCellFocusChangeListener& operator=(
      const DateCellFocusChangeListener& other) = delete;
  ~DateCellFocusChangeListener() override {
    focus_manager_->RemoveFocusChangeListener(this);
    EXPECT_EQ(steps_taken_, steps_to_find_);
  }

  bool found() const { return found_; }

  // views::FocusChangeListener:
  void OnWillChangeFocus(views::View* focused_before,
                         views::View* focused_now) override {}
  void OnDidChangeFocus(views::View* focused_before,
                        views::View* focused_now) override {
    if (found_) {
      return;
    }

    steps_taken_++;
    found_ = static_cast<const views::LabelButton*>(focused_now)->GetText() ==
             looking_for_;
    DCHECK_LE(steps_taken_, steps_to_find_);
  }

 private:
  // Whether a `views::Labelbutton` matching `looking_for_` was focused.
  bool found_ = false;
  // How many focus changes have occurred so far.
  int steps_taken_ = 0;

  // Unowned.
  const raw_ptr<views::FocusManager> focus_manager_;
  // The string being looked for.
  const std::u16string looking_for_;
  // The number of steps it is acceptable to have made before finding the
  // appropriate view.
  const int steps_to_find_;
};

// Tests that keyboard focus movement mixed with non-keyboard date cell
// activation results in proper focus directly to the date cell.
TEST_F(CalendarViewTest, MixedInput) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  auto* focus_manager = calendar_view()->GetFocusManager();

  // Focus starts on todays CalendarDateCellView.
  ASSERT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  const auto* non_focused_date_cell_view =
      GetDateCell(/*month=*/current_month(), /*day=*/u"9");

  {
    auto focus_change_listener = DateCellFocusChangeListener(
        focus_manager, /*looking_for=*/u"9", /*steps_to_find=*/1);
    ClickDateCell(non_focused_date_cell_view);
    EXPECT_TRUE(focus_change_listener.found());
  }
}

// Tests that focus returns to a DateCellView after closing the EventListView if
// the EventListView view tree had a focused view.
TEST_F(CalendarViewTest, FocusAfterClosingEventListView) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  auto* focus_manager = calendar_view()->GetFocusManager();

  // Should start with focus on today's cell.
  ASSERT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  ASSERT_FALSE(event_list_view());

  PressEnter();
  EXPECT_TRUE(event_list_view());

  EXPECT_EQ(calendar_view()->GetFocusManager()->GetFocusedView(),
            close_button());

  PressEnter();
  EXPECT_STREQ(
      calendar_view()->GetFocusManager()->GetFocusedView()->GetClassName(),
      "CalendarDateCellView");
}

TEST_F(CalendarViewTest, FocusReturnsToTodaysDate) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  auto* focus_manager = calendar_view()->GetFocusManager();
  // Todays DateCellView should be focused on open.
  ASSERT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  const auto* todays_date_cell_view = focus_manager->GetFocusedView();
  ClickDateCell(static_cast<const views::LabelButton*>(todays_date_cell_view));

  ASSERT_TRUE(event_list_view());

  PressEnter();

  // After EventListView is closed, todays DateCellView should be focused.
  EXPECT_EQ(todays_date_cell_view, focus_manager->GetFocusedView());
}

TEST_F(CalendarViewTest, OpenListAndCloseListFireAccessibilityEvents) {
  views::test::AXEventCounter counter(views::AXEventManager::Get());
  CreateCalendarView();
  auto* focus_manager = calendar_view()->GetFocusManager();
  const auto* todays_date_cell_view = focus_manager->GetFocusedView();
  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kTextChanged, scroll_view()));

  // Clicking on the date cell will open the event list. There should be one
  // text-changed accessibility event fired on the scroll view.
  ClickDateCell(static_cast<const views::LabelButton*>(todays_date_cell_view));
  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kTextChanged, scroll_view()));

  counter.ResetAllCounts();

  // Pressing enter will close the event list. There should be one text-changed
  // accessibility event fired on the scroll view.
  PressEnter();
  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kTextChanged, scroll_view()));
}

// Tests `RequestFocusForEventListCloseButton()`.
TEST_F(CalendarViewTest, CloseButtonFocusing) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  auto* focus_manager = calendar_view()->GetFocusManager();
  // Todays DateCellView should be focused on open.
  ASSERT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  ASSERT_FALSE(event_list_view());

  PressEnter();
  EXPECT_TRUE(event_list_view());

  EXPECT_EQ(focus_manager->GetFocusedView(), close_button());

  // Focus moves back to the date cell.
  PressShiftTab();
  EXPECT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  // Manually moves the focus to the close button.
  RequestFocusForEventListCloseButton();
  EXPECT_EQ(focus_manager->GetFocusedView(), close_button());
}

// Tests when the focus changes to another date cell with the event list opened,
// the focusing ring will go to the close button automatically.
TEST_F(CalendarViewTest, FocusingToCloseButtonWithEventListOpened) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  auto* focus_manager = calendar_view()->GetFocusManager();
  // Todays DateCellView should be focused on open.
  ASSERT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  ASSERT_FALSE(event_list_view());

  PressEnter();
  EXPECT_TRUE(event_list_view());

  EXPECT_EQ(focus_manager->GetFocusedView(), close_button());

  // Focus moves back to today's date cell.
  PressShiftTab();
  EXPECT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  // Navigates to another date cell and focuses on it. The focusing ring should
  // go to the close button automatically.
  PressUp();
  EXPECT_EQ(u"31",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  PressEnter();
  EXPECT_TRUE(event_list_view());
  EXPECT_EQ(focus_manager->GetFocusedView(), close_button());

  // Tests different date cells and expects the same focusing behavior.
  PressShiftTab();
  PressLeft();
  EXPECT_EQ(u"29",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  PressEnter();
  EXPECT_TRUE(event_list_view());
  EXPECT_EQ(focus_manager->GetFocusedView(), close_button());

  PressShiftTab();
  PressRight();
  PressRight();
  EXPECT_EQ(u"25",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  PressEnter();
  EXPECT_TRUE(event_list_view());
  EXPECT_EQ(focus_manager->GetFocusedView(), close_button());

  PressShiftTab();
  PressDown();
  EXPECT_EQ(u"30",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  PressEnter();
  EXPECT_TRUE(event_list_view());
  EXPECT_EQ(focus_manager->GetFocusedView(), close_button());
}

TEST_F(CalendarViewTest, MonthViewFocusing) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  auto* focus_manager = calendar_view()->GetFocusManager();
  // Todays DateCellView should be focused on open.
  EXPECT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  // Tapping on arrow keys should start navigating inside the month view.
  PressUp();
  EXPECT_EQ(u"31",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  PressDown();
  EXPECT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  PressLeft();
  EXPECT_EQ(u"6",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  PressLeft();
  EXPECT_EQ(u"5",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  PressDown();
  EXPECT_EQ(u"12",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  PressRight();
  EXPECT_EQ(u"13",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  PressRight();
  EXPECT_EQ(u"14",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  PressRight();
  EXPECT_EQ(u"15",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
}

// Should be able to use the arrow keys to navigate to the previous months or
// next months.
TEST_F(CalendarViewTest, FocusingToNavigate) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  auto* focus_manager = calendar_view()->GetFocusManager();
  // Focus starts on todays CalendarDateCellView.
  EXPECT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  EXPECT_EQ(u"June", GetCurrentLabelText());

  // Tapping on arrow keys should start navigating inside the month view.
  PressUp();
  EXPECT_EQ(u"31",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  EXPECT_EQ(u"May", GetCurrentLabelText());

  PressUp();
  EXPECT_EQ(u"24",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  PressUp();
  EXPECT_EQ(u"17",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  PressUp();
  EXPECT_EQ(u"10",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  PressUp();
  EXPECT_EQ(u"3",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  EXPECT_EQ(u"May", GetCurrentLabelText());

  PressUp();
  EXPECT_EQ(u"26",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  EXPECT_EQ(u"April", GetCurrentLabelText());
}

TEST_F(CalendarViewTest, ExpandableViewFocusing) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  EXPECT_EQ(views::ScrollView::ScrollBarMode::kHiddenButEnabled,
            GetScrollBarMode());

  auto* focus_manager = calendar_view()->GetFocusManager();
  // Focus starts on todays CalendarDateCellView.
  EXPECT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  EXPECT_EQ(u"June", GetCurrentLabelText());

  // Opens the event list.
  PressEnter();
  EXPECT_EQ(views::ScrollView::ScrollBarMode::kDisabled, GetScrollBarMode());

  // Focus moves to the event list close button.
  EXPECT_EQ(close_button(), focus_manager->GetFocusedView());

  // Focus moves back to the date cell.
  PressShiftTab();
  EXPECT_EQ(u"7",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  EXPECT_EQ(u"June", GetCurrentLabelText());

  // Tapping on up arrow keys should go to the previous month, which mens the
  // scroll bar is enabled during the key pressed.
  PressUp();
  EXPECT_EQ(u"31",
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());
  EXPECT_EQ(u"May", GetCurrentLabelText());
  EXPECT_EQ(views::ScrollView::ScrollBarMode::kDisabled, GetScrollBarMode());

  // Moves to the event list.
  PressTab();
  EXPECT_EQ(close_button(), focus_manager->GetFocusedView());

  // Goes to empty list view.
  PressTab();
  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_CALENDAR_NO_EVENTS),
            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
                ->GetText());

  // Moves to the next focusable view. Today's button.
  PressTab();

  EXPECT_EQ(reset_to_today_button(), focus_manager->GetFocusedView());
}

// Tests that the metric is recorded when calling `UpdateMonth`.
TEST_F(CalendarViewTest, RecordDwellTimeMetric) {
  base::HistogramTester histogram_tester;
  CreateCalendarView();

  // Does not record metric if `UpdateMonth` is called with same month as the
  // one in `current_date_`.
  calendar_view()->calendar_view_controller()->UpdateMonth(base::Time::Now());
  histogram_tester.ExpectTotalCount("Ash.Calendar.MonthDwellTime",
                                    /*expected_count=*/0);

  // Records metric when `UpdateMonth` is called with a month different than the
  // one in `current_date_`.
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("24 Aug 2021 10:00 GMT", &date));
  calendar_view()->calendar_view_controller()->UpdateMonth(date);
  histogram_tester.ExpectTotalCount("Ash.Calendar.MonthDwellTime",
                                    /*expected_count=*/1);
}

TEST_F(CalendarViewTest, OnSessionBlocked) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  GetSessionControllerClient()->SetSessionState(
      session_manager::SessionState::OOBE);

  // The 17th child cell in the month, which is the non-grayed out 15th date
  // cell.
  EXPECT_EQ(u"15",
            static_cast<views::LabelButton*>(current_month()->children()[16])
                ->GetText());

  // The calendar view will show today's row as the first row, so only dates
  // that are below today's row are in the visible rect.
  GestureTapOn(
      static_cast<views::LabelButton*>(current_month()->children()[16]));
  bool is_showing_event_list_view = event_list_view();
  EXPECT_FALSE(is_showing_event_list_view);

  GetSessionControllerClient()->SetSessionState(
      session_manager::SessionState::ACTIVE);
  EXPECT_EQ(u"15",
            static_cast<views::LabelButton*>(current_month()->children()[16])
                ->GetText());

  GestureTapOn(
      static_cast<views::LabelButton*>(current_month()->children()[16]));

  is_showing_event_list_view = event_list_view();
  EXPECT_TRUE(is_showing_event_list_view);
  GestureTapOn(close_button());
}

// Tests multiple scenarios that should record the metric when scrolling.
// TODO(crbug.com/333283676): Re-enable once the test failure is fixed.
TEST_F(CalendarViewTest, DISABLED_RecordDwellTimeMetricWhenScrolling) {
  base::HistogramTester histogram_tester;
  CreateCalendarView();

  // Scroll up two times.
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  histogram_tester.ExpectTotalCount("Ash.Calendar.MonthDwellTime",
                                    /*expected_count=*/2);

  // Scroll down three times.
  ScrollDownOneMonth();
  ScrollDownOneMonth();
  ScrollDownOneMonth();
  histogram_tester.ExpectTotalCount("Ash.Calendar.MonthDwellTime",
                                    /*expected_count=*/5);

  // Reset to today.
  ResetToToday();
  histogram_tester.ExpectTotalCount("Ash.Calendar.MonthDwellTime",
                                    /*expected_count=*/6);

  // Opening and closing the CalendarEventListView through a date cell within
  // the current month does not record the metric.
  auto* first_of_month_date_cell =
      GetDateCell(/*month=*/current_month(), /*day=*/u"1");
  ClickDateCell(first_of_month_date_cell);
  CloseEventList();
  histogram_tester.ExpectTotalCount("Ash.Calendar.MonthDwellTime",
                                    /*expected_count=*/6);

  // Closing the calendar view should record the metric.
  DestroyCalendarViewWidget();
  histogram_tester.ExpectTotalCount("Ash.Calendar.MonthDwellTime",
                                    /*expected_count=*/7);
}

// Tests that EventListView has proper bounds when shown.
TEST_F(CalendarViewTest, EventListBoundsTest) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));

  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  ASSERT_EQ(u"15",
            static_cast<views::LabelButton*>(current_month()->children()[16])
                ->GetText());
  GestureTapOn(
      static_cast<views::LabelButton*>(current_month()->children()[16]));
  ASSERT_TRUE(event_list_view());
  ASSERT_FALSE(current_month()->layer()->GetAnimator()->is_animating());

  // EventListView should be flush with the bottom of the scroll views visible
  // area. EventListView is ignored by the CalendarViews LayoutManager, but it
  // should be flush with the bottom of `scroll_view_`'s clipped height.
  // `scroll_view_`'s bounds extend further to the end of the view, but the
  // contents of `scroll_view_` are clipped.
  const int bottom_of_scroll_view_visible_area =
      scroll_view()->bounds().y() + scroll_view()->GetMaxHeight();
  const int top_of_event_list_view = calendar_sliding_surface_view()->y();
  EXPECT_EQ(bottom_of_scroll_view_visible_area, top_of_event_list_view);
}

TEST_F(CalendarViewTest, AdminDisabledTest) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);
  client()->set_is_disabled_by_admin(true);

  CreateCalendarView();

  auto* focus_manager = calendar_view()->GetFocusManager();
  // Todays `DateCellView` should be focused on open.
  ASSERT_TRUE(focus_manager->GetFocusedView()->GetClassName());
  ASSERT_TRUE(focus_manager->GetFocusedView());

  // Moves to the next focusable view - managed icon button.
  PressTab();
  EXPECT_EQ(managed_button(), focus_manager->GetFocusedView());

  // Moves to the next focusable view. Today's button.
  PressTab();
  EXPECT_EQ(reset_to_today_button(), focus_manager->GetFocusedView());

  // Moves to settings button.
  PressTab();
  EXPECT_EQ(settings_button(), focus_manager->GetFocusedView());

  // Moves back to managed icon button.
  PressShiftTab();
  PressShiftTab();

  EXPECT_EQ(managed_button(), focus_manager->GetFocusedView());
}

TEST_F(CalendarViewTest, ManagedButtonTest) {
  base::Time date;
  // Create a monthview based on Jun,7th 2021.
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);
  client()->set_is_disabled_by_admin(true);

  CreateCalendarView();

  // Click on managed button to open chrome://management.
  GestureTapOn(managed_button());
}

// A test class for testing animation. This class cannot set fake now since it's
// using `MOCK_TIME` to test the animations, and it can't inherit from
// CalendarAnimationTest due to the same reason.
class CalendarViewAnimationTest
    : public AshTestBase,
      public testing::WithParamInterface</*multi_calendar_enabled=*/bool> {
 public:
  CalendarViewAnimationTest()
      : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
    scoped_feature_list_.InitWithFeatureStates(
        {{features::kMultiCalendarSupport, IsMultiCalendarEnabled()},
         {features::kGlanceablesTimeManagementTasksView, false}});
  }
  CalendarViewAnimationTest(const CalendarViewAnimationTest&) = delete;
  CalendarViewAnimationTest& operator=(const CalendarViewAnimationTest&) =
      delete;
  ~CalendarViewAnimationTest() override = default;

  void SetUp() override {
    AshTestBase::SetUp();

    widget_ = CreateFramelessTestWidget();
    widget_->SetFullscreen(true);

    // Register a mock `CalendarClient` to the `CalendarController`.
    const std::string email = "[email protected]";
    AccountId account_id = AccountId::FromUserEmail(email);
    Shell::Get()->calendar_controller()->SetActiveUserAccountIdForTesting(
        account_id);
    calendar_list_model_ =
        Shell::Get()->system_tray_model()->calendar_list_model();
    calendar_model_ = Shell::Get()->system_tray_model()->calendar_model();
    calendar_client_ =
        std::make_unique<calendar_test_utils::CalendarClientTestImpl>();
    Shell::Get()->calendar_controller()->RegisterClientForUser(
        account_id, calendar_client_.get());

    if (IsMultiCalendarEnabled()) {
      // Set a mock calendar list so the calendar list fetch returns
      // successfully.
      SetCalendarList();
    }
  }

  void TearDown() override {
    calendar_list_model_ = nullptr;
    calendar_model_ = nullptr;
    calendar_view_ = nullptr;

    widget_.reset();
    time_overrides_.reset();
    scoped_feature_list_.Reset();

    AshTestBase::TearDown();
  }

  bool IsMultiCalendarEnabled() { return GetParam(); }

  void CreateCalendarView() {
    // Don't allow calendar_view_ to temporarily dangle while we're replacing
    // the Widget's contents view. Otherwise, inside SetContentsView() the old
    // calendar_view_ will be destroyed and will temporarily dangle.
    calendar_view_ = nullptr;
    calendar_view_ = widget_->SetContentsView(std::make_unique<CalendarView>(
        /*use_glanceables_container_style=*/false));
  }

  void SetCalendarList() {
    // Set a mock calendar list.
    std::list<std::unique_ptr<google_apis::calendar::SingleCalendar>> calendars;
    calendars.push_back(calendar_test_utils::CreateCalendar(
        kCalendarId1, kCalendarSummary1, kCalendarColorId1, kCalendarSelected1,
        kCalendarPrimary1));
    calendar_client_->SetCalendarList(
        calendar_test_utils::CreateMockCalendarList(std::move(calendars)));
  }

  // Gets date cell of a given CalendarMonthView and numerical `day`.
  const views::LabelButton* GetDateCell(CalendarMonthView* month,
                                        std::u16string day) {
    const views::LabelButton* date_cell = nullptr;
    for (const views::View* child_view : month->children()) {
      auto* current_date_cell =
          static_cast<const views::LabelButton*>(child_view);
      if (day != current_date_cell->GetText()) {
        continue;
      }

      date_cell = current_date_cell;
      break;
    }
    return date_cell;
  }

  int GetPositionOfToday() { return calendar_view()->GetPositionOfToday(); }

  // Clicks on a given `date_cell`.
  void ClickDateCell(const views::LabelButton* date_cell) {
    auto* event_generator = GetEventGenerator();
    event_generator->MoveMouseTo(date_cell->GetBoundsInScreen().CenterPoint());
    event_generator->ClickLeftButton();
  }

  std::optional<base::Time> GetSelectedDate() {
    return calendar_view_->calendar_view_controller()->selected_date_;
  }

  void CloseEventList() { calendar_view_->CloseEventList(); }

  void UpdateMonth(base::Time date) {
    calendar_view_->calendar_view_controller()->UpdateMonth(date);
    // Advances the time to allow `on_screen_month_` to update.
    task_environment()->FastForwardBy(
        calendar_test_utils::kAnimationSettleDownDuration);
    calendar_view_->content_view_->RemoveAllChildViews();
    calendar_view_->SetMonthViews();
    scroll_view()->ScrollToPosition(
        scroll_view()->vertical_scroll_bar(),
        calendar_view_->GetPositionOfCurrentMonth());
  }

  // The position of the `next_month_`.
  int NextMonthPosition() {
    return previous_label()->GetPreferredSize().height() +
           calendar_view_->previous_month_->GetPreferredSize().height() +
           current_label()->GetPreferredSize().height() +
           calendar_view_->current_month_->GetPreferredSize().height() +
           next_label()->GetPreferredSize().height();
  }

  // The position of `current_month_`.
  int CurrentMonthPosition() {
    return calendar_view_->GetPositionOfCurrentMonth();
  }

  void ScrollUpOneMonth() {
    calendar_view_->ScrollOneMonthWithAnimation(/*scroll_up=*/true);
  }

  void ScrollDownOneMonth() {
    calendar_view_->ScrollOneMonthWithAnimation(/*scroll_up=*/false);
  }

  void ResetToTodayWithAnimation() {
    calendar_view_->ResetToTodayWithAnimation();
  }

  bool IsAnimating() { return calendar_view_->IsAnimating(); }

  bool is_scrolling_up() { return calendar_view_->is_scrolling_up_; }

  views::ScrollView::ScrollBarMode GetScrollBarMode() {
    return scroll_view()->GetVerticalScrollBarMode();
  }

  views::Widget* widget() { return widget_.get(); }
  CalendarView* calendar_view() { return calendar_view_; }

  views::Label* month_header() { return calendar_view_->header_->header_; }
  views::Label* header_year() { return calendar_view_->header_->header_year_; }
  CalendarHeaderView* header() { return calendar_view_->header_; }
  CalendarHeaderView* temp_header() { return calendar_view_->temp_header_; }
  CalendarMonthView* current_month() { return calendar_view_->current_month_; }
  CalendarMonthView* previous_month() {
    return calendar_view_->previous_month_;
  }
  CalendarMonthView* next_month() { return calendar_view_->next_month_; }
  views::View* previous_label() { return calendar_view_->previous_label_; }
  views::View* current_label() { return calendar_view_->current_label_; }
  views::View* next_label() { return calendar_view_->next_label_; }
  views::ScrollView* scroll_view() { return calendar_view_->scroll_view_; }
  views::View* event_list_view() { return calendar_view_->event_list_view_; }
  CalendarListModel* calendar_list_model() { return calendar_list_model_; }
  CalendarModel* calendar_model() { return calendar_model_; }
  views::View* calendar_sliding_surface_view() {
    return calendar_view_->calendar_sliding_surface_;
  }
  calendar_test_utils::CalendarClientTestImpl* calendar_client() {
    return calendar_client_.get();
  }

  bool should_months_animate() {
    return calendar_view_->should_months_animate_;
  }

  std::map<base::Time, CalendarModel::FetchingStatus> on_screen_month() {
    return calendar_view_->on_screen_month_;
  }

  // Wait until the response is back. Since we used `PostDelayedTask` with 1
  // second to mimic the behavior of fetching, duration of 1 minute should be
  // enough.
  void WaitUntilFetched() {
    task_environment()->FastForwardBy(base::Minutes(1));
    base::RunLoop().RunUntilIdle();
  }

  void SetTodayFromTime(base::Time date) {
    std::set<base::Time> months = calendar_utils::GetSurroundingMonthsUTC(
        date, calendar_utils::kNumSurroundingMonthsCached);

    calendar_model_->non_prunable_months_.clear();
    // Non-prunable months are today's date and the two surrounding months.
    calendar_model_->AddNonPrunableMonths(months);
  }

 private:
  std::unique_ptr<views::Widget> widget_;
  // Owned by `widget_`.
  raw_ptr<CalendarView> calendar_view_ = nullptr;
  std::unique_ptr<base::subtle::ScopedTimeClockOverrides> time_overrides_;
  raw_ptr<CalendarListModel> calendar_list_model_ = nullptr;
  raw_ptr<CalendarModel> calendar_model_ = nullptr;
  std::unique_ptr<calendar_test_utils::CalendarClientTestImpl> calendar_client_;
  base::test::ScopedFeatureList scoped_feature_list_;
};

INSTANTIATE_TEST_SUITE_P(MultiCalendar,
                         CalendarViewAnimationTest,
                         testing::Bool());

// The header should show the new header with animation once there's an update
// when the event list view is shown.
TEST_P(CalendarViewAnimationTest, HeaderAnimation) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("24 Oct 2021 10:00 GMT", &date));
  GetSessionControllerClient()->SetSessionState(
      session_manager::SessionState::ACTIVE);
  // Sets the timezone to "America/Los_Angeles".
  ash::system::ScopedTimezoneSettings timezone_settings(u"America/Los_Angeles");

  CreateCalendarView();
  // Gives it a duration to let the animation finish and pass the cool down
  // duration.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  UpdateMonth(date);
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  // Update the header to next month.
  calendar_view()->calendar_view_controller()->UpdateMonth(date +
                                                           base::Days(10));

  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationStartBufferDuration);

  // Should not show animation if the event list is not open.
  EXPECT_FALSE(temp_header()->GetVisible());
  EXPECT_FALSE(header()->layer()->GetAnimator()->is_animating());
  EXPECT_FALSE(temp_header()->layer()->GetAnimator()->is_animating());
  EXPECT_EQ(u"November", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  // Also update the month views to click on the correct date cell.
  UpdateMonth(date + base::Days(10));
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  // Opens the event list view by click on a radom non-grayed out cell.
  const auto* date_cell = GetDateCell(/*month=*/current_month(), /*day=*/u"10");
  ClickDateCell(date_cell);

  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  EXPECT_TRUE(event_list_view());

  // Update the header to next month.
  calendar_view()->calendar_view_controller()->UpdateMonth(date +
                                                           base::Days(45));
  EXPECT_EQ(u"November", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationStartBufferDuration);

  // Showing the temp header for animation. All header should be animating.
  EXPECT_TRUE(temp_header()->GetVisible());
  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(temp_header()->layer()->GetAnimator()->is_animating());
  EXPECT_GE(1.0f, temp_header()->layer()->opacity());
  EXPECT_EQ(u"November", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  // Now the header is updated to the new month and year.
  EXPECT_EQ(u"December", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());
}

TEST_P(CalendarViewAnimationTest, HeaderAnimationDirection) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("24 Aug 2023 10:00 GMT", &date));
  GetSessionControllerClient()->SetSessionState(
      session_manager::SessionState::ACTIVE);
  ash::system::ScopedTimezoneSettings timezone_settings(u"America/Los_Angeles");
  CreateCalendarView();

  // Gives it a duration to let the animation finish and pass the cool down
  // duration.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  UpdateMonth(date);
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  // Scrolls to the next month.
  ScrollDownOneMonth();
  EXPECT_FALSE(is_scrolling_up());

  // Gives it a duration to let the animation finish.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  // Scrolls to the previous month.
  ScrollUpOneMonth();
  EXPECT_TRUE(is_scrolling_up());

  // Gives it a duration to let the animation finish.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  // Opens the event list view by clicking on a non-grayed out cell on the next
  // month, so that the header will animate to the next month's header.
  const auto* date_cell = GetDateCell(/*month=*/next_month(), /*day=*/u"10");
  ClickDateCell(date_cell);

  // Gives it a duration to let the animation finish.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  EXPECT_FALSE(is_scrolling_up());
  EXPECT_TRUE(event_list_view());

  // Gives it a duration to let the animation finish.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
}

// The month views and header should animate when scrolling up or down.
TEST_P(CalendarViewAnimationTest, MonthAndHeaderAnimation) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);

  base::Time date;
  ASSERT_TRUE(base::Time::FromString("24 Oct 2021 10:00 GMT", &date));

  CreateCalendarView();
  // Gives it a duration to let the animation finish and pass the cool down
  // duration.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  UpdateMonth(date);
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());
  EXPECT_FALSE(temp_header()->GetVisible());

  // Scrolls to the next month.
  ScrollDownOneMonth();

  EXPECT_FALSE(is_scrolling_up());

  // If scrolls down, the month views and labels will be animating.
  EXPECT_EQ(1.0f, header()->layer()->opacity());
  task_environment()->FastForwardBy(
      calendar_utils::kAnimationDurationForMoving);
  EXPECT_TRUE(current_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(next_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(next_label()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(previous_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(previous_label()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(current_label()->layer()->GetAnimator()->is_animating());
  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  // Showing the temp header for animation. All header should be animating.
  EXPECT_TRUE(temp_header()->GetVisible());
  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(temp_header()->layer()->GetAnimator()->is_animating());
  EXPECT_GE(1.0f, temp_header()->layer()->opacity());

  // Gives it a duration to let the animation finish.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  // Now the header is updated to the new month and year before it starts
  // showing up.
  EXPECT_EQ(u"November", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());
  EXPECT_EQ(1.0f, header()->layer()->opacity());
  EXPECT_FALSE(temp_header()->GetVisible());

  // Scrolls to the previous month.
  ScrollUpOneMonth();

  EXPECT_TRUE(is_scrolling_up());

  // If scrolls up, the month views and labels will be animating.
  EXPECT_EQ(1.0f, header()->layer()->opacity());
  task_environment()->FastForwardBy(
      calendar_utils::kAnimationDurationForVisibility);
  EXPECT_TRUE(current_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(current_label()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(previous_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(next_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(previous_label()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(next_label()->layer()->GetAnimator()->is_animating());
  EXPECT_EQ(u"November", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  // Showing the temp header for animation. All header should be animating.
  EXPECT_TRUE(temp_header()->GetVisible());
  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(temp_header()->layer()->GetAnimator()->is_animating());
  EXPECT_GE(1.0f, temp_header()->layer()->opacity());

  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  // Multiple clicking on the up/down buttons. Here only checks the label since
  // if it scrolls too fast some scroll actions might be skipped due to
  // `is_resetting_scroll_` == true.
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  EXPECT_EQ(u"2021", header_year()->GetText());

  ScrollDownOneMonth();
  ScrollDownOneMonth();
  ScrollDownOneMonth();
  ScrollDownOneMonth();
  ScrollDownOneMonth();
  ScrollDownOneMonth();
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  EXPECT_EQ(u"2021", header_year()->GetText());
}

// The content view should not be scrollable when the month view is animating.
TEST_P(CalendarViewAnimationTest, NotScrollableWhenAnimating) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);

  base::Time date;
  ASSERT_TRUE(base::Time::FromString("24 Oct 2021 10:00 GMT", &date));

  CreateCalendarView();
  // Gives it a duration to let the animation finish and pass the cool down
  // duration.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  UpdateMonth(date);
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  // The scroll bar is enaled before tapping on the up button.
  EXPECT_EQ(views::ScrollView::ScrollBarMode::kHiddenButEnabled,
            GetScrollBarMode());

  // Scrolls to the previous month.
  ScrollUpOneMonth();

  // If scrolls down, the month views and labels will be animating.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationStartBufferDuration);
  EXPECT_TRUE(current_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(next_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(next_label()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(previous_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(previous_label()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(current_label()->layer()->GetAnimator()->is_animating());
  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  // Showing the temp header for animation. All header should be animating.
  EXPECT_TRUE(temp_header()->GetVisible());
  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(temp_header()->layer()->GetAnimator()->is_animating());

  // Try to scrol to the next month.
  scroll_view()->ScrollToPosition(scroll_view()->vertical_scroll_bar(),
                                  NextMonthPosition());

  // Should not scroll and keep showing the animation.
  EXPECT_EQ(views::ScrollView::ScrollBarMode::kDisabled, GetScrollBarMode());
  EXPECT_TRUE(current_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(next_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(next_label()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(previous_month()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(previous_label()->layer()->GetAnimator()->is_animating());
  EXPECT_TRUE(current_label()->layer()->GetAnimator()->is_animating());
  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  // Animation finished. On the previous month.
  EXPECT_EQ(u"September", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());
  EXPECT_EQ(views::ScrollView::ScrollBarMode::kHiddenButEnabled,
            GetScrollBarMode());

  // Try to scroll to the next month. Should get to the next month.
  scroll_view()->ScrollToPosition(scroll_view()->vertical_scroll_bar(),
                                  NextMonthPosition());
  EXPECT_EQ(views::ScrollView::ScrollBarMode::kHiddenButEnabled,
            GetScrollBarMode());
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());
}

TEST_P(CalendarViewAnimationTest, ResetToTodayWithAnimation) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);

  // Create calendar view and wait for the animation to finish.
  CreateCalendarView();
  ui::LayerAnimationStoppedWaiter animation_waiter;
  animation_waiter.Wait(header()->layer());

  // Expect header visible before starting ResetToToday animation.
  EXPECT_EQ(1.0f, header()->layer()->opacity());

  ResetToTodayWithAnimation();
  // The header starts to animate.
  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());

  // The header's opacity should be between 0~1 after the first animator
  // finished and in the middle of the second animation.
  animation_waiter.Wait(header()->layer());
  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());
  EXPECT_GT(1.0f, header()->layer()->opacity());

  // The header's opacity should be 1 after the second animator finished.
  animation_waiter.Wait(header()->layer());
  EXPECT_FALSE(header()->layer()->GetAnimator()->is_animating());
  EXPECT_EQ(1.0f, header()->layer()->opacity());

  // Open event list by selecting the next month's first cell.
  const auto* date_cell = GetDateCell(/*month=*/next_month(), /*day=*/u"1");
  ClickDateCell(date_cell);

  // Event list view starts to animate.
  EXPECT_TRUE(
      calendar_sliding_surface_view()->layer()->GetAnimator()->is_animating());
  animation_waiter.Wait(current_label()->layer());
  animation_waiter.Wait(calendar_sliding_surface_view()->layer());

  // Event list view just finished animating.
  EXPECT_FALSE(
      calendar_sliding_surface_view()->layer()->GetAnimator()->is_animating());
  EXPECT_FALSE(should_months_animate());
  // The cool-down time for enabling animation. Otherwise the next reset to
  // today animation will not be enabled.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  EXPECT_TRUE(should_months_animate());
  EXPECT_TRUE(event_list_view());
  EXPECT_EQ(1.0f, event_list_view()->layer()->opacity());

  ResetToTodayWithAnimation();
  // The header starts to animate.
  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());

  // The header's opacity should be between 0~1 after the first animator
  // finished and in the middle of the second animation.
  animation_waiter.Wait(header()->layer());
  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());
  EXPECT_GT(1.0f, header()->layer()->opacity());

  // The header's opacity should be 1 after the second animator finished.
  animation_waiter.Wait(header()->layer());
  EXPECT_FALSE(header()->layer()->GetAnimator()->is_animating());
  EXPECT_EQ(1.0f, header()->layer()->opacity());
  // Expect today's date in `selected_date_` after resetting to today.
  EXPECT_EQ(calendar_utils::GetMonthDayYear(base::Time::Now()),
            calendar_utils::GetMonthDayYear(GetSelectedDate().value()));

  // Expect header visible after closing event list and resetting to today.
  CloseEventList();
  EXPECT_TRUE(
      calendar_sliding_surface_view()->layer()->GetAnimator()->is_animating());
  // Wait `event_list_view()`'s layer first since it can be deleted after the
  // animation finished.
  animation_waiter.Wait(event_list_view()->layer());
  animation_waiter.Wait(calendar_sliding_surface_view()->layer());
  EXPECT_FALSE(
      calendar_sliding_surface_view()->layer()->GetAnimator()->is_animating());

  ResetToTodayWithAnimation();
  // The header starts to animate.
  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());

  // The header's opacity should be between 0~1 after the first animator
  // finished and in the middle of the second animation.
  animation_waiter.Wait(header()->layer());
  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());
  EXPECT_GT(1.0f, header()->layer()->opacity());

  // The header's opacity should be 1 after the second animator finished.
  animation_waiter.Wait(header()->layer());
  EXPECT_FALSE(header()->layer()->GetAnimator()->is_animating());
  EXPECT_EQ(1.0f, header()->layer()->opacity());
}

// Tests that the loading bar becomes visible when any of the on screen months
// has not finished fetching and becomes invisible once all months on screen
// have finished fetching events.
TEST_P(CalendarViewAnimationTest, LoadingBarVisibilityForOneMonthOnScreen) {
  // Sets the timezone to "America/Los_Angeles".
  ash::system::ScopedTimezoneSettings timezone_settings(u"America/Los_Angeles");

  // Tests when the `CalendarView` size is small to hold only one month on
  // screen.
  UpdateDisplay("800x200");
  CreateCalendarView();

  // Advances the time to allow `on_screen_month_` to initialize.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  EXPECT_EQ(1U, on_screen_month().size());
  const auto* progress_bar = calendar_view()->GetViewByID(
      base::to_underlying(GlanceablesViewId::kProgressBar));
  EXPECT_TRUE(progress_bar->GetVisible());

  // Waits until the events are fetched, and tests the loading bar is invisible.
  WaitUntilFetched();
  EXPECT_FALSE(progress_bar->GetVisible());
}

TEST_P(CalendarViewAnimationTest, LoadingBarVisibility) {
  // Sets the timezone to "America/Los_Angeles".
  ash::system::ScopedTimezoneSettings timezone_settings(u"America/Los_Angeles");

  // Tests when the `CalendarView` has two months on screen.
  UpdateDisplay("800x600");
  CreateCalendarView();

  // Advances the time to allow `on_screen_month_` to initialize.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  const auto* progress_bar = calendar_view()->GetViewByID(
      base::to_underlying(GlanceablesViewId::kProgressBar));
  EXPECT_TRUE(progress_bar->GetVisible());

  // Waits until the events are fetched, and tests the loading bar is invisible.
  WaitUntilFetched();
  EXPECT_FALSE(progress_bar->GetVisible());

  // Resets to today so that a new fetching request will be sent for on-screen
  // months who have cached events(eg. a refetching status). Tests the loading
  // bar is visible.
  SetTodayFromTime(base::Time::Now());
  ResetToTodayWithAnimation();

  // Advances the time to allow `on_screen_month_` to update.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  EXPECT_TRUE(progress_bar->GetVisible());
}

// Tests the loading bar visibility for different user sessions.
TEST_P(CalendarViewAnimationTest,
       LoadingBarVisibilityForDifferentUserSessions) {
  // Make sure that the `CalendarView` can have enough space to hold at least 1
  // month. 600 should be enough since the calendar bubble's height is set to
  // 464 in the `UnifiedSystemTrayBubble`.
  UpdateDisplay("800x600");

  // Tests when the user is logged in, the loading bar is visible.
  GetSessionControllerClient()->SetSessionState(
      session_manager::SessionState::ACTIVE);
  CreateCalendarView();
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  EXPECT_TRUE(
      calendar_view()
          ->GetViewByID(base::to_underlying(GlanceablesViewId::kProgressBar))
          ->GetVisible());

  // Tests when the screen is locked, the loading bar is invisible.
  calendar_model()->ClearAllCachedEvents();
  GetSessionControllerClient()->SetSessionState(
      session_manager::SessionState::LOCKED);
  CreateCalendarView();

  // Advances the time to allow `on_screen_month_` to initialize.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  EXPECT_FALSE(
      calendar_view()
          ->GetViewByID(base::to_underlying(GlanceablesViewId::kProgressBar))
          ->GetVisible());

  // Tests when the user starts the login process, the loading bar is invisible.
  calendar_model()->ClearAllCachedEvents();
  GetSessionControllerClient()->SetSessionState(
      session_manager::SessionState::LOGIN_PRIMARY);
  CreateCalendarView();
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  EXPECT_FALSE(
      calendar_view()
          ->GetViewByID(base::to_underlying(GlanceablesViewId::kProgressBar))
          ->GetVisible());
}

// Tests the loading bar visibility for when fetching events errors.
TEST_P(CalendarViewAnimationTest, LoadingBarVisibilityForErrorFetchingEvents) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("04 May 2022 15:00 GMT", &date));

  // Sets the timezone to "America/Los_Angeles".
  ash::system::ScopedTimezoneSettings timezone_settings(u"America/Los_Angeles");

  // Tests when the `CalendarView` size is small to hold only one month on
  // screen.
  UpdateDisplay("800x200");
  CreateCalendarView();
  // Advances the time to allow `on_screen_month_` to initialize.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  UpdateMonth(date);

  EXPECT_EQ(1U, on_screen_month().size());

  // Sets the fetching status of current month to be kFetching, and tests the
  // loading bar is visible.
  base::Time start_of_month = calendar_utils::GetStartOfMonthUTC(date);
  base::Time current_date =
      calendar_view()->calendar_view_controller()->currently_shown_date();
  base::Time start_of_current_month = calendar_utils::GetStartOfMonthUTC(
      current_date + calendar_utils::GetTimeDifference(current_date));
  EXPECT_EQ(start_of_month, start_of_current_month);

  calendar_client()->SetError(google_apis::NO_CONNECTION);
  calendar_model()->FetchEvents(start_of_current_month);
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  const auto* progress_bar = calendar_view()->GetViewByID(
      base::to_underlying(GlanceablesViewId::kProgressBar));
  EXPECT_TRUE(progress_bar->GetVisible());

  // Waits until the events are fetched, and tests the loading bar is invisible.
  WaitUntilFetched();
  EXPECT_FALSE(progress_bar->GetVisible());
}

// Tests the loading bar visibility for when fetching events times out.
TEST_P(CalendarViewAnimationTest,
       LoadingBarVisibilityForTimeoutFetchingEvents) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("04 May 2022 15:00 GMT", &date));

  // Sets the timezone to "America/Los_Angeles".
  ash::system::ScopedTimezoneSettings timezone_settings(u"America/Los_Angeles");

  // Tests when the `CalendarView` size is small to hold only one month on
  // screen.
  UpdateDisplay("800x200");
  CreateCalendarView();
  // Advances the time to allow `on_screen_month_` to initialize.
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  UpdateMonth(date);

  EXPECT_EQ(1U, on_screen_month().size());

  // Sets the fetching status of current month to be kFetching, and tests the
  // loading bar is visible.
  base::Time start_of_month = calendar_utils::GetStartOfMonthUTC(date);
  base::Time current_date =
      calendar_view()->calendar_view_controller()->currently_shown_date();
  base::Time start_of_current_month = calendar_utils::GetStartOfMonthUTC(
      current_date + calendar_utils::GetTimeDifference(current_date));
  EXPECT_EQ(start_of_month, start_of_current_month);

  calendar_client()->ForceTimeout();
  calendar_model()->FetchEvents(start_of_current_month);
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);
  const auto* progress_bar = calendar_view()->GetViewByID(
      base::to_underlying(GlanceablesViewId::kProgressBar));
  EXPECT_TRUE(progress_bar->GetVisible());

  // Waits until the events are fetched, and tests the loading bar is invisible.
  WaitUntilFetched();
  EXPECT_FALSE(progress_bar->GetVisible());
}

// Tests that the EventListView does not crash if shown during the initial open.
TEST_P(CalendarViewAnimationTest, QuickShowEventListInitialOpen) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);

  // Creates calendar view, which will trigger an animation.
  CreateCalendarView();

  // Try to show the EventListView during the initial animation by selecting a
  // today's date cell.
  ClickDateCell(
      calendar_view()->calendar_view_controller()->todays_date_cell_view());

  EXPECT_TRUE(IsAnimating());
  EXPECT_TRUE(event_list_view());

  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  EXPECT_TRUE(event_list_view());
}

// Tests that the EventListView does not show during the month change animation.
TEST_P(CalendarViewAnimationTest, DontShowEventListDuringMonthAnimation) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);

  CreateCalendarView();
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  // Start the scroll down animation.
  ScrollUpOneMonth();
  task_environment()->FastForwardBy(
      calendar_utils::kAnimationDurationForVisibility);
  ASSERT_TRUE(current_month()->layer()->GetAnimator()->is_animating());

  // Try to open the EventListView.
  const auto* tomorrow_date_cell =
      GetDateCell(/*month=*/current_month(), /*day=*/u"25");
  ClickDateCell(tomorrow_date_cell);

  EXPECT_FALSE(event_list_view());
}

// Regression test for b/265203105
// Tests open/close the `CalendarEventListView`. Also tests one corner case:
// when closing the event list right after opening it, do nothing since the
// animation is not finished.
TEST_P(CalendarViewAnimationTest, OpenAndCloseEventList) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  // Sets the timezone to "America/Los_Angeles".
  ash::system::ScopedTimezoneSettings timezone_settings(u"America/Los_Angeles");

  CreateCalendarView();
  ui::LayerAnimationStoppedWaiter animation_waiter;
  animation_waiter.Wait(header()->layer());

  // Opens the `CalendarEventListView`.
  ClickDateCell(
      calendar_view()->calendar_view_controller()->todays_date_cell_view());

  EXPECT_TRUE(IsAnimating());
  EXPECT_TRUE(event_list_view());
  EXPECT_TRUE(GetSelectedDate().has_value());

  // Should not close the event list before showing up animation is finished.
  CloseEventList();
  EXPECT_TRUE(IsAnimating());
  EXPECT_TRUE(event_list_view());
  EXPECT_TRUE(GetSelectedDate().has_value());

  // After the showing up animation is finished, the event list view should be
  // up.
  animation_waiter.Wait(event_list_view()->layer());
  animation_waiter.Wait(calendar_sliding_surface_view()->layer());
  animation_waiter.Wait(current_label()->layer());
  EXPECT_FALSE(IsAnimating());
  EXPECT_TRUE(event_list_view());
  EXPECT_TRUE(GetSelectedDate().has_value());

  // Should close the event list now.
  CloseEventList();

  // The event list the view is still showing and `selected_date_` value is
  // still set during the animation.
  EXPECT_TRUE(IsAnimating());
  EXPECT_TRUE(event_list_view());
  EXPECT_TRUE(GetSelectedDate().has_value());

  // Resets the `selected_date_` after the fading out animation is done.
  animation_waiter.Wait(event_list_view()->layer());
  animation_waiter.Wait(calendar_sliding_surface_view()->layer());
  animation_waiter.Wait(current_label()->layer());
  EXPECT_FALSE(IsAnimating());
  EXPECT_FALSE(event_list_view());
  EXPECT_FALSE(GetSelectedDate().has_value());
}

class CalendarViewWithUpNextViewTest : public CalendarViewTest {
 public:
  CalendarViewWithUpNextViewTest() = default;
  CalendarViewWithUpNextViewTest(const CalendarViewWithUpNextViewTest&) =
      delete;
  CalendarViewWithUpNextViewTest& operator=(
      const CalendarViewWithUpNextViewTest&) = delete;
  ~CalendarViewWithUpNextViewTest() override = default;

  // Assumes current time is "18 Nov 2021 10:00 GMT".
  std::unique_ptr<google_apis::calendar::EventList>
  CreateMockEventListWithEventStartTimeMoreThanTwoHoursAway() {
    auto event_list = std::make_unique<google_apis::calendar::EventList>();
    event_list->set_time_zone("Greenwich Mean Time");
    event_list->InjectItemForTesting(calendar_test_utils::CreateEvent(
        "id_0", "summary_0", "18 Nov 2021 12:30 GMT", "18 Nov 2021 13:30 GMT"));

    return event_list;
  }

  // Assumes current time is "18 Nov 2021 10:00 GMT".
  std::unique_ptr<google_apis::calendar::EventList>
  CreateMockEventListWithEventStartTimeTenMinsAway() {
    auto event_list = std::make_unique<google_apis::calendar::EventList>();
    event_list->set_time_zone("Greenwich Mean Time");
    event_list->InjectItemForTesting(calendar_test_utils::CreateEvent(
        "id_0", "summary_0", "18 Nov 2021 10:10 GMT", "18 Nov 2021 13:30 GMT"));

    return event_list;
  }

  // Assumes current time is "18 Nov 2021 10:00 GMT".
  std::unique_ptr<google_apis::calendar::EventList>
  CreateMockEventListWithTwoEventsOneEndingInOneMin() {
    auto event_list = std::make_unique<google_apis::calendar::EventList>();
    event_list->set_time_zone("Greenwich Mean Time");
    event_list->InjectItemForTesting(calendar_test_utils::CreateEvent(
        "id_0", "summary_0", "18 Nov 2021 10:00 GMT", "18 Nov 2021 13:30 GMT"));
    event_list->InjectItemForTesting(calendar_test_utils::CreateEvent(
        "id_1", "summary_1", "18 Nov 2021 09:00 GMT", "18 Nov 2021 10:01 GMT"));

    return event_list;
  }

  // Assumes current time is "18 Nov 2021 23:55 GMT".
  std::unique_ptr<google_apis::calendar::EventList>
  CreateMockEventListStartingFivePastMidnight() {
    auto event_list = std::make_unique<google_apis::calendar::EventList>();
    event_list->set_time_zone("Greenwich Mean Time");
    event_list->InjectItemForTesting(calendar_test_utils::CreateEvent(
        "id_0", "summary_0", "19 Nov 2021 00:05 GMT", "19 Nov 2021 01:30 GMT"));

    return event_list;
  }

  void MockEventsFetched(
      base::Time date,
      std::unique_ptr<google_apis::calendar::EventList> event_list) {
    Shell::Get()->system_tray_model()->calendar_model()->OnEventsFetched(
        calendar_utils::GetStartOfMonthUTC(date),
        google_apis::calendar::kPrimaryCalendarId,
        google_apis::ApiErrorCode::HTTP_SUCCESS, event_list.get());
  }
};

TEST_F(CalendarViewWithUpNextViewTest,
       GivenNoEvents_WhenCalendarViewOpens_ThenUpNextViewShouldNotBeShown) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);
  client()->set_is_disabled_by_admin(true);

  CreateCalendarView();

  // When we've just created the calendar view and not fetched any events, then
  // up next shouldn't have been created.
  bool is_showing_up_next_view = up_next_view();
  EXPECT_FALSE(is_showing_up_next_view);
}

TEST_F(
    CalendarViewWithUpNextViewTest,
    GivenEventsStartingMoreThanTwoHoursAway_WhenCalendarViewOpens_ThenUpNextViewShouldNotBeShown) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(
      calendar_utils::GetStartOfMonthUTC(date),
      CreateMockEventListWithEventStartTimeMoreThanTwoHoursAway());

  // Fetch an event starting more than two hours away, up next should show.
  bool is_showing_up_next_view = up_next_view();
  EXPECT_TRUE(is_showing_up_next_view);
}

TEST_F(CalendarViewWithUpNextViewTest, ShouldShowUpNextView) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // When fetched events are in the next 10 mins, then up next should have been
  // created.
  EXPECT_TRUE(up_next_view());
}

TEST_F(CalendarViewWithUpNextViewTest,
       ShouldNotShowUpNextView_WhenEventListViewIsOpen) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  // Open the event list view for any day.
  GestureTapOn(
      static_cast<views::LabelButton*>(current_month()->children()[25]));
  ASSERT_TRUE(event_list_view());
  // Mock events that start in ten mins coming in.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  bool is_showing_up_next_view = up_next_view();
  EXPECT_FALSE(is_showing_up_next_view);
  const int bottom_of_scroll_view_visible_area =
      scroll_view()->bounds().y() + scroll_view()->GetMaxHeight();
  const int top_of_event_list_view = calendar_sliding_surface_view()->y();
  EXPECT_EQ(bottom_of_scroll_view_visible_area, top_of_event_list_view);
}

// If there are upcoming events and the up next view should have been shown but
// the event list was open, then when it closes we should show the up next view.
TEST_F(
    CalendarViewWithUpNextViewTest,
    ShouldShowUpNextView_WhenEventListViewHasFinishedClosing_SelectedDateIsToday) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  // Open the event list view for today.
  GestureTapOn(
      static_cast<views::LabelButton*>(current_month()->children()[18]));
  ASSERT_TRUE(event_list_view());
  // Mock events that start in ten mins coming in.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());
  CloseEventList();

  // After closing the event list view, we expect the up next view to now be
  // shown.
  bool is_showing_up_next_view = up_next_view();
  EXPECT_TRUE(is_showing_up_next_view);
}

// If there are upcoming events and the event list was open for another day
// that's not on the same row with today, then when it closes we should not show
// the up next view.
TEST_F(
    CalendarViewWithUpNextViewTest,
    ShouldNotShowUpNextView_WhenEventListViewHasFinishedClosing_SelectedDateNotOnTheSameRowWithToday) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  // Open the event list view for a day that's not on the same row with today.
  GestureTapOn(
      static_cast<views::LabelButton*>(current_month()->children()[25]));
  ASSERT_TRUE(event_list_view());
  // Mock events that start in ten mins coming in.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());
  CloseEventList();

  // After closing the event list view, we expect there's no up next view
  // showing.
  EXPECT_FALSE(up_next_view());
}

TEST_F(CalendarViewWithUpNextViewTest,
       ShouldOpenEventListView_WhenUpNextShowTodaysEventsButtonPressed) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // When fetched events are in the next 10 mins, then up next should have been
  // created.
  bool is_showing_up_next_view = up_next_view();
  EXPECT_TRUE(is_showing_up_next_view);
  bool is_showing_event_list_view = event_list_view();
  EXPECT_FALSE(is_showing_event_list_view);

  LeftClickOn(up_next_todays_events_button());

  is_showing_event_list_view = event_list_view();
  EXPECT_TRUE(is_showing_event_list_view);
}

TEST_F(
    CalendarViewWithUpNextViewTest,
    GivenUpNextIsShown_WhenNewEventsMoreThanTwoHoursAwayAreFetched_UpNextViewShouldStillBeShown) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // When fetched events are in the next 10 mins, then up next should have been
  // created.
  EXPECT_TRUE((up_next_view() && up_next_view()->GetVisible()));

  MockEventsFetched(
      calendar_utils::GetStartOfMonthUTC(date),
      CreateMockEventListWithEventStartTimeMoreThanTwoHoursAway());

  // When fetched events are now more than two hours away, the up next should
  // still show.
  EXPECT_TRUE((up_next_view() && up_next_view()->GetVisible()));
}

// Tests the following:
// - 2 upcoming events are displayed in the up next view
// - Time passes so that one event has ended
// - Up next refreshes to show the 1 upcoming event (as the other has now ended)
TEST_F(CalendarViewWithUpNextViewTest,
       ShouldHideCompletedEventsInTheUpNextView) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithTwoEventsOneEndingInOneMin());

  // Up next should be shown with the 2 events.
  EXPECT_TRUE(up_next_view());
  EXPECT_EQ(size_t(2), up_next_scroll_contents()->children().size());

  // Move base::Time::Now to 1 minute past the event end time and force the
  // upcoming events timer to fire.
  base::Time time_one_min_past_event_end;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:02 GMT",
                                     &time_one_min_past_event_end));
  SetFakeNow(time_one_min_past_event_end);
  run_upcoming_events_timer_task();

  // Up next should still be showing but with a single event as the other has
  // ended.
  EXPECT_TRUE(up_next_view());
  EXPECT_EQ(size_t(1), up_next_scroll_contents()->children().size());
}

TEST_F(CalendarViewWithUpNextViewTest,
       ShouldClipHeightOfScrollView_WhenUpNextIsShown) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();

  // Expect the scrollview to have max int height when neither up next nor the
  // event list view are showing.
  EXPECT_EQ(INT_MAX, scroll_view()->GetMaxHeight());

  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // When fetched events are in the next 10 mins, then up next should have been
  // created.
  EXPECT_TRUE(up_next_view());
  // When up next is showing, the scrollview should be clipped to sit above but
  // slightly overlapping the up next view bounds.
  const int expected_max_height = scroll_view()->height() -
                                  up_next_view()->GetPreferredSize().height() +
                                  calendar_utils::kUpNextOverlapInPx;
  EXPECT_EQ(expected_max_height, scroll_view()->GetMaxHeight());
}

TEST_F(
    CalendarViewWithUpNextViewTest,
    ShouldClipHeightOfScrollView_WhenEventListHasClosed_AndUpNextIsStillShown) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // When fetched events are in the next 10 mins, then up next should have been
  // created.
  EXPECT_TRUE(up_next_view());

  // Open the event list view for today.
  ASSERT_EQ(u"18",
            static_cast<views::LabelButton*>(current_month()->children()[18])
                ->GetText());
  GestureTapOn(
      static_cast<views::LabelButton*>(current_month()->children()[18]));
  ASSERT_TRUE(event_list_view());

  // Close the event list view.
  GestureTapOn(close_button());
  ASSERT_FALSE(event_list_view());

  // When the event list view closes, the scrollview max height should have been
  // clipped back to the right height for the up next view.
  const int expected_max_height = scroll_view()->height() -
                                  up_next_view()->GetPreferredSize().height() +
                                  calendar_utils::kUpNextOverlapInPx;
  EXPECT_EQ(expected_max_height, scroll_view()->GetMaxHeight());
}

TEST_F(CalendarViewWithUpNextViewTest,
       ShouldShowUpNext_WhenEventListHasClosed_AndAnUpcomingEventJustCameIn) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(
      calendar_utils::GetStartOfMonthUTC(date),
      CreateMockEventListWithEventStartTimeMoreThanTwoHoursAway());

  EXPECT_TRUE(up_next_view());

  // Open the event list view for today.
  ASSERT_EQ(u"18",
            static_cast<views::LabelButton*>(current_month()->children()[18])
                ->GetText());
  GestureTapOn(
      static_cast<views::LabelButton*>(current_month()->children()[18]));
  ASSERT_TRUE(event_list_view());

  // Mock upcoming events coming in.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // Close the event list view.
  GestureTapOn(close_button());
  ASSERT_FALSE(event_list_view());

  // Up next should be showing.
  EXPECT_TRUE(up_next_view());

  // When the event list view closes, the scrollview max height should have been
  // clipped back to the right height for the up next view.
  const int expected_max_height = scroll_view()->height() -
                                  up_next_view()->GetPreferredSize().height() +
                                  calendar_utils::kUpNextOverlapInPx;
  EXPECT_EQ(expected_max_height, scroll_view()->GetMaxHeight());
}

// Tests the following:
// - 1 upcoming events are displayed in the up next view
// - Scroll down a couple of months
// - Up next view should be invisible after scrolling
// - Scroll back to today's month, and the up next view should remain invisible.
TEST_F(CalendarViewWithUpNextViewTest, ShouldNotShowUpNextView_AfterScrolling) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  // Mock upcoming events coming in.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // Up next view should be shown.
  EXPECT_TRUE(up_next_view());

  // Scroll down a couple of months so we're not on today's date or month any
  // more.
  ScrollDownOneMonthWithAnimation();
  ScrollDownOneMonthWithAnimation();

  EXPECT_EQ(u"December", GetPreviousLabelText());
  EXPECT_EQ(u"January", GetCurrentLabelText());
  EXPECT_EQ(u"February", GetNextLabelText());
  EXPECT_EQ(u"March", GetNextNextLabelText());
  EXPECT_EQ(u"January", month_header()->GetText());
  EXPECT_EQ(u"2022", header_year()->GetText());

  // `up_next_view()` still exists, just invisible.
  EXPECT_FALSE(up_next_view()->GetVisible());

  // Scroll up back to today's month, and `up_next_view()` should not be visible
  // after scrolling.
  ScrollUpOneMonthWithAnimation();
  ScrollUpOneMonthWithAnimation();
  EXPECT_EQ(u"November", GetCurrentLabelText());
  EXPECT_EQ(u"November", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());
  EXPECT_FALSE(up_next_view()->GetVisible());
}

// Tests the following:
// - 1 upcoming events are displayed in the up next view
// - Scroll down a couple of months
// - Up next view should be invisible after scrolling
// - Clicking the reset to today button
// - Up next view should be visible
TEST_F(CalendarViewWithUpNextViewTest,
       ShouldShowUpNextView_AfterResettingToToday) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // When fetched events are in the next 10 mins, then up next should have been
  // created.
  EXPECT_TRUE(up_next_view());

  // Scroll down a couple of months so we're not on today's date or month any
  // more.
  ScrollDownOneMonthWithAnimation();
  ScrollDownOneMonthWithAnimation();

  EXPECT_EQ(u"December", GetPreviousLabelText());
  EXPECT_EQ(u"January", GetCurrentLabelText());
  EXPECT_EQ(u"February", GetNextLabelText());
  EXPECT_EQ(u"March", GetNextNextLabelText());
  EXPECT_EQ(u"January", month_header()->GetText());
  EXPECT_EQ(u"2022", header_year()->GetText());

  // `up_next_view()` still exists, just invisible.
  EXPECT_FALSE(up_next_view()->GetVisible());

  // Clicks the reset to today button and `up_next_view()` should be visible.
  GestureTapOn(reset_to_today_button());
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());
  EXPECT_TRUE(up_next_view()->GetVisible());
}

// Tests the following:
// - 1 upcoming events are displayed in the up next view
// - Open the event list view and scroll down
// - Close the event list view
// - Up next view should be invisible
TEST_F(CalendarViewWithUpNextViewTest,
       ShouldNotShowUpNextView_WhenEventListHasClosedAfterScrolling) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // When fetched events are in the next 10 mins, then up next should have been
  // created.
  EXPECT_TRUE(up_next_view());

  // Open the event list view.
  ASSERT_EQ(u"25",
            static_cast<views::LabelButton*>(current_month()->children()[25])
                ->GetText());
  GestureTapOn(
      static_cast<views::LabelButton*>(current_month()->children()[25]));
  ASSERT_TRUE(event_list_view());

  // Scroll down one row with the event list open.
  ScrollDownOneMonthWithAnimation();

  EXPECT_EQ(u"October", GetPreviousLabelText());
  EXPECT_EQ(u"November", GetCurrentLabelText());
  EXPECT_EQ(u"December", GetNextLabelText());
  EXPECT_EQ(u"January", GetNextNextLabelText());
  EXPECT_EQ(u"November", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());

  // Close the event list view.
  GestureTapOn(close_button());
  ASSERT_FALSE(event_list_view());

  // `up_next_view()` still exists, just invisible.
  EXPECT_FALSE(up_next_view()->GetVisible());
}

// Tests the following:
// - 1 upcoming events are displayed in the up next view
// - Open the event list view
// - Close the event list view
// - Up next view should be visible
TEST_F(CalendarViewWithUpNextViewTest,
       ShouldShowUpNextView_WhenEventListHasClosedWithoutScrolling) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // When fetched events are in the next 10 mins, then up next should have been
  // created.
  EXPECT_TRUE(up_next_view());

  // Open the event list view for today.
  ASSERT_EQ(u"18",
            static_cast<views::LabelButton*>(current_month()->children()[18])
                ->GetText());
  GestureTapOn(
      static_cast<views::LabelButton*>(current_month()->children()[18]));
  ASSERT_TRUE(event_list_view());

  // Close the event list view.
  GestureTapOn(close_button());
  ASSERT_FALSE(event_list_view());

  // `up_next_view()` should be visible.
  EXPECT_TRUE(up_next_view()->GetVisible());
}

// Tests an upcoming event that starts at 00:05 but "now" is 23:55. In this case
// the up next view shouldn't be shown.
TEST_F(CalendarViewWithUpNextViewTest,
       ShouldNotShowUpNextView_WhenNextEventStartsTomorrow) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 23:55 GMT", &date));

  // Sets the timezone to GMT.
  ash::system::ScopedTimezoneSettings timezone_settings(u"GMT");

  // Sets time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  // Mock upcoming events coming in.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListStartingFivePastMidnight());

  // Up next view should not be shown.
  ASSERT_FALSE(up_next_view());
}

TEST_F(
    CalendarViewWithUpNextViewTest,
    ShouldFocusEventListCloseButton_WhenEventListViewLaunchedFromUpNextView) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // When fetched events are in the next 10 mins, then up next should have been
  // created.
  ASSERT_TRUE(up_next_view());

  auto* focus_manager = calendar_view()->GetFocusManager();
  up_next_todays_events_button()->RequestFocus();
  ASSERT_EQ(up_next_todays_events_button(), focus_manager->GetFocusedView());

  PressEnter();
  ASSERT_TRUE(event_list_view());

  EXPECT_EQ(focus_manager->GetFocusedView(), close_button());
}

TEST_F(CalendarViewWithUpNextViewTest,
       ShouldFocusEventListCloseButton_WhenFocusMovedFromTodayButton) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // When fetched events are in the next 10 mins, then up next should have been
  // created.
  ASSERT_TRUE(up_next_view());

  auto* focus_manager = calendar_view()->GetFocusManager();
  reset_to_today_button()->RequestFocus();
  ASSERT_EQ(reset_to_today_button(), focus_manager->GetFocusedView());
  GestureTapOn(up_next_todays_events_button());

  ASSERT_TRUE(event_list_view());
  EXPECT_EQ(focus_manager->GetFocusedView(), close_button());
}

TEST_F(CalendarViewWithUpNextViewTest, RecordEventsDisplayedToUserOnce) {
  base::HistogramTester histogram_tester;
  base::Time now;
  ASSERT_TRUE(base::Time::FromString("1 Oct 2021 10:00 GMT", &now));
  base::Time next_month;
  ASSERT_TRUE(base::Time::FromString("1 Nov 2021 10:00 GMT", &next_month));
  // Set time override.
  SetFakeNow(now);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  CreateCalendarView();
  // Mock events will be in the `next_month` so when the calendar opens we don't
  // record the metric.
  MockEventsFetched(
      calendar_utils::GetStartOfMonthUTC(next_month),
      CreateMockEventListWithEventStartTimeMoreThanTwoHoursAway());

  // Make sure we're on the current month and no events have been displayed to
  // the user.
  EXPECT_EQ(u"October", GetCurrentLabelText());
  EXPECT_EQ(u"October", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());
  histogram_tester.ExpectTotalCount("Ash.Calendar.EventsDisplayedToUser", 0);

  // Scroll down a month, this month should contain events.
  ScrollDownOneMonth();

  EXPECT_EQ(u"November", GetCurrentLabelText());
  EXPECT_EQ(u"November", month_header()->GetText());
  EXPECT_EQ(u"2021", header_year()->GetText());
  histogram_tester.ExpectTotalCount("Ash.Calendar.EventsDisplayedToUser", 1);

  // Scroll back and forward to land on the month containing events again.
  ScrollDownOneMonth();
  ScrollUpOneMonth();

  // We should still have only logged the metric once.
  histogram_tester.ExpectTotalCount("Ash.Calendar.EventsDisplayedToUser", 1);
}

TEST_F(CalendarViewWithUpNextViewTest, ShouldShowUpNextWithCachedData) {
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
  // Set time override.
  SetFakeNow(date);
  base::subtle::ScopedTimeClockOverrides time_override(
      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
      /*thread_ticks_override=*/nullptr);

  // Build the Calendar view.
  CreateCalendarView();

  // Populate model with events.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithEventStartTimeTenMinsAway());

  // Up next should be displayed (with cached events).
  EXPECT_TRUE(up_next_view());
  EXPECT_EQ(size_t(1), up_next_scroll_contents()->children().size());

  // Replace the cached data with new data.
  Shell::Get()->system_tray_model()->calendar_model()->ClearAllCachedEvents();
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateMockEventListWithTwoEventsOneEndingInOneMin());
  EXPECT_TRUE(up_next_view());
  EXPECT_EQ(size_t(2), up_next_scroll_contents()->children().size());
}

class CalendarViewWithUpNextViewAnimationTest
    : public CalendarViewAnimationTest {
 public:
  CalendarViewWithUpNextViewAnimationTest() = default;
  CalendarViewWithUpNextViewAnimationTest(
      const CalendarViewWithUpNextViewAnimationTest&) = delete;
  CalendarViewWithUpNextViewAnimationTest& operator=(
      const CalendarViewWithUpNextViewAnimationTest&) = delete;
  ~CalendarViewWithUpNextViewAnimationTest() override = default;

  std::unique_ptr<google_apis::calendar::EventList> CreateUpcomingEvents(
      base::Time date) {
    const auto start_time = date + base::Minutes(5);
    const auto end_time = start_time + base::Hours(1);
    auto event_list = std::make_unique<google_apis::calendar::EventList>();
    event_list->set_time_zone("Greenwich Mean Time");
    event_list->InjectItemForTesting(calendar_test_utils::CreateEvent(
        "id_0", "summary_0", start_time, end_time));

    return event_list;
  }

  void MockEventsFetched(
      base::Time date,
      std::unique_ptr<google_apis::calendar::EventList> event_list) {
    Shell::Get()->system_tray_model()->calendar_model()->OnEventsFetched(
        calendar_utils::GetStartOfMonthUTC(date),
        google_apis::calendar::kPrimaryCalendarId,
        google_apis::ApiErrorCode::HTTP_SUCCESS, event_list.get());
  }

  void MockOnCalendarListFetched() {
    std::list<std::unique_ptr<google_apis::calendar::SingleCalendar>> calendars;
    calendars.push_back(calendar_test_utils::CreateCalendar(
        kCalendarId1, kCalendarSummary1, kCalendarColorId1, kCalendarSelected1,
        kCalendarPrimary1));
    std::unique_ptr<google_apis::calendar::CalendarList> calendar_list =
        calendar_test_utils::CreateMockCalendarList(std::move(calendars));
    Shell::Get()
        ->system_tray_model()
        ->calendar_list_model()
        ->OnCalendarListFetched(google_apis::ApiErrorCode::HTTP_SUCCESS,
                                std::move(calendar_list));
  }
};

INSTANTIATE_TEST_SUITE_P(MultiCalendar,
                         CalendarViewWithUpNextViewAnimationTest,
                         testing::Bool());

TEST_P(CalendarViewWithUpNextViewAnimationTest,
       UpNextViewShouldNotCoversToday) {
  auto histogram_tester = std::make_unique<base::HistogramTester>();
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  base::Time date;
  // Pick a date towards the end of the month so it's initial position is not on
  // the top when it's created.
  ASSERT_TRUE(base::Time::FromString("25 Apr 2023 10:00 GMT", &date));
  task_environment()->AdvanceClock(date - base::Time::Now());

  CreateCalendarView();
  // Force the size of the calendar to be small enough that the bottom row of
  // date cells will be covered by the up next view.
  widget()->SetFullscreen(false);
  widget()->SetSize(gfx::Size(kTrayMenuWidth, 350));
  const int initial_scroll_position = scroll_view()->GetVisibleRect().y();

  EXPECT_EQ(initial_scroll_position, GetPositionOfToday());

  // Today should be visible.
  bool todays_date_cell_is_visible =
      scroll_view()->GetBoundsInScreen().Intersects(
          calendar_view()
              ->calendar_view_controller()
              ->todays_date_cell_view()
              ->GetBoundsInScreen());
  EXPECT_TRUE(todays_date_cell_is_visible);

  // Fetch an upcoming event so up next is displayed.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateUpcomingEvents(date));

  // Wait for the show up next animation and smooth scrolling to complete.
  EXPECT_TRUE(calendar_view()->up_next_view());
  ui::LayerAnimationStoppedWaiter animation_waiter;
  animation_waiter.Wait(calendar_view()->up_next_view()->layer());
  animation_waiter.Wait(current_month()->layer());

  // After the up next view is shown, the scroll view should not have moved.
  EXPECT_EQ(initial_scroll_position, scroll_view()->GetVisibleRect().y());
  todays_date_cell_is_visible = scroll_view()->GetBoundsInScreen().Intersects(
      calendar_view()
          ->calendar_view_controller()
          ->todays_date_cell_view()
          ->GetBoundsInScreen());
  EXPECT_TRUE(todays_date_cell_is_visible);
}

TEST_P(CalendarViewWithUpNextViewAnimationTest,
       ShouldNotScrollToShowTodaysCell_WhenUpNextViewDoesNotCoverIt) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  base::Time date;
  // Pick a date at the start of the month so up next doesn't cover it.
  ASSERT_TRUE(base::Time::FromString("1 Apr 2023 10:00 GMT", &date));
  task_environment()->AdvanceClock(date - base::Time::Now());
  CreateCalendarView();
  // Force the size of the calendar to be small enough that the bottom row of
  // date cells will be covered by the up next view.
  widget()->SetFullscreen(false);
  widget()->SetSize(gfx::Size(kTrayMenuWidth, 350));

  const int initial_scroll_position = scroll_view()->GetVisibleRect().y();

  // Fetch an upcoming event so up next is displayed.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateUpcomingEvents(date));

  // Wait for the show up next animation to complete.
  EXPECT_TRUE(calendar_view()->up_next_view());
  ui::LayerAnimationStoppedWaiter().Wait(
      calendar_view()->up_next_view()->layer());

  // After the up next view is shown, the scroll view should not have moved as
  // the today date cell should remain visible.
  EXPECT_EQ(initial_scroll_position, scroll_view()->GetVisibleRect().y());
  const bool todays_date_cell_is_visible =
      scroll_view()->GetBoundsInScreen().Intersects(
          calendar_view()
              ->calendar_view_controller()
              ->todays_date_cell_view()
              ->GetBoundsInScreen());
  EXPECT_TRUE(todays_date_cell_is_visible);
}

TEST_P(
    CalendarViewWithUpNextViewAnimationTest,
    ShouldNotScrollToShowTodaysCell_WhenUserHasScrolled_AndAnUpcomingEventAppears) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  base::Time date;
  // Pick a date towards the end of the month so up next covers the bottom row.
  ASSERT_TRUE(base::Time::FromString("25 Apr 2023 10:00 GMT", &date));
  task_environment()->AdvanceClock(date - base::Time::Now());
  CreateCalendarView();
  // Force the size of the calendar to be small enough that the bottom row of
  // date cells will be covered by the up next view.
  widget()->SetFullscreen(false);
  widget()->SetSize(gfx::Size(kTrayMenuWidth, 350));

  if (IsMultiCalendarEnabled()) {
    MockOnCalendarListFetched();
    WaitUntilFetched();
  }

  // Fetch an event that starts in 11 mins and up next will show.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateUpcomingEvents(date + base::Minutes(6)));
  WaitUntilFetched();
  EXPECT_TRUE(calendar_view()->up_next_view()->GetVisible());

  // Scroll up a bit so that today is off the screen.
  ScrollUpOneMonth();
  ui::LayerAnimationStoppedWaiter animation_waiter;
  animation_waiter.Wait(current_month()->layer());

  const int initial_scroll_position = scroll_view()->GetVisibleRect().y();

  // Now advance time so that our upcoming meeting is about to start and up next
  // should be invisible since the user has scrolled.
  task_environment()->FastForwardBy(base::Minutes(5));
  EXPECT_FALSE(calendar_view()->up_next_view()->GetVisible());

  // The scroll view should not have moved as the user has previously interacted
  // with the scroll view.
  EXPECT_EQ(initial_scroll_position, scroll_view()->GetVisibleRect().y());
}

TEST_P(CalendarViewWithUpNextViewAnimationTest,
       ShouldNotScroll_WhenAnUpcomingEventAppears) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  base::Time date;
  // Pick a date towards the end of the month so up next covers the bottom row.
  ASSERT_TRUE(base::Time::FromString("25 Apr 2023 10:00 GMT", &date));
  task_environment()->AdvanceClock(date - base::Time::Now());
  CreateCalendarView();
  // Force the size of the calendar to be small enough that the bottom row of
  // date cells will be covered by the up next view.
  widget()->SetFullscreen(false);
  widget()->SetSize(gfx::Size(kTrayMenuWidth, 350));

  // Fetch an event that starts in 11 mins and up next will show.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateUpcomingEvents(date + base::Minutes(6)));
  EXPECT_TRUE(calendar_view()->up_next_view());

  ui::LayerAnimationStoppedWaiter animation_waiter;
  animation_waiter.Wait(current_month()->layer());
  const int initial_scroll_position = scroll_view()->GetVisibleRect().y();

  // The initial position show be the position of today's row.
  EXPECT_EQ(initial_scroll_position, GetPositionOfToday());

  // Now advance time so that our upcoming meeting is about to start and up next
  // should not appear since the user has scrolled.
  task_environment()->FastForwardBy(base::Minutes(5));
  EXPECT_TRUE(calendar_view()->up_next_view());
  animation_waiter.Wait(calendar_view()->up_next_view()->layer());

  // The scroll view should have not moved to show the `up_next_view_`.
  EXPECT_EQ(initial_scroll_position, scroll_view()->GetVisibleRect().y());
}

TEST_P(CalendarViewWithUpNextViewAnimationTest,
       ShouldNotScrollToShowTodaysCell_WhenTodaysDateCellIsNull) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  base::Time date;
  // Pick a date towards the end of the month.
  ASSERT_TRUE(base::Time::FromString("25 Apr 2023 10:00 GMT", &date));
  task_environment()->AdvanceClock(date - base::Time::Now());
  CreateCalendarView();
  // Force the size of the calendar to be small enough that the bottom row of
  // date cells will be covered by the up next view.
  widget()->SetFullscreen(false);
  widget()->SetSize(gfx::Size(kTrayMenuWidth, 350));

  if (IsMultiCalendarEnabled()) {
    MockOnCalendarListFetched();
    WaitUntilFetched();
  }

  // Fetch an event that starts in 11 mins and up next view will be shown.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateUpcomingEvents(date + base::Minutes(6)));
  WaitUntilFetched();
  EXPECT_TRUE(calendar_view()->up_next_view()->GetVisible());

  // Scrolls 4 months so that todays date cell is null.
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  ScrollUpOneMonth();
  EXPECT_EQ(
      calendar_view()->calendar_view_controller()->todays_date_cell_view(),
      nullptr);

  const int initial_scroll_position = scroll_view()->GetVisibleRect().y();

  // Now advance time so that our upcoming meeting is about to start and up next
  // shouldn't show since the user has scrolled, and the scroll view should not
  // have moved as well.
  task_environment()->FastForwardBy(base::Minutes(5));
  EXPECT_FALSE(calendar_view()->up_next_view()->GetVisible());
  EXPECT_EQ(initial_scroll_position, scroll_view()->GetVisibleRect().y());
}

// Tests that the scroll view scrolls up to today's row without the up-next
// view.
TEST_P(CalendarViewWithUpNextViewAnimationTest,
       ShouldScrollToToday_WithoutUpNextView) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  base::Time date;

  // Pick a date towards the end of the month so that it need to scroll up to
  // today's row.
  ASSERT_TRUE(base::Time::FromString("30 Nov 2023 10:00 GMT", &date));
  task_environment()->AdvanceClock(date - base::Time::Now());
  SetTodayFromTime(date);
  CreateCalendarView();

  // After the view is settled, it should scroll to today's row.
  EXPECT_EQ(GetPositionOfToday(), scroll_view()->GetVisibleRect().y());
}

// Tests that the scroll view scrolls up to today's row with the up-next view.
TEST_P(CalendarViewWithUpNextViewAnimationTest,
       ShouldScrollToToday_WithUpNextView) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  base::Time date;
  // Pick a date towards the end of the month so that it need to scroll up to
  // today's row.
  ASSERT_TRUE(base::Time::FromString("30 Nov 2023 10:00 GMT", &date));
  task_environment()->AdvanceClock(date - base::Time::Now());
  SetTodayFromTime(date);
  CreateCalendarView();

  // Fetch an event that starts in 6 mins and up next will show.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateUpcomingEvents(date + base::Minutes(6)));
  EXPECT_TRUE(calendar_view()->up_next_view());

  ui::LayerAnimationStoppedWaiter animation_waiter;
  animation_waiter.Wait(current_month()->layer());
  animation_waiter.Wait(calendar_view()->up_next_view()->layer());

  // After the view is settled, it should scroll to today's row.
  EXPECT_EQ(GetPositionOfToday(), scroll_view()->GetVisibleRect().y());
}

// Tests that the up-next view can show up after showing the event listview
// first. Regression test for b/336722659.
TEST_P(CalendarViewWithUpNextViewAnimationTest,
       ShowUpNextViewAfterEventListViewCorrectly) {
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  base::Time date;
  ASSERT_TRUE(base::Time::FromString("30 Nov 2023 10:00 GMT", &date));
  task_environment()->AdvanceClock(date - base::Time::Now());
  SetTodayFromTime(date);
  CreateCalendarView();
  ui::LayerAnimationStoppedWaiter animation_waiter;
  animation_waiter.Wait(current_month()->layer());
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  // There's no `up_next_view()` before the events are fetched.
  EXPECT_FALSE(calendar_view()->up_next_view());

  // Open the event list view for today.
  ASSERT_EQ(u"30",
            static_cast<views::LabelButton*>(current_month()->children()[32])
                ->GetText());
  GestureTapOn(
      static_cast<views::LabelButton*>(current_month()->children()[32]));

  animation_waiter.Wait(calendar_sliding_surface_view()->layer());
  animation_waiter.Wait(current_label()->layer());
  ASSERT_TRUE(event_list_view());
  EXPECT_TRUE(event_list_view()->GetVisible());
  task_environment()->FastForwardBy(
      calendar_test_utils::kAnimationSettleDownDuration);

  // Fetch an event that starts in 6 mins.
  MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
                    CreateUpcomingEvents(date + base::Minutes(6)));
  EXPECT_FALSE(calendar_view()->up_next_view());

  // Close the event list view.
  CloseEventList();
  animation_waiter.Wait(calendar_sliding_surface_view()->layer());
  animation_waiter.Wait(current_label()->layer());
  EXPECT_FALSE(event_list_view());

  // `up_next_view()` should be visible.
  EXPECT_TRUE(calendar_view()->up_next_view()->GetVisible());
}

}  // namespace ash