#include "ash/user_education/views/help_bubble_view_ash.h"
#include <optional>
#include <vector>
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/user_education/user_education_types.h"
#include "ash/user_education/user_education_util.h"
#include "ash/user_education/views/help_bubble_view_ash_test_base.h"
#include "ash/wm/window_util.h"
#include "components/user_education/common/help_bubble/help_bubble_params.h"
#include "components/vector_icons/vector_icons.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/window.h"
#include "ui/aura/window_targeter.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/color/color_provider.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/interaction/element_tracker_views.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/coordinate_conversion.h"
namespace ash {
namespace {
using ::testing::AnyOf;
using ::testing::Conditional;
using ::testing::Eq;
using ::testing::IsNull;
using ::testing::Not;
using ::testing::Property;
using ::user_education::HelpBubbleArrow;
MATCHER_P(Contains, window, "") {
return arg->Contains(window);
}
ui::MouseEvent CreateMouseMovedEvent(aura::Window* target,
const gfx::Point& location_in_screen) {
gfx::Point location(location_in_screen);
wm::ConvertPointFromScreen(target, &location);
ui::MouseEvent mouse_moved_event(ui::EventType::kMouseMoved, location,
location_in_screen, ui::EventTimeForNow(),
ui::EF_NONE,
ui::EF_NONE);
ui::Event::DispatcherApi(&mouse_moved_event).set_target(target);
return mouse_moved_event;
}
std::vector<gfx::Point> GetPerimeterPoints(const gfx::Rect& rect) {
return std::vector<gfx::Point>({rect.origin(), rect.top_center(),
rect.top_right(), rect.right_center(),
rect.bottom_right(), rect.bottom_center(),
rect.bottom_left(), rect.left_center()});
}
}
using HelpBubbleViewAshTest = HelpBubbleViewAshTestBase;
TEST_F(HelpBubbleViewAshTest, ParentWindow) {
auto* const help_bubble_view = CreateHelpBubbleView();
EXPECT_TRUE(help_bubble_view->anchor_widget()
->GetNativeWindow()
->GetRootWindow()
->GetChildById(kShellWindowId_HelpBubbleContainer)
->Contains(help_bubble_view->GetWidget()->GetNativeWindow()));
}
class HelpBubbleViewAshBodyIconTest
: public HelpBubbleViewAshTestBase,
public ::testing::WithParamInterface<
std::tuple</*body_icon_from_params=*/std::optional<
std::reference_wrapper<const gfx::VectorIcon>>,
std::optional<
std::reference_wrapper<const gfx::VectorIcon>>>> {
public:
const std::optional<std::reference_wrapper<const gfx::VectorIcon>>
body_icon_from_params() const {
return std::get<0>(GetParam());
}
const std::optional<std::reference_wrapper<const gfx::VectorIcon>>
body_icon_from_extended_properties() const {
return std::get<1>(GetParam());
}
};
INSTANTIATE_TEST_SUITE_P(
All,
HelpBubbleViewAshBodyIconTest,
::testing::Combine(
::testing::Values(
std::make_optional(std::cref(gfx::VectorIcon::EmptyIcon())),
std::make_optional(std::cref(vector_icons::kCelebrationIcon)),
std::make_optional(std::cref(vector_icons::kHelpIcon)),
std::nullopt),
::testing::Values(
std::make_optional(std::cref(gfx::VectorIcon::EmptyIcon())),
std::make_optional(std::cref(vector_icons::kCelebrationIcon)),
std::make_optional(std::cref(vector_icons::kHelpIcon)),
std::nullopt)));
TEST_P(HelpBubbleViewAshBodyIconTest, BodyIcon) {
user_education::HelpBubbleParams params;
params.extended_properties =
user_education_util::CreateExtendedProperties(HelpBubbleId::kTest);
if (const auto& body_icon_from_params = this->body_icon_from_params()) {
params.body_icon = &body_icon_from_params->get();
}
if (const auto& body_icon_from_extended_properties =
this->body_icon_from_extended_properties()) {
params.extended_properties = user_education_util::CreateExtendedProperties(
std::move(params.extended_properties),
user_education_util::CreateExtendedProperties(
body_icon_from_extended_properties->get()));
}
auto* const help_bubble_view = CreateHelpBubbleView(std::move(params));
ASSERT_NE(help_bubble_view, nullptr);
const gfx::VectorIcon& expected_body_icon =
body_icon_from_extended_properties().value_or(
body_icon_from_params().value_or(gfx::VectorIcon::EmptyIcon()));
EXPECT_THAT(
views::ElementTrackerViews::GetInstance()
->GetUniqueViewAs<views::ImageView>(
HelpBubbleViewAsh::kBodyIconIdForTesting,
views::ElementTrackerViews::GetContextForView(help_bubble_view)),
Conditional(&expected_body_icon != &gfx::VectorIcon::EmptyIcon(),
Property(&views::ImageView::GetImageModel,
Eq(ui::ImageModel::FromVectorIcon(
expected_body_icon,
cros_tokens::kCrosSysDialogContainer, 20))),
IsNull()));
}
TEST_F(HelpBubbleViewAshTest, BackgroundColor) {
const auto* const help_bubble_view = CreateHelpBubbleView();
EXPECT_EQ(help_bubble_view->background_color(),
cros_tokens::kCrosSysDialogContainer);
}
TEST_F(HelpBubbleViewAshTest, CanActivate) {
const auto* const help_bubble_view = CreateHelpBubbleView();
EXPECT_TRUE(help_bubble_view->CanActivate());
}
TEST_F(HelpBubbleViewAshTest, RootViewAccessibleName) {
auto* const help_bubble_view = CreateHelpBubbleView();
ui::AXNodeData root_view_data;
help_bubble_view->GetWidget()
->GetRootView()
->GetViewAccessibility()
.GetAccessibleNodeData(&root_view_data);
EXPECT_EQ(
root_view_data.GetString16Attribute(ax::mojom::StringAttribute::kName),
help_bubble_view->GetAccessibleWindowTitle());
}
TEST_F(HelpBubbleViewAshTest, HitTest) {
auto* const help_bubble_view = CreateHelpBubbleView();
auto* const help_bubble_widget = help_bubble_view->GetWidget();
auto* const help_bubble_window = help_bubble_widget->GetNativeWindow();
auto* const root_window = help_bubble_window->GetRootWindow();
auto* const root_window_targeter = root_window->targeter();
gfx::Rect contents_bounds(help_bubble_view->GetBoundsInScreen());
contents_bounds.Inset(1);
for (const gfx::Point& point : GetPerimeterPoints(contents_bounds)) {
EXPECT_THAT(window_util::GetEventHandlerForEvent(
CreateMouseMovedEvent(root_window, point)),
AnyOf(Eq(help_bubble_window), Contains(help_bubble_window)));
ui::MouseEvent press(ui::EventType::kMousePressed, point, point,
base::TimeTicks::Now(), ui::EF_NONE,
ui::EF_LEFT_MOUSE_BUTTON);
EXPECT_EQ(root_window_targeter->FindTargetForEvent(root_window, &press),
help_bubble_window);
}
gfx::Rect shadow_bounds(help_bubble_view->GetBoundsInScreen());
shadow_bounds.Outset(1);
for (const gfx::Point& point : GetPerimeterPoints(shadow_bounds)) {
EXPECT_THAT(
window_util::GetEventHandlerForEvent(
CreateMouseMovedEvent(root_window, point)),
Not(AnyOf(Eq(help_bubble_window), Contains(help_bubble_window))));
ui::MouseEvent press(ui::EventType::kMousePressed, point, point,
base::TimeTicks::Now(), ui::EF_NONE,
ui::EF_LEFT_MOUSE_BUTTON);
EXPECT_NE(root_window_targeter->FindTargetForEvent(root_window, &press),
help_bubble_window);
}
}
}