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
ash / app_menu / notification_menu_controller_unittest.cc [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 "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