1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
// 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 "ash/app_menu/notification_menu_controller.h"
#include "ash/app_menu/app_menu_model_adapter.h"
#include "ash/test/ash_test_base.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/mojom/menu_source_type.mojom.h"
#include "ui/menus/simple_menu_model.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/submenu_view.h"
namespace ash {
namespace {
constexpr char kTestAppId[] = "test-app-id";
void BuildAndSendNotification(const std::string& app_id,
const std::string& notification_id) {
const message_center::NotifierId notifier_id(
message_center::NotifierType::APPLICATION, app_id);
std::unique_ptr<message_center::Notification> notification =
std::make_unique<message_center::Notification>(
message_center::NOTIFICATION_TYPE_SIMPLE, notification_id,
u"Test Web Notification", u"Notification message body.",
ui::ImageModel(), u"www.test.org", GURL(), notifier_id,
message_center::RichNotificationData(), nullptr /* delegate */);
message_center::MessageCenter::Get()->AddNotification(
std::move(notification));
}
class TestAppMenuModelAdapter : public AppMenuModelAdapter {
public:
TestAppMenuModelAdapter(const std::string& app_id,
std::unique_ptr<ui::SimpleMenuModel> model)
: AppMenuModelAdapter(app_id,
std::move(model),
nullptr,
ui::mojom::MenuSourceType::kMaxValue,
base::OnceClosure(),
false /* is_tablet_mode */) {}
TestAppMenuModelAdapter(const TestAppMenuModelAdapter&) = delete;
TestAppMenuModelAdapter& operator=(const TestAppMenuModelAdapter&) = delete;
private:
// AppMenuModelAdapter overrides:
void RecordHistogramOnMenuClosed() override {}
};
} // namespace
class NotificationMenuControllerTest : public AshTestBase {
public:
NotificationMenuControllerTest() = default;
NotificationMenuControllerTest(const NotificationMenuControllerTest&) =
delete;
NotificationMenuControllerTest& operator=(
const NotificationMenuControllerTest&) = delete;
~NotificationMenuControllerTest() override {}
// Overridden from AshTestBase:
void TearDown() override {
root_menu_item_view_.reset();
// NotificationMenuController removes itself from MessageCenter's observer
// list in the dtor, so force it to happen first to prevent a crash. This
// crash does not repro in production.
notification_menu_controller_.reset();
AshTestBase::TearDown();
}
void BuildMenu() {
test_app_menu_model_adapter_ = std::make_unique<TestAppMenuModelAdapter>(
kTestAppId,
std::make_unique<ui::SimpleMenuModel>(
nullptr /*ui::SimpleMenuModel::Delegate not required*/));
test_app_menu_model_adapter_->model()->AddItem(0, u"item 0");
test_app_menu_model_adapter_->model()->AddItem(1, u"item 1");
root_menu_item_view_ = std::make_unique<views::MenuItemView>(
test_app_menu_model_adapter_.get());
test_app_menu_model_adapter_->BuildMenu(root_menu_item_view());
notification_menu_controller_ =
std::make_unique<NotificationMenuController>(
kTestAppId, root_menu_item_view(),
test_app_menu_model_adapter_.get());
}
views::MenuItemView* root_menu_item_view() {
return root_menu_item_view_.get();
}
private:
// The root `MenuItemView`. In production, it is created and owned by the
// `AppMenuModelAdapter`. In this test setup, the test fixture owns it.
std::unique_ptr<views::MenuItemView> root_menu_item_view_;
std::unique_ptr<NotificationMenuController> notification_menu_controller_;
std::unique_ptr<TestAppMenuModelAdapter> test_app_menu_model_adapter_;
};
// Tests that NotificationMenuController does not add the
// NotificationMenuView container until a notification arrives.
TEST_F(NotificationMenuControllerTest, NotificationsArriveAfterBuilt) {
// Build the context menu without adding a notification for
// kTestAppId.
BuildMenu();
// There should only be two items in the context menu.
EXPECT_EQ(2u, root_menu_item_view()->GetSubmenu()->children().size());
// Add a notification.
BuildAndSendNotification(kTestAppId, std::string("notification_id"));
// NotificationMenuController should have added a third item, the
// container for NotificationMenuView, to the menu.
EXPECT_EQ(3u, root_menu_item_view()->GetSubmenu()->children().size());
}
// Tests that NotificationMenuController adds and removes the container
// MenuItemView when notifications come in before and after the menu has been
// built.
TEST_F(NotificationMenuControllerTest, NotificationsExistBeforeMenuIsBuilt) {
// Add the notification before the menu is built.
const std::string notification_id("notification_id");
BuildAndSendNotification(kTestAppId, notification_id);
// Build the menu, the container should be added.
BuildMenu();
EXPECT_EQ(3u, root_menu_item_view()->GetSubmenu()->children().size());
// Remove the notification, this should result in the NotificationMenuView
// container being removed.
message_center::MessageCenter::Get()->RemoveNotification(notification_id,
true);
EXPECT_EQ(2u, root_menu_item_view()->GetSubmenu()->children().size());
// Add the same notification.
BuildAndSendNotification(kTestAppId, notification_id);
// The container MenuItemView should be added again.
EXPECT_EQ(3u, root_menu_item_view()->GetSubmenu()->children().size());
}
// Tests that adding multiple notifications for kTestAppId does not add
// additional containers beyond the first.
TEST_F(NotificationMenuControllerTest, MultipleNotifications) {
// Add two notifications, then build the menu.
const std::string notification_id_0("notification_id_0");
BuildAndSendNotification(kTestAppId, notification_id_0);
const std::string notification_id_1("notification_id_1");
BuildAndSendNotification(kTestAppId, notification_id_1);
BuildMenu();
// Only one container MenuItemView should be added.
EXPECT_EQ(3u, root_menu_item_view()->GetSubmenu()->children().size());
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
// Remove one of the notifications.
message_center->RemoveNotification(notification_id_0, true);
// The container should still exist.
EXPECT_EQ(3u, root_menu_item_view()->GetSubmenu()->children().size());
// Remove the final notification.
message_center->RemoveNotification(notification_id_1, true);
// The container should be removed.
EXPECT_EQ(2u, root_menu_item_view()->GetSubmenu()->children().size());
}
} // namespace ash
|