// Copyright 2019 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/views/tabs/tab_strip.h"

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

#include "base/byte_size.h"
#include "base/strings/string_util.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/ui/browser_command_controller.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_ui_controller/browser_ui_controller.h"
#include "chrome/browser/ui/performance_controls/tab_resource_usage_tab_helper.h"
#include "chrome/browser/ui/recently_audible_helper.h"
#include "chrome/browser/ui/tabs/features.h"
#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
#include "chrome/browser/ui/tabs/split_tab_metrics.h"
#include "chrome/browser/ui/tabs/tab_data.h"
#include "chrome/browser/ui/tabs/tab_group_attention_indicator.h"
#include "chrome/browser/ui/tabs/tab_group_features.h"
#include "chrome/browser/ui/tabs/tab_group_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.h"
#include "chrome/browser/ui/window_metadata/window_metadata_controller.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/data_sharing/public/features.h"
#include "components/prefs/pref_service.h"
#include "components/tab_groups/tab_group_id.h"
#include "components/tabs/public/tab_group.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/test/browser_test.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/text/bytes_formatting.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event_constants.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/test/ax_event_counter.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ash/boca/on_task/on_task_locked_controller.h"
#endif

namespace {
ui::MouseEvent GetDummyEvent() {
  return ui::MouseEvent(ui::EventType::kMousePressed, gfx::PointF(),
                        gfx::PointF(), base::TimeTicks::Now(), 0, 0);
}
}  // namespace

// Integration tests for interactions between TabStripModel and TabStrip.
class TabStripBrowsertest : public InProcessBrowserTest {
 public:
  TabStripBrowsertest() {
    // The TabStrip is not used in Vertical Tabs. Ensure this suite is not run
    // which would end up testing behavior that is not part of the browser.
    feature_list_.InitWithFeatures(
        {}, {tabs::kTabStripUnification, features::kTabGroupHoverCards});
  }

  TabStripModel* tab_strip_model() { return browser()->GetTabStripModel(); }

  TabStrip* tab_strip() {
    return views::AsViewClass<HorizontalTabStripRegionView>(
               BrowserView::GetBrowserViewForBrowser(browser())
                   ->tab_strip_view())
        ->tab_strip();
  }

  void AppendTab() { chrome::AddTabAt(browser(), GURL(), -1, true); }

  tab_groups::TabGroupId AddTabToNewGroup(int tab_index) {
    return tab_strip_model()->AddToNewGroup({tab_index});
  }

  void AddTabToExistingGroup(int tab_index, tab_groups::TabGroupId group) {
    ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

    tab_strip_model()->AddToExistingGroup({tab_index}, group);
  }

  std::vector<content::WebContents*> GetWebContentses() {
    std::vector<content::WebContents*> contentses;
    for (int i = 0; i < tab_strip()->GetTabCount(); ++i) {
      contentses.push_back(tab_strip_model()->GetWebContentsAt(i));
    }
    return contentses;
  }

  std::vector<content::WebContents*> GetWebContentsesInOrder(
      const std::vector<int>& order) {
    std::vector<content::WebContents*> contentses;
    for (int i = 0; i < tab_strip()->GetTabCount(); ++i) {
      contentses.push_back(tab_strip_model()->GetWebContentsAt(order[i]));
    }
    return contentses;
  }

  std::u16string GetCollapsedState(tab_groups::TabGroupId group) {
    std::u16string collapsed_state = std::u16string();

#if !BUILDFLAG(IS_WIN)
    collapsed_state =
        tab_strip()->IsGroupCollapsed(group)
            ? l10n_util::GetStringUTF16(IDS_GROUP_AX_LABEL_COLLAPSED)
            : l10n_util::GetStringUTF16(IDS_GROUP_AX_LABEL_EXPANDED);
#endif

    return collapsed_state;
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

// Regression test for crbug.com/40636026.
IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabAndDeleteGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  AddTabToNewGroup(2);

  Tab* tab0 = tab_strip()->tab_at(0);
  Tab* tab1 = tab_strip()->tab_at(1);
  Tab* tab2 = tab_strip()->tab_at(2);

  tab_strip_model()->AddToExistingGroup({2}, group);

  EXPECT_EQ(tab0, tab_strip()->tab_at(0));
  EXPECT_EQ(tab2, tab_strip()->tab_at(1));
  EXPECT_EQ(tab1, tab_strip()->tab_at(2));

  EXPECT_EQ(group, tab_strip_model()->GetTabGroupForTab(1));

  std::vector<tab_groups::TabGroupId> groups =
      tab_strip_model()->group_model()->ListTabGroups();
  EXPECT_EQ(groups.size(), 1U);
  EXPECT_EQ(groups[0], group);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, DetachAndReInsertGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = tab_strip_model()->AddToNewGroup({0, 1});

  std::unique_ptr<DetachedTabCollection> detached_group =
      tab_strip_model()->DetachTabGroupForInsertion(group);

  EXPECT_EQ(tab_strip()->GetTabCount(), 1);
  EXPECT_EQ(tab_strip()->tab_at(0)->group(), std::nullopt);

  tab_strip_model()->InsertDetachedTabGroupAt(std::move(detached_group), 1);

  EXPECT_EQ(tab_strip()->GetTabCount(), 3);
  EXPECT_EQ(tab_strip()->tab_at(0)->group(), std::nullopt);
  EXPECT_EQ(tab_strip()->tab_at(1)->group(), group);
  EXPECT_EQ(tab_strip()->tab_at(2)->group(), group);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftTabPrevious_Success) {
  AppendTab();
  AppendTab();

  const auto expected = GetWebContentsesInOrder({1, 0, 2});
  tab_strip()->ShiftTabPrevious(tab_strip()->tab_at(1));
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftTabPrevious_AddsToGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);

  // Instead of moving, the tab should be added to the group.
  const auto expected = GetWebContentsesInOrder({0, 1, 2});
  tab_strip()->ShiftTabPrevious(tab_strip()->tab_at(2));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_EQ(tab_strip()->tab_at(2)->group().value(), group);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftTabPrevious_PastCollapsedGroup_Success) {
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  AddTabToExistingGroup(1, group);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group));

  const auto expected = GetWebContentsesInOrder({2, 0, 1});
  tab_strip()->ShiftTabPrevious(tab_strip()->tab_at(2));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group));
  EXPECT_EQ(tab_strip()->tab_at(0)->group(), std::nullopt);
  EXPECT_EQ(tab_strip()->tab_at(1)->group().value(), group);
  EXPECT_EQ(tab_strip()->tab_at(2)->group().value(), group);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftTabPrevious_BetweenTwoCollapsedGroups_Success) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());
  AppendTab();
  AppendTab();
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group1 = AddTabToNewGroup(0);
  AddTabToExistingGroup(1, group1);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group1));
  tab_strip()->ToggleTabGroupCollapsedState(group1);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group1));

  tab_groups::TabGroupId group2 = AddTabToNewGroup(2);
  AddTabToExistingGroup(3, group2);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group2));
  tab_strip()->ToggleTabGroupCollapsedState(group2);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group2));

  const auto expected = GetWebContentsesInOrder({0, 1, 4, 2, 3});
  tab_strip()->ShiftTabPrevious(tab_strip()->tab_at(4));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group1));
  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group2));
  EXPECT_EQ(tab_strip()->tab_at(0)->group().value(), group1);
  EXPECT_EQ(tab_strip()->tab_at(1)->group().value(), group1);
  EXPECT_EQ(tab_strip()->tab_at(2)->group(), std::nullopt);
  EXPECT_EQ(tab_strip()->tab_at(3)->group().value(), group2);
  EXPECT_EQ(tab_strip()->tab_at(4)->group().value(), group2);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftTabPrevious_RemovesFromGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  AddTabToNewGroup(1);

  // Instead of moving, the tab should be removed from the group.
  const auto expected = GetWebContentsesInOrder({0, 1, 2});
  tab_strip()->ShiftTabPrevious(tab_strip()->tab_at(1));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_EQ(tab_strip()->tab_at(1)->group(), std::nullopt);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftTabPrevious_ShiftsBetweenGroups) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  AddTabToNewGroup(1);

  // Instead of moving, the tab should be removed from its old group, then added
  // to the new group.
  const auto expected = GetWebContentsesInOrder({0, 1, 2});
  tab_strip()->ShiftTabPrevious(tab_strip()->tab_at(1));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_EQ(tab_strip()->tab_at(1)->group(), std::nullopt);
  tab_strip()->ShiftTabPrevious(tab_strip()->tab_at(1));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_EQ(tab_strip()->tab_at(1)->group().value(), group);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftTabPrevious_Failure_EdgeOfTabstrip) {
  AppendTab();
  AppendTab();

  const auto contentses = GetWebContentses();
  tab_strip()->ShiftTabPrevious(tab_strip()->tab_at(0));
  // No change expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftTabPrevious_Failure_Pinned) {
  AppendTab();
  AppendTab();
  tab_strip_model()->SetTabPinned(0, true);

  const auto contentses = GetWebContentses();
  tab_strip()->ShiftTabPrevious(tab_strip()->tab_at(1));
  // No change expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftTabNext_Success) {
  AppendTab();
  AppendTab();

  const auto expected = GetWebContentsesInOrder({1, 0, 2});
  tab_strip()->ShiftTabNext(tab_strip()->tab_at(0));
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftTabNext_AddsToGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);

  // Instead of moving, the tab should be added to the group.
  const auto expected = GetWebContentsesInOrder({0, 1, 2});
  tab_strip()->ShiftTabNext(tab_strip()->tab_at(0));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_EQ(tab_strip()->tab_at(0)->group().value(), group);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftTabNext_PastCollapsedGroup_Success) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  AddTabToExistingGroup(2, group);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group));

  const auto expected = GetWebContentsesInOrder({1, 2, 0});
  tab_strip()->ShiftTabNext(tab_strip()->tab_at(0));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group));
  EXPECT_EQ(tab_strip()->tab_at(0)->group().value(), group);
  EXPECT_EQ(tab_strip()->tab_at(1)->group().value(), group);
  EXPECT_EQ(tab_strip()->tab_at(2)->group(), std::nullopt);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftTabNext_BetweenTwoCollapsedGroups_Success) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group1 = AddTabToNewGroup(1);
  AddTabToExistingGroup(2, group1);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group1));
  tab_strip()->ToggleTabGroupCollapsedState(group1);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group1));

  tab_groups::TabGroupId group2 = AddTabToNewGroup(3);
  AddTabToExistingGroup(4, group2);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group2));
  tab_strip()->ToggleTabGroupCollapsedState(group2);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group2));

  const auto expected = GetWebContentsesInOrder({1, 2, 0, 3, 4});
  tab_strip()->ShiftTabNext(tab_strip()->tab_at(0));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group1));
  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group2));
  EXPECT_EQ(tab_strip()->tab_at(0)->group().value(), group1);
  EXPECT_EQ(tab_strip()->tab_at(1)->group().value(), group1);
  EXPECT_EQ(tab_strip()->tab_at(2)->group(), std::nullopt);
  EXPECT_EQ(tab_strip()->tab_at(3)->group().value(), group2);
  EXPECT_EQ(tab_strip()->tab_at(4)->group().value(), group2);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftTabNext_RemovesFromGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  AddTabToNewGroup(1);

  // Instead of moving, the tab should be removed from the group.
  const auto expected = GetWebContentsesInOrder({0, 1, 2});
  tab_strip()->ShiftTabNext(tab_strip()->tab_at(1));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_EQ(tab_strip()->tab_at(1)->group(), std::nullopt);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftTabNext_ShiftsBetweenGroups) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  AddTabToNewGroup(0);
  tab_groups::TabGroupId group = AddTabToNewGroup(1);

  // Instead of moving, the tab should be removed from its old group, then added
  // to the new group.
  const auto expected = GetWebContentsesInOrder({0, 1, 2});
  tab_strip()->ShiftTabNext(tab_strip()->tab_at(0));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_EQ(tab_strip()->tab_at(0)->group(), std::nullopt);
  tab_strip()->ShiftTabNext(tab_strip()->tab_at(0));
  EXPECT_EQ(expected, GetWebContentses());
  EXPECT_EQ(tab_strip()->tab_at(0)->group().value(), group);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftTabNext_Failure_EdgeOfTabstrip) {
  AppendTab();
  AppendTab();

  const auto contentses = GetWebContentses();
  tab_strip()->ShiftTabNext(tab_strip()->tab_at(2));
  // No change expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftTabNext_Failure_Pinned) {
  AppendTab();
  AppendTab();
  tab_strip_model()->SetTabPinned(0, true);

  const auto contentses = GetWebContentses();
  tab_strip()->ShiftTabNext(tab_strip()->tab_at(0));
  // No change expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

// Regression test for crbug.com/394381780. When active tab is the tab right
// after the collapsed group and a new foreground tab is added to the end of the
// group, the group should expand.
IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       AddForegroundTabToCollapsedGroupExpandsGroup) {
  AppendTab();
  AppendTab();
  ASSERT_EQ(3, tab_strip_model()->count());

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  tab_strip_model()->ActivateTabAt(2);

  tab_strip()->ToggleTabGroupCollapsedState(group);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group));

  // Add a tab to the group.
  chrome::AddTabAt(browser(), GURL(), 2, true, group);

  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabFirst_NoPinnedTabs_Success) {
  AppendTab();
  AppendTab();
  AppendTab();

  const auto expected = GetWebContentsesInOrder({2, 0, 1, 3});
  tab_strip()->MoveTabFirst(tab_strip()->tab_at(2));
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabFirst_PinnedTabs_Success) {
  AppendTab();
  AppendTab();
  AppendTab();
  tab_strip_model()->SetTabPinned(0, true);

  const auto expected = GetWebContentsesInOrder({0, 2, 1, 3});
  tab_strip()->MoveTabFirst(tab_strip()->tab_at(2));
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabFirst_DoesNotAddToGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  AddTabToNewGroup(0);

  tab_strip()->MoveTabFirst(tab_strip()->tab_at(1));
  EXPECT_EQ(tab_strip()->tab_at(0)->group(), std::nullopt);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabFirst_RemovesFromGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  AddTabToNewGroup(0);
  AddTabToNewGroup(1);

  tab_strip()->MoveTabFirst(tab_strip()->tab_at(0));
  EXPECT_EQ(tab_strip()->tab_at(0)->group(), std::nullopt);

  tab_strip()->MoveTabFirst(tab_strip()->tab_at(1));
  EXPECT_EQ(tab_strip()->tab_at(0)->group(), std::nullopt);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabFirst_NoPinnedTabs_Failure) {
  AppendTab();
  AppendTab();
  AppendTab();

  const auto contentses = GetWebContentses();
  tab_strip()->MoveTabFirst(tab_strip()->tab_at(0));
  // No changes expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabFirst_PinnedTabs_Failure) {
  AppendTab();
  AppendTab();
  AppendTab();
  tab_strip_model()->SetTabPinned(0, true);

  const auto contentses = GetWebContentses();
  tab_strip()->MoveTabFirst(tab_strip()->tab_at(1));
  // No changes expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       MoveTabFirst_MovePinnedTab_Success) {
  AppendTab();
  AppendTab();
  AppendTab();
  tab_strip_model()->SetTabPinned(0, true);
  tab_strip_model()->SetTabPinned(1, true);
  tab_strip_model()->SetTabPinned(2, true);

  const auto expected = GetWebContentsesInOrder({2, 0, 1, 3});
  tab_strip()->MoveTabFirst(tab_strip()->tab_at(2));
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabLast_NoPinnedTabs_Success) {
  AppendTab();
  AppendTab();

  const auto expected = GetWebContentsesInOrder({1, 2, 0});
  tab_strip()->MoveTabLast(tab_strip()->tab_at(0));
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabLast_MovePinnedTab_Success) {
  AppendTab();
  AppendTab();
  AppendTab();
  tab_strip_model()->SetTabPinned(0, true);
  tab_strip_model()->SetTabPinned(1, true);
  tab_strip_model()->SetTabPinned(2, true);

  const auto expected = GetWebContentsesInOrder({0, 2, 1, 3});
  tab_strip()->MoveTabLast(tab_strip()->tab_at(1));
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabLast_AllPinnedTabs_Success) {
  AppendTab();
  AppendTab();
  tab_strip_model()->SetTabPinned(0, true);
  tab_strip_model()->SetTabPinned(1, true);
  tab_strip_model()->SetTabPinned(2, true);

  const auto expected = GetWebContentsesInOrder({0, 2, 1});
  tab_strip()->MoveTabLast(tab_strip()->tab_at(1));
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabLast_DoesNotAddToGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  AddTabToNewGroup(2);

  tab_strip()->MoveTabLast(tab_strip()->tab_at(1));
  EXPECT_EQ(tab_strip()->tab_at(2)->group(), std::nullopt);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabLast_RemovesFromGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  AddTabToNewGroup(1);
  AddTabToNewGroup(2);

  tab_strip()->MoveTabLast(tab_strip()->tab_at(2));
  EXPECT_EQ(tab_strip()->tab_at(2)->group(), std::nullopt);

  tab_strip()->MoveTabLast(tab_strip()->tab_at(1));
  EXPECT_EQ(tab_strip()->tab_at(2)->group(), std::nullopt);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabLast_NoPinnedTabs_Failure) {
  AppendTab();
  AppendTab();

  const auto contentses = GetWebContentses();
  tab_strip()->MoveTabLast(tab_strip()->tab_at(2));
  // No changes expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabLast_PinnedTabs_Failure) {
  AppendTab();
  AppendTab();
  tab_strip_model()->SetTabPinned(0, true);
  tab_strip_model()->SetTabPinned(1, true);

  const auto contentses = GetWebContentses();
  tab_strip()->MoveTabLast(tab_strip()->tab_at(1));
  // No changes expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MoveTabLast_AllPinnedTabs_Failure) {
  AppendTab();
  AppendTab();
  tab_strip_model()->SetTabPinned(0, true);
  tab_strip_model()->SetTabPinned(1, true);
  tab_strip_model()->SetTabPinned(2, true);

  const auto contentses = GetWebContentses();
  tab_strip()->MoveTabLast(tab_strip()->tab_at(2));
  // No changes expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftGroupLeft_Success) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  AddTabToExistingGroup(2, group);

  const auto expected = GetWebContentsesInOrder({1, 2, 0});
  tab_strip()->ShiftGroupLeft(group);
  EXPECT_EQ(expected, GetWebContentses());
}

// TODO(crbug.com/353618704): Re-enable this test
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
#define MAYBE_ShiftGroupLeft_OtherGroup DISABLED_ShiftGroupLeft_OtherGroup
#else
#define MAYBE_ShiftGroupLeft_OtherGroup ShiftGroupLeft_OtherGroup
#endif
IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, MAYBE_ShiftGroupLeft_OtherGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group1 = AddTabToNewGroup(2);
  AddTabToExistingGroup(3, group1);

  tab_groups::TabGroupId group2 = AddTabToNewGroup(0);
  AddTabToExistingGroup(1, group2);

  const auto expected = GetWebContentsesInOrder({2, 3, 0, 1});
  tab_strip()->ShiftGroupLeft(group1);
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftGroupLeft_Failure_EdgeOfTabstrip) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  AddTabToExistingGroup(1, group);

  const auto contentses = GetWebContentses();
  tab_strip()->ShiftGroupLeft(group);
  // No change expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftGroupLeft_Failure_Pinned) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();
  tab_strip_model()->SetTabPinned(0, true);

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  AddTabToExistingGroup(2, group);

  const auto contentses = GetWebContentses();
  tab_strip()->ShiftGroupLeft(group);
  // No change expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftGroupRight_Success) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  AddTabToExistingGroup(1, group);

  const auto expected = GetWebContentsesInOrder({2, 0, 1});
  tab_strip()->ShiftGroupRight(group);
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftGroupRight_OtherGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group1 = AddTabToNewGroup(0);
  AddTabToExistingGroup(1, group1);

  tab_groups::TabGroupId group2 = AddTabToNewGroup(2);
  AddTabToExistingGroup(3, group2);

  const auto expected = GetWebContentsesInOrder({2, 3, 0, 1});
  tab_strip()->ShiftGroupRight(group1);
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftGroupRight_Failure_EdgeOfTabstrip) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  AddTabToExistingGroup(2, group);

  const auto contentses = GetWebContentses();
  tab_strip()->ShiftGroupRight(group);
  // No change expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftCollapsedGroupLeft_Success) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  AddTabToExistingGroup(2, group);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group));

  const auto expected = GetWebContentsesInOrder({1, 2, 0});
  tab_strip()->ShiftGroupLeft(group);
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftCollapsedGroupLeft_OtherCollapsedGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group1 = AddTabToNewGroup(2);
  AddTabToExistingGroup(3, group1);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group1));
  tab_strip()->ToggleTabGroupCollapsedState(group1);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group1));

  tab_groups::TabGroupId group2 = AddTabToNewGroup(0);
  AddTabToExistingGroup(1, group2);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group2));
  tab_strip()->ToggleTabGroupCollapsedState(group2);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group2));

  const auto expected = GetWebContentsesInOrder({2, 3, 0, 1, 4});
  tab_strip()->ShiftGroupLeft(group1);
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftCollapsedGroupLeft_Failure_EdgeOfTabstrip) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  AddTabToExistingGroup(1, group);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group));

  const auto contentses = GetWebContentses();
  tab_strip()->ShiftGroupLeft(group);

  // No change expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftCollapsedGroupLeft_Failure_Pinned) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();
  tab_strip_model()->SetTabPinned(0, true);

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  AddTabToExistingGroup(2, group);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group));

  const auto contentses = GetWebContentses();
  tab_strip()->ShiftGroupLeft(group);

  // No change expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ShiftCollapsedGroupRight_Success) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  AddTabToExistingGroup(1, group);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group));

  const auto expected = GetWebContentsesInOrder({2, 0, 1});
  tab_strip()->ShiftGroupRight(group);
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftCollapsedGroupRight_OtherCollapsedGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group1 = AddTabToNewGroup(0);
  AddTabToExistingGroup(1, group1);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group1));
  tab_strip()->ToggleTabGroupCollapsedState(group1);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group1));

  tab_groups::TabGroupId group2 = AddTabToNewGroup(2);
  AddTabToExistingGroup(3, group2);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group2));
  tab_strip()->ToggleTabGroupCollapsedState(group2);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group2));

  const auto expected = GetWebContentsesInOrder({2, 3, 0, 1, 4});
  tab_strip()->ShiftGroupRight(group1);
  EXPECT_EQ(expected, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ShiftCollapsedGroupRight_Failure_EdgeOfTabstrip) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  AddTabToExistingGroup(2, group);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group));

  const auto contentses = GetWebContentses();
  tab_strip()->ShiftGroupRight(group);
  // No change expected.
  EXPECT_EQ(contentses, GetWebContentses());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       CollapseGroup_WithActiveTabInGroup_SelectsNext) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  tab_strip()->SelectTab(tab_strip()->tab_at(0), GetDummyEvent());
  ASSERT_EQ(0, tab_strip()->GetActiveIndex());
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);

  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group));
  EXPECT_EQ(1, tab_strip()->GetActiveIndex());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       CollapseGroup_WhenAddingActiveTab_ExpandsGroup) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);

  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group));
  EXPECT_EQ(1, tab_strip()->GetActiveIndex());

  tab_strip_model()->AddToExistingGroup({1}, group);
  EXPECT_FALSE(tab_strip()->IsGroupCollapsed(group));
  EXPECT_EQ(1, tab_strip()->GetActiveIndex());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       CollapseGroup_WhenAddingInactiveTab_StaysCollapsed) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);

  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);

  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group));
  EXPECT_EQ(2, tab_strip()->GetActiveIndex());

  tab_strip_model()->AddToExistingGroup({1}, group);

  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group));
  EXPECT_EQ(2, tab_strip()->GetActiveIndex());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       CollapseGroup_WithActiveTabInGroup_SelectsPrevious) {
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  tab_strip()->SelectTab(tab_strip()->tab_at(1), GetDummyEvent());
  ASSERT_EQ(1, tab_strip()->GetActiveIndex());
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);

  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group));
  EXPECT_EQ(0, tab_strip()->GetActiveIndex());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, AccessibleName) {
  AppendTab();
  AppendTab();

  ui::AXNodeData data;
  tab_strip()->tab_at(1)->GetViewAccessibility().GetAccessibleNodeData(&data);
  EXPECT_EQ(u"New Tab",
            data.GetString16Attribute(ax::mojom::StringAttribute::kName));

  // AccessibleName should update when tab group is changed
  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  std::u16string tab_title =
      WindowMetadataController::From(browser())->GetTitleForTab(
          browser()->GetTabStripModel()->GetTabAtIndex(1)->GetHandle());
  std::u16string group_title = tab_strip()->GetGroupTitle(group);
  std::u16string title =
      group_title.empty()
          ? l10n_util::GetStringFUTF16(IDS_TAB_AX_LABEL_UNNAMED_GROUP_FORMAT,
                                       tab_title)
          : l10n_util::GetStringFUTF16(IDS_TAB_AX_LABEL_UNNAMED_GROUP_FORMAT,
                                       tab_title, group_title);
  data = ui::AXNodeData();
  tab_strip()->tab_at(1)->GetViewAccessibility().GetAccessibleNodeData(&data);
  EXPECT_EQ(title,
            data.GetString16Attribute(ax::mojom::StringAttribute::kName));

  // AccessibleName should update with crashedstatus
  tabs::TabData tab_data = tab_strip()->tab_at(1)->data();
  tab_data.is_crashed = true;
  tab_strip()->tab_at(1)->SetDataForTesting(tab_data);
  data = ui::AXNodeData();
  tab_strip()->tab_at(1)->GetViewAccessibility().GetAccessibleNodeData(&data);
  EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_TAB_AX_LABEL_CRASHED_FORMAT, title),
            data.GetString16Attribute(ax::mojom::StringAttribute::kName));

  // AccessibleName update with pinned status and network status change
  int new_index = tab_strip_model()->SetTabPinned(1, true);
  title = l10n_util::GetStringFUTF16(IDS_TAB_AX_LABEL_PINNED_FORMAT, tab_title);
  tab_data = tab_strip()->tab_at(new_index)->data();
  tab_data.network_state = tabs::TabNetworkState::kError;
  tab_strip()->tab_at(new_index)->SetDataForTesting(tab_data);
  data = ui::AXNodeData();
  tab_strip()->tab_at(new_index)->GetViewAccessibility().GetAccessibleNodeData(
      &data);
  EXPECT_EQ(
      l10n_util::GetStringFUTF16(IDS_TAB_AX_LABEL_NETWORK_ERROR_FORMAT, title),
      data.GetString16Attribute(ax::mojom::StringAttribute::kName));

  // AccessibleName update with alert on tab
  tab_data = tab_strip()->tab_at(new_index)->data();
  tab_data.network_state = tabs::TabNetworkState::kLoading;
  RecentlyAudibleHelper::FromWebContents(
      tab_strip_model()->GetWebContentsAt(new_index))
      ->SetCurrentlyAudibleForTesting();
  tab_strip()->tab_at(new_index)->SetDataForTesting(tab_data);
  data = ui::AXNodeData();
  tab_strip()->tab_at(new_index)->GetViewAccessibility().GetAccessibleNodeData(
      &data);
  EXPECT_EQ(
      l10n_util::GetStringFUTF16(IDS_TAB_AX_LABEL_AUDIO_PLAYING_FORMAT, title),
      data.GetString16Attribute(ax::mojom::StringAttribute::kName));

  // AccessibleName update with tab resource usage update
  g_browser_process->local_state()->SetBoolean(
      prefs::kHoverCardMemoryUsageEnabled, true);
  tab_data = tab_strip()->tab_at(new_index)->data();
  auto tab_resource_usage = base::MakeRefCounted<TabResourceUsage>();
  tab_resource_usage->SetMemoryUsage(base::ByteSize(100));
  tab_data.tab_resource_usage = std::move(tab_resource_usage);
  tab_strip()->tab_at(new_index)->SetDataForTesting(tab_data);
  data = ui::AXNodeData();
  tab_strip()->tab_at(new_index)->GetViewAccessibility().GetAccessibleNodeData(
      &data);
  EXPECT_EQ(l10n_util::GetStringFUTF16(
                IDS_TAB_AX_MEMORY_USAGE,
                l10n_util::GetStringFUTF16(
                    IDS_TAB_AX_LABEL_AUDIO_PLAYING_FORMAT, title),
                ui::FormatBytes(base::ByteSize(100))),
            data.GetString16Attribute(ax::mojom::StringAttribute::kName));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, AccessibleNameUpdatesOnTabFocus) {
  g_browser_process->local_state()->SetBoolean(
      prefs::kHoverCardMemoryUsageEnabled, false);
  AppendTab();
  Tab* tab = tab_strip()->tab_at(1);
  tabs::TabInterface* tab_interface = tab->tab_handle().Get();
  ASSERT_TRUE(tab_interface);

  auto* helper = TabResourceUsageTabHelper::From(tab_interface);
  ASSERT_TRUE(helper);

  // Initially, the accessible name should be just the tab title.
  ui::AXNodeData data;
  tab->GetViewAccessibility().GetAccessibleNodeData(&data);
  std::u16string initial_name =
      data.GetString16Attribute(ax::mojom::StringAttribute::kName);
  EXPECT_EQ(u"New Tab", initial_name);

  // Set memory usage.
  base::ByteSize memory_usage = base::ByteSize(1024 * 1024);  // 1 MiB
  helper->SetMemoryUsage(memory_usage);

  // Focus the tab via FocusManager. Gaining focus should update the accessible
  // name.
  tab->GetFocusManager()->SetFocusedView(tab);

  data = ui::AXNodeData();
  tab->GetViewAccessibility().GetAccessibleNodeData(&data);
  std::u16string updated_name =
      data.GetString16Attribute(ax::mojom::StringAttribute::kName);

  // The updated name should not contain the memory usage because the hover card
  // doesn't show tab memory usage by default.
  std::u16string expected_memory_string = ui::FormatBytes(memory_usage);
  EXPECT_EQ(std::u16string::npos, updated_name.find(expected_memory_string));

  // Enabling the hover card memory usage pref and refreshing the accessible
  // name should include the tab memory usage value in the accessible name.
  g_browser_process->local_state()->SetBoolean(
      prefs::kHoverCardMemoryUsageEnabled, true);
  tab->UpdateAccessibleName();

  data = ui::AXNodeData();
  tab->GetViewAccessibility().GetAccessibleNodeData(&data);
  updated_name = data.GetString16Attribute(ax::mojom::StringAttribute::kName);
  EXPECT_NE(std::u16string::npos, updated_name.find(expected_memory_string));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       DISABLED_TabGroupHeaderAccessibleProperties) {
  BrowserUiController::From(browser())->set_update_ui_immediately_for_testing();
  AppendTab();
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  tab_strip()->tab_at(1)->SetGroup(group);
  tab_strip_model()->ChangeTabGroupVisuals(
      group, tab_groups::TabGroupVisualData(
                 u"Test title", tab_groups::TabGroupColorId::kBlue));

  auto* group_header = tab_strip()->group_header(group);
  std::u16string group_title = tab_strip_model()
                                   ->group_model()
                                   ->GetTabGroup(group)
                                   ->visual_data()
                                   ->title();

  EXPECT_FALSE(tab_strip()->IsGroupCollapsed(group));
  std::u16string collapsed_state = GetCollapsedState(group);
  EXPECT_FALSE(group_title.empty());
  ui::AXNodeData data;
  group_header->GetViewAccessibility().GetAccessibleNodeData(&data);
  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
            l10n_util::GetStringFUTF16(IDS_GROUP_AX_LABEL_NAMED_GROUP_FORMAT,
                                       u"Test title", u"\"New Tab\"",
                                       collapsed_state));

  // Validating tab_group name update & collapsed state change should update the
  // accessible name.
  tab_strip_model()->ChangeTabGroupVisuals(
      group,
      tab_groups::TabGroupVisualData(u"", tab_groups::TabGroupColorId::kBlue));
  group_title = tab_strip_model()
                    ->group_model()
                    ->GetTabGroup(group)
                    ->visual_data()
                    ->title();
  EXPECT_TRUE(group_title.empty());
  tab_strip()->ToggleTabGroupCollapsedState(group);
  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group));
  collapsed_state = GetCollapsedState(group);
  data = ui::AXNodeData();
  group_header->GetViewAccessibility().GetAccessibleNodeData(&data);
  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
            l10n_util::GetStringFUTF16(IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT,
                                       u"\"New Tab\"", collapsed_state));

  tab_strip_model()->ChangeTabGroupVisuals(
      group, tab_groups::TabGroupVisualData(
                 u"New test title", tab_groups::TabGroupColorId::kBlue));
  collapsed_state = GetCollapsedState(group);
  EXPECT_FALSE(tab_strip()->IsGroupCollapsed(group));
  group_title = tab_strip_model()
                    ->group_model()
                    ->GetTabGroup(group)
                    ->visual_data()
                    ->title();
  EXPECT_FALSE(group_title.empty());
  data = ui::AXNodeData();
  group_header->GetViewAccessibility().GetAccessibleNodeData(&data);
  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
            l10n_util::GetStringFUTF16(IDS_GROUP_AX_LABEL_NAMED_GROUP_FORMAT,
                                       u"New test title", u"\"New Tab\"",
                                       collapsed_state));

  // Validating tab's title update in a tab_group should update accessible name.
  AppendTab();
  AppendTab();
  AppendTab();
  group = AddTabToNewGroup(3);
  AddTabToExistingGroup(4, group);
  AddTabToExistingGroup(5, group);
  base::RunLoop run_loop;
  run_loop.RunUntilIdle();
  auto* tab_group = tab_strip_model()->group_model()->GetTabGroup(group);

  gfx::Range tabs_in_group = tab_group->ListTabs();
  auto* web_contents =
      tab_strip_model()->GetWebContentsAt(tabs_in_group.start());
  content::NavigationEntry* entry =
      web_contents->GetController().GetVisibleEntry();
  std::u16string new_title = u"New Tab Title For Test";
  web_contents->UpdateTitleForEntry(entry, new_title);
  run_loop.RunUntilIdle();
  EXPECT_EQ(web_contents->GetTitle(), new_title);

  collapsed_state = GetCollapsedState(group);
  group_header = tab_strip()->group_header(group);
  data = ui::AXNodeData();
  group_header->GetViewAccessibility().GetAccessibleNodeData(&data);
  std::u16string group_header_contents = base::ReplaceStringPlaceholders(
      l10n_util::GetPluralStringFUTF16(IDS_TAB_CXMENU_PLACEHOLDER_GROUP_TITLE,
                                       2),
      std::vector<std::u16string>{u"New Tab Title For Test"}, nullptr);
  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
            l10n_util::GetStringFUTF16(IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT,
                                       group_header_contents, collapsed_state));

  // Other than first tab in a group, if any tab's title is updated, it should
  // not update the accessible name.
  web_contents = tab_strip_model()->GetWebContentsAt(tabs_in_group.start() + 1);
  entry = web_contents->GetController().GetVisibleEntry();
  new_title = u"New Tab Title For Test 2";
  web_contents->UpdateTitleForEntry(entry, new_title);
  run_loop.RunUntilIdle();
  EXPECT_EQ(web_contents->GetTitle(), new_title);
  data = ui::AXNodeData();
  group_header->GetViewAccessibility().GetAccessibleNodeData(&data);
  group_header_contents = base::ReplaceStringPlaceholders(
      l10n_util::GetPluralStringFUTF16(IDS_TAB_CXMENU_PLACEHOLDER_GROUP_TITLE,
                                       2),
      std::vector<std::u16string>{u"New Tab Title For Test"}, nullptr);
  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
            l10n_util::GetStringFUTF16(IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT,
                                       group_header_contents, collapsed_state));

  // Validate accessible name update with tab move.
  gfx::Range initial_tabs_in_group(3, 6);
  EXPECT_EQ(initial_tabs_in_group, tab_group->ListTabs());
  tab_strip()->MoveTabFirst(tab_strip()->tab_at(3));
  gfx::Range updated_tabs_in_group(4, 6);
  EXPECT_EQ(updated_tabs_in_group, tab_group->ListTabs());
  EXPECT_EQ(new_title, tab_strip_model()->GetWebContentsAt(4)->GetTitle());

  data = ui::AXNodeData();
  group_header->GetViewAccessibility().GetAccessibleNodeData(&data);
  group_header_contents = base::ReplaceStringPlaceholders(
      l10n_util::GetPluralStringFUTF16(IDS_TAB_CXMENU_PLACEHOLDER_GROUP_TITLE,
                                       1),
      std::vector<std::u16string>{u"New Tab Title For Test 2"}, nullptr);

  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
            l10n_util::GetStringFUTF16(IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT,
                                       group_header_contents, collapsed_state));

  // Rearrange tabs in a group should update the accessible name.
  initial_tabs_in_group = gfx::Range(4, 6);
  EXPECT_EQ(initial_tabs_in_group, tab_group->ListTabs());

  tabs_in_group = tab_group->ListTabs();
  web_contents = tab_strip_model()->GetWebContentsAt(tabs_in_group.start() + 1);
  entry = web_contents->GetController().GetVisibleEntry();
  new_title = u"Middle Tab Title";
  web_contents->UpdateTitleForEntry(entry, new_title);
  run_loop.RunUntilIdle();
  EXPECT_EQ(web_contents->GetTitle(), new_title);

  // tab_strip_model()->MoveTabToIndexImpl(6, 4, group, false, false);
  tab_strip()->SelectTab(tab_strip()->tab_at(5), GetDummyEvent());
  tab_strip_model()->MoveTabPrevious();
  updated_tabs_in_group = gfx::Range(4, 6);
  EXPECT_EQ(updated_tabs_in_group, tab_group->ListTabs());
  EXPECT_EQ(new_title, tab_strip_model()->GetWebContentsAt(4)->GetTitle());

  data = ui::AXNodeData();
  group_header->GetViewAccessibility().GetAccessibleNodeData(&data);
  group_header_contents = base::ReplaceStringPlaceholders(
      l10n_util::GetPluralStringFUTF16(IDS_TAB_CXMENU_PLACEHOLDER_GROUP_TITLE,
                                       1),
      std::vector<std::u16string>{new_title}, nullptr);

  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
            l10n_util::GetStringFUTF16(IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT,
                                       group_header_contents, collapsed_state));

  // Moving out all tabs in a tab group to another group
  tab_strip()->SelectTab(tab_strip()->tab_at(4), GetDummyEvent());
  tab_strip()->ExtendSelectionTo(tab_strip()->tab_at(6));
  tab_groups::TabGroupId new_group = tab_strip_model()->AddToNewGroup({0});
  tab_strip_model()->MoveSelectedTabsTo(0, new_group);
  collapsed_state = GetCollapsedState(new_group);
  group_header = tab_strip()->group_header(new_group);
  data = ui::AXNodeData();
  group_header->GetViewAccessibility().GetAccessibleNodeData(&data);
  group_header_contents = base::ReplaceStringPlaceholders(
      l10n_util::GetPluralStringFUTF16(IDS_TAB_CXMENU_PLACEHOLDER_GROUP_TITLE,
                                       3),
      std::vector<std::u16string>{new_title}, nullptr);

  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
            l10n_util::GetStringFUTF16(IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT,
                                       group_header_contents, collapsed_state));
}

IN_PROC_BROWSER_TEST_F(
    TabStripBrowsertest,
    CollapseGroup_WithActiveTabOutsideGroup_DoesNotChangeActiveTab) {
  ASSERT_TRUE(tab_strip_model()->SupportsTabGroups());

  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  tab_strip()->SelectTab(tab_strip()->tab_at(1), GetDummyEvent());
  ASSERT_EQ(1, tab_strip()->GetActiveIndex());
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);

  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group));
  EXPECT_EQ(1, tab_strip()->GetActiveIndex());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, TabGroupHeaderTooltipText) {
  BrowserUiController::From(browser())->set_update_ui_immediately_for_testing();
  AppendTab();
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  tab_strip()->tab_at(1)->SetGroup(group);
  tab_strip_model()->ChangeTabGroupVisuals(
      group, tab_groups::TabGroupVisualData(
                 u"Non empty title text", tab_groups::TabGroupColorId::kBlue));

  auto* group_header = tab_strip()->group_header(group);
  std::u16string group_title = tab_strip_model()
                                   ->group_model()
                                   ->GetTabGroup(group)
                                   ->visual_data()
                                   ->title();

  EXPECT_EQ(group_title, group_header->GetTitleTextForTesting());
  EXPECT_EQ(
      group_header->GetRenderedTooltipText(gfx::Point()),
      l10n_util::GetStringFUTF16(
          IDS_TAB_GROUPS_NAMED_GROUP_TOOLTIP,
          std::u16string(group_header->GetTitleTextForTesting()),
          tab_strip()->GetGroupContentString(group_header->group().value())));

  tab_strip_model()->ChangeTabGroupVisuals(
      group, tab_groups::TabGroupVisualData(
                 std::u16string(), tab_groups::TabGroupColorId::kBlue));

  EXPECT_EQ(group_header->GetRenderedTooltipText(gfx::Point()),
            l10n_util::GetStringFUTF16(IDS_TAB_GROUPS_UNNAMED_GROUP_TOOLTIP,
                                       tab_strip()->GetGroupContentString(
                                           group_header->group().value())));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       TabGroupHeaderTooltipTextAccessibility) {
  BrowserUiController::From(browser())->set_update_ui_immediately_for_testing();
  AppendTab();
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  tab_strip()->tab_at(1)->SetGroup(group);
  tab_strip_model()->ChangeTabGroupVisuals(
      group, tab_groups::TabGroupVisualData(
                 u"Non empty title text", tab_groups::TabGroupColorId::kBlue));

  auto* group_header = tab_strip()->group_header(group);
  std::u16string group_title = tab_strip_model()
                                   ->group_model()
                                   ->GetTabGroup(group)
                                   ->visual_data()
                                   ->title();

  EXPECT_EQ(group_title, group_header->GetTitleTextForTesting());

  EXPECT_EQ(
      group_header->GetRenderedTooltipText(gfx::Point()),
      l10n_util::GetStringFUTF16(
          IDS_TAB_GROUPS_NAMED_GROUP_TOOLTIP,
          std::u16string(group_header->GetTitleTextForTesting()),
          tab_strip()->GetGroupContentString(group_header->group().value())));

  ui::AXNodeData data;

  group_header->GetViewAccessibility().GetAccessibleNodeData(&data);
  EXPECT_NE(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
            group_header->GetRenderedTooltipText(gfx::Point()));
  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kDescription),
            group_header->GetRenderedTooltipText(gfx::Point()));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, CollapseGroup_CreatesNewTab) {
  ASSERT_EQ(1, tab_strip_model()->count());
  AppendTab();
  ASSERT_EQ(2, tab_strip_model()->count());

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  tab_strip_model()->AddToExistingGroup({1}, group);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));

  // Any origin other than kMenuAction will work here. At the time this was
  // written, it was impossible to trigger this specific interaction (collapsing
  // a group) from a context menu.
  tab_strip()->ToggleTabGroupCollapsedState(
      group, ToggleTabGroupCollapsedStateOrigin::kMouse);
  ASSERT_EQ(3, tab_strip_model()->count());

  EXPECT_TRUE(tab_strip()->IsGroupCollapsed(group));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       ActivateTabInCollapsedGroup_ExpandsCollapsedGroup) {
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(0);
  ASSERT_FALSE(tab_strip()->IsGroupCollapsed(group));
  tab_strip()->ToggleTabGroupCollapsedState(group);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group));
  ASSERT_EQ(1, tab_strip()->GetActiveIndex());

  tab_strip()->SelectTab(tab_strip()->tab_at(0), GetDummyEvent());
  EXPECT_FALSE(tab_strip()->IsGroupCollapsed(group));
}

// Tests IDC_SELECT_TAB_0, IDC_SELECT_NEXT_TAB, IDC_SELECT_PREVIOUS_TAB and
// IDC_SELECT_LAST_TAB. The tab navigation accelerators should ignore tabs in
// collapsed groups.
IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, TabGroupTabNavigationAccelerators) {
  ASSERT_TRUE(browser()->GetTabStripModel()->SupportsTabGroups());
  // Create five tabs.
  for (int i = 0; i < 4; i++) {
    AppendTab();
  }

  ASSERT_EQ(5, tab_strip_model()->count());

  // Add the first, second, and last tab into their own collapsed groups.
  tab_groups::TabGroupId group1 = tab_strip_model()->AddToNewGroup({0});
  tab_groups::TabGroupId group2 = tab_strip_model()->AddToNewGroup({1});
  tab_groups::TabGroupId group3 = tab_strip_model()->AddToNewGroup({4});
  tab_strip()->ToggleTabGroupCollapsedState(group1);
  tab_strip()->ToggleTabGroupCollapsedState(group2);
  tab_strip()->ToggleTabGroupCollapsedState(group3);

  // Select the fourth tab.
  tab_strip_model()->ActivateTabAt(3);

  CommandUpdater* updater = chrome::BrowserCommandController::From(browser());

  // Navigate to the first tab using an accelerator.
  updater->ExecuteCommand(IDC_SELECT_TAB_0);
  ASSERT_EQ(2, tab_strip_model()->active_index());

  // Navigate back to the first tab using the previous accelerators.
  updater->ExecuteCommand(IDC_SELECT_PREVIOUS_TAB);
  ASSERT_EQ(3, tab_strip_model()->active_index());

  // Navigate to the second tab using the next accelerators.
  updater->ExecuteCommand(IDC_SELECT_NEXT_TAB);
  ASSERT_EQ(2, tab_strip_model()->active_index());

  // Navigate to the last tab using the select last accelerator.
  updater->ExecuteCommand(IDC_SELECT_LAST_TAB);
  ASSERT_EQ(3, tab_strip_model()->active_index());
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       TabHasCorrectAccessibleSelectedState) {
  AppendTab();
  AppendTab();

  Tab* tab0 = tab_strip()->tab_at(0);
  Tab* tab1 = tab_strip()->tab_at(1);
  ui::AXNodeData ax_node_data_0;
  ui::AXNodeData ax_node_data_1;
  views::test::AXEventCounter counter(views::AXUpdateNotifier::Get());

  tab_strip()->SelectTab(tab_strip()->tab_at(0), GetDummyEvent());
  tab0->GetViewAccessibility().GetAccessibleNodeData(&ax_node_data_0);
  tab1->GetViewAccessibility().GetAccessibleNodeData(&ax_node_data_1);
  EXPECT_TRUE(tab0->IsSelected());
  EXPECT_TRUE(
      ax_node_data_0.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
  EXPECT_FALSE(tab1->IsSelected());
  EXPECT_FALSE(
      ax_node_data_1.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
  EXPECT_EQ(counter.GetCount(ax::mojom::Event::kSelection), 2);

  tab_strip()->SelectTab(tab_strip()->tab_at(1), GetDummyEvent());
  ax_node_data_0 = ui::AXNodeData();
  ax_node_data_1 = ui::AXNodeData();
  tab0->GetViewAccessibility().GetAccessibleNodeData(&ax_node_data_0);
  tab1->GetViewAccessibility().GetAccessibleNodeData(&ax_node_data_1);
  EXPECT_FALSE(tab0->IsSelected());
  EXPECT_FALSE(
      ax_node_data_0.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
  EXPECT_TRUE(tab1->IsSelected());
  EXPECT_TRUE(
      ax_node_data_1.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
  EXPECT_EQ(counter.GetCount(ax::mojom::Event::kSelection), 4);
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, TabGroupHeaderAccessibleState) {
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  auto* group_header = tab_strip()->group_header(group);

  ui::AXNodeData data;
  group_header->GetViewAccessibility().GetAccessibleNodeData(&data);
  EXPECT_TRUE(data.HasState(ax::mojom::State::kEditable));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ToggleTabSelection) {
  base::UserActionTester user_action_tester;
  AppendTab();
  AppendTab();
  tab_strip()->SelectTab(tab_strip()->tab_at(0), GetDummyEvent());

  const ui::EventFlags modifier =
#if BUILDFLAG(IS_MAC)
      ui::EF_COMMAND_DOWN;
#else
      ui::EF_CONTROL_DOWN;
#endif
  ui::MouseEvent click(ui::EventType::kMousePressed, gfx::Point(0, 0),
                       gfx::Point(0, 0), ui::EventTimeForNow(),
                       ui::EF_LEFT_MOUSE_BUTTON | modifier,
                       ui::EF_LEFT_MOUSE_BUTTON);
  tab_strip()->tab_at(1)->OnMousePressed(click);
  EXPECT_TRUE(tab_strip()->IsTabSelected(tab_strip()->tab_at(0)));
  EXPECT_TRUE(tab_strip()->IsTabSelected(tab_strip()->tab_at(1)));
  EXPECT_EQ(1,
            user_action_tester.GetActionCount("TabMultiSelect_ToggleSelected"));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, ExtendTabSelection) {
  base::UserActionTester user_action_tester;
  AppendTab();
  AppendTab();
  AppendTab();
  AppendTab();
  tab_strip()->SelectTab(tab_strip()->tab_at(1), GetDummyEvent());

  ui::MouseEvent click(ui::EventType::kMousePressed, gfx::Point(0, 0),
                       gfx::Point(0, 0), ui::EventTimeForNow(),
                       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_SHIFT_DOWN,
                       ui::EF_LEFT_MOUSE_BUTTON);
  tab_strip()->tab_at(3)->OnMousePressed(click);
  EXPECT_FALSE(tab_strip()->IsTabSelected(tab_strip()->tab_at(0)));
  EXPECT_TRUE(tab_strip()->IsTabSelected(tab_strip()->tab_at(1)));
  EXPECT_TRUE(tab_strip()->IsTabSelected(tab_strip()->tab_at(2)));
  EXPECT_TRUE(tab_strip()->IsTabSelected(tab_strip()->tab_at(3)));
  EXPECT_EQ(
      1, user_action_tester.GetActionCount("TabMultiSelect_ExtendSelectionTo"));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, AddSelectionFromAnchorTo) {
  base::UserActionTester user_action_tester;
  AppendTab();
  AppendTab();
  AppendTab();
  AppendTab();
  tab_strip()->SelectTab(tab_strip()->tab_at(0), GetDummyEvent());

  const ui::EventFlags modifier =
#if BUILDFLAG(IS_MAC)
      ui::EF_COMMAND_DOWN;
#else
      ui::EF_CONTROL_DOWN;
#endif
  ui::MouseEvent click(ui::EventType::kMousePressed, gfx::Point(0, 0),
                       gfx::Point(0, 0), ui::EventTimeForNow(),
                       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_SHIFT_DOWN | modifier,
                       ui::EF_LEFT_MOUSE_BUTTON);
  tab_strip()->tab_at(2)->OnMousePressed(click);
  EXPECT_TRUE(tab_strip()->IsTabSelected(tab_strip()->tab_at(0)));
  EXPECT_TRUE(tab_strip()->IsTabSelected(tab_strip()->tab_at(1)));
  EXPECT_TRUE(tab_strip()->IsTabSelected(tab_strip()->tab_at(2)));
  EXPECT_EQ(1, user_action_tester.GetActionCount(
                   "TabMultiSelect_AddSelectionFromAnchorTo"));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, CreateSplitUKMLogged) {
  std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_ =
      std::make_unique<ukm::TestAutoSetUkmRecorder>();

  // Create two tabs with the first two being split.
  GURL a_url = GURL("https://a.com");
  GURL b_url = GURL("https://b.com");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), a_url));
  AppendTab();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), b_url));
  tab_strip_model()->ActivateTabAt(0);
  tab_strip_model()->AddToNewSplit(
      {1}, split_tabs::SplitTabVisualData(),
      split_tabs::SplitTabCreatedSource::kTabContextMenu);

  // Ensure UKM is recorded.
  auto entries = ukm_recorder_->GetEntriesByName(
      ukm::builders::SplitView_Created::kEntryName);
  EXPECT_EQ(2u, entries.size());
  ukm_recorder_->ExpectEntrySourceHasUrl(entries[0], a_url);
  ukm_recorder_->ExpectEntrySourceHasUrl(entries[1], b_url);
  EXPECT_EQ(
      *ukm_recorder_->GetEntryMetric(
          entries[0], ukm::builders::SplitView_Created::kSplitEventIdName),
      *ukm_recorder_->GetEntryMetric(
          entries[1], ukm::builders::SplitView_Created::kSplitEventIdName));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, SwapTabIntoSplitUKMLogged) {
  std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_ =
      std::make_unique<ukm::TestAutoSetUkmRecorder>();

  // Create three tabs with the first two being split.
  GURL a_url = GURL("https://a.com");
  GURL b_url = GURL("https://b.com");
  GURL c_url = GURL("https://c.com");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), a_url));
  AppendTab();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), b_url));
  AppendTab();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), c_url));
  tab_strip_model()->ActivateTabAt(0);
  tab_strip_model()->AddToNewSplit(
      {1}, split_tabs::SplitTabVisualData(),
      split_tabs::SplitTabCreatedSource::kTabContextMenu);

  // Swap the first tab with the last.
  tab_strip_model()->UpdateTabInSplit(tab_strip_model()->GetTabAtIndex(0), 2,
                                      TabStripModel::SplitUpdateType::kSwap);

  // Ensure UKM is recorded.
  auto entries = ukm_recorder_->GetEntriesByName(
      ukm::builders::SplitView_Updated::kEntryName);
  EXPECT_EQ(2u, entries.size());
  ukm_recorder_->ExpectEntrySourceHasUrl(entries[0], c_url);
  ukm_recorder_->ExpectEntrySourceHasUrl(entries[1], b_url);
  EXPECT_EQ(
      *ukm_recorder_->GetEntryMetric(
          entries[0], ukm::builders::SplitView_Updated::kSplitEventIdName),
      *ukm_recorder_->GetEntryMetric(
          entries[1], ukm::builders::SplitView_Updated::kSplitEventIdName));
}

IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, NavigateSplitTabUKMLogged) {
  std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_ =
      std::make_unique<ukm::TestAutoSetUkmRecorder>();

  // Create three tabs with the first two being split.
  GURL a_url = GURL("https://a.com");
  GURL b_url = GURL("https://b.com");
  GURL c_url = GURL("https://c.com");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), a_url));
  AppendTab();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), b_url));
  tab_strip_model()->ActivateTabAt(0);
  tab_strip_model()->AddToNewSplit(
      {1}, split_tabs::SplitTabVisualData(),
      split_tabs::SplitTabCreatedSource::kTabContextMenu);

  // Navigate the first tab to a new URL.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), c_url));

  // Ensure UKM is recorded.
  auto entries = ukm_recorder_->GetEntriesByName(
      ukm::builders::SplitView_Updated::kEntryName);
  EXPECT_EQ(2u, entries.size());
  ukm_recorder_->ExpectEntrySourceHasUrl(entries[0], c_url);
  ukm_recorder_->ExpectEntrySourceHasUrl(entries[1], b_url);
  EXPECT_EQ(
      *ukm_recorder_->GetEntryMetric(
          entries[0], ukm::builders::SplitView_Updated::kSplitEventIdName),
      *ukm_recorder_->GetEntryMetric(
          entries[1], ukm::builders::SplitView_Updated::kSplitEventIdName));
}

#if BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
                       CloseButtonHiddenWhenLockedForOnTask) {
  ash::boca::OnTaskLockedController::From(browser())->set_locked_for_on_task(
      true);

  AppendTab();
  AppendTab();
  tab_strip_model()->ActivateTabAt(1);
  ASSERT_EQ(3, tab_strip_model()->count());
  TabStrip* const tab_strip = BrowserView::GetBrowserViewForBrowser(browser())
                                  ->horizontal_tab_strip_for_testing();

  Tab* const tab0 = tab_strip->tab_at(0);
  ASSERT_FALSE(tab0->IsActive());
  EXPECT_FALSE(tab0->showing_close_button_for_testing());

  Tab* const tab1 = tab_strip->tab_at(1);
  ASSERT_TRUE(tab1->IsActive());
  EXPECT_FALSE(tab1->showing_close_button_for_testing());

  Tab* tab2 = tab_strip->tab_at(2);
  ASSERT_FALSE(tab2->IsActive());
  EXPECT_FALSE(tab2->showing_close_button_for_testing());

  // Switch tabs and confirm close button remains hidden for all opened tabs.
  // tab_strip_->SelectTab(tab2, dummy_event_);
  tab_strip_model()->ActivateTabAt(2);
  ASSERT_TRUE(tab2->IsActive());
  EXPECT_FALSE(tab0->showing_close_button_for_testing());
  EXPECT_FALSE(tab1->showing_close_button_for_testing());
  EXPECT_FALSE(tab2->showing_close_button_for_testing());

  // Closing a tab should not alter tab close button visibility either.
  tab_strip->CloseTab(tab2, CloseTabSource::kFromMouse);
  tab2 = nullptr;
  EXPECT_FALSE(tab0->showing_close_button_for_testing());
  EXPECT_FALSE(tab1->showing_close_button_for_testing());
}
#endif

class TabStripSaveBrowsertest : public TabStripBrowsertest {
 public:
  TabStripSaveBrowsertest() {
    scoped_feature_list_.InitWithFeatures(
        {data_sharing::features::kDataSharingFeature}, {});
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(TabStripSaveBrowsertest, AttentionIndicatorIsShown) {
  AppendTab();
  AppendTab();

  tab_groups::TabGroupId group = AddTabToNewGroup(1);
  tab_strip()->ToggleTabGroupCollapsedState(group);
  ASSERT_TRUE(tab_strip()->IsGroupCollapsed(group));

  auto* group_header = tab_strip()->group_header(group);

  TabGroup* tab_group =
      browser()->GetTabStripModel()->group_model()->GetTabGroup(group);

  TabGroupAttentionIndicator* attention_indicator =
      tab_group->GetTabGroupFeatures()->attention_indicator();
  attention_indicator->SetHasAttention(true);
  EXPECT_TRUE(group_header->attention_indicator_->GetVisible());

  attention_indicator->SetHasAttention(false);
  EXPECT_FALSE(group_header->attention_indicator_->GetVisible());
}
