blob: 8d0fbedea29e76e6306d8a0b6ba6cdf765ab4ede [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/bubble/bubble_dialog_model_host.h"
#include <memory>
#include <utility>
#include "base/functional/callback_helpers.h"
#include "base/test/gtest_util.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/interaction/element_tracker_views.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/view_class_properties.h"
namespace views {
using BubbleDialogModelHostTest = ViewsTestBase;
// TODO(pbos): Consider moving tests from this file into a test base for
// DialogModel that can be instantiated by any DialogModelHost implementation to
// check its compliance.
namespace {
// WeakPtrs to this delegate is used to infer when DialogModel is destroyed.
class WeakDialogModelDelegate : public ui::DialogModelDelegate {
public:
base::WeakPtr<WeakDialogModelDelegate> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
private:
base::WeakPtrFactory<WeakDialogModelDelegate> weak_ptr_factory_{this};
};
} // namespace
TEST_F(BubbleDialogModelHostTest, CloseIsSynchronousAndCallsWindowClosing) {
std::unique_ptr<Widget> anchor_widget =
CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
auto delegate = std::make_unique<WeakDialogModelDelegate>();
auto weak_delegate = delegate->GetWeakPtr();
int window_closing_count = 0;
auto host = std::make_unique<BubbleDialogModelHost>(
ui::DialogModel::Builder(std::move(delegate))
.SetDialogDestroyingCallback(base::BindOnce(base::BindOnce(
[](int* window_closing_count) { ++(*window_closing_count); },
&window_closing_count)))
.Build(),
anchor_widget->GetContentsView(), BubbleBorder::Arrow::TOP_RIGHT);
auto* host_ptr = host.get();
Widget* bubble_widget = BubbleDialogDelegate::CreateBubble(std::move(host));
test::WidgetDestroyedWaiter waiter(bubble_widget);
EXPECT_EQ(0, window_closing_count);
DCHECK_EQ(host_ptr, weak_delegate->dialog_model()->host());
weak_delegate->dialog_model()->host()->Close();
EXPECT_EQ(1, window_closing_count);
// The model (and hence delegate) should destroy synchronously, so the
// WeakPtr should disappear before waiting for the views Widget to close.
EXPECT_FALSE(weak_delegate);
waiter.Wait();
}
TEST_F(BubbleDialogModelHostTest, ElementIDsReportedCorrectly) {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kMenuItemId);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOkButtonId);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kExtraButtonId);
constexpr char16_t kMenuItemText[] = u"Menu Item";
constexpr char16_t kOkButtonText[] = u"OK";
constexpr char16_t kExtraButtonText[] = u"Button";
std::unique_ptr<Widget> anchor_widget =
CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
anchor_widget->Show();
const auto context =
views::ElementTrackerViews::GetContextForWidget(anchor_widget.get());
ui::DialogModelMenuItem::Params menu_item_params;
menu_item_params.SetId(kMenuItemId);
// TODO(crbug.com/1324298): Remove after addressing this issue.
menu_item_params.SetIsEnabled(false);
ui::DialogModelButton::Params ok_button_params;
ok_button_params.SetId(kOkButtonId);
ok_button_params.SetLabel(kOkButtonText);
ui::DialogModelButton::Params extra_button_params;
extra_button_params.SetId(kExtraButtonId);
extra_button_params.SetLabel(kExtraButtonText);
auto host = std::make_unique<BubbleDialogModelHost>(
ui::DialogModel::Builder()
.AddMenuItem(ui::ImageModel(), kMenuItemText, base::DoNothing(),
menu_item_params)
.AddOkButton(base::DoNothing(), ok_button_params)
.AddExtraButton(base::DoNothing(), extra_button_params)
.Build(),
anchor_widget->GetContentsView(), BubbleBorder::Arrow::TOP_RIGHT);
Widget* const bubble_widget =
BubbleDialogDelegate::CreateBubble(std::move(host));
test::WidgetVisibleWaiter waiter(bubble_widget);
bubble_widget->Show();
waiter.Wait();
ASSERT_TRUE(bubble_widget->IsVisible());
EXPECT_NE(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
kMenuItemId, context));
EXPECT_NE(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
kOkButtonId, context));
EXPECT_NE(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
kExtraButtonId, context));
bubble_widget->CloseNow();
}
TEST_F(BubbleDialogModelHostTest, OverrideDefaultButton) {
auto host = std::make_unique<BubbleDialogModelHost>(
ui::DialogModel::Builder()
.AddCancelButton(base::OnceClosure())
.OverrideDefaultButton(ui::DialogButton::DIALOG_BUTTON_CANCEL)
.Build(),
/*anchor_view=*/nullptr, BubbleBorder::Arrow::TOP_RIGHT);
EXPECT_EQ(host->GetDefaultDialogButton(),
ui::DialogButton::DIALOG_BUTTON_CANCEL);
}
TEST_F(BubbleDialogModelHostTest, OverrideDefaultButtonDeathTest) {
EXPECT_DCHECK_DEATH(std::make_unique<BubbleDialogModelHost>(
ui::DialogModel::Builder()
.AddCancelButton(base::OnceClosure())
.OverrideDefaultButton(ui::DialogButton::DIALOG_BUTTON_OK)
.Build(),
/*anchor_view=*/nullptr, BubbleBorder::Arrow::TOP_RIGHT))
<< "Cannot override the default button with a button which does not "
"exist.";
}
TEST_F(BubbleDialogModelHostTest,
SetInitiallyFocusedViewOverridesDefaultButtonFocus) {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFocusedField);
auto host = std::make_unique<BubbleDialogModelHost>(
ui::DialogModel::Builder()
.AddCancelButton(base::OnceClosure())
.OverrideDefaultButton(ui::DialogButton::DIALOG_BUTTON_CANCEL)
.AddTextfield(kFocusedField, u"label", u"text")
.SetInitiallyFocusedField(kFocusedField)
.Build(),
/*anchor_view=*/nullptr, BubbleBorder::Arrow::TOP_RIGHT);
EXPECT_EQ(host->GetDefaultDialogButton(),
ui::DialogButton::DIALOG_BUTTON_CANCEL);
EXPECT_EQ(host->GetInitiallyFocusedView()->GetProperty(kElementIdentifierKey),
kFocusedField);
}
} // namespace views