chromium/chrome/browser/ui/tabs/existing_window_sub_menu_model_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 "chrome/browser/ui/tabs/existing_window_sub_menu_model.h"

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/test_browser_window.h"
#include "ui/gfx/text_elider.h"

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/public/cpp/autotest_desks_api.h"
#include "chrome/browser/ui/tabs/existing_window_sub_menu_model_chromeos.h"
#include "chrome/browser/ui/tabs/tab_menu_model_delegate.h"
#endif

#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#include "ui/views/widget/widget.h"
#endif

namespace {

#if BUILDFLAG(IS_CHROMEOS_LACROS)
class TestBrowserWindowViewsWithDesktopNativeWidgetAura
    : public TestBrowserWindow {
 public:
  explicit TestBrowserWindowViewsWithDesktopNativeWidgetAura(
      bool popup = false);
  TestBrowserWindowViewsWithDesktopNativeWidgetAura(
      const TestBrowserWindowViewsWithDesktopNativeWidgetAura&) = delete;
  TestBrowserWindowViewsWithDesktopNativeWidgetAura& operator=(
      const TestBrowserWindowViewsWithDesktopNativeWidgetAura&) = delete;
  ~TestBrowserWindowViewsWithDesktopNativeWidgetAura() override;

 private:
  std::unique_ptr<views::Widget> CreateDesktopWidget(bool popup);

  std::unique_ptr<views::Widget> widget_;
};

TestBrowserWindowViewsWithDesktopNativeWidgetAura::
    TestBrowserWindowViewsWithDesktopNativeWidgetAura(bool popup) {
  widget_ = CreateDesktopWidget(popup);
  SetNativeWindow(widget_->GetNativeWindow());
}

TestBrowserWindowViewsWithDesktopNativeWidgetAura::
    ~TestBrowserWindowViewsWithDesktopNativeWidgetAura() = default;

std::unique_ptr<views::Widget>
TestBrowserWindowViewsWithDesktopNativeWidgetAura::CreateDesktopWidget(
    bool popup) {
  auto widget = std::make_unique<views::Widget>();
  views::Widget::InitParams params(
      views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
      popup ? views::Widget::InitParams::TYPE_POPUP
            : views::Widget::InitParams::TYPE_WINDOW);
  params.native_widget = new views::DesktopNativeWidgetAura(widget.get());
  params.bounds = gfx::Rect(0, 0, 20, 20);
  widget->Init(std::move(params));
  return widget;
}
#endif

class ExistingWindowSubMenuModelTest : public BrowserWithTestWindowTest {};

std::unique_ptr<Browser> ExistingWindowSubMenuModelTest::CreateTestBrowser(
    bool incognito,
    bool popup) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
std::unique_ptr<Browser>
ExistingWindowSubMenuModelTest::CreateTestBrowserOnWorkspace(
    std::string desk_index) {
  auto browser = CreateTestBrowser(/*incognito=*/false, /*popup=*/false);
  auto* test_browser_window =
      static_cast<TestBrowserWindow*>(browser->window());
  test_browser_window->set_workspace(desk_index);
  return browser;
}
#endif

void ExistingWindowSubMenuModelTest::AddTabWithTitle(Browser* browser,
                                                     std::string title) {}

// static
void ExistingWindowSubMenuModelTest::CheckBrowserTitle(
    const std::u16string& title,
    const std::string& expected_title,
    const int expected_num_tabs) {}

// Ensure that the move to existing window menu only appears when another window
// of the current profile exists.
TEST_F(ExistingWindowSubMenuModelTest, ShouldShowSubmenu) {}

// Ensure we only show the menu on incognito when at least one other incognito
// window exists.
TEST_F(ExistingWindowSubMenuModelTest, ShouldShowSubmenuIncognito) {}

// Ensure we don't show the menu on a popup window.
TEST_F(ExistingWindowSubMenuModelTest, ShouldShowSubmenuPopup) {}

// Validate that windows appear in MRU order and with the expected labels.
TEST_F(ExistingWindowSubMenuModelTest, BuildSubmenuOrder) {}

// Ensure that normal browsers and incognito browsers have their own lists.
TEST_F(ExistingWindowSubMenuModelTest, BuildSubmenuIncognito) {}

// Ensure that popups don't appear in the list of existing windows.
TEST_F(ExistingWindowSubMenuModelTest, BuildSubmenuPopups) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
// Ensure that when there are multiple desks the browsers are grouped by which
// desk they belong to.
TEST_F(ExistingWindowSubMenuModelTest, BuildSubmenuGroupedByDesks) {
  const std::string kBrowser2TabTitle("Browser 2 Tab 1");
  const std::string kBrowser3TabTitle("Browser 3 Tab 1");
  const std::string kBrowser4TabTitle("Browser 4 Tab 1");
  const std::string kBrowser5TabTitle("Browser 5 Tab 1");
  const std::string kBrowser6TabTitle("Browser 6 Tab 1");
  const std::string kBrowser7TabTitle("Browser 7 Tab 1");

  // Create 4 desks so we have 5 in total.
  ash::AutotestDesksApi().CreateNewDesk();
  ash::AutotestDesksApi().CreateNewDesk();
  ash::AutotestDesksApi().CreateNewDesk();
  ash::AutotestDesksApi().CreateNewDesk();

  // Add some browsers and put them in each desk.
  BrowserList::SetLastActive(browser());
  std::unique_ptr<Browser> browser_2(CreateTestBrowserOnWorkspace("0"));
  std::unique_ptr<Browser> browser_3(CreateTestBrowserOnWorkspace("1"));
  std::unique_ptr<Browser> browser_4(CreateTestBrowserOnWorkspace("1"));
  std::unique_ptr<Browser> browser_5(CreateTestBrowserOnWorkspace("2"));
  std::unique_ptr<Browser> browser_6(CreateTestBrowserOnWorkspace("2"));
  std::unique_ptr<Browser> browser_7(CreateTestBrowserOnWorkspace("3"));

  // Add tabs.
  AddTabWithTitle(browser_2.get(), kBrowser2TabTitle);
  AddTabWithTitle(browser_3.get(), kBrowser3TabTitle);
  AddTabWithTitle(browser_4.get(), kBrowser4TabTitle);
  AddTabWithTitle(browser_5.get(), kBrowser5TabTitle);
  AddTabWithTitle(browser_6.get(), kBrowser6TabTitle);
  AddTabWithTitle(browser_7.get(), kBrowser7TabTitle);

  // Scramble their MRU order by activating them. The MRU order should be:
  // [b7, b5, b4, b2, b3, b6] (left-most is MRU).
  BrowserList::SetLastActive(browser_6.get());
  BrowserList::SetLastActive(browser_3.get());
  BrowserList::SetLastActive(browser_2.get());
  BrowserList::SetLastActive(browser_4.get());
  BrowserList::SetLastActive(browser_5.get());
  BrowserList::SetLastActive(browser_7.get());

  const std::vector<Browser*> kExpectedMRUOrder{
      browser_7.get(), browser_5.get(), browser_4.get(),
      browser_2.get(), browser_3.get(), browser_6.get()};
  const auto& mru_ordered_windows =
      browser()->tab_menu_model_delegate()->GetOtherBrowserWindows(
          /*is_app=*/false);
  ASSERT_EQ(6u, mru_ordered_windows.size());
  ASSERT_EQ(mru_ordered_windows, kExpectedMRUOrder);

  // Create the menu from browser 1. The labels should be grouped by desk and
  // respect MRU order within each desk grouping. Also a label shouldn't be made
  // for the 5th desk since no browsers are in it.
  auto menu1 = ExistingWindowSubMenuModel::Create(
      nullptr, browser()->tab_menu_model_delegate(),
      browser()->tab_strip_model(), 0);
  ASSERT_EQ(15u, menu1->GetItemCount());
  EXPECT_EQ(u"Desk 1 (Current)", menu1->GetLabelAt(2));
  CheckBrowserTitle(menu1->GetLabelAt(3), kBrowser2TabTitle, 1);
  EXPECT_EQ(ui::SPACING_SEPARATOR, menu1->GetSeparatorTypeAt(4));
  EXPECT_EQ(u"Desk 2", menu1->GetLabelAt(5));
  CheckBrowserTitle(menu1->GetLabelAt(6), kBrowser4TabTitle, 1);
  CheckBrowserTitle(menu1->GetLabelAt(7), kBrowser3TabTitle, 1);
  EXPECT_EQ(ui::SPACING_SEPARATOR, menu1->GetSeparatorTypeAt(8));
  EXPECT_EQ(u"Desk 3", menu1->GetLabelAt(9));
  CheckBrowserTitle(menu1->GetLabelAt(10), kBrowser5TabTitle, 1);
  CheckBrowserTitle(menu1->GetLabelAt(11), kBrowser6TabTitle, 1);
  EXPECT_EQ(ui::SPACING_SEPARATOR, menu1->GetSeparatorTypeAt(12));
  EXPECT_EQ(u"Desk 4", menu1->GetLabelAt(13));
  CheckBrowserTitle(menu1->GetLabelAt(14), kBrowser7TabTitle, 1);

  // Clean up.
  chrome::CloseTab(browser_2.get());
  chrome::CloseTab(browser_3.get());
  chrome::CloseTab(browser_4.get());
  chrome::CloseTab(browser_5.get());
  chrome::CloseTab(browser_6.get());
  chrome::CloseTab(browser_7.get());
}

// Tests out that executing the commands in the submenu grouped by desks work
// properly.
TEST_F(ExistingWindowSubMenuModelTest, EnsureGroupedByDesksCommands) {
  // Create 2 desks so we have 3 in total.
  ash::AutotestDesksApi().CreateNewDesk();
  ash::AutotestDesksApi().CreateNewDesk();

  // Add some browsers and put them in desks.
  std::unique_ptr<Browser> browser_2(CreateTestBrowserOnWorkspace("0"));
  std::unique_ptr<Browser> browser_3(CreateTestBrowserOnWorkspace("1"));
  std::unique_ptr<Browser> browser_4(CreateTestBrowserOnWorkspace("1"));
  std::unique_ptr<Browser> browser_5(CreateTestBrowserOnWorkspace("2"));

  // Scramble the MRU order by activating them. The MRU order should be:
  // [b4, b2, b3, b5] (left-most is MRU).
  BrowserList::SetLastActive(browser_5.get());
  BrowserList::SetLastActive(browser_3.get());
  BrowserList::SetLastActive(browser_2.get());
  BrowserList::SetLastActive(browser_4.get());

  const std::vector<Browser*> kExpectedMRUOrder{
      browser_4.get(), browser_2.get(), browser_3.get(), browser_5.get()};
  const auto& mru_ordered_windows =
      browser()->tab_menu_model_delegate()->GetOtherBrowserWindows(
          /*is_app=*/false);
  ASSERT_EQ(4u, mru_ordered_windows.size());
  ASSERT_EQ(mru_ordered_windows, kExpectedMRUOrder);

  // Create the menu from browser 1 and ensure that the command indexes properly
  // map to their browser indices.
  auto menu1 = ExistingWindowSubMenuModel::Create(
      nullptr, browser()->tab_menu_model_delegate(),
      browser()->tab_strip_model(), 0);
  const auto& command_id_to_target_index =
      static_cast<chromeos::ExistingWindowSubMenuModelChromeOS*>(menu1.get())
          ->command_id_to_target_index_for_testing();

  // A vector of the expected mappings. The first element of each pair is the
  // commdand id. The second element of each pair is the browser index.
  const std::vector<std::pair<int, int>> kExpectedMappings{
      {1002, 1}, {1003, 0}, {1004, 2}, {1005, 3}};
  for (const auto& pair : kExpectedMappings) {
    EXPECT_EQ(pair.second,
              static_cast<int>(command_id_to_target_index.at(pair.first)));
  }

  // Clean up.
  chrome::CloseTab(browser_2.get());
  chrome::CloseTab(browser_3.get());
  chrome::CloseTab(browser_4.get());
  chrome::CloseTab(browser_5.get());
}
#endif

}  // namespace