blob: f327ed890dee34a0bb67e566f2ac8e6bae4da647 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/ui_devtools/views/view_element.h"
#include "base/strings/utf_string_conversions.h"
#include "components/ui_devtools/Protocol.h"
#include "components/ui_devtools/ui_devtools_unittest_utils.h"
#include "ui/views/test/views_test_base.h"
namespace ui_devtools {
namespace {
// Returns a pair of the ClassProperties index and UIProperty index
std::pair<size_t, size_t> GetPropertyIndices(ui_devtools::ViewElement* element,
const std::string& property_name) {
std::vector<UIElement::ClassProperties> props =
element->GetCustomPropertiesForMatchedStyle();
for (size_t cp_index = 0; cp_index < props.size(); ++cp_index) {
for (size_t uip_index = 0; uip_index < props[cp_index].properties_.size();
++uip_index) {
if (props[cp_index].properties_[uip_index].name_ == property_name)
return std::make_pair(cp_index, uip_index);
}
}
DCHECK(false) << "Property " << property_name << " can not be found.";
return std::make_pair(-1, -1);
}
void TestBooleanCustomPropertySetting(ui_devtools::ViewElement* element,
const std::string& property_name,
bool init_value) {
std::pair<size_t, size_t> indices =
GetPropertyIndices(element, property_name);
std::string old_value(init_value ? "true" : "false");
std::vector<UIElement::ClassProperties> props =
element->GetCustomPropertiesForMatchedStyle();
std::vector<UIElement::UIProperty> ui_props =
props[indices.first].properties_;
EXPECT_EQ(ui_props[indices.second].value_, old_value);
// Check the property can be set accordingly.
std::string new_value(init_value ? "false" : "true");
std::string separator(":");
element->SetPropertiesFromString(property_name + separator + new_value);
props = element->GetCustomPropertiesForMatchedStyle();
ui_props = props[indices.first].properties_;
EXPECT_EQ(ui_props[indices.second].name_, property_name);
EXPECT_EQ(ui_props[indices.second].value_, new_value);
element->SetPropertiesFromString(property_name + separator + old_value);
props = element->GetCustomPropertiesForMatchedStyle();
ui_props = props[indices.first].properties_;
EXPECT_EQ(ui_props[indices.second].name_, property_name);
EXPECT_EQ(ui_props[indices.second].value_, old_value);
}
} // namespace
using ::testing::_;
class NamedTestView : public views::View {
public:
METADATA_HEADER(NamedTestView);
// For custom properties test.
base::string16 GetTooltipText(const gfx::Point& p) const override {
return base::ASCIIToUTF16("This is the tooltip");
}
int GetBoolProperty() const { return bool_property_; }
void SetBoolProperty(bool bool_property) { bool_property_ = bool_property; }
SkColor GetColorProperty() const { return color_property_; }
void SetColorProperty(SkColor color) { color_property_ = color; }
private:
bool bool_property_ = false;
SkColor color_property_ = SK_ColorGRAY;
};
BEGIN_METADATA(NamedTestView)
METADATA_PARENT_CLASS(views::View)
ADD_PROPERTY_METADATA(NamedTestView, bool, BoolProperty)
ADD_PROPERTY_METADATA(NamedTestView, SkColor, ColorProperty)
END_METADATA()
class ViewElementTest : public views::ViewsTestBase {
public:
ViewElementTest() {}
~ViewElementTest() override {}
protected:
void SetUp() override {
views::ViewsTestBase::SetUp();
view_.reset(new NamedTestView);
delegate_.reset(new testing::NiceMock<MockUIElementDelegate>);
// |OnUIElementAdded| is called on element creation.
EXPECT_CALL(*delegate_, OnUIElementAdded(_, _)).Times(1);
element_.reset(new ViewElement(view_.get(), delegate_.get(), nullptr));
}
NamedTestView* view() { return view_.get(); }
ViewElement* element() { return element_.get(); }
MockUIElementDelegate* delegate() { return delegate_.get(); }
private:
std::unique_ptr<NamedTestView> view_;
std::unique_ptr<ViewElement> element_;
std::unique_ptr<MockUIElementDelegate> delegate_;
DISALLOW_COPY_AND_ASSIGN(ViewElementTest);
};
TEST_F(ViewElementTest, SettingsBoundsOnViewCallsDelegate) {
EXPECT_CALL(*delegate(), OnUIElementBoundsChanged(element())).Times(1);
view()->SetBounds(1, 2, 3, 4);
}
TEST_F(ViewElementTest, AddingChildView) {
// The first call is from the element being created, before it
// gets parented to |element_|.
EXPECT_CALL(*delegate(), OnUIElementAdded(nullptr, _)).Times(1);
EXPECT_CALL(*delegate(), OnUIElementAdded(element(), _)).Times(1);
views::View child_view;
view()->AddChildView(&child_view);
DCHECK_EQ(element()->children().size(), 1U);
UIElement* child_element = element()->children()[0];
EXPECT_CALL(*delegate(), OnUIElementRemoved(child_element)).Times(1);
view()->RemoveChildView(&child_view);
}
TEST_F(ViewElementTest, SettingsBoundsOnElementSetsOnView) {
DCHECK(view()->bounds() == gfx::Rect());
element()->SetBounds(gfx::Rect(1, 2, 3, 4));
EXPECT_EQ(view()->bounds(), gfx::Rect(1, 2, 3, 4));
}
TEST_F(ViewElementTest, SetPropertiesFromString) {
static const char* kEnabledProperty = "Enabled";
TestBooleanCustomPropertySetting(element(), kEnabledProperty, true);
std::pair<size_t, size_t> indices =
GetPropertyIndices(element(), kEnabledProperty);
// Test setting a non-existent property has no effect.
element()->SetPropertiesFromString("Enable:false");
std::vector<UIElement::ClassProperties> props =
element()->GetCustomPropertiesForMatchedStyle();
std::vector<UIElement::UIProperty> ui_props =
props[indices.first].properties_;
EXPECT_EQ(ui_props[indices.second].name_, kEnabledProperty);
EXPECT_EQ(ui_props[indices.second].value_, "true");
// Test setting empty string for property value has no effect.
element()->SetPropertiesFromString("Enabled:");
props = element()->GetCustomPropertiesForMatchedStyle();
ui_props = props[indices.first].properties_;
EXPECT_EQ(ui_props[indices.second].name_, kEnabledProperty);
EXPECT_EQ(ui_props[indices.second].value_, "true");
// Ensure setting pure whitespace doesn't crash.
ASSERT_NO_FATAL_FAILURE(element()->SetPropertiesFromString(" \n "));
}
TEST_F(ViewElementTest, SettingVisibilityOnView) {
TestBooleanCustomPropertySetting(element(), "Visible", true);
}
TEST_F(ViewElementTest, SettingSubclassBoolProperty) {
TestBooleanCustomPropertySetting(element(), "BoolProperty", false);
}
TEST_F(ViewElementTest, GetBounds) {
gfx::Rect bounds;
view()->SetBounds(10, 20, 30, 40);
element()->GetBounds(&bounds);
EXPECT_EQ(bounds, gfx::Rect(10, 20, 30, 40));
}
TEST_F(ViewElementTest, GetAttributes) {
std::vector<std::string> attrs = element()->GetAttributes();
EXPECT_THAT(attrs,
testing::ElementsAre("name", NamedTestView::kViewClassName));
}
TEST_F(ViewElementTest, GetCustomProperties) {
std::vector<UIElement::ClassProperties> props =
element()->GetCustomPropertiesForMatchedStyle();
// The ClassProperties vector should be of size 2.
DCHECK_EQ(props.size(), 2U);
std::pair<size_t, size_t> indices =
GetPropertyIndices(element(), "BoolProperty");
// The BoolProperty property should be in the second ClassProperties object in
// the vector.
DCHECK_EQ(indices.first, 0U);
indices = GetPropertyIndices(element(), "tooltip");
// The tooltip property should be in the second ClassProperties object in the
// vector.
DCHECK_EQ(indices.first, 1U);
std::vector<UIElement::UIProperty> ui_props =
props[indices.first].properties_;
EXPECT_EQ(ui_props[indices.second].name_, "tooltip");
EXPECT_EQ(ui_props[indices.second].value_, "This is the tooltip");
}
TEST_F(ViewElementTest, CheckCustomProperties) {
std::vector<UIElement::ClassProperties> props =
element()->GetCustomPropertiesForMatchedStyle();
DCHECK_GT(props.size(), 1U);
DCHECK_GT(props[1].properties_.size(), 1U);
// Check visibility information is passed in.
bool is_visible_set = false;
for (size_t i = 0; i < props[1].properties_.size(); ++i) {
if (props[1].properties_[i].name_ == "Visible")
is_visible_set = true;
}
EXPECT_TRUE(is_visible_set);
}
TEST_F(ViewElementTest, GetNodeWindowAndScreenBounds) {
// For this to be meaningful, the view must be in
// a widget.
auto widget = std::make_unique<views::Widget>();
views::Widget::InitParams params =
CreateParams(views::Widget::InitParams::TYPE_WINDOW);
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget->Init(std::move(params));
widget->Show();
widget->GetContentsView()->AddChildView(view());
gfx::Rect bounds(50, 60, 70, 80);
view()->SetBoundsRect(bounds);
std::pair<gfx::NativeWindow, gfx::Rect> window_and_bounds =
element()->GetNodeWindowAndScreenBounds();
EXPECT_EQ(window_and_bounds.first, widget->GetNativeWindow());
EXPECT_EQ(window_and_bounds.second, view()->GetBoundsInScreen());
view()->parent()->RemoveChildView(view());
}
TEST_F(ViewElementTest, ColorProperty) {
EXPECT_EQ(GetPropertyIndices(element(), "--ColorProperty").first, 0U);
DCHECK_EQ(view()->GetColorProperty(), SK_ColorGRAY);
EXPECT_TRUE(element()->SetPropertiesFromString(
"--ColorProperty: rgba(0,0, 255, 1);"));
EXPECT_EQ(view()->GetColorProperty(), SK_ColorBLUE);
EXPECT_TRUE(element()->SetPropertiesFromString("--ColorProperty: #0352fc"));
EXPECT_EQ(view()->GetColorProperty(), SkColorSetARGB(255, 3, 82, 252));
EXPECT_TRUE(element()->SetPropertiesFromString(
"--ColorProperty: hsl(240, 84%, 28%);"));
EXPECT_EQ(view()->GetColorProperty(), SkColorSetARGB(255, 11, 11, 131));
}
TEST_F(ViewElementTest, BadColorProperty) {
DCHECK_EQ(view()->GetColorProperty(), SK_ColorGRAY);
EXPECT_FALSE(
element()->SetPropertiesFromString("-ColorProperty: rgba(1,2,3,4);"));
EXPECT_EQ(view()->GetColorProperty(), SK_ColorGRAY);
EXPECT_FALSE(
element()->SetPropertiesFromString("--ColorProperty: rgba(1,2,3,4;"));
EXPECT_EQ(view()->GetColorProperty(), SK_ColorGRAY);
EXPECT_FALSE(
element()->SetPropertiesFromString("--ColorProperty: rgb(1,2,3,4;)"));
EXPECT_EQ(view()->GetColorProperty(), SK_ColorGRAY);
}
TEST_F(ViewElementTest, GetSources) {
std::vector<UIElement::Source> sources = element()->GetSources();
// ViewElement should have two sources: from NamedTestView and from View.
EXPECT_EQ(sources.size(), 2U);
EXPECT_EQ(sources[0].path_,
"components/ui_devtools/views/view_element_unittest.cc");
EXPECT_EQ(sources[1].path_, "ui/views/view.h");
}
} // namespace ui_devtools