[go: nahoru, domu]

blob: efbcc02fb2343aa45dc3a9d42acf723e17759693 [file] [log] [blame]
Alex Newcomer9099fd92018-06-19 03:54:201// Copyright 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ash/app_menu/notification_menu_view.h"
6
7#include "ash/app_menu/notification_item_view.h"
8#include "ash/app_menu/notification_menu_view_test_api.h"
Tetsui Ohkubo801f7212018-11-12 02:32:219#include "base/run_loop.h"
Alex Newcomer9099fd92018-06-19 03:54:2010#include "base/strings/string_number_conversions.h"
11#include "base/strings/utf_string_conversions.h"
12#include "testing/gtest/include/gtest/gtest.h"
Alex Newcomerc650008c2018-07-02 21:01:0613#include "ui/compositor/scoped_animation_duration_scale_mode.h"
14#include "ui/events/base_event_utils.h"
15#include "ui/events/test/event_generator.h"
16#include "ui/gfx/transform.h"
Alex Newcomer9099fd92018-06-19 03:54:2017#include "ui/message_center/public/cpp/notification.h"
18#include "ui/views/controls/label.h"
19#include "ui/views/test/views_test_base.h"
20#include "ui/views/view.h"
Alex Newcomerc650008c2018-07-02 21:01:0621#include "ui/views/widget/widget_delegate.h"
Peter Kasting20e1d892018-11-16 03:08:3022#include "ui/views/widget/widget_utils.h"
Alex Newcomer9099fd92018-06-19 03:54:2023
24namespace ash {
Alex Newcomer9099fd92018-06-19 03:54:2025
26namespace {
27
28// The app id used in tests.
29constexpr char kTestAppId[] = "test-app-id";
30
Tommy Steimelb0f8f832019-10-11 18:11:1731class MockNotificationMenuController : public views::SlideOutControllerDelegate,
32 public NotificationMenuView::Delegate {
Alex Newcomerc650008c2018-07-02 21:01:0633 public:
34 MockNotificationMenuController() = default;
Alex Newcomer1fe9929e2018-07-20 21:18:5035 ~MockNotificationMenuController() override = default;
Alex Newcomerc650008c2018-07-02 21:01:0636
37 void ActivateNotificationAndClose(
38 const std::string& notification_id) override {
39 activation_count_++;
40 }
41
Alex Newcomer1fe9929e2018-07-20 21:18:5042 void OnOverflowAddedOrRemoved() override {
43 overflow_added_or_removed_count_++;
44 }
45
Alex Newcomerc650008c2018-07-02 21:01:0646 ui::Layer* GetSlideOutLayer() override {
47 return notification_menu_view_->GetSlideOutLayer();
48 }
49
yoshiki iguchi1e545ff2018-10-26 06:15:3850 void OnSlideChanged(bool in_progress) override {}
Alex Newcomerc650008c2018-07-02 21:01:0651
52 void OnSlideOut() override { slide_out_count_++; }
53
54 void set_notification_menu_view(
55 NotificationMenuView* notification_menu_view) {
56 notification_menu_view_ = notification_menu_view;
57 }
58
59 int slide_out_count_ = 0;
60 int activation_count_ = 0;
Alex Newcomer1fe9929e2018-07-20 21:18:5061 int overflow_added_or_removed_count_ = 0;
Alex Newcomerc650008c2018-07-02 21:01:0662
63 // Owned by NotificationMenuViewTest.
64 NotificationMenuView* notification_menu_view_ = nullptr;
65
66 DISALLOW_COPY_AND_ASSIGN(MockNotificationMenuController);
67};
68
Alex Newcomer9099fd92018-06-19 03:54:2069} // namespace
70
71class NotificationMenuViewTest : public views::ViewsTestBase {
72 public:
73 NotificationMenuViewTest() {}
74 ~NotificationMenuViewTest() override = default;
75
76 // views::ViewsTestBase:
77 void SetUp() override {
78 views::ViewsTestBase::SetUp();
Alex Newcomerc650008c2018-07-02 21:01:0679
80 zero_duration_scope_ =
81 std::make_unique<ui::ScopedAnimationDurationScaleMode>(
82 ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
83
84 mock_notification_menu_controller_ =
85 std::make_unique<MockNotificationMenuController>();
86
Allen Bauerd8857e02020-05-26 21:51:3287 auto notification_menu_view = std::make_unique<NotificationMenuView>(
Alex Newcomerc650008c2018-07-02 21:01:0688 mock_notification_menu_controller_.get(),
89 mock_notification_menu_controller_.get(), kTestAppId);
Alex Newcomerc650008c2018-07-02 21:01:0690
91 // Set the NotificationMenuView so |mock_notification_menu_controller_|
92 // can get the slide out layer. In production NotificationMenuController is
93 // the NotificationItemViewDelegate, and it gets a reference to
94 // NotificationMenuView when it is created.
95 mock_notification_menu_controller_->set_notification_menu_view(
Allen Bauerd8857e02020-05-26 21:51:3296 notification_menu_view.get());
Alex Newcomerc650008c2018-07-02 21:01:0697
Alex Newcomer9099fd92018-06-19 03:54:2098 test_api_ = std::make_unique<NotificationMenuViewTestAPI>(
Allen Bauerd8857e02020-05-26 21:51:3299 notification_menu_view.get());
Alex Newcomerc650008c2018-07-02 21:01:06100
101 widget_ = std::make_unique<views::Widget>();
102 views::Widget::InitParams init_params(
103 CreateParams(views::Widget::InitParams::TYPE_POPUP));
104 init_params.ownership =
105 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
106 init_params.activatable = views::Widget::InitParams::ACTIVATABLE_YES;
Ahmed Fakhry32f3c452019-08-01 16:36:34107 widget_->Init(std::move(init_params));
Allen Bauerd8857e02020-05-26 21:51:32108 notification_menu_view_ =
109 widget_->SetContentsView(std::move(notification_menu_view));
Alex Newcomerc650008c2018-07-02 21:01:06110 widget_->SetSize(notification_menu_view_->GetPreferredSize());
111 widget_->Show();
112 widget_->Activate();
113 }
114
115 void TearDown() override {
116 widget_->Close();
117 views::ViewsTestBase::TearDown();
Alex Newcomer9099fd92018-06-19 03:54:20118 }
119
120 message_center::Notification AddNotification(
121 const std::string& notification_id,
Jan Wilken Dörrie85285b02021-03-11 23:38:47122 const std::u16string& title,
123 const std::u16string& message) {
Alex Newcomer9099fd92018-06-19 03:54:20124 const message_center::NotifierId notifier_id(
Daniel Chenga925cbb52018-11-06 21:52:35125 message_center::NotifierType::APPLICATION, kTestAppId);
Alex Newcomer9099fd92018-06-19 03:54:20126 message_center::Notification notification(
127 message_center::NOTIFICATION_TYPE_SIMPLE, notification_id, title,
128 message, gfx::Image(), base::ASCIIToUTF16("www.test.org"), GURL(),
129 notifier_id, message_center::RichNotificationData(),
130 nullptr /* delegate */);
131 notification_menu_view_->AddNotificationItemView(notification);
Alex Newcomerc650008c2018-07-02 21:01:06132 notification_menu_view_->Layout();
Alex Newcomer9099fd92018-06-19 03:54:20133 return notification;
134 }
135
Alex Newcomer0e0fecc2018-07-19 22:51:19136 message_center::Notification UpdateNotification(
137 const std::string& notification_id,
Jan Wilken Dörrie85285b02021-03-11 23:38:47138 const std::u16string& title,
139 const std::u16string& message) {
Alex Newcomer0e0fecc2018-07-19 22:51:19140 const message_center::NotifierId notifier_id(
Daniel Chenga925cbb52018-11-06 21:52:35141 message_center::NotifierType::APPLICATION, kTestAppId);
Alex Newcomer0e0fecc2018-07-19 22:51:19142 message_center::Notification notification(
143 message_center::NOTIFICATION_TYPE_SIMPLE, notification_id, title,
144 message, gfx::Image(), base::ASCIIToUTF16("www.test.org"), GURL(),
145 notifier_id, message_center::RichNotificationData(),
146 nullptr /* delegate */);
147 notification_menu_view_->UpdateNotificationItemView(notification);
148 return notification;
149 }
150
Alex Newcomer9099fd92018-06-19 03:54:20151 void CheckDisplayedNotification(
152 const message_center::Notification& notification) {
153 // Check whether the notification and view contents match.
Peter Kastingad5dcb92019-03-30 01:08:44154 const auto* item =
155 notification_menu_view_->GetDisplayedNotificationItemView();
156 ASSERT_TRUE(item);
157 EXPECT_EQ(item->notification_id(), notification.id());
158 EXPECT_EQ(item->title(), notification.title());
159 EXPECT_EQ(item->message(), notification.message());
Alex Newcomer9099fd92018-06-19 03:54:20160 }
161
Alex Newcomerc650008c2018-07-02 21:01:06162 void BeginScroll() {
163 DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
164 }
165
166 void EndScroll() {
167 DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
168 }
169
170 void ScrollBy(int dx) {
171 DispatchGesture(
172 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, dx, 0));
173 }
174
175 void DispatchGesture(const ui::GestureEventDetails& details) {
176 ui::test::EventGenerator generator(
Peter Kasting20e1d892018-11-16 03:08:30177 GetRootWindow(notification_menu_view_->GetWidget()));
Alex Newcomerc650008c2018-07-02 21:01:06178
Peter Kastingad5dcb92019-03-30 01:08:44179 const auto* item =
180 notification_menu_view_->GetDisplayedNotificationItemView();
181 ui::GestureEvent event(0, item->GetBoundsInScreen().y(), 0,
182 ui::EventTimeForNow(), details);
Alex Newcomerc650008c2018-07-02 21:01:06183 generator.Dispatch(&event);
184 }
185
186 float GetSlideAmount() const {
187 return notification_menu_view_->GetSlideOutLayer()
188 ->transform()
189 .To2dTranslation()
190 .x();
191 }
192
Alex Newcomer9099fd92018-06-19 03:54:20193 NotificationMenuView* notification_menu_view() {
Allen Bauerd8857e02020-05-26 21:51:32194 return notification_menu_view_;
Alex Newcomer9099fd92018-06-19 03:54:20195 }
196
197 NotificationMenuViewTestAPI* test_api() { return test_api_.get(); }
198
Alex Newcomerc650008c2018-07-02 21:01:06199 MockNotificationMenuController* mock_notification_menu_controller() {
200 return mock_notification_menu_controller_.get();
201 }
202
Alex Newcomer9099fd92018-06-19 03:54:20203 private:
Alex Newcomerc650008c2018-07-02 21:01:06204 std::unique_ptr<MockNotificationMenuController>
205 mock_notification_menu_controller_;
Allen Bauerd8857e02020-05-26 21:51:32206 NotificationMenuView* notification_menu_view_;
Alex Newcomer9099fd92018-06-19 03:54:20207 std::unique_ptr<NotificationMenuViewTestAPI> test_api_;
Alex Newcomerc650008c2018-07-02 21:01:06208 std::unique_ptr<views::Widget> widget_;
209 std::unique_ptr<ui::ScopedAnimationDurationScaleMode> zero_duration_scope_;
Alex Newcomer9099fd92018-06-19 03:54:20210
211 DISALLOW_COPY_AND_ASSIGN(NotificationMenuViewTest);
212};
213
214// Tests that the correct NotificationItemView is shown when notifications come
215// and go.
216TEST_F(NotificationMenuViewTest, Basic) {
217 // Add a notification to the view.
218 const message_center::Notification notification_0 =
219 AddNotification("notification_id_0", base::ASCIIToUTF16("title_0"),
220 base::ASCIIToUTF16("message_0"));
221
222 // The counter should update to 1, and the displayed NotificationItemView
223 // should match the notification.
Raul Tambre73f906942019-02-11 16:47:21224 EXPECT_EQ(base::NumberToString16(1), test_api()->GetCounterViewContents());
Alex Newcomer9099fd92018-06-19 03:54:20225 EXPECT_EQ(1, test_api()->GetItemViewCount());
226 CheckDisplayedNotification(notification_0);
227
228 // Add a second notification to the view, the counter view and displayed
229 // NotificationItemView should change.
230 const message_center::Notification notification_1 =
231 AddNotification("notification_id_1", base::ASCIIToUTF16("title_1"),
232 base::ASCIIToUTF16("message_1"));
Raul Tambre73f906942019-02-11 16:47:21233 EXPECT_EQ(base::NumberToString16(2), test_api()->GetCounterViewContents());
Alex Newcomer9099fd92018-06-19 03:54:20234 EXPECT_EQ(2, test_api()->GetItemViewCount());
235 CheckDisplayedNotification(notification_1);
236
237 // Remove |notification_1|, |notification_0| should be shown.
Alex Newcomer1fe9929e2018-07-20 21:18:50238 notification_menu_view()->OnNotificationRemoved(notification_1.id());
Raul Tambre73f906942019-02-11 16:47:21239 EXPECT_EQ(base::NumberToString16(1), test_api()->GetCounterViewContents());
Alex Newcomer9099fd92018-06-19 03:54:20240 EXPECT_EQ(1, test_api()->GetItemViewCount());
241 CheckDisplayedNotification(notification_0);
242}
243
Alex Newcomer1fe9929e2018-07-20 21:18:50244TEST_F(NotificationMenuViewTest, MultipleNotificationsBasic) {
245 // Add multiple notifications to the view.
246 const message_center::Notification notification_0 =
247 AddNotification("notification_id_0", base::ASCIIToUTF16("title_0"),
248 base::ASCIIToUTF16("message_0"));
249
250 // Overflow should not be created until there are two notifications.
251 EXPECT_FALSE(test_api()->GetOverflowView());
252 EXPECT_EQ(
253 0, mock_notification_menu_controller()->overflow_added_or_removed_count_);
254
255 // Add a second notification, this will push |notification_0| into overflow.
256 const message_center::Notification notification_1 =
257 AddNotification("notification_id_1", base::ASCIIToUTF16("title_1"),
258 base::ASCIIToUTF16("message_1"));
259
260 CheckDisplayedNotification(notification_1);
261 EXPECT_TRUE(test_api()->GetOverflowView());
262 EXPECT_EQ(
263 1, mock_notification_menu_controller()->overflow_added_or_removed_count_);
264
265 // Remove the notification that is in overflow.
266 notification_menu_view()->OnNotificationRemoved(notification_0.id());
267
268 // The displayed notification should not have changed, and the overflow view
269 // should be deleted.
270 CheckDisplayedNotification(notification_1);
271 EXPECT_FALSE(test_api()->GetOverflowView());
272}
273
274// Tests that when the displayed NotificationItemView is removed, the
275// notification from the overflow view becomes the displayed view.
276TEST_F(NotificationMenuViewTest, ShowNotificationFromOverflow) {
277 // Add multiple notifications to the view.
278 const message_center::Notification notification_0 =
279 AddNotification("notification_id_0", base::ASCIIToUTF16("title_0"),
280 base::ASCIIToUTF16("message_0"));
281
282 EXPECT_FALSE(test_api()->GetOverflowView());
283 const message_center::Notification notification_1 =
284 AddNotification("notification_id_1", base::ASCIIToUTF16("title_1"),
285 base::ASCIIToUTF16("message_1"));
286
287 // |notification_1| should be the displayed NotificationItemView.
288 CheckDisplayedNotification(notification_1);
289 EXPECT_TRUE(test_api()->GetOverflowView());
290
291 // Remove the displayed NotificationItemView, the overflow notification should
292 // take its place and overflow should be removed.
293 notification_menu_view()->OnNotificationRemoved(notification_1.id());
294
295 CheckDisplayedNotification(notification_0);
296 EXPECT_FALSE(test_api()->GetOverflowView());
297}
298
Alex Newcomer9099fd92018-06-19 03:54:20299// Tests that removing a notification that is not being shown only updates the
300// counter.
301TEST_F(NotificationMenuViewTest, RemoveOlderNotification) {
302 // Add two notifications.
303 const message_center::Notification notification_0 =
304 AddNotification("notification_id_0", base::ASCIIToUTF16("title_0"),
305 base::ASCIIToUTF16("message_0"));
306 const message_center::Notification notification_1 =
307 AddNotification("notification_id_1", base::ASCIIToUTF16("title_1"),
308 base::ASCIIToUTF16("message_1"));
309
310 // The latest notification should be shown.
Raul Tambre73f906942019-02-11 16:47:21311 EXPECT_EQ(base::NumberToString16(2), test_api()->GetCounterViewContents());
Alex Newcomer9099fd92018-06-19 03:54:20312 EXPECT_EQ(2, test_api()->GetItemViewCount());
313 CheckDisplayedNotification(notification_1);
314
315 // Remove the older notification, |notification_0|.
Alex Newcomer1fe9929e2018-07-20 21:18:50316 notification_menu_view()->OnNotificationRemoved(notification_0.id());
Alex Newcomer9099fd92018-06-19 03:54:20317
318 // The latest notification, |notification_1|, should be shown.
Raul Tambre73f906942019-02-11 16:47:21319 EXPECT_EQ(base::NumberToString16(1), test_api()->GetCounterViewContents());
Alex Newcomer9099fd92018-06-19 03:54:20320 EXPECT_EQ(1, test_api()->GetItemViewCount());
321 CheckDisplayedNotification(notification_1);
322}
323
Alex Newcomerc650008c2018-07-02 21:01:06324// Tests that the displayed NotificationItemView is only dismissed when dragged
325// beyond the threshold.
326TEST_F(NotificationMenuViewTest, SlideOut) {
327 AddNotification("notification_id", base::ASCIIToUTF16("title"),
328 base::ASCIIToUTF16("message"));
329
330 EXPECT_EQ(0, mock_notification_menu_controller()->slide_out_count_);
331
332 BeginScroll();
333 // Scroll by a small amount, the notification should move but not slide out.
334 ScrollBy(-10);
335 EXPECT_EQ(0, mock_notification_menu_controller()->slide_out_count_);
336 EXPECT_EQ(-10.f, GetSlideAmount());
337
338 // End the scroll gesture, the notifications should return to its resting
339 // place.
340 EndScroll();
341 EXPECT_EQ(0, mock_notification_menu_controller()->slide_out_count_);
342 EXPECT_EQ(0.f, GetSlideAmount());
343
344 BeginScroll();
345 // Scroll beyond the threshold but do not release the gesture scroll.
346 ScrollBy(-200);
347 EXPECT_EQ(-200.f, GetSlideAmount());
348 // Release the gesture, the notification should slide out.
349 EndScroll();
Tetsui Ohkubo801f7212018-11-12 02:32:21350 base::RunLoop().RunUntilIdle();
Alex Newcomerc650008c2018-07-02 21:01:06351 EXPECT_EQ(1, mock_notification_menu_controller()->slide_out_count_);
352 EXPECT_EQ(0, mock_notification_menu_controller()->activation_count_);
353}
354
355// Tests that tapping a notification activates it.
356TEST_F(NotificationMenuViewTest, TapNotification) {
357 AddNotification("notification_id", base::ASCIIToUTF16("title"),
358 base::ASCIIToUTF16("message"));
359 EXPECT_EQ(0, mock_notification_menu_controller()->activation_count_);
360 DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_TAP));
361
362 EXPECT_EQ(1, mock_notification_menu_controller()->activation_count_);
363}
364
365// Tests that an in bounds mouse release activates a notification.
366TEST_F(NotificationMenuViewTest, ClickNotification) {
367 AddNotification("notification_id", base::ASCIIToUTF16("title"),
368 base::ASCIIToUTF16("message"));
369 EXPECT_EQ(0, mock_notification_menu_controller()->activation_count_);
370
Peter Kastingad5dcb92019-03-30 01:08:44371 const auto* item =
372 notification_menu_view()->GetDisplayedNotificationItemView();
373 const gfx::Point cursor_location = item->GetBoundsInScreen().origin();
Alex Newcomerc650008c2018-07-02 21:01:06374 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, cursor_location, cursor_location,
375 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
376 ui::EF_NONE);
377 notification_menu_view()->GetWidget()->OnMouseEvent(&press);
378 EXPECT_EQ(0, mock_notification_menu_controller()->activation_count_);
379
380 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, cursor_location,
381 cursor_location, ui::EventTimeForNow(),
382 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_NONE);
383 notification_menu_view()->GetWidget()->OnMouseEvent(&release);
384 EXPECT_EQ(1, mock_notification_menu_controller()->activation_count_);
385}
386
387// Tests that an out of bounds mouse release does not activate a notification.
388TEST_F(NotificationMenuViewTest, OutOfBoundsClick) {
389 AddNotification("notification_id", base::ASCIIToUTF16("title"),
390 base::ASCIIToUTF16("message"));
391 EXPECT_EQ(0, mock_notification_menu_controller()->activation_count_);
392
Peter Kastingad5dcb92019-03-30 01:08:44393 const auto* item =
394 notification_menu_view()->GetDisplayedNotificationItemView();
395 const gfx::Point cursor_location = item->GetBoundsInScreen().origin();
Alex Newcomerc650008c2018-07-02 21:01:06396 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, cursor_location, cursor_location,
397 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
398 ui::EF_NONE);
399 notification_menu_view()->GetWidget()->OnMouseEvent(&press);
400 EXPECT_EQ(0, mock_notification_menu_controller()->activation_count_);
401
402 const gfx::Point out_of_bounds;
403 ui::MouseEvent out_of_bounds_release(ui::ET_MOUSE_RELEASED, out_of_bounds,
404 out_of_bounds, ui::EventTimeForNow(),
405 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_NONE);
406 notification_menu_view()->GetWidget()->OnMouseEvent(&out_of_bounds_release);
407
408 EXPECT_EQ(0, mock_notification_menu_controller()->activation_count_);
409}
410
Alex Newcomer0e0fecc2018-07-19 22:51:19411// Tests updating notifications that do and do not exist.
412TEST_F(NotificationMenuViewTest, UpdateNotification) {
413 // Add a notification.
414 const std::string notification_id = "notification_id";
415 AddNotification(notification_id, base::ASCIIToUTF16("title"),
416 base::ASCIIToUTF16("message"));
417 // Send an updated notification with a matching |notification_id|.
418 const message_center::Notification updated_notification =
419 UpdateNotification(notification_id, base::ASCIIToUTF16("new_title"),
420 base::ASCIIToUTF16("new_message"));
421
422 // The displayed notification's contents should have changed to match the
423 // updated notification.
Raul Tambre73f906942019-02-11 16:47:21424 EXPECT_EQ(base::NumberToString16(1), test_api()->GetCounterViewContents());
Alex Newcomer0e0fecc2018-07-19 22:51:19425 EXPECT_EQ(1, test_api()->GetItemViewCount());
426 CheckDisplayedNotification(updated_notification);
427
428 // Send an updated notification for a notification which doesn't yet exist.
429 UpdateNotification("Bad notification", base::ASCIIToUTF16("Bad Title"),
430 base::ASCIIToUTF16("Bad Message"));
431
432 // Test that the displayed notification has not been changed.
Raul Tambre73f906942019-02-11 16:47:21433 EXPECT_EQ(base::NumberToString16(1), test_api()->GetCounterViewContents());
Alex Newcomer0e0fecc2018-07-19 22:51:19434 EXPECT_EQ(1, test_api()->GetItemViewCount());
435 CheckDisplayedNotification(updated_notification);
436}
437
Alex Newcomer9099fd92018-06-19 03:54:20438} // namespace ash