chromium/ash/app_list/views/assistant/assistant_test_api_impl.cc

// 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 "ash/app_list/views/assistant/assistant_test_api_impl.h"

#include "ash/app_list/app_list_bubble_presenter.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/app_list/app_list_presenter_impl.h"
#include "ash/app_list/views/app_list_bubble_view.h"
#include "ash/app_list/views/app_list_main_view.h"
#include "ash/app_list/views/app_list_page.h"
#include "ash/app_list/views/app_list_view.h"
#include "ash/app_list/views/assistant/app_list_bubble_assistant_page.h"
#include "ash/app_list/views/contents_view.h"
#include "ash/assistant/ui/assistant_view_ids.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/assistant/assistant_state.h"
#include "ash/public/cpp/tablet_mode.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/wm/overview/overview_controller.h"
#include "base/functional/bind.h"
#include "components/prefs/pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/display/screen.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/controls/textfield/textfield.h"

namespace ash {
namespace {

AppListBubbleView* GetAppListBubbleView() {
  return Shell::Get()
      ->app_list_controller()
      ->bubble_presenter_for_test()  // IN-TEST
      ->bubble_view_for_test();      // IN-TEST
}

}  // namespace

std::unique_ptr<AssistantTestApi> AssistantTestApi::Create() {
  return std::make_unique<AssistantTestApiImpl>();
}

AssistantTestApiImpl::AssistantTestApiImpl() = default;

AssistantTestApiImpl::~AssistantTestApiImpl() = default;

void AssistantTestApiImpl::DisableAnimations() {
  scoped_animation_duration_ =
      std::make_unique<ui::ScopedAnimationDurationScaleMode>(
          ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
}

bool AssistantTestApiImpl::IsVisible() {
  if (!display::Screen::GetScreen()->InTabletMode()) {
    return Shell::Get()->app_list_controller()->IsVisible() &&
           GetAppListBubbleView()->assistant_page_->GetVisible();
  }
  return AppListViewsHaveBeenCreated() && page_view()->GetVisible();
}

void AssistantTestApiImpl::SendTextQuery(const std::string& query) {
  if (!input_text_field()->HasFocus()) {
    ADD_FAILURE()
        << "The TextField should be focussed before we can send a query";
  }

  input_text_field()->SetText(base::UTF8ToUTF16(query));
  // Send <return> to commit the query.
  SendKeyPress(ui::KeyboardCode::VKEY_RETURN);
}

views::View* AssistantTestApiImpl::page_view() {
  if (!display::Screen::GetScreen()->InTabletMode()) {
    auto* bubble_view = GetAppListBubbleView();
    DCHECK(bubble_view)
        << "App list is not showing. Display the assistant UI first.";
    return bubble_view->assistant_page_;
  }
  const int index = contents_view()->GetPageIndexForState(
      AppListState::kStateEmbeddedAssistant);
  return static_cast<views::View*>(contents_view()->GetPageView(index));
}

views::View* AssistantTestApiImpl::main_view() {
  return page_view()->GetViewByID(AssistantViewID::kMainView);
}

views::View* AssistantTestApiImpl::ui_element_container() {
  return page_view()->GetViewByID(AssistantViewID::kUiElementContainer);
}

views::Textfield* AssistantTestApiImpl::input_text_field() {
  return static_cast<views::Textfield*>(
      page_view()->GetViewByID(AssistantViewID::kTextQueryField));
}

views::View* AssistantTestApiImpl::mic_view() {
  return page_view()->GetViewByID(AssistantViewID::kMicView);
}

views::View* AssistantTestApiImpl::greeting_label() {
  return page_view()->GetViewByID(AssistantViewID::kGreetingLabel);
}

views::View* AssistantTestApiImpl::voice_input_toggle() {
  return page_view()->GetViewByID(AssistantViewID::kVoiceInputToggle);
}

views::View* AssistantTestApiImpl::keyboard_input_toggle() {
  return page_view()->GetViewByID(AssistantViewID::kKeyboardInputToggle);
}

views::View* AssistantTestApiImpl::suggestion_chip_container() {
  return page_view()->GetViewByID(AssistantViewID::kSuggestionContainer);
}

views::View* AssistantTestApiImpl::onboarding_view() {
  return page_view()->GetViewByID(AssistantViewID::kOnboardingView);
}

views::View* AssistantTestApiImpl::opt_in_view() {
  return page_view()->GetViewByID(AssistantViewID::kOptInView);
}

aura::Window* AssistantTestApiImpl::window() {
  return main_view()->GetWidget()->GetNativeWindow();
}

AppListView* AssistantTestApiImpl::app_list_view() {
  return contents_view()->app_list_view();
}

aura::Window* AssistantTestApiImpl::root_window() {
  return Shell::Get()->GetPrimaryRootWindow();
}

void AssistantTestApiImpl::EnableAssistantAndWait() {
  SetAssistantEnabled(true);
  GetAssistantState()->NotifyFeatureAllowed(
      assistant::AssistantAllowedState::ALLOWED);
  GetAssistantState()->NotifyStatusChanged(assistant::AssistantStatus::READY);
  WaitUntilIdle();
}

void AssistantTestApiImpl::SetAssistantEnabled(bool enabled) {
  Shell::Get()->session_controller()->GetPrimaryUserPrefService()->SetBoolean(
      assistant::prefs::kAssistantEnabled, enabled);

  // Ensure the value has taken effect.
  ASSERT_EQ(GetAssistantState()->settings_enabled(), enabled)
      << "Changing this preference did not take effect immediately, which will "
         "cause timing issues in this test. If this trace is seen we must add "
         "a waiter here to wait for the new state to take effect.";
}

void AssistantTestApiImpl::SetScreenContextEnabled(bool enabled) {
  Shell::Get()->session_controller()->GetPrimaryUserPrefService()->SetBoolean(
      assistant::prefs::kAssistantContextEnabled, enabled);

  // Ensure the value has taken effect.
  ASSERT_EQ(GetAssistantState()->context_enabled(), enabled)
      << "Changing this preference did not take effect immediately, which will "
         "cause timing issues in this test. If this trace is seen we must add "
         "a waiter here to wait for the new state to take effect.";
}

void AssistantTestApiImpl::SetTabletMode(bool enable) {
  TabletMode::Get()->SetEnabledForTest(enable);
}

void AssistantTestApiImpl::StartOverview() {
  Shell::Get()->overview_controller()->StartOverview(
      OverviewStartAction::kTests);
}

void AssistantTestApiImpl::SetConsentStatus(
    assistant::prefs::ConsentStatus consent_status) {
  Shell::Get()->session_controller()->GetPrimaryUserPrefService()->SetInteger(
      assistant::prefs::kAssistantConsentStatus, consent_status);

  // Ensure the value has taken effect.
  ASSERT_EQ(GetAssistantState()->consent_status(), consent_status)
      << "Changing this preference did not take effect immediately, which will "
         "cause timing issues in this test. If this trace is seen we must add "
         "a waiter here to wait for the new state to take effect.";
}

void AssistantTestApiImpl::SetNumberOfSessionsWhereOnboardingShown(
    int number_of_sessions) {
  Shell::Get()->session_controller()->GetPrimaryUserPrefService()->SetInteger(
      prefs::kAssistantNumSessionsWhereOnboardingShown, number_of_sessions);
}

void AssistantTestApiImpl::SetOnboardingMode(
    assistant::prefs::AssistantOnboardingMode onboarding_mode) {
  Shell::Get()->session_controller()->GetPrimaryUserPrefService()->SetString(
      assistant::prefs::kAssistantOnboardingMode,
      assistant::prefs::ToOnboardingModeString(onboarding_mode));

  // Ensure the value has taken effect.
  ASSERT_EQ(GetAssistantState()->onboarding_mode(), onboarding_mode)
      << "Changing this preference did not take effect immediately, which will "
         "cause timing issues in this test. If this trace is seen we must add "
         "a waiter here to wait for the new state to take effect.";
}

void AssistantTestApiImpl::SetPreferVoice(bool value) {
  Shell::Get()->session_controller()->GetPrimaryUserPrefService()->SetBoolean(
      assistant::prefs::kAssistantLaunchWithMicOpen, value);

  // Ensure the value has taken effect.
  ASSERT_EQ(GetAssistantState()->launch_with_mic_open(), value)
      << "Changing this preference did not take effect immediately, which will "
         "cause timing issues in this test. If this trace is seen we must add "
         "a waiter here to wait for the new state to take effect.";
}

void AssistantTestApiImpl::SetTimeOfLastInteraction(base::Time time) {
  Shell::Get()->session_controller()->GetPrimaryUserPrefService()->SetTime(
      prefs::kAssistantTimeOfLastInteraction, time);
}

AssistantState* AssistantTestApiImpl::GetAssistantState() {
  return AssistantState::Get();
}

void AssistantTestApiImpl::WaitUntilIdle() {
  base::RunLoop().RunUntilIdle();
}

bool AssistantTestApiImpl::AppListViewsHaveBeenCreated() const {
  return contents_view_or_null() != nullptr;
}

ContentsView* AssistantTestApiImpl::contents_view() {
  ContentsView* result = contents_view_or_null();
  DCHECK(result) << "App list has not been initialized yet. "
                    "Be sure to display the Assistant UI first.";
  return result;
}

ContentsView* AssistantTestApiImpl::contents_view_or_null() const {
  auto* app_list_view =
      Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();

  if (!app_list_view)
    return nullptr;

  return app_list_view->app_list_main_view()->contents_view();
}

void AssistantTestApiImpl::SendKeyPress(ui::KeyboardCode key) {
  ui::test::EventGenerator event_generator(root_window());
  event_generator.PressKey(key, /*flags=*/ui::EF_NONE);
}

}  // namespace ash