chromium/ash/system/media/media_tray_unittest.cc

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

#include "ash/system/media/media_tray.h"

#include "ash/constants/tray_background_view_catalog.h"
#include "ash/shell.h"
#include "ash/system/media/media_notification_provider.h"
#include "ash/system/media/mock_media_notification_provider.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/status_area_widget_test_helper.h"
#include "ash/system/tray/tray_bubble_wrapper.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/raw_ptr.h"
#include "components/media_message_center/media_notification_view_impl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/events/event.h"

using ::testing::_;

namespace ash {

class MediaTrayTest : public AshTestBase {
 public:
  MediaTrayTest() = default;
  ~MediaTrayTest() override = default;

  void SetUp() override {
    AshTestBase::SetUp();
    provider_ = std::make_unique<MockMediaNotificationProvider>();
    media_tray_ = status_area_widget()->media_tray();
    ASSERT_TRUE(MediaTray::IsPinnedToShelf());
  }

  void TearDown() override {
    provider_.reset();
    AshTestBase::TearDown();
  }

  void SimulateNotificationListChanged() {
    media_tray_->OnNotificationListChanged();
  }

  void SimulateTapOnPinButton() {
    ASSERT_TRUE(media_tray_->pin_button_for_testing());
    ui::test::EventGenerator* generator = GetEventGenerator();
    generator->MoveMouseTo(media_tray_->pin_button_for_testing()
                               ->GetBoundsInScreen()
                               .CenterPoint());
    generator->ClickLeftButton();
  }

  TrayBubbleWrapper* GetBubbleWrapper() {
    return media_tray_->tray_bubble_wrapper_for_testing();
  }

  StatusAreaWidget* status_area_widget() {
    return StatusAreaWidgetTestHelper::GetStatusAreaWidget();
  }

  MockMediaNotificationProvider* provider() { return provider_.get(); }

  MediaTray* media_tray() { return media_tray_; }

  views::View* empty_state_view() { return media_tray_->empty_state_view_; }

 private:
  std::unique_ptr<MockMediaNotificationProvider> provider_;
  raw_ptr<MediaTray, DanglingUntriaged> media_tray_;
};

TEST_F(MediaTrayTest, MediaTrayVisibilityTest) {
  // Media tray should be invisible initially.
  ASSERT_TRUE(media_tray());
  EXPECT_FALSE(media_tray()->GetVisible());

  // Media tray should be visible when there is active notification.
  provider()->SetHasActiveNotifications(true);
  SimulateNotificationListChanged();
  EXPECT_TRUE(media_tray()->GetVisible());

  // Media tray should hide itself when no media is playing.
  provider()->SetHasActiveNotifications(false);
  SimulateNotificationListChanged();
  EXPECT_FALSE(media_tray()->GetVisible());

  // Media tray should be visible when there is frozen notification.
  provider()->SetHasFrozenNotifications(true);
  SimulateNotificationListChanged();
  EXPECT_TRUE(media_tray()->GetVisible());

  // Media tray should be hidden when screen is locked.
  GetSessionControllerClient()->LockScreen();
  GetSessionControllerClient()->FlushForTest();
  EXPECT_FALSE(media_tray()->GetVisible());

  // Media tray should be visible again when we unlock the screen.
  GetSessionControllerClient()->UnlockScreen();
  EXPECT_TRUE(media_tray()->GetVisible());

  // Media tray should not be visible if global media controls is
  // not pinned to shelf.
  MediaTray::SetPinnedToShelf(false);
  EXPECT_FALSE(media_tray()->GetVisible());
}

TEST_F(MediaTrayTest, ShowAndHideBubbleTest) {
  // Media tray should be visible when there is active notification.
  provider()->SetHasActiveNotifications(true);
  SimulateNotificationListChanged();
  EXPECT_TRUE(media_tray()->GetVisible());

  // Bubble should not exist initially, and media tray should not
  // be active.
  EXPECT_EQ(GetBubbleWrapper(), nullptr);
  EXPECT_FALSE(media_tray()->is_active());

  // Tap the media tray should show the bubble, and media tray should
  // be active. GetMediaNotificationlistview also should be called for
  // getting active notifications.
  EXPECT_CALL(*provider(), GetMediaNotificationListView(
                               _, /*should_clip_height=*/true, _, _));
  GestureTapOn(media_tray());
  EXPECT_NE(GetBubbleWrapper(), nullptr);
  EXPECT_TRUE(media_tray()->is_active());

  // Tap again should close the bubble and MediaNotificationProvider should
  // be notified.
  EXPECT_CALL(*provider(), OnBubbleClosing());
  GestureTapOn(media_tray());
  EXPECT_EQ(GetBubbleWrapper(), nullptr);
  EXPECT_FALSE(media_tray()->is_active());
}

// Tests that the shelf is forced to show when the bubble is visible (this is so
// that the shelf doesn't hide when shelf auto-hide is enabled).
TEST_F(MediaTrayTest, OpenBubbleForcesShelfToShow) {
  provider()->SetHasActiveNotifications(true);
  SimulateNotificationListChanged();
  ASSERT_TRUE(media_tray()->GetVisible());

  // The shelf should not be forced to show initially.
  EXPECT_FALSE(status_area_widget()->ShouldShowShelf());

  // Open the media tray bubble and verify that the shelf is forced to show.
  GestureTapOn(media_tray());
  ;
  EXPECT_TRUE(status_area_widget()->ShouldShowShelf());
}

TEST_F(MediaTrayTest, ShowEmptyStateWhenNoActiveNotification) {
  // Media tray should be visible when there is active notification.
  provider()->SetHasActiveNotifications(true);
  SimulateNotificationListChanged();
  EXPECT_TRUE(media_tray()->GetVisible());

  // Bubble should not exist initially, and media tray should not
  // be active.
  EXPECT_EQ(GetBubbleWrapper(), nullptr);
  EXPECT_FALSE(media_tray()->is_active());

  // Tap and show bubble.
  GestureTapOn(media_tray());
  EXPECT_NE(GetBubbleWrapper(), nullptr);
  EXPECT_TRUE(media_tray()->is_active());

  // We should display empty state if no media is playing.
  provider()->SetHasActiveNotifications(false);
  SimulateNotificationListChanged();
  EXPECT_NE(GetBubbleWrapper(), nullptr);
  EXPECT_TRUE(media_tray()->GetVisible());
  EXPECT_NE(empty_state_view(), nullptr);
  EXPECT_TRUE(empty_state_view()->GetVisible());

  // Empty state should be hidden if a new media starts playing.
  provider()->SetHasActiveNotifications(true);
  SimulateNotificationListChanged();
  EXPECT_FALSE(empty_state_view()->GetVisible());
}

TEST_F(MediaTrayTest, PinButtonTest) {
  // Media tray should be invisible initially.
  ASSERT_TRUE(media_tray());
  EXPECT_FALSE(media_tray()->GetVisible());

  // Open global media controls dialog.
  provider()->SetHasActiveNotifications(true);
  SimulateNotificationListChanged();
  EXPECT_TRUE(media_tray()->GetVisible());
  GestureTapOn(media_tray());
  EXPECT_NE(GetBubbleWrapper(), nullptr);

  // Tapping the pin button while the media controls dialog is opened should not
  // hide the media tray.
  SimulateTapOnPinButton();
  EXPECT_NE(GetBubbleWrapper(), nullptr);
  EXPECT_TRUE(media_tray()->GetVisible());
  EXPECT_FALSE(MediaTray::IsPinnedToShelf());

  // Tap the pin button again and the media tray should still be visible.
  SimulateTapOnPinButton();
  EXPECT_TRUE(media_tray()->GetVisible());
  EXPECT_TRUE(MediaTray::IsPinnedToShelf());
}

TEST_F(MediaTrayTest, PinToShelfDefaultBehavior) {
  // Media controls should be pinned on shelf by default on a
  // 10 inch display.
  UpdateDisplay("800x530");
  EXPECT_FALSE(MediaTray::IsPinnedToShelf());

  // Media controls should be pinned on shelf by default on a
  // display larger than 10 inches.
  UpdateDisplay("800x600");
  EXPECT_TRUE(MediaTray::IsPinnedToShelf());
}

TEST_F(MediaTrayTest, BubbleGetsFocusWhenOpenWithKeyboard) {
  media_tray()->ShowBubble();

  // Generate a tab key press.
  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
  generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EF_NONE);

  EXPECT_TRUE(GetBubbleWrapper()->GetBubbleWidget()->IsActive());
}

TEST_F(MediaTrayTest, ShowBubble) {
  // We start with no bubble view.
  EXPECT_EQ(nullptr, media_tray()->GetBubbleView());

  EXPECT_CALL(*provider(), GetMediaNotificationListView(_, _, _, ""));
  media_tray()->ShowBubble();
  EXPECT_NE(nullptr, media_tray()->GetBubbleView());
}

TEST_F(MediaTrayTest, ShowBubbleWithItem) {
  // We start with no bubble view.
  EXPECT_EQ(nullptr, media_tray()->GetBubbleView());

  const std::string item_id = "my-item-id";
  EXPECT_CALL(*provider(), GetMediaNotificationListView(_, _, _, item_id));
  media_tray()->ShowBubbleWithItem(item_id);
  EXPECT_NE(nullptr, media_tray()->GetBubbleView());
}

TEST_F(MediaTrayTest, CloseBubbleIsNoopWhenNoBubble) {
  // Start out with no bubble.
  ASSERT_EQ(nullptr, media_tray()->GetBubbleView());

  // `OnBubbleClosing()` should not be called when there is no bubble to close.
  EXPECT_CALL(*provider(), OnBubbleClosing).Times(0);
  media_tray()->CloseBubble();
}

}  // namespace ash