#include "ash/system/focus_mode/focus_mode_tray.h"
#include <optional>
#include "ash/accessibility/accessibility_controller.h"
#include "ash/api/tasks/fake_tasks_client.h"
#include "ash/constants/ash_features.h"
#include "ash/glanceables/common/glanceables_util.h"
#include "ash/root_window_controller.h"
#include "ash/screen_util.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/pill_button.h"
#include "ash/system/focus_mode/focus_mode_controller.h"
#include "ash/system/focus_mode/focus_mode_countdown_view.h"
#include "ash/system/focus_mode/focus_mode_ending_moment_view.h"
#include "ash/system/focus_mode/focus_mode_task_test_utils.h"
#include "ash/system/focus_mode/focus_mode_util.h"
#include "ash/system/progress_indicator/progress_indicator.h"
#include "ash/system/status_area_widget_test_helper.h"
#include "ash/system/tray/tray_bubble_wrapper.h"
#include "ash/system/tray/tray_container.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer_animator.h"
#include "ui/gfx/scoped_animation_duration_scale_mode.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/button/image_button.h"
#include "url/gurl.h"
namespace ash {
namespace {
constexpr base::TimeDelta kStartAnimationDelay = base::Milliseconds(300);
constexpr float kMaxAnimationScale = 10.0f;
}
class FocusModeTrayTest : public AshTestBase {
public:
FocusModeTrayTest()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
~FocusModeTrayTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
glanceables_util::SetIsNetworkConnectedForTest(true);
auto& tasks_client =
CreateFakeTasksClient(AccountId::FromUserEmail("user0@tray"));
tasks_client.set_http_error(google_apis::ApiErrorCode::HTTP_SUCCESS);
AddFakeTaskList(tasks_client, "default");
AddFakeTask(tasks_client, "default", "task1", "Task 1");
focus_mode_tray_ =
StatusAreaWidgetTestHelper::GetStatusAreaWidget()->focus_mode_tray();
}
void TearDown() override {
focus_mode_tray_ = nullptr;
AshTestBase::TearDown();
}
void AdvanceClock(base::TimeDelta time_delta) {
task_environment()->AdvanceClock(time_delta);
task_environment()->RunUntilIdle();
}
void SkipMinutes(int minutes) {
task_environment()->AdvanceClock(base::Seconds(minutes * 60 - 5));
task_environment()->FastForwardBy(base::Seconds(5));
}
TrayBubbleView* GetBubbleView() {
return focus_mode_tray_->bubble_->bubble_view();
}
FocusModeTray::TaskItemView* GetTaskItemView() {
return focus_mode_tray_->task_item_view_.get();
}
ProgressIndicator* GetProgressIndicator() {
return focus_mode_tray_->progress_indicator_.get();
}
PillButton* GetEndingMomentExtendTimeButton() {
return focus_mode_tray_->ending_moment_view_for_testing()
->extend_session_duration_button_;
}
views::Label* GetCountdownTimeRemainingLabel() {
return focus_mode_tray_->countdown_view_for_testing()
->time_remaining_label_;
}
std::optional<TaskId> GetSelectedTask() {
return focus_mode_tray_->selected_task_;
}
bool IsCountdownViewVisible() const {
return focus_mode_tray_->countdown_view_for_testing()->GetVisible();
}
bool IsEndingMomentViewVisible() const {
return focus_mode_tray_->ending_moment_view_for_testing()->GetVisible();
}
void ClickOutsideBubble() {
auto* event_generator = GetEventGenerator();
const gfx::Rect work_area =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
Shell::GetPrimaryRootWindow());
event_generator->MoveMouseTo(work_area.CenterPoint());
event_generator->ClickLeftButton();
}
void HandleCompleteTaskButton() {
focus_mode_tray_->HandleCompleteTaskButton();
}
protected:
raw_ptr<FocusModeTray> focus_mode_tray_ = nullptr;
};
TEST_F(FocusModeTrayTest, DefaultVisibility) {
EXPECT_TRUE(focus_mode_tray_);
EXPECT_FALSE(focus_mode_tray_->GetVisible());
EXPECT_FALSE(focus_mode_tray_->is_active());
}
TEST_F(FocusModeTrayTest, ActiveVisibility) {
FocusModeController* controller = FocusModeController::Get();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_FALSE(focus_mode_tray_->GetVisible());
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_FALSE(focus_mode_tray_->GetVisible());
}
TEST_F(FocusModeTrayTest, HiddenInLockScreen) {
FocusModeController* controller = FocusModeController::Get();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_FALSE(focus_mode_tray_->GetVisible());
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
GetSessionControllerClient()->LockScreen();
EXPECT_FALSE(focus_mode_tray_->GetVisible());
GetSessionControllerClient()->UnlockScreen();
EXPECT_TRUE(focus_mode_tray_->GetVisible());
}
TEST_F(FocusModeTrayTest, LockScreenNoAnimationAndStaysHidden) {
FocusModeController* controller = FocusModeController::Get();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_FALSE(focus_mode_tray_->GetVisible());
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
GetSessionControllerClient()->LockScreen();
EXPECT_FALSE(focus_mode_tray_->GetVisible());
controller->ToggleFocusMode();
EXPECT_FALSE(focus_mode_tray_->GetVisible());
GetSessionControllerClient()->UnlockScreen();
EXPECT_FALSE(focus_mode_tray_->GetVisible());
}
TEST_F(FocusModeTrayTest, ClickActivateDeactivate) {
FocusModeController* controller = FocusModeController::Get();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_FALSE(focus_mode_tray_->GetVisible());
controller->ToggleFocusMode();
EXPECT_TRUE(focus_mode_tray_->GetVisible());
EXPECT_FALSE(focus_mode_tray_->is_active());
EXPECT_EQ(1, GetProgressIndicator()->layer()->opacity());
LeftClickOn(focus_mode_tray_);
EXPECT_TRUE(focus_mode_tray_->is_active());
EXPECT_EQ(0, GetProgressIndicator()->layer()->opacity());
LeftClickOn(focus_mode_tray_);
EXPECT_FALSE(focus_mode_tray_->is_active());
EXPECT_EQ(1, GetProgressIndicator()->layer()->opacity());
LeftClickOn(focus_mode_tray_);
EXPECT_TRUE(focus_mode_tray_->is_active());
EXPECT_EQ(0, GetProgressIndicator()->layer()->opacity());
ClickOutsideBubble();
EXPECT_FALSE(focus_mode_tray_->is_active());
EXPECT_EQ(1, GetProgressIndicator()->layer()->opacity());
}
TEST_F(FocusModeTrayTest, MarkTaskAsCompleted) {
gfx::ScopedAnimationDurationScaleMode duration(kMaxAnimationScale);
FocusModeTask task;
task.task_id = {.list_id = "default", .id = "task1"};
task.title = "make a travel plan";
task.updated = base::Time::Now();
FocusModeController* controller = FocusModeController::Get();
controller->SetSelectedTask(task);
controller->ToggleFocusMode();
LeftClickOn(focus_mode_tray_);
EXPECT_TRUE(focus_mode_tray_->is_active());
EXPECT_TRUE(GetTaskItemView());
const auto* const radio_button = focus_mode_tray_->GetRadioButtonForTesting();
EXPECT_TRUE(radio_button);
LeftClickOn(radio_button);
AdvanceClock(kStartAnimationDelay);
auto* bubble_view = GetBubbleView();
ui::Layer* bubble_view_layer = bubble_view->layer();
auto* animator = bubble_view_layer->GetAnimator();
EXPECT_TRUE(animator &&
animator->IsAnimatingProperty(
ui::LayerAnimationElement::AnimatableProperty::BOUNDS));
EXPECT_FALSE(GetTaskItemView());
}
TEST_F(FocusModeTrayTest, MarkTaskAsCompletedDoubleClick) {
gfx::ScopedAnimationDurationScaleMode duration(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
FocusModeTask task;
task.task_id = {.list_id = "default", .id = "task1"};
task.title = "make a travel plan";
task.updated = base::Time::Now();
FocusModeController* controller = FocusModeController::Get();
controller->SetSelectedTask(task);
controller->ToggleFocusMode();
LeftClickOn(focus_mode_tray_);
EXPECT_TRUE(focus_mode_tray_->is_active());
EXPECT_TRUE(GetSelectedTask());
EXPECT_TRUE(GetTaskItemView());
const auto* const radio_button = focus_mode_tray_->GetRadioButtonForTesting();
EXPECT_TRUE(radio_button);
LeftClickOn(radio_button);
EXPECT_FALSE(GetSelectedTask());
LeftClickOn(radio_button);
AdvanceClock(kStartAnimationDelay);
EXPECT_FALSE(GetTaskItemView());
}
TEST_F(FocusModeTrayTest, MarkTaskAsCompletedBeforeModelUpdate) {
gfx::ScopedAnimationDurationScaleMode duration(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
FocusModeTask task;
task.task_id = {.list_id = "default", .id = "task1"};
task.title = "make a travel plan";
task.updated = base::Time::Now();
FocusModeController* controller = FocusModeController::Get();
controller->SetSelectedTask(task);
controller->ToggleFocusMode();
LeftClickOn(focus_mode_tray_);
EXPECT_TRUE(focus_mode_tray_->is_active());
EXPECT_TRUE(GetSelectedTask());
EXPECT_TRUE(GetTaskItemView());
const auto* const radio_button = focus_mode_tray_->GetRadioButtonForTesting();
EXPECT_TRUE(radio_button);
HandleCompleteTaskButton();
focus_mode_tray_->OnTaskCompleted(task);
focus_mode_tray_->OnSelectedTaskChanged(std::nullopt);
AdvanceClock(kStartAnimationDelay);
EXPECT_FALSE(GetTaskItemView());
}
TEST_F(FocusModeTrayTest, MarkTaskAsCompletedAfterModelUpdate) {
gfx::ScopedAnimationDurationScaleMode duration(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
FocusModeTask task;
task.task_id = {.list_id = "default", .id = "task1"};
task.title = "make a travel plan";
task.updated = base::Time::Now();
FocusModeController* controller = FocusModeController::Get();
controller->SetSelectedTask(task);
controller->ToggleFocusMode();
LeftClickOn(focus_mode_tray_);
EXPECT_TRUE(focus_mode_tray_->is_active());
EXPECT_TRUE(GetSelectedTask());
EXPECT_TRUE(GetTaskItemView());
const auto* const radio_button = focus_mode_tray_->GetRadioButtonForTesting();
EXPECT_TRUE(radio_button);
focus_mode_tray_->OnTaskCompleted(task);
focus_mode_tray_->OnSelectedTaskChanged(std::nullopt);
HandleCompleteTaskButton();
AdvanceClock(kStartAnimationDelay);
EXPECT_FALSE(GetTaskItemView());
}
TEST_F(FocusModeTrayTest, ProgressIndicatorProgresses) {
FocusModeController* controller = FocusModeController::Get();
controller->SetInactiveSessionDuration(base::Minutes(40));
controller->ToggleFocusMode();
constexpr float allowed_difference = 0.01f;
SkipMinutes(10);
EXPECT_NEAR(0.25, GetProgressIndicator()->progress().value(),
allowed_difference);
SkipMinutes(10);
EXPECT_NEAR(0.5, GetProgressIndicator()->progress().value(),
allowed_difference);
}
TEST_F(FocusModeTrayTest, ProgressIndicatorCentered) {
FocusModeController* controller = FocusModeController::Get();
controller->ToggleFocusMode();
EXPECT_EQ(focus_mode_tray_->tray_container()->GetLocalBounds().CenterPoint(),
GetProgressIndicator()->layer()->bounds().CenterPoint());
EXPECT_EQ(gfx::Size(32, 32),
GetProgressIndicator()->layer()->bounds().size());
Shelf* shelf = Shell::GetPrimaryRootWindowController()->shelf();
shelf->SetAlignment(ShelfAlignment::kLeft);
EXPECT_EQ(focus_mode_tray_->tray_container()->GetLocalBounds().CenterPoint(),
GetProgressIndicator()->layer()->bounds().CenterPoint());
EXPECT_EQ(gfx::Size(32, 32),
GetProgressIndicator()->layer()->bounds().size());
shelf->SetAlignment(ShelfAlignment::kRight);
EXPECT_EQ(focus_mode_tray_->tray_container()->GetLocalBounds().CenterPoint(),
GetProgressIndicator()->layer()->bounds().CenterPoint());
EXPECT_EQ(gfx::Size(32, 32),
GetProgressIndicator()->layer()->bounds().size());
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(focus_mode_tray_->tray_container()->GetLocalBounds().CenterPoint(),
GetProgressIndicator()->layer()->bounds().CenterPoint());
EXPECT_EQ(gfx::Size(32, 32),
GetProgressIndicator()->layer()->bounds().size());
}
TEST_F(FocusModeTrayTest, BubbleTabbingAndAccessibility) {
AccessibilityController* accessibility_controller =
Shell::Get()->accessibility_controller();
accessibility_controller->spoken_feedback().SetEnabled(true);
EXPECT_TRUE(accessibility_controller->spoken_feedback().enabled());
FocusModeController* controller = FocusModeController::Get();
const std::string task_name = "Task 1";
const base::TimeDelta session_duration = base::Minutes(40);
const std::u16string time_remaining = focus_mode_util::GetDurationString(
session_duration, false);
controller->SetInactiveSessionDuration(session_duration);
FocusModeTask task;
task.task_id = {.list_id = "default", .id = "task1"};
task.title = task_name;
task.updated = base::Time::Now();
controller->SetSelectedTask(task);
controller->ToggleFocusMode();
LeftClickOn(focus_mode_tray_);
EXPECT_EQ(l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_FOCUS_MODE_TRAY_BUBBLE_TASK_ACCESSIBLE_NAME,
time_remaining, base::UTF8ToUTF16(task_name)),
focus_mode_tray_->GetAccessibleNameForBubble());
{
ui::AXNodeData node_data;
GetBubbleView()->GetViewAccessibility().GetAccessibleNodeData(&node_data);
EXPECT_EQ(node_data.GetString16Attribute(ax::mojom::StringAttribute::kName),
focus_mode_tray_->GetAccessibleNameForBubble());
}
{
ui::AXNodeData node_data;
focus_mode_tray_->GetViewAccessibility().GetAccessibleNodeData(&node_data);
EXPECT_EQ(node_data.GetString16Attribute(ax::mojom::StringAttribute::kName),
l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_FOCUS_MODE_TRAY_BUBBLE_ACCESSIBLE_NAME,
time_remaining));
}
{
ui::AXNodeData node_data;
focus_mode_tray_->image_view()
->GetViewAccessibility()
.GetAccessibleNodeData(&node_data);
EXPECT_EQ(node_data.GetString16Attribute(ax::mojom::StringAttribute::kName),
l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_FOCUS_MODE_TRAY_BUBBLE_ACCESSIBLE_NAME,
time_remaining));
}
PressAndReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
views::FocusManager* focus_manager =
GetBubbleView()->GetWidget()->GetFocusManager();
{
ui::AXNodeData node_data;
focus_manager->GetFocusedView()
->GetViewAccessibility()
.GetAccessibleNodeData(&node_data);
EXPECT_EQ(
l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_FOCUS_MODE_TOGGLE_END_BUTTON_ACCESSIBLE_NAME),
node_data.GetString16Attribute(ax::mojom::StringAttribute::kName));
}
PressAndReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
{
ui::AXNodeData node_data;
focus_manager->GetFocusedView()
->GetViewAccessibility()
.GetAccessibleNodeData(&node_data);
EXPECT_EQ(
l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_FOCUS_MODE_INCREASE_TEN_MINUTES_BUTTON_ACCESSIBLE_NAME),
node_data.GetString16Attribute(ax::mojom::StringAttribute::kName));
}
PressAndReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
{
ui::AXNodeData node_data;
focus_manager->GetFocusedView()
->GetViewAccessibility()
.GetAccessibleNodeData(&node_data);
EXPECT_EQ(
l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_FOCUS_MODE_TASK_VIEW_RADIO_BUTTON),
node_data.GetString16Attribute(ax::mojom::StringAttribute::kName));
EXPECT_EQ(base::UTF8ToUTF16(task_name),
node_data.GetString16Attribute(
ax::mojom::StringAttribute::kDescription));
}
}
TEST_F(FocusModeTrayTest, EndingMoment) {
FocusModeController* controller = FocusModeController::Get();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_FALSE(focus_mode_tray_->GetVisible());
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
AdvanceClock(controller->GetSessionDuration());
EXPECT_FALSE(controller->in_focus_session());
EXPECT_TRUE(controller->in_ending_moment());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
AdvanceClock(base::Minutes(10));
EXPECT_TRUE(focus_mode_tray_->GetVisible());
FocusModeTray* focus_mode_tray =
StatusAreaWidgetTestHelper::GetStatusAreaWidget()->focus_mode_tray();
LeftClickOn(focus_mode_tray);
LeftClickOn(focus_mode_tray);
EXPECT_FALSE(focus_mode_tray_->GetVisible());
}
TEST_F(FocusModeTrayTest, EndingMomentPersists) {
base::HistogramTester histogram_tester;
FocusModeController* controller = FocusModeController::Get();
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
AdvanceClock(controller->GetSessionDuration());
EXPECT_FALSE(controller->in_focus_session());
EXPECT_TRUE(controller->in_ending_moment());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
AdvanceClock(base::Minutes(2));
EXPECT_TRUE(controller->in_ending_moment());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
LeftClickOn(focus_mode_tray_);
EXPECT_TRUE(focus_mode_tray_->is_active());
AdvanceClock(base::Minutes(2));
EXPECT_TRUE(focus_mode_tray_->is_active());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
EXPECT_TRUE(controller->in_ending_moment());
ClickOutsideBubble();
EXPECT_FALSE(focus_mode_tray_->is_active());
EXPECT_FALSE(focus_mode_tray_->GetVisible());
EXPECT_FALSE(controller->in_ending_moment());
histogram_tester.ExpectBucketCount(
focus_mode_histogram_names::kEndingMomentBubbleActionHistogram,
focus_mode_histogram_names::EndingMomentBubbleClosedReason::kOpended,
1);
}
TEST_F(FocusModeTrayTest, EndingMomentPanelFunctionality) {
base::TimeDelta kSessionDuration = base::Minutes(20);
FocusModeController* controller = FocusModeController::Get();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_FALSE(focus_mode_tray_->GetVisible());
controller->SetInactiveSessionDuration(kSessionDuration);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
LeftClickOn(focus_mode_tray_);
EXPECT_TRUE(focus_mode_tray_->is_active());
EXPECT_TRUE(IsCountdownViewVisible());
AdvanceClock(kSessionDuration);
EXPECT_FALSE(controller->in_focus_session());
EXPECT_TRUE(controller->in_ending_moment());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
ASSERT_TRUE(focus_mode_tray_->is_active());
EXPECT_FALSE(IsCountdownViewVisible());
EXPECT_TRUE(IsEndingMomentViewVisible());
AdvanceClock(base::Minutes(2));
EXPECT_TRUE(controller->in_ending_moment());
EXPECT_TRUE(focus_mode_tray_->GetVisible());
ASSERT_TRUE(focus_mode_tray_->is_active());
EXPECT_TRUE(IsEndingMomentViewVisible());
}
TEST_F(FocusModeTrayTest, EndingMomentUpdateSessionDuration) {
const base::TimeDelta kStartingDuration =
focus_mode_util::kMaximumDuration -
(2 * focus_mode_util::kExtendDuration);
FocusModeController* controller = FocusModeController::Get();
controller->SetInactiveSessionDuration(kStartingDuration);
controller->ToggleFocusMode();
AdvanceClock(kStartingDuration);
EXPECT_TRUE(focus_mode_tray_->GetVisible());
LeftClickOn(focus_mode_tray_);
EXPECT_TRUE(IsEndingMomentViewVisible());
auto* button = GetEndingMomentExtendTimeButton();
EXPECT_TRUE(button->GetEnabled());
AdvanceClock(base::Minutes(1));
EXPECT_TRUE(focus_mode_tray_->GetBubbleView());
LeftClickOn(button);
EXPECT_TRUE(focus_mode_tray_->GetVisible());
EXPECT_TRUE(focus_mode_tray_->GetBubbleView());
EXPECT_TRUE(IsCountdownViewVisible());
EXPECT_FALSE(IsEndingMomentViewVisible());
EXPECT_EQ(kStartingDuration + focus_mode_util::kExtendDuration,
controller->GetSessionDuration());
EXPECT_EQ(u"10:00", GetCountdownTimeRemainingLabel()->GetText());
AdvanceClock(focus_mode_util::kExtendDuration);
LeftClickOn(button);
AdvanceClock(focus_mode_util::kExtendDuration);
EXPECT_TRUE(focus_mode_tray_->GetVisible());
EXPECT_TRUE(focus_mode_tray_->GetBubbleView());
EXPECT_FALSE(IsCountdownViewVisible());
EXPECT_TRUE(IsEndingMomentViewVisible());
EXPECT_FALSE(button->GetEnabled());
EXPECT_EQ(focus_mode_util::kMaximumDuration,
controller->current_session()->session_duration());
}
TEST_F(FocusModeTrayTest, EndingMomentMultiDisplay) {
UpdateDisplay("800x600,800x600");
FocusModeTray* first_tray = focus_mode_tray_;
FocusModeTray* second_tray =
StatusAreaWidgetTestHelper::GetSecondaryStatusAreaWidget()
->focus_mode_tray();
FocusModeController* controller = FocusModeController::Get();
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_TRUE(first_tray->GetVisible());
EXPECT_TRUE(second_tray->GetVisible());
AdvanceClock(controller->GetSessionDuration());
EXPECT_FALSE(controller->in_focus_session());
EXPECT_TRUE(controller->in_ending_moment());
EXPECT_TRUE(first_tray->GetVisible());
EXPECT_TRUE(second_tray->GetVisible());
LeftClickOn(first_tray);
EXPECT_TRUE(first_tray->is_active());
EXPECT_TRUE(first_tray->GetBubbleView());
EXPECT_FALSE(second_tray->is_active());
EXPECT_FALSE(second_tray->GetBubbleView());
LeftClickOn(second_tray);
EXPECT_TRUE(controller->in_ending_moment());
EXPECT_TRUE(first_tray->GetVisible());
EXPECT_FALSE(first_tray->is_active());
EXPECT_FALSE(first_tray->GetBubbleView());
EXPECT_TRUE(second_tray->GetVisible());
EXPECT_TRUE(second_tray->is_active());
EXPECT_TRUE(second_tray->GetBubbleView());
LeftClickOn(second_tray);
EXPECT_FALSE(controller->in_ending_moment());
EXPECT_FALSE(first_tray->GetVisible());
EXPECT_FALSE(second_tray->GetVisible());
EXPECT_FALSE(second_tray->GetBubbleView());
}
}