chromium/components/global_media_controls/public/views/media_item_ui_detailed_view_unittest.cc

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

#include "components/global_media_controls/public/views/media_item_ui_detailed_view.h"

#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "components/global_media_controls/public/test/mock_media_item_ui_device_selector.h"
#include "components/global_media_controls/public/test/mock_media_item_ui_footer.h"
#include "components/global_media_controls/public/views/media_progress_view.h"
#include "components/media_message_center/media_notification_container.h"
#include "components/media_message_center/mock_media_notification_item.h"
#include "components/strings/grit/components_strings.h"
#include "media/base/media_switches.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/label.h"
#include "ui/views/test/button_test_api.h"
#include "ui/views/test/widget_test.h"

namespace global_media_controls {

MockMediaItemUIDeviceSelector;
MockMediaItemUIFooter;
MockMediaNotificationItem;
MediaSessionAction;
_;
NiceMock;
Return;

namespace {

class MockMediaNotificationContainer
    : public media_message_center::MediaNotificationContainer {};

}  // namespace

class MediaItemUIDetailedViewTest : public views::ViewsTestBase {};

TEST_F(MediaItemUIDetailedViewTest, ChevronIconVisibilityCheck) {}

TEST_F(MediaItemUIDetailedViewTest, AccessibleProperties) {}

TEST_F(MediaItemUIDetailedViewTest, DeviceSelectorViewCheck) {}

TEST_F(MediaItemUIDetailedViewTest, FooterViewCheck) {}

TEST_F(MediaItemUIDetailedViewTest, MetadataUpdated) {}

TEST_F(MediaItemUIDetailedViewTest, PlayPauseButtonDisplay) {}

TEST_F(MediaItemUIDetailedViewTest, PictureInPictureButtonDisplay) {}

TEST_F(MediaItemUIDetailedViewTest, ButtonVisibilityCheck) {}

TEST_F(MediaItemUIDetailedViewTest, NextTrackButtonClick) {}

TEST_F(MediaItemUIDetailedViewTest, PlayButtonClick) {}

TEST_F(MediaItemUIDetailedViewTest, PauseButtonClick) {}

TEST_F(MediaItemUIDetailedViewTest, PreviousTrackButtonClick) {}

TEST_F(MediaItemUIDetailedViewTest, EnterPictureInPictureButtonClick) {}

TEST_F(MediaItemUIDetailedViewTest, ExitPictureInPictureButtonClick) {}

TEST_F(MediaItemUIDetailedViewTest, ProgressViewCheck) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(MediaItemUIDetailedViewTest, ChapterList) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(media::kBackgroundListening);
  auto widget = CreateTestWidget(views::Widget::InitParams::CLIENT_OWNS_WIDGET);
  auto* view = widget->SetContentsView(
      CreateView(MediaDisplayPage::kSystemShelfMediaDetailedView));

  // Chapter list is not created yet.
  EXPECT_EQ(view->GetTitleLabelForTesting()->GetText(), u"");
  EXPECT_FALSE(!!view->GetChapterListViewForTesting());
  EXPECT_EQ(view->GetChaptersForTesting().find(0),
            view->GetChaptersForTesting().end());
  EXPECT_FALSE(view->GetChapterListButtonForTesting()->GetVisible());

  std::vector<media_session::ChapterInformation> expected_chapters;
  media_session::MediaImage test_image_1;
  test_image_1.src = GURL("https://www.google.com");
  media_session::MediaImage test_image_2;
  test_image_2.src = GURL("https://www.example.org");
  media_session::ChapterInformation test_chapter_1(
      /*title=*/u"chapter1", /*startTime=*/base::Seconds(10),
      /*artwork=*/{test_image_1});
  media_session::ChapterInformation test_chapter_2(
      /*title=*/u"chapter2", /*startTime=*/base::Seconds(20),
      /*artwork=*/{test_image_2});
  expected_chapters.push_back(test_chapter_1);
  expected_chapters.push_back(test_chapter_2);

  media_session::MediaMetadata metadata;
  metadata.source_title = u"source title";
  metadata.title = u"title";
  metadata.artist = u"artist";
  metadata.chapters = expected_chapters;

  EXPECT_CALL(container(), OnMediaSessionMetadataChanged(_));
  view->UpdateWithMediaMetadata(metadata);

  // Before clicking on the chapter list button, the list is not visible.
  EXPECT_TRUE(view->GetChapterListButtonForTesting()->GetVisible());
  EXPECT_FALSE(view->GetChapterListViewForTesting()->GetVisible());

  // Click the start chapter list button to show the chapters.
  views::test::ButtonTestApi(view->GetChapterListButtonForTesting())
      .NotifyClick(ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(),
                                  gfx::Point(), ui::EventTimeForNow(), 0, 0));
  EXPECT_TRUE(view->GetChapterListViewForTesting()->GetVisible());
  EXPECT_EQ(view->GetTitleLabelForTesting()->GetText(), metadata.title);
  EXPECT_EQ(view->GetChapterListViewForTesting()->children().size(), 2u);
  EXPECT_EQ(view->GetChaptersForTesting().find(0)->second->chapter().title(),
            u"chapter1");
  EXPECT_EQ(view->GetChaptersForTesting().find(1)->second->chapter().title(),
            u"chapter2");
  EXPECT_EQ(view->GetChaptersForTesting()
                .find(0)
                ->second->chapter()
                .startTime()
                .InSeconds(),
            10);
  EXPECT_EQ(view->GetChaptersForTesting()
                .find(1)
                ->second->chapter()
                .startTime()
                .InSeconds(),
            20);

  // Clicking on a chapter item should seek to the start time of that chapter.
  EXPECT_CALL(item(), SeekTo(base::Seconds(10)));
  views::test::ButtonTestApi(view->GetChaptersForTesting().find(0)->second)
      .NotifyClick(ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(),
                                  gfx::Point(), ui::EventTimeForNow(), 0, 0));
  testing::Mock::VerifyAndClearExpectations(this);

  EXPECT_CALL(item(), SeekTo(base::Seconds(20)));
  views::test::ButtonTestApi(view->GetChaptersForTesting().find(1)->second)
      .NotifyClick(ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(),
                                  gfx::Point(), ui::EventTimeForNow(), 0, 0));
  testing::Mock::VerifyAndClearExpectations(this);

  // Showing cast view should hide the chapter list view.
  view->UpdateDeviceSelectorAvailability(/*has_devices=*/true);
  EXPECT_TRUE(view->GetStartCastingButtonForTesting()->GetVisible());
  views::test::ButtonTestApi(view->GetStartCastingButtonForTesting())
      .NotifyClick(ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(),
                                  gfx::Point(), ui::EventTimeForNow(), 0, 0));
  EXPECT_TRUE(view->GetChapterListButtonForTesting()->GetVisible());
  EXPECT_FALSE(view->GetChapterListViewForTesting()->GetVisible());

  // Showing the chapter list view should also hide the cast view.
  views::test::ButtonTestApi(view->GetChapterListButtonForTesting())
      .NotifyClick(ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(),
                                  gfx::Point(), ui::EventTimeForNow(), 0, 0));
  EXPECT_FALSE(view->GetDeviceSelectorForTesting()->IsDeviceSelectorExpanded());
  EXPECT_TRUE(view->GetChapterListViewForTesting()->GetVisible());
  EXPECT_EQ(view->GetChapterListViewForTesting()->children().size(), 2u);

  // Should not show chapter list button when there's no chapters.
  media_session::MediaMetadata metadata_with_0_chpaters;
  metadata_with_0_chpaters.source_title = u"source title 0";
  metadata_with_0_chpaters.title = u"title 0";
  metadata_with_0_chpaters.artist = u"artist 0";

  EXPECT_CALL(container(), OnMediaSessionMetadataChanged(_));
  view->UpdateWithMediaMetadata(metadata_with_0_chpaters);
  EXPECT_FALSE(view->GetChapterListButtonForTesting()->GetVisible());
  EXPECT_FALSE(view->GetChapterListViewForTesting()->GetVisible());
  EXPECT_EQ(view->GetTitleLabelForTesting()->GetText(),
            metadata_with_0_chpaters.title);
  EXPECT_EQ(view->GetChapterListViewForTesting()->children().size(), 0u);
}

TEST_F(MediaItemUIDetailedViewTest, ShouldNotShowDeviceSelectorViewForAsh) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(media::kBackgroundListening);
  auto* start_casting_button = view()->GetStartCastingButtonForTesting();
  auto* separator = view()->GetDeviceSelectorSeparatorForTesting();
  auto* device_selector_view = view()->GetDeviceSelectorForTesting();

  ASSERT_TRUE(start_casting_button);
  EXPECT_FALSE(start_casting_button->GetVisible());
  EXPECT_EQ(device_selector_view, device_selector());
  EXPECT_FALSE(device_selector_view->GetVisible());
  ASSERT_TRUE(separator);
  EXPECT_FALSE(separator->GetVisible());

  // Add devices to the list to show the start casting button.
  EXPECT_CALL(*device_selector(), IsDeviceSelectorExpanded())
      .WillOnce(Return(false));
  view()->UpdateDeviceSelectorAvailability(/*has_devices=*/true);
  EXPECT_TRUE(start_casting_button->GetVisible());
  EXPECT_TRUE(device_selector_view->GetVisible());
  EXPECT_FALSE(separator->GetVisible());

  // Click the start casting button to show devices.
  EXPECT_CALL(*device_selector(), ShowDevices());
  EXPECT_CALL(*device_selector(), IsDeviceSelectorExpanded())
      .WillOnce(Return(false))
      .WillOnce(Return(true));
  views::test::ButtonTestApi(start_casting_button)
      .NotifyClick(ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(),
                                  gfx::Point(), ui::EventTimeForNow(), 0, 0));
  EXPECT_FALSE(separator->GetVisible());

  // Click the start casting button to hide devices.
  EXPECT_CALL(*device_selector(), HideDevices());
  EXPECT_CALL(*device_selector(), IsDeviceSelectorExpanded())
      .WillOnce(Return(true))
      .WillOnce(Return(false));
  views::test::ButtonTestApi(start_casting_button)
      .NotifyClick(ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(),
                                  gfx::Point(), ui::EventTimeForNow(), 0, 0));
  EXPECT_FALSE(separator->GetVisible());
}

TEST_F(MediaItemUIDetailedViewTest, Forward10ButtonClick) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(media::kBackgroundListening);
  auto widget = CreateTestWidget(views::Widget::InitParams::CLIENT_OWNS_WIDGET);
  auto* view = widget->SetContentsView(
      CreateView(MediaDisplayPage::kSystemShelfMediaDetailedView));
  view->UpdateWithMediaActions({MediaSessionAction::kSeekForward});
  media_session::MediaPosition media_position(
      /*playback_rate=*/1, /*duration=*/base::Seconds(58),
      /*position=*/base::Seconds(5), /*end_of_media=*/false);
  view->UpdateWithMediaPosition(media_position);

  EXPECT_CALL(item(), OnMediaSessionActionButtonPressed(
                          MediaSessionAction::kSeekForward))
      .Times(0);
  EXPECT_CALL(item(),
              SeekTo(::testing::AllOf(::testing::Ge(base::Seconds(15)),
                                      ::testing::Le(base::Seconds(16)))));
  views::Button* button =
      view->GetActionButtonForTesting(MediaSessionAction::kSeekForward);
  EXPECT_TRUE(button && button->GetVisible());
  views::test::ButtonTestApi(button).NotifyClick(
      ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(), gfx::Point(),
                     ui::EventTimeForNow(), 0, 0));
}

TEST_F(MediaItemUIDetailedViewTest, Backward10ButtonClick) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(media::kBackgroundListening);
  auto widget = CreateTestWidget(views::Widget::InitParams::CLIENT_OWNS_WIDGET);
  auto* view = widget->SetContentsView(
      CreateView(MediaDisplayPage::kSystemShelfMediaDetailedView));
  view->UpdateWithMediaActions({MediaSessionAction::kSeekBackward});
  media_session::MediaPosition media_position(
      /*playback_rate=*/1, /*duration=*/base::Seconds(58),
      /*position=*/base::Seconds(38), /*end_of_media=*/false);
  view->UpdateWithMediaPosition(media_position);

  EXPECT_CALL(item(), OnMediaSessionActionButtonPressed(
                          MediaSessionAction::kSeekBackward))
      .Times(0);
  EXPECT_CALL(item(),
              SeekTo(::testing::AllOf(::testing::Ge(base::Seconds(28)),
                                      ::testing::Le(base::Seconds(29)))));
  views::Button* button =
      view->GetActionButtonForTesting(MediaSessionAction::kSeekBackward);
  EXPECT_TRUE(button && button->GetVisible());
  views::test::ButtonTestApi(button).NotifyClick(
      ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(), gfx::Point(),
                     ui::EventTimeForNow(), 0, 0));
  testing::Mock::VerifyAndClearExpectations(this);
}

TEST_F(MediaItemUIDetailedViewTest, TimestampView) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(media::kBackgroundListening);
  auto view = CreateView(MediaDisplayPage::kSystemShelfMediaDetailedView);
  EXPECT_NE(view->GetProgressViewForTesting(), nullptr);

  // Check that the timestamp gets updated when the progress position is
  // updated.
  media_session::MediaPosition media_position(
      /*playback_rate=*/1, /*duration=*/base::Seconds(58),
      /*position=*/base::Seconds(5), /*end_of_media=*/false);
  view->UpdateWithMediaPosition(media_position);
  EXPECT_EQ(view->GetCurrentTimestampViewForTesting()->GetText(), u"0:05");
  EXPECT_EQ(view->GetTotalDurationViewForTesting()->GetText(), u" / 0:58");

  media_session::MediaPosition media_position2(
      /*playback_rate=*/1, /*duration=*/base::Seconds(108),
      /*position=*/base::Seconds(66), /*end_of_media=*/false);
  view->UpdateWithMediaPosition(media_position2);
  EXPECT_EQ(view->GetCurrentTimestampViewForTesting()->GetText(), u"1:06");
  EXPECT_EQ(view->GetTotalDurationViewForTesting()->GetText(), u" / 1:48");
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

}  // namespace global_media_controls