chromium/ash/system/mahi/mahi_error_status_view_pixeltest.cc

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

#include <optional>
#include <string>
#include <utility>

#include "ash/system/mahi/mahi_constants.h"
#include "ash/system/mahi/mahi_panel_widget.h"
#include "ash/system/mahi/mahi_ui_controller.h"
#include "ash/system/mahi/test/mock_mahi_manager.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/pixel/ash_pixel_differ.h"
#include "base/functional/callback.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/components/mahi/public/cpp/mahi_manager.h"
#include "chromeos/constants/chromeos_features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/display/display.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/view.h"
#include "ui/views/view_utils.h"
#include "ui/views/widget/unique_widget_ptr.h"
#include "ui/views/widget/widget.h"

namespace ash {

namespace {

// Aliases
using chromeos::MahiResponseStatus;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::Values;

std::string GetScreenShotNameForErrorStatus(MahiResponseStatus status) {
  switch (status) {
    case chromeos::MahiResponseStatus::kCantFindOutputData:
      return "CantFindOutputData";
    case chromeos::MahiResponseStatus::kContentExtractionError:
      return "ContentExtractionError";
    case chromeos::MahiResponseStatus::kInappropriate:
      return "Inappropriate";
    case chromeos::MahiResponseStatus::kUnknownError:
      return "UnknownError";
    case chromeos::MahiResponseStatus::kQuotaLimitHit:
      return "QuotaLimitHit";
    case chromeos::MahiResponseStatus::kResourceExhausted:
      return "ResourceExhausted";
    case chromeos::MahiResponseStatus::kRestrictedCountry:
      return "RestrictedCountry";
    case chromeos::MahiResponseStatus::kUnsupportedLanguage:
      return "UnsupportedLanguage";
    case chromeos::MahiResponseStatus::kLowQuota:
    case chromeos::MahiResponseStatus::kSuccess:
      NOTREACHED();
  }
}

}  // namespace

class MahiErrorStatusViewPixelTestBase : public AshTestBase {
 protected:
  void ShowMahiPanel() {
    mahi_panel_widget_ = MahiPanelWidget::CreateAndShowPanelWidget(
        GetPrimaryDisplay().id(), /*mahi_menu_bounds=*/gfx::Rect(),
        ui_controller());
    mahi_panel_widget_->Show();
  }

  views::Widget* mahi_panel_widget() { return mahi_panel_widget_.get(); }
  MockMahiManager& mock_mahi_manager() { return mock_mahi_manager_; }
  MahiUiController* ui_controller() { return &ui_controller_; }

 private:
  // AshTestBase:
  std::optional<pixel_test::InitParams> CreatePixelTestInitParams()
      const override {
    return pixel_test::InitParams();
  }

  void SetUp() override {
    scoped_feature_list_.InitWithFeatures(
        /*enabled_features=*/{chromeos::features::kMahi,
                              chromeos::features::kFeatureManagementMahi},
        /*disabled_features=*/{});
    AshTestBase::SetUp();

    ON_CALL(mock_mahi_manager_, GetContentTitle)
        .WillByDefault(Return(u"content title"));
  }

  void TearDown() override {
    mahi_panel_widget_.reset();

    AshTestBase::TearDown();
  }

  base::test::ScopedFeatureList scoped_feature_list_;
  NiceMock<MockMahiManager> mock_mahi_manager_;
  MahiUiController ui_controller_;
  chromeos::ScopedMahiManagerSetter scoped_setter_{&mock_mahi_manager_};
  views::UniqueWidgetPtr mahi_panel_widget_;
};

// MahiErrorStatusViewPixelTest ------------------------------------------------

class MahiErrorStatusViewPixelTest
    : public MahiErrorStatusViewPixelTestBase,
      public testing::WithParamInterface<MahiResponseStatus> {};

INSTANTIATE_TEST_SUITE_P(All,
                         MahiErrorStatusViewPixelTest,
                         Values(MahiResponseStatus::kCantFindOutputData,
                                MahiResponseStatus::kContentExtractionError,
                                MahiResponseStatus::kInappropriate,
                                MahiResponseStatus::kQuotaLimitHit,
                                MahiResponseStatus::kResourceExhausted,
                                MahiResponseStatus::kRestrictedCountry,
                                MahiResponseStatus::kUnsupportedLanguage,
                                MahiResponseStatus::kUnknownError));

// Verifies the error status view when a summary update incurs an error
// specified by the test param.
TEST_P(MahiErrorStatusViewPixelTest, Basics) {
  ON_CALL(mock_mahi_manager(), GetSummary)
      .WillByDefault([&](chromeos::MahiManager::MahiSummaryCallback callback) {
        std::move(callback).Run(u"fake summary", GetParam());
      });

  ShowMahiPanel();
  views::View* const error_status_view =
      mahi_panel_widget()->GetContentsView()->GetViewByID(
          mahi_constants::ViewId::kErrorStatusView);
  ASSERT_TRUE(error_status_view);
  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
      GetScreenShotNameForErrorStatus(GetParam()), /*revision_number=*/5,
      error_status_view));
}

// Verifies the error status on the Mahi panel scroll view when asking a
// question.
TEST_P(MahiErrorStatusViewPixelTest, QuestionAnswerView) {
  ON_CALL(mock_mahi_manager(), AnswerQuestion)
      .WillByDefault(
          [](const std::u16string& question, bool current_panel_content,
             chromeos::MahiManager::MahiAnswerQuestionCallback callback) {
            std::move(callback).Run(u"answer", GetParam());
          });

  ShowMahiPanel();
  views::View* const mahi_contents_view =
      mahi_panel_widget()->GetContentsView();
  auto* const question_textfield =
      views::AsViewClass<views::Textfield>(mahi_contents_view->GetViewByID(
          mahi_constants::ViewId::kQuestionTextfield));
  ASSERT_TRUE(question_textfield);
  question_textfield->SetText(u"fake inappropriate question");

  auto* const send_button = mahi_contents_view->GetViewByID(
      mahi_constants::ViewId::kAskQuestionSendButton);
  ASSERT_TRUE(send_button);
  LeftClickOn(send_button);

  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
      GetScreenShotNameForErrorStatus(GetParam()), /*revision_number=*/2,
      mahi_contents_view->GetViewByID(mahi_constants::ViewId::kScrollView)));
}

}  // namespace ash