// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/time/calendar_up_next_view.h"
#include <utility>
#include "ash/public/cpp/test/test_system_tray_client.h"
#include "ash/shell.h"
#include "ash/system/model/system_tray_model.h"
#include "ash/system/time/calendar_event_list_item_view.h"
#include "ash/system/time/calendar_unittest_utils.h"
#include "ash/system/time/calendar_view_controller.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "google_apis/calendar/calendar_api_requests.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/scroll_view.h"
namespace ash {
namespace {
std::unique_ptr<google_apis::calendar::CalendarEvent> CreateEvent(
const base::Time start_time,
const base::Time end_time,
bool all_day_event = false,
const GURL video_conference_url = GURL(),
const char* summary = "summary") {
return calendar_test_utils::CreateEvent(
"id_0", summary, start_time, end_time,
google_apis::calendar::CalendarEvent::EventStatus::kConfirmed,
google_apis::calendar::CalendarEvent::ResponseStatus::kAccepted,
all_day_event, video_conference_url);
}
std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>>
CreateUpcomingEvents(int event_count = 1,
bool all_day_event = false,
const GURL video_conference_url = GURL(),
const char* summary = "summary") {
std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>> events;
auto event_in_ten_mins_start_time =
base::subtle::TimeNowIgnoringOverride().LocalMidnight() +
base::Minutes(10);
auto event_in_ten_mins_end_time =
base::subtle::TimeNowIgnoringOverride().LocalMidnight() + base::Hours(1);
for (int i = 0; i < event_count; ++i) {
events.push_back(CreateEvent(event_in_ten_mins_start_time,
event_in_ten_mins_end_time, all_day_event,
video_conference_url, summary));
}
return events;
}
} // namespace
class CalendarUpNextViewTest : public AshTestBase {
public:
CalendarUpNextViewTest() = default;
explicit CalendarUpNextViewTest(
base::test::TaskEnvironment::TimeSource time_source)
: AshTestBase(time_source) {}
void SetUp() override {
AshTestBase::SetUp();
controller_ = std::make_unique<CalendarViewController>();
}
void TearDown() override {
controller_.reset();
AshTestBase::TearDown();
}
void CreateUpNextView(
std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>> events,
views::Button::PressedCallback callback =
views::Button::PressedCallback()) {
if (!widget_) {
widget_ = CreateFramelessTestWidget();
}
// Mock events being fetched.
Shell::Get()->system_tray_model()->calendar_model()->OnEventsFetched(
calendar_utils::GetStartOfMonthUTC(
base::subtle::TimeNowIgnoringOverride().LocalMidnight()),
google_apis::calendar::kPrimaryCalendarId,
google_apis::ApiErrorCode::HTTP_SUCCESS,
calendar_test_utils::CreateMockEventList(std::move(events)).get());
auto up_next_view = std::make_unique<CalendarUpNextView>(
controller_.get(), std::move(callback));
up_next_view_ = widget_->SetContentsView(std::move(up_next_view));
// Set the widget to reflect the CalendarUpNextView size in reality. If we
// don't then the view will never be scrollable.
widget_->SetSize(
gfx::Size(kTrayMenuWidth, up_next_view_->GetPreferredSize().height()));
}
const views::View* GetHeaderView() { return up_next_view_->header_view_; }
const views::Label* GetHeaderLabel() {
return static_cast<views::Label*>(GetHeaderView()->children()[0]);
}
const views::View* GetContentsView() { return up_next_view_->content_view_; }
const views::ScrollView* GetScrollView() {
return up_next_view_->scroll_view_;
}
const views::View* GetScrollLeftButton() {
return up_next_view_->left_scroll_button_;
}
const views::View* GetScrollRightButton() {
return up_next_view_->right_scroll_button_;
}
const views::View* GetTodaysEventsButton() {
return up_next_view_->todays_events_button_container_->children()[0];
}
virtual void PressScrollLeftButton() {
PressScrollButton(GetScrollLeftButton());
}
virtual void PressScrollRightButton() {
PressScrollButton(GetScrollRightButton());
}
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);
}
int ScrollPosition() { return GetScrollView()->GetVisibleRect().x(); }
void ScrollHorizontalPositionTo(int position_in_px) {
up_next_view_->scroll_view_->ScrollToPosition(
up_next_view_->scroll_view_->horizontal_scroll_bar(), position_in_px);
}
// End the scrolling animation.
void EndScrollingAnimation() { up_next_view_->scrolling_animation_->End(); }
CalendarViewController* controller() { return controller_.get(); }
CalendarUpNextView* up_next_view() { return up_next_view_; }
private:
void PressScrollButton(const views::View* button) {
LeftClickOn(button);
// End the scrolling animation immediately so tests can assert the results
// of scrolling. If we don't do this, the test assertions run immediately
// (and fail) due to animations concurrently running.
EndScrollingAnimation();
}
std::unique_ptr<views::Widget> widget_;
raw_ptr<CalendarUpNextView> up_next_view_;
std::unique_ptr<CalendarViewController> controller_;
};
TEST_F(CalendarUpNextViewTest, ShouldShowMultipleUpcomingEvents) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Add event starting in 10 mins.
std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>> events;
auto event_in_ten_mins_start_time =
base::subtle::TimeNowIgnoringOverride().LocalMidnight() +
base::Minutes(10);
auto event_in_ten_mins_end_time =
base::subtle::TimeNowIgnoringOverride().LocalMidnight() + base::Hours(1);
// Add event that's in progress.
auto event_in_progress_start_time =
base::subtle::TimeNowIgnoringOverride().LocalMidnight() -
base::Minutes(30);
auto event_in_progress_end_time =
base::subtle::TimeNowIgnoringOverride().LocalMidnight() +
base::Minutes(30);
events.push_back(
CreateEvent(event_in_ten_mins_start_time, event_in_ten_mins_end_time));
events.push_back(
CreateEvent(event_in_progress_start_time, event_in_progress_end_time));
CreateUpNextView(std::move(events));
EXPECT_EQ(GetHeaderLabel()->GetText(), u"Up next");
EXPECT_EQ(GetContentsView()->children().size(), size_t(2));
}
TEST_F(CalendarUpNextViewTest,
ShouldShowSingleEventWithShortTitleTakingUpFullWidthOfParentView) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Create UpNextView with a single upcoming event.
CreateUpNextView(CreateUpcomingEvents());
EXPECT_EQ(GetContentsView()->children().size(), size_t(1));
EXPECT_EQ(GetContentsView()->children()[0]->width(),
GetScrollView()->width());
}
TEST_F(CalendarUpNextViewTest,
ShouldShowSingleEventWithLongTitleTakingUpFullWidthOfParentView) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Create UpNextView with a single upcoming event.
CreateUpNextView(
CreateUpcomingEvents(1, false, GURL(),
"Meeting title with really long long long long long "
"long long name that should ellipsis"));
EXPECT_EQ(GetContentsView()->children().size(), size_t(1));
EXPECT_EQ(GetContentsView()->children()[0]->width(),
GetScrollView()->width());
}
TEST_F(CalendarUpNextViewTest,
ShouldScrollLeftAndRightWhenScrollButtonsArePressed) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Add multiple upcoming events.
const int event_count = 5;
CreateUpNextView(CreateUpcomingEvents(event_count));
EXPECT_EQ(GetContentsView()->children().size(), size_t(event_count));
EXPECT_EQ(ScrollPosition(), 0);
// Press scroll right. We should scroll past the first event + margin.
const int first_event_width =
GetContentsView()->children()[0]->GetContentsBounds().width() +
calendar_utils::kUpNextBetweenChildSpacing;
PressScrollRightButton();
EXPECT_EQ(ScrollPosition(), first_event_width);
// Press scroll right again. We should scroll past the second event +
// margin.
const int second_event_width =
GetContentsView()->children()[1]->GetContentsBounds().width() +
calendar_utils::kUpNextBetweenChildSpacing;
PressScrollRightButton();
EXPECT_EQ(ScrollPosition(), first_event_width + second_event_width);
// Press scroll left. Now we should be back to being past the first event +
// margin.
PressScrollLeftButton();
EXPECT_EQ(ScrollPosition(), first_event_width);
// Press scroll left again. We should be back at the beginning of the scroll
// view.
PressScrollLeftButton();
EXPECT_EQ(ScrollPosition(), 0);
}
TEST_F(CalendarUpNextViewTest,
ShouldScrollLeftAndRightWhenScrollButtonsArePressed_RTL) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Add multiple upcoming events.
const int event_count = 5;
CreateUpNextView(CreateUpcomingEvents(event_count));
EXPECT_EQ(GetContentsView()->children().size(), size_t(event_count));
EXPECT_EQ(ScrollPosition(), 0);
// Sets the UI to be RTL.
base::i18n::SetRTLForTesting(true);
// Press scroll right. We should scroll past the first event + margin.
const int first_event_width =
GetContentsView()->children()[0]->GetContentsBounds().width() +
calendar_utils::kUpNextBetweenChildSpacing;
PressScrollRightButton();
EXPECT_EQ(ScrollPosition(), first_event_width);
// Press scroll right again. We should scroll past the second event +
// margin.
const int second_event_width =
GetContentsView()->children()[1]->GetContentsBounds().width() +
calendar_utils::kUpNextBetweenChildSpacing;
PressScrollRightButton();
EXPECT_EQ(ScrollPosition(), first_event_width + second_event_width);
// Press scroll left. Now we should be back to being past the first event +
// margin.
PressScrollLeftButton();
EXPECT_EQ(ScrollPosition(), first_event_width);
// Press scroll left again. We should be back at the beginning of the scroll
// view.
PressScrollLeftButton();
EXPECT_EQ(ScrollPosition(), 0);
}
TEST_F(CalendarUpNextViewTest, ShouldHideScrollButtons_WhenOnlyOneEvent) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Create UpNextView with a single upcoming event.
CreateUpNextView(CreateUpcomingEvents());
EXPECT_EQ(GetContentsView()->children().size(), size_t(1));
EXPECT_EQ(ScrollPosition(), 0);
// With only one event, there won't be any room to scroll in either direction
// so the buttons should be hidden.
EXPECT_FALSE(GetScrollLeftButton()->GetVisible());
EXPECT_FALSE(GetScrollRightButton()->GetVisible());
}
TEST_F(CalendarUpNextViewTest, ShouldShowScrollButtons_WhenMultipleEvents) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Add multiple upcoming events.
const int event_count = 5;
CreateUpNextView(CreateUpcomingEvents(event_count));
EXPECT_EQ(GetContentsView()->children().size(), size_t(event_count));
// At the start the scroll left button should be disabled and visible.
EXPECT_EQ(ScrollPosition(), 0);
EXPECT_FALSE(GetScrollLeftButton()->GetEnabled());
EXPECT_TRUE(GetScrollLeftButton()->GetVisible());
EXPECT_TRUE(GetScrollRightButton()->GetEnabled());
EXPECT_TRUE(GetScrollRightButton()->GetVisible());
PressScrollRightButton();
// After scrolling right a bit, both buttons should be enabled and visible.
EXPECT_TRUE(GetScrollLeftButton()->GetEnabled());
EXPECT_TRUE(GetScrollLeftButton()->GetVisible());
EXPECT_TRUE(GetScrollRightButton()->GetEnabled());
EXPECT_TRUE(GetScrollRightButton()->GetVisible());
PressScrollRightButton();
PressScrollRightButton();
PressScrollRightButton();
// After scrolling to the end, the scroll right button should be disabled and
// visible.
EXPECT_TRUE(GetScrollLeftButton()->GetEnabled());
EXPECT_FALSE(GetScrollRightButton()->GetEnabled());
EXPECT_TRUE(GetScrollRightButton()->GetVisible());
}
// If we have a partially visible event view and the scroll left button is
// pressed, we should scroll to put the whole event into view, aligned to the
// start of the viewport.
// [---------------] <-- ScrollView viewport
// [-E1-] [---E2---] <-- Event 2 partially shown in the viewport.
// Press scroll left button.
// [---------------] <-- ScrollView viewport
// [-E1-] [---E2---] <-- Event 2 now fully shown in viewport.
TEST_F(
CalendarUpNextViewTest,
ShouldMakeCurrentOrPreviousEventFullyVisibleAndLeftAligned_WhenScrollLeftButtonIsPressed) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
calendar_test_utils::ScopedLibcTimeZone scoped_libc_timezone("GMT");
ASSERT_TRUE(scoped_libc_timezone.is_success());
// Add multiple upcoming events.
const int event_count = 5;
CreateUpNextView(CreateUpcomingEvents(event_count));
EXPECT_EQ(GetContentsView()->children().size(), size_t(event_count));
EXPECT_EQ(ScrollPosition(), 0);
// Scroll right past the first event and so that the second event is partially
// visible on the left of the scrollview.
const int first_event_width =
GetContentsView()->children()[0]->GetContentsBounds().width() +
calendar_utils::kUpNextBetweenChildSpacing;
const int scroll_position_partially_over_second_event =
first_event_width + 50;
ScrollHorizontalPositionTo(scroll_position_partially_over_second_event);
ASSERT_EQ(scroll_position_partially_over_second_event, ScrollPosition());
const views::View* first_event = GetContentsView()->children()[0];
const views::View* second_event = GetContentsView()->children()[1];
// Assert first view is not visible and second view is partially visible.
ASSERT_EQ(0, first_event->GetVisibleBounds().width());
EXPECT_LT(second_event->GetVisibleBounds().width(),
second_event->GetContentsBounds().width());
// Press scroll left. We should scroll so that the second event is aligned to
// the start of the scroll view and fully visible. This is the equivalent
// position of being scrolled to the right of the width of the first event.
PressScrollLeftButton();
EXPECT_EQ(first_event_width, ScrollPosition());
}
// If we have a partially visible event and the scroll right button is pressed,
// we should scroll to put the whole event into view, aligned to the start of
// the viewport.
// If we scroll right for a partially visible event view.
// [---------------] <-- ScrollView viewport
// [--E1--] [--E2--] <-- Event 2 partially shown in the viewport.
// Press scroll right button.
// [---------------] <-- ScrollView viewport
// [--E1--] [--E2--] <-- Event 2 now fully shown in the viewport.
TEST_F(
CalendarUpNextViewTest,
ShouldMakeNextEventFullyVisibleAndLeftAligned_WhenScrollRightButtonIsPressed) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Add multiple upcoming events.
const int event_count = 5;
CreateUpNextView(CreateUpcomingEvents(event_count));
EXPECT_EQ(GetContentsView()->children().size(), size_t(event_count));
EXPECT_EQ(ScrollPosition(), 0);
ScrollHorizontalPositionTo(100);
ASSERT_EQ(ScrollPosition(), 100);
const views::View* first_event = GetContentsView()->children()[0];
// Assert first view is partially visible.
EXPECT_TRUE(first_event->GetVisibleBounds().width() <
first_event->GetContentsBounds().width());
// Press scroll right. We should scroll past the first event + margin to
// show the second event, aligned to the start of the scroll view.
const int first_event_width = first_event->GetContentsBounds().width() +
calendar_utils::kUpNextBetweenChildSpacing;
PressScrollRightButton();
EXPECT_EQ(ScrollPosition(), first_event_width);
}
TEST_F(CalendarUpNextViewTest,
ShouldInvokeCallback_WhenTodaysEventButtonPressed) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
bool called = false;
auto callback = base::BindLambdaForTesting(
[&called](const ui::Event& event) { called = true; });
// Create UpNextView with a single upcoming event.
CreateUpNextView(CreateUpcomingEvents(), callback);
EXPECT_FALSE(called);
LeftClickOn(GetTodaysEventsButton());
EXPECT_TRUE(called);
}
TEST_F(CalendarUpNextViewTest, ShouldTrackLaunchingFromEventListItem) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Create UpNextView with a single upcoming event.
auto histogram_tester = std::make_unique<base::HistogramTester>();
CreateUpNextView(CreateUpcomingEvents());
EXPECT_EQ(GetContentsView()->children().size(), size_t(1));
// Click event inside the scrollview contents.
LeftClickOn(GetContentsView()->children()[0]);
histogram_tester->ExpectTotalCount(
"Ash.Calendar.UpNextView.EventListItem.Pressed", 1);
}
TEST_F(CalendarUpNextViewTest, ShouldTrackEventDisplayedCount) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Add 5 upcoming events.
auto histogram_tester = std::make_unique<base::HistogramTester>();
const int event_count = 5;
CreateUpNextView(CreateUpcomingEvents(event_count));
EXPECT_EQ(GetContentsView()->children().size(), size_t(event_count));
histogram_tester->ExpectBucketCount(
"Ash.Calendar.UpNextView.EventDisplayedCount", event_count, 1);
}
TEST_F(CalendarUpNextViewTest,
ShouldLaunchAndTrackGoogleMeet_WhenJoinMeetingButtonPressed) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
auto histogram_tester = std::make_unique<base::HistogramTester>();
// Create up next view with upcoming google meet event.
CreateUpNextView(
CreateUpcomingEvents(1, false, GURL("https://meet.google.com/abc-123")));
EXPECT_EQ(GetContentsView()->children().size(), size_t(1));
EXPECT_EQ(GetSystemTrayClient()->show_video_conference_count(), 0);
// Click the "Join" meeting button.
const auto* join_meeting_button =
GetContentsView()->children()[0]->GetViewByID(kJoinButtonID);
ASSERT_TRUE(join_meeting_button);
LeftClickOn(join_meeting_button);
EXPECT_EQ(GetSystemTrayClient()->show_video_conference_count(), 1);
histogram_tester->ExpectTotalCount(
"Ash.Calendar.UpNextView.JoinMeetingButton.Pressed", 1);
}
// Greenlines can be found in b/258648030.
TEST_F(CalendarUpNextViewTest, ShouldFocusViewsInCorrectOrder_WhenPressingTab) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Create up next view with 2 upcoming google meet events.
CreateUpNextView(
CreateUpcomingEvents(2, false, GURL("https://meet.google.com/abc-123")));
EXPECT_EQ(GetContentsView()->children().size(), size_t(2));
auto* focus_manager = up_next_view()->GetFocusManager();
// First the event list item view should be focused.
PressTab();
auto* first_item = GetContentsView()->children()[0].get();
ASSERT_TRUE(first_item);
EXPECT_EQ(first_item, focus_manager->GetFocusedView());
EXPECT_STREQ("CalendarEventListItemView",
focus_manager->GetFocusedView()->GetClassName());
// Next, the "Join" button should be focused.
PressTab();
EXPECT_EQ(first_item->GetViewByID(kJoinButtonID),
focus_manager->GetFocusedView());
// Next, the second event list item view should be focused.
PressTab();
auto* second_item = GetContentsView()->children()[1].get();
ASSERT_TRUE(second_item);
EXPECT_EQ(second_item, focus_manager->GetFocusedView());
EXPECT_STREQ("CalendarEventListItemView",
focus_manager->GetFocusedView()->GetClassName());
// Next, the second event list item view "Join" button should be focused.
PressTab();
EXPECT_EQ(second_item->GetViewByID(kJoinButtonID),
focus_manager->GetFocusedView());
// Finally, the show upcoming events button should be focused.
PressTab();
EXPECT_EQ(GetTodaysEventsButton(), focus_manager->GetFocusedView());
// Going back, the second event list item view "Join" button should be
// focused.
PressShiftTab();
EXPECT_EQ(second_item->GetViewByID(kJoinButtonID),
focus_manager->GetFocusedView());
// Going back again, the second event list item view should be focused.
PressShiftTab();
EXPECT_EQ(second_item, focus_manager->GetFocusedView());
EXPECT_STREQ("CalendarEventListItemView",
focus_manager->GetFocusedView()->GetClassName());
}
// Add unittest for the fix of this bug: b/286596205.
TEST_F(CalendarUpNextViewTest, ShouldPreserveFocusAfterRefreshEvent) {
// Set time override.
base::subtle::ScopedTimeClockOverrides time_override(
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
nullptr, nullptr);
// Create up next view with 2 upcoming google meet events.
CreateUpNextView(
CreateUpcomingEvents(2, false, GURL("https://meet.google.com/abc-123")));
EXPECT_EQ(GetContentsView()->children().size(), size_t(2));
auto* focus_manager = up_next_view()->GetFocusManager();
// First the event list item view should be focused.
PressTab();
auto* first_item = GetContentsView()->children()[0].get();
ASSERT_TRUE(first_item);
EXPECT_EQ(first_item, focus_manager->GetFocusedView());
EXPECT_STREQ("CalendarEventListItemView",
focus_manager->GetFocusedView()->GetClassName());
up_next_view()->RefreshEvents();
// After refresh the events, the first event list item view should still be
// focused.
EXPECT_EQ(first_item, focus_manager->GetFocusedView());
EXPECT_STREQ("CalendarEventListItemView",
focus_manager->GetFocusedView()->GetClassName());
}
class CalendarUpNextViewAnimationTest : public CalendarUpNextViewTest {
public:
CalendarUpNextViewAnimationTest()
: CalendarUpNextViewTest(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
void PressScrollLeftButton() override { LeftClickOn(GetScrollLeftButton()); }
void PressScrollRightButton() override {
LeftClickOn(GetScrollRightButton());
}
bool IsAnimating() {
return up_next_view()->scrolling_animation_ &&
up_next_view()->scrolling_animation_->is_animating();
}
const base::TimeDelta kAnimationStartBufferDuration = base::Milliseconds(50);
const base::TimeDelta kAnimationFinishedDuration = base::Seconds(1);
};
// Flaky: https://crbug.com/1401505
TEST_F(CalendarUpNextViewAnimationTest,
DISABLED_ShouldAnimateScrollView_WhenScrollButtonsArePressed) {
// Add multiple upcoming events.
const int event_count = 5;
CreateUpNextView(CreateUpcomingEvents(event_count));
EXPECT_FALSE(IsAnimating());
PressScrollRightButton();
task_environment()->FastForwardBy(kAnimationStartBufferDuration);
EXPECT_TRUE(IsAnimating());
task_environment()->FastForwardBy(kAnimationFinishedDuration);
EXPECT_FALSE(IsAnimating());
PressScrollLeftButton();
task_environment()->FastForwardBy(kAnimationStartBufferDuration);
EXPECT_TRUE(IsAnimating());
task_environment()->FastForwardBy(kAnimationFinishedDuration);
EXPECT_FALSE(IsAnimating());
}
} // namespace ash