chromium/ios/chrome/browser/tabs/ui_bundled/tab_strip_controller_unittest.mm

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

#import "ios/chrome/browser/tabs/ui_bundled/tab_strip_controller.h"

#import <Foundation/Foundation.h>

#import <memory>

#import "base/strings/utf_string_conversions.h"
#import "ios/chrome/browser/sessions/model/test_session_service.h"
#import "ios/chrome/browser/shared/coordinator/layout_guide/layout_guide_util.h"
#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_opener.h"
#import "ios/chrome/browser/shared/public/commands/application_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/commands/popup_menu_commands.h"
#import "ios/chrome/browser/shared/public/commands/settings_commands.h"
#import "ios/chrome/browser/tabs/ui_bundled/tab_strip_view.h"
#import "ios/chrome/browser/tabs/ui_bundled/tab_view.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/test/fakes/fake_navigation_manager.h"
#import "ios/web/public/test/fakes/fake_web_state.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/gtest_support.h"
#import "ui/base/device_form_factor.h"

namespace {

class TabStripControllerTest : public PlatformTest {
 protected:
  TabStripControllerTest() {
    browser_state_ = TestChromeBrowserState::Builder().Build();
    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
  }

  void SetUp() override {
    if (ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_TABLET)
      return;

    visible_navigation_item_ = web::NavigationItem::Create();
    mock_application_commands_handler_ =
        OCMStrictProtocolMock(@protocol(ApplicationCommands));
    mock_popup_menu_commands_handler_ =
        OCMStrictProtocolMock(@protocol(PopupMenuCommands));
    mock_settings_commands_handler_ =
        OCMStrictProtocolMock(@protocol(SettingsCommands));

    [browser_->GetCommandDispatcher()
        startDispatchingToTarget:mock_application_commands_handler_
                     forProtocol:@protocol(ApplicationCommands)];
    [browser_->GetCommandDispatcher()
        startDispatchingToTarget:mock_settings_commands_handler_
                     forProtocol:@protocol(SettingsCommands)];
    [browser_->GetCommandDispatcher()
        startDispatchingToTarget:mock_popup_menu_commands_handler_
                     forProtocol:@protocol(PopupMenuCommands)];

    controller_ = [[TabStripController alloc]
        initWithBaseViewController:nil
                           browser:browser_.get()
                             style:NORMAL
                 layoutGuideCenter:LayoutGuideCenterForBrowser(browser_.get())];
  }

  void TearDown() override {
    if (ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_TABLET)
      return;
    [controller_ disconnect];
  }

  void AddWebStateForTesting(std::string title) {
    auto web_state = std::make_unique<web::FakeWebState>();
    web_state->SetTitle(base::UTF8ToUTF16(title));
    auto navigation_manager = std::make_unique<web::FakeNavigationManager>();
    navigation_manager->SetVisibleItem(visible_navigation_item_.get());
    web_state->SetNavigationManager(std::move(navigation_manager));
    browser_->GetWebStateList()->InsertWebState(std::move(web_state));
  }

  void DetachWebStateForTesting(int index) {
    browser_->GetWebStateList()->DetachWebStateAt(index);
  }

  void ActivateWebStateForTesting(int index) {
    browser_->GetWebStateList()->ActivateWebStateAt(index);
  }

  web::WebTaskEnvironment task_environment_;
  std::unique_ptr<TestChromeBrowserState> browser_state_;
  std::unique_ptr<TestBrowser> browser_;
  std::unique_ptr<web::NavigationItem> visible_navigation_item_;
  id mock_application_commands_handler_;
  id mock_popup_menu_commands_handler_;
  id mock_settings_commands_handler_;
  TabStripController* controller_;
  UIWindow* window_;
};

TEST_F(TabStripControllerTest, LoadAndDisplay) {
  if (ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_TABLET)
    return;
  AddWebStateForTesting("Tab Title 1");
  AddWebStateForTesting("Tab Title 2");
  // Force the view to load.
  UIWindow* window = [[UIWindow alloc] initWithFrame:CGRectZero];
  [window addSubview:[controller_ view]];
  window_ = window;

  // There should be two TabViews and one new tab button nested within the
  // parent view (which contains exactly one scroll view).
  EXPECT_EQ(3U,
            [[[[[controller_ view] subviews] objectAtIndex:0] subviews] count]);
}

// Tests the active index when an active WebState is detached.
TEST_F(TabStripControllerTest, DetachActiveWebState) {
  if (ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_TABLET) {
    return;
  }
  AddWebStateForTesting("Tab Title 1");
  AddWebStateForTesting("Tab Title 2");
  AddWebStateForTesting("Tab Title 3");

  ActivateWebStateForTesting(1);
  EXPECT_EQ(1, browser_->GetWebStateList()->active_index());

  // Detaching an active WebState shouldn't change the active index.
  DetachWebStateForTesting(1);
  EXPECT_EQ(1, browser_->GetWebStateList()->active_index());
}

// Tests the active index when a WebState before an active WebState is detached.
TEST_F(TabStripControllerTest, DetachWebStateBeforeActiveWebState) {
  if (ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_TABLET) {
    return;
  }
  AddWebStateForTesting("Tab Title 1");
  AddWebStateForTesting("Tab Title 2");
  AddWebStateForTesting("Tab Title 3");

  ActivateWebStateForTesting(1);
  EXPECT_EQ(1, browser_->GetWebStateList()->active_index());

  // Detaching a WebState before an active WebState decrements the active index.
  DetachWebStateForTesting(0);
  EXPECT_EQ(0, browser_->GetWebStateList()->active_index());
}

// Tests the active index when a WebState after an active WebState is detached.
TEST_F(TabStripControllerTest, DetachWebStateAfterActiveWebState) {
  if (ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_TABLET) {
    return;
  }
  AddWebStateForTesting("Tab Title 1");
  AddWebStateForTesting("Tab Title 2");
  AddWebStateForTesting("Tab Title 3");

  ActivateWebStateForTesting(1);
  EXPECT_EQ(1, browser_->GetWebStateList()->active_index());

  // Detaching a WebState after an active WebState shouldn't change the active
  // index.
  DetachWebStateForTesting(2);
  EXPECT_EQ(1, browser_->GetWebStateList()->active_index());
}

}  // namespace