blob: d0bd828d3644eefc20706ed26d93761ccfc83e2a [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// 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/widget_element.h"
#include <memory>
#include "base/memory/raw_ptr.h"
#include "components/ui_devtools/protocol.h"
#include "components/ui_devtools/root_element.h"
#include "components/ui_devtools/ui_devtools_unittest_utils.h"
#include "components/ui_devtools/views/view_element.h"
#include "ui/views/test/views_test_base.h"
namespace ui_devtools {
using ::testing::_;
namespace {
const std::string kWidgetName = "A test widget";
}
class MockWidgetElementDelegate : public MockUIElementDelegate {
public:
MockWidgetElementDelegate() = default;
MockWidgetElementDelegate(const MockWidgetElementDelegate&) = delete;
MockWidgetElementDelegate& operator=(const MockWidgetElementDelegate&) =
delete;
~MockWidgetElementDelegate() override = default;
UIElement* root_element() { return &root_element_; }
private:
RootElement root_element_{this};
};
class WidgetElementTest : public views::ViewsTestBase {
public:
WidgetElementTest() = default;
WidgetElementTest(const WidgetElementTest&) = delete;
WidgetElementTest& operator=(const WidgetElementTest&) = delete;
~WidgetElementTest() override = default;
void SetUp() override {
views::ViewsTestBase::SetUp();
widget_ = new views::Widget;
views::Widget::InitParams params =
CreateParams(views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET,
views::Widget::InitParams::TYPE_WINDOW);
params.name = kWidgetName;
widget_->Init(std::move(params));
delegate_ =
std::make_unique<testing::NiceMock<MockWidgetElementDelegate>>();
element_ =
std::make_unique<WidgetElement>(widget_, delegate_.get(), nullptr);
// The widget element will delete the ViewElement in |OnWillRemoveView|
// TODO(lgrey): I think probably WidgetElement should do this itself
// rather than making the DOMAgent (or test) put the tree together.
element()->AddChild(
new ViewElement(widget_->GetRootView(), delegate_.get(), element()));
}
void TearDown() override {
if (widget())
CloseWidget();
views::ViewsTestBase::TearDown();
}
protected:
views::Widget* widget() { return widget_; }
void CloseWidget() {
widget_->CloseNow();
widget_ = nullptr;
}
WidgetElement* element() { return element_.get(); }
MockWidgetElementDelegate* delegate() { return delegate_.get(); }
void AddWidgetElementToTree() {
element_->set_parent(delegate_->root_element());
delegate_->root_element()->AddChild(element_.get());
}
private:
raw_ptr<views::Widget, DanglingUntriaged> widget_ = nullptr;
std::unique_ptr<WidgetElement> element_;
std::unique_ptr<MockWidgetElementDelegate> delegate_;
};
TEST_F(WidgetElementTest, SettingsBoundsOnWidgetCallsDelegate) {
// Once for the root view, and once for the widget.
EXPECT_CALL(*delegate(), OnUIElementBoundsChanged(_))
.Times(::testing::AtLeast(1));
EXPECT_CALL(*delegate(), OnUIElementBoundsChanged(element()))
.Times(::testing::AtLeast(1));
widget()->SetBounds(gfx::Rect(10, 20, 300, 400));
}
TEST_F(WidgetElementTest, SettingsBoundsOnElementSetsOnWidget) {
gfx::Rect old_bounds = widget()->GetRestoredBounds();
gfx::Rect new_bounds = gfx::Rect(10, 20, 300, 400);
DCHECK(old_bounds != new_bounds);
element()->SetBounds(new_bounds);
EXPECT_EQ(widget()->GetRestoredBounds(), new_bounds);
}
TEST_F(WidgetElementTest, SettingVisibleOnElementSetsOnWidget) {
DCHECK(!widget()->IsVisible());
element()->SetVisible(true);
EXPECT_TRUE(widget()->IsVisible());
element()->SetVisible(false);
EXPECT_FALSE(widget()->IsVisible());
}
TEST_F(WidgetElementTest, GetBounds) {
gfx::Rect actual_bounds;
gfx::Rect new_bounds = gfx::Rect(10, 20, 300, 400);
widget()->SetBounds(new_bounds);
element()->GetBounds(&actual_bounds);
EXPECT_EQ(actual_bounds, new_bounds);
}
TEST_F(WidgetElementTest, GetVisible) {
DCHECK(!widget()->IsVisible());
bool visible;
element()->GetVisible(&visible);
EXPECT_FALSE(visible);
widget()->Show();
element()->GetVisible(&visible);
EXPECT_TRUE(visible);
widget()->Hide();
element()->GetVisible(&visible);
EXPECT_FALSE(visible);
}
TEST_F(WidgetElementTest, GetAttributes) {
std::vector<std::string> attrs = element()->GetAttributes();
ASSERT_FALSE(widget()->IsActive());
EXPECT_THAT(attrs,
testing::ElementsAre("name", kWidgetName, "active", "false"));
widget()->Activate();
attrs = element()->GetAttributes();
EXPECT_THAT(attrs,
testing::ElementsAre("name", kWidgetName, "active", "true"));
}
TEST_F(WidgetElementTest, GetNodeWindowAndScreenBounds) {
std::pair<gfx::NativeWindow, gfx::Rect> window_and_bounds =
element()->GetNodeWindowAndScreenBounds();
EXPECT_EQ(widget()->GetNativeWindow(), window_and_bounds.first);
EXPECT_EQ(widget()->GetWindowBoundsInScreen(), window_and_bounds.second);
}
TEST_F(WidgetElementTest, GetNodeBoundsInScreen) {
gfx::Rect test_bounds(10, 20, 300, 400);
widget()->SetBounds(test_bounds);
EXPECT_EQ(widget()->GetWindowBoundsInScreen(),
element()->GetNodeBoundsInScreen());
}
TEST_F(WidgetElementTest, TrackNonParentedElementLifetime) {
ASSERT_TRUE(widget());
EXPECT_FALSE(element()->parent());
// While closing the widget, the widget element and its children should be
// removed as well.
// Remove the root view element.
EXPECT_CALL(*delegate(), OnUIElementRemoved(_));
// Remove the widget element.
EXPECT_CALL(*delegate(), OnUIElementRemoved(element()));
CloseWidget();
}
TEST_F(WidgetElementTest, TrackParentedElementLifetime) {
ASSERT_TRUE(widget());
AddWidgetElementToTree();
UIElement* root = delegate()->root_element();
EXPECT_EQ(root, element()->parent());
EXPECT_EQ(1u, root->children().size());
// Remove the root view element.
EXPECT_CALL(*delegate(), OnUIElementRemoved(_));
// Remove the widget element.
EXPECT_CALL(*delegate(), OnUIElementRemoved(element()));
CloseWidget();
// After closing the widget, the element tree should only has the root.
EXPECT_EQ(0u, root->children().size());
}
} // namespace ui_devtools