blob: 5d62ad3ed483d4cb833725f3598ee817e23d8db8 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/message_center/views/message_popup_collection.h"
#include <stddef.h>
#include <algorithm>
#include <list>
#include <numeric>
#include <utility>
#include "base/message_loop/message_loop.h"
#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/display.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_utils.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/message_center/fake_message_center.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/views/desktop_popup_alignment_delegate.h"
#include "ui/message_center/views/toast_contents_view.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace {
std::unique_ptr<message_center::Notification> CreateTestNotification(
std::string id,
std::string text) {
return base::MakeUnique<message_center::Notification>(
message_center::NOTIFICATION_TYPE_BASE_FORMAT, id,
base::UTF8ToUTF16("test title"), base::ASCIIToUTF16(text), gfx::Image(),
base::string16() /* display_source */, GURL(),
message_center::NotifierId(message_center::NotifierId::APPLICATION, id),
message_center::RichNotificationData(),
new message_center::NotificationDelegate());
}
// Provides an aura window context for widget creation.
class TestPopupAlignmentDelegate
: public message_center::DesktopPopupAlignmentDelegate {
public:
explicit TestPopupAlignmentDelegate(gfx::NativeWindow context)
: context_(context) {}
~TestPopupAlignmentDelegate() override {}
// PopupAlignmentDelegate:
void ConfigureWidgetInitParamsForContainer(
views::Widget* widget,
views::Widget::InitParams* init_params) override {
init_params->context = context_;
}
private:
gfx::NativeWindow context_;
DISALLOW_COPY_AND_ASSIGN(TestPopupAlignmentDelegate);
};
} // namespace
namespace message_center {
namespace test {
class MessagePopupCollectionTest : public views::ViewsTestBase {
public:
void SetUp() override {
views::ViewsTestBase::SetUp();
MessageCenter::Initialize();
MessageCenter::Get()->DisableTimersForTest();
alignment_delegate_.reset(new TestPopupAlignmentDelegate(GetContext()));
collection_.reset(new MessagePopupCollection(MessageCenter::Get(), NULL,
alignment_delegate_.get()));
// This size fits test machines resolution and also can keep a few toasts
// w/o ill effects of hitting the screen overflow. This allows us to assume
// and verify normal layout of the toast stack.
SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // taskbar at the bottom.
gfx::Rect(0, 0, 600, 400));
id_ = 0;
PrepareForWait();
}
void TearDown() override {
collection_.reset();
MessageCenter::Shutdown();
views::ViewsTestBase::TearDown();
}
protected:
MessagePopupCollection* collection() { return collection_.get(); }
size_t GetToastCounts() {
return collection_->toasts_.size();
}
bool MouseInCollection() {
return collection_->latest_toast_entered_ != NULL;
}
bool IsToastShown(const std::string& id) {
views::Widget* widget = collection_->GetWidgetForTest(id);
return widget && widget->IsVisible();
}
views::Widget* GetWidget(const std::string& id) {
return collection_->GetWidgetForTest(id);
}
void SetDisplayInfo(const gfx::Rect& work_area,
const gfx::Rect& display_bounds) {
display::Display dummy_display;
dummy_display.set_bounds(display_bounds);
dummy_display.set_work_area(work_area);
alignment_delegate_->RecomputeAlignment(dummy_display);
PrepareForWait();
}
gfx::Rect GetWorkArea() {
return alignment_delegate_->work_area_;
}
ToastContentsView* GetToast(const std::string& id) {
for (MessagePopupCollection::Toasts::iterator iter =
collection_->toasts_.begin();
iter != collection_->toasts_.end(); ++iter) {
if ((*iter)->id() == id)
return *iter;
}
return NULL;
}
std::string AddNotification() {
std::string id = base::IntToString(id_++);
std::unique_ptr<Notification> notification(new Notification(
NOTIFICATION_TYPE_BASE_FORMAT, id, base::UTF8ToUTF16("test title"),
base::UTF8ToUTF16("test message"), gfx::Image(),
base::string16() /* display_source */, GURL(), NotifierId(),
message_center::RichNotificationData(), new NotificationDelegate()));
MessageCenter::Get()->AddNotification(std::move(notification));
return id;
}
void PrepareForWait() { collection_->CreateRunLoopForTest(); }
// Assumes there is non-zero pending work.
void WaitForTransitionsDone() {
collection_->WaitForTest();
collection_->CreateRunLoopForTest();
}
void CloseAllToasts() {
// Assumes there is at least one toast to close.
EXPECT_TRUE(GetToastCounts() > 0);
MessageCenter::Get()->RemoveAllNotifications(
false /* by_user */, MessageCenter::RemoveType::ALL);
}
gfx::Rect GetToastRectAt(size_t index) {
return collection_->GetToastRectAt(index);
}
// Checks:
// 1) sizes of toast and corresponding widget are equal;
// 2) widgets do not owerlap;
// 3) after animation is done, aligment is propper;
class CheckedAnimationDelegate : public gfx::AnimationDelegate {
public:
explicit CheckedAnimationDelegate(MessagePopupCollectionTest* test);
// returns first encountered error
const base::Optional<std::string>& error_msg() const { return error_msg_; }
// gfx::AnimationDelegate overrides
void AnimationEnded(const gfx::Animation* animation) override;
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override;
private:
// we attach ourselves to the last toast, because we accept
// invalidation of invariants during intermidiate
// notification updates.
ToastContentsView& animation_delegate() { return *toasts_->back(); }
const ToastContentsView& animation_delegate() const {
return *toasts_->back();
}
void CheckWidgetLEView(const std::string& calling_func);
void CheckToastsAreAligned(const std::string& calling_func);
void CheckToastsDontOverlap(const std::string& calling_func);
static int ComputeYDistance(const ToastContentsView& top,
const ToastContentsView& bottom);
MessagePopupCollection::Toasts* toasts_;
// first encountered error
base::Optional<std::string> error_msg_;
DISALLOW_COPY_AND_ASSIGN(CheckedAnimationDelegate);
};
static std::string YPositionsToString(
const MessagePopupCollection::Toasts& toasts);
private:
std::unique_ptr<MessagePopupCollection> collection_;
std::unique_ptr<DesktopPopupAlignmentDelegate> alignment_delegate_;
int id_;
};
MessagePopupCollectionTest::CheckedAnimationDelegate::CheckedAnimationDelegate(
MessagePopupCollectionTest* test)
: toasts_(&test->collection_->toasts_) {
DCHECK(!toasts_->empty());
animation_delegate().bounds_animation_->set_delegate(this);
}
void MessagePopupCollectionTest::CheckedAnimationDelegate::AnimationEnded(
const gfx::Animation* animation) {
animation_delegate().AnimationEnded(animation);
CheckWidgetLEView("AnimationEnded");
CheckToastsAreAligned("AnimationEnded");
}
void MessagePopupCollectionTest::CheckedAnimationDelegate::AnimationProgressed(
const gfx::Animation* animation) {
animation_delegate().AnimationProgressed(animation);
CheckWidgetLEView("AnimationProgressed");
CheckToastsDontOverlap("AnimationProgressed");
}
void MessagePopupCollectionTest::CheckedAnimationDelegate::AnimationCanceled(
const gfx::Animation* animation) {
animation_delegate().AnimationCanceled(animation);
CheckWidgetLEView("AnimationCanceled");
CheckToastsDontOverlap("AnimationCanceled");
}
void MessagePopupCollectionTest::CheckedAnimationDelegate::CheckWidgetLEView(
const std::string& calling_func) {
if (error_msg_)
return;
for (ToastContentsView* toast : *toasts_) {
auto* widget = toast->GetWidget();
DCHECK(widget) << "no widget for: " << toast->id();
if (toast->bounds().height() < widget->GetWindowBoundsInScreen().height()) {
error_msg_ = calling_func + " CheckWidgetSizeLEView: id: " + toast->id() +
"\ntoast size: " + toast->bounds().size().ToString() +
"\nwidget size: " +
widget->GetWindowBoundsInScreen().size().ToString();
return;
}
}
};
void MessagePopupCollectionTest::CheckedAnimationDelegate::
CheckToastsAreAligned(const std::string& calling_func) {
if (error_msg_)
return;
auto poorly_aligned = std::adjacent_find(
toasts_->begin(), toasts_->end(),
[this](ToastContentsView* top, ToastContentsView* bottom) {
return ComputeYDistance(*top, *bottom) != kMarginBetweenItems;
});
if (poorly_aligned != toasts_->end())
error_msg_ = calling_func + " CheckToastsAreAligned: distance between: " +
(*poorly_aligned)->id() + ' ' +
(*std::next(poorly_aligned))->id() + ": " +
std::to_string(ComputeYDistance(**poorly_aligned,
**std::next(poorly_aligned))) +
" expected: " + std::to_string(kMarginBetweenItems) +
"\nLayout:\n" + YPositionsToString(*toasts_);
}
void MessagePopupCollectionTest::CheckedAnimationDelegate::
CheckToastsDontOverlap(const std::string& calling_func) {
if (error_msg_)
return;
auto poorly_aligned = std::adjacent_find(
toasts_->begin(), toasts_->end(),
[this](ToastContentsView* top, ToastContentsView* bottom) {
return ComputeYDistance(*top, *bottom) < 0;
});
if (poorly_aligned != toasts_->end())
error_msg_ = calling_func + " CheckToastsDontOverlap: distance between: " +
(*poorly_aligned)->id() + ' ' +
(*std::next(poorly_aligned))->id() + ": " +
std::to_string(ComputeYDistance(**poorly_aligned,
**std::next(poorly_aligned))) +
"\nLayout:\n" + YPositionsToString(*toasts_);
}
// static
std::string MessagePopupCollectionTest::YPositionsToString(
const MessagePopupCollection::Toasts& toasts) {
return std::accumulate(toasts.begin(), toasts.end(), std::string(),
[](std::string res, const ToastContentsView* toast) {
const auto& bounds =
toast->GetWidget()->GetWindowBoundsInScreen();
res += toast->id();
res += ' ';
res += std::to_string(bounds.y());
res += ", ";
res += std::to_string(bounds.y() + bounds.height());
res += '\n';
return res;
});
}
// static
int MessagePopupCollectionTest::CheckedAnimationDelegate::ComputeYDistance(
const ToastContentsView& top,
const ToastContentsView& bottom) {
const auto* top_widget = top.GetWidget();
const auto* bottom_widget = bottom.GetWidget();
const auto& top_bounds = top_widget->GetWindowBoundsInScreen();
const auto& bottom_bounds = bottom_widget->GetWindowBoundsInScreen();
return bottom_bounds.y() - (top_bounds.y() + top_bounds.height());
}
#if defined(OS_CHROMEOS)
TEST_F(MessagePopupCollectionTest, DismissOnClick) {
std::string id1 = AddNotification();
std::string id2 = AddNotification();
WaitForTransitionsDone();
EXPECT_EQ(2u, GetToastCounts());
EXPECT_TRUE(IsToastShown(id1));
EXPECT_TRUE(IsToastShown(id2));
MessageCenter::Get()->ClickOnNotification(id2);
WaitForTransitionsDone();
EXPECT_EQ(1u, GetToastCounts());
EXPECT_TRUE(IsToastShown(id1));
EXPECT_FALSE(IsToastShown(id2));
MessageCenter::Get()->ClickOnNotificationButton(id1, 0);
WaitForTransitionsDone();
EXPECT_EQ(0u, GetToastCounts());
EXPECT_FALSE(IsToastShown(id1));
EXPECT_FALSE(IsToastShown(id2));
}
#else
TEST_F(MessagePopupCollectionTest, NotDismissedOnClick) {
std::string id1 = AddNotification();
std::string id2 = AddNotification();
WaitForTransitionsDone();
EXPECT_EQ(2u, GetToastCounts());
EXPECT_TRUE(IsToastShown(id1));
EXPECT_TRUE(IsToastShown(id2));
MessageCenter::Get()->ClickOnNotification(id2);
collection()->DoUpdateIfPossible();
EXPECT_EQ(2u, GetToastCounts());
EXPECT_TRUE(IsToastShown(id1));
EXPECT_TRUE(IsToastShown(id2));
MessageCenter::Get()->ClickOnNotificationButton(id1, 0);
collection()->DoUpdateIfPossible();
EXPECT_EQ(2u, GetToastCounts());
EXPECT_TRUE(IsToastShown(id1));
EXPECT_TRUE(IsToastShown(id2));
GetWidget(id1)->CloseNow();
GetWidget(id2)->CloseNow();
}
#endif // OS_CHROMEOS
TEST_F(MessagePopupCollectionTest, ShutdownDuringShowing) {
std::string id1 = AddNotification();
std::string id2 = AddNotification();
WaitForTransitionsDone();
EXPECT_EQ(2u, GetToastCounts());
EXPECT_TRUE(IsToastShown(id1));
EXPECT_TRUE(IsToastShown(id2));
// Finish without cleanup of notifications, which may cause use-after-free.
// See crbug.com/236448
GetWidget(id1)->CloseNow();
collection()->OnMouseExited(GetToast(id2));
GetWidget(id2)->CloseNow();
}
TEST_F(MessagePopupCollectionTest, DefaultPositioning) {
std::string id0 = AddNotification();
std::string id1 = AddNotification();
std::string id2 = AddNotification();
std::string id3 = AddNotification();
WaitForTransitionsDone();
gfx::Rect r0 = GetToastRectAt(0);
gfx::Rect r1 = GetToastRectAt(1);
gfx::Rect r2 = GetToastRectAt(2);
gfx::Rect r3 = GetToastRectAt(3);
// 3 toasts are shown, equal size, vertical stack.
EXPECT_TRUE(IsToastShown(id0));
EXPECT_TRUE(IsToastShown(id1));
EXPECT_TRUE(IsToastShown(id2));
EXPECT_EQ(r0.width(), r1.width());
EXPECT_EQ(r1.width(), r2.width());
EXPECT_EQ(r0.height(), r1.height());
EXPECT_EQ(r1.height(), r2.height());
EXPECT_GT(r0.y(), r1.y());
EXPECT_GT(r1.y(), r2.y());
EXPECT_EQ(r0.x(), r1.x());
EXPECT_EQ(r1.x(), r2.x());
// The 4th toast is not shown yet.
EXPECT_FALSE(IsToastShown(id3));
EXPECT_EQ(0, r3.width());
EXPECT_EQ(0, r3.height());
CloseAllToasts();
EXPECT_EQ(0u, GetToastCounts());
WaitForTransitionsDone();
}
TEST_F(MessagePopupCollectionTest, DefaultPositioningWithRightTaskbar) {
// If taskbar is on the right we show the toasts bottom to top as usual.
// Simulate a taskbar at the right.
SetDisplayInfo(gfx::Rect(0, 0, 590, 400), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
gfx::Rect r0 = GetToastRectAt(0);
gfx::Rect r1 = GetToastRectAt(1);
// 2 toasts are shown, equal size, vertical stack.
EXPECT_TRUE(IsToastShown(id0));
EXPECT_TRUE(IsToastShown(id1));
EXPECT_EQ(r0.width(), r1.width());
EXPECT_EQ(r0.height(), r1.height());
EXPECT_GT(r0.y(), r1.y());
EXPECT_EQ(r0.x(), r1.x());
CloseAllToasts();
EXPECT_EQ(0u, GetToastCounts());
WaitForTransitionsDone();
}
TEST_F(MessagePopupCollectionTest, TopDownPositioningWithTopTaskbar) {
// Simulate a taskbar at the top.
SetDisplayInfo(gfx::Rect(0, 10, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
gfx::Rect r0 = GetToastRectAt(0);
gfx::Rect r1 = GetToastRectAt(1);
// 2 toasts are shown, equal size, vertical stack.
EXPECT_TRUE(IsToastShown(id0));
EXPECT_TRUE(IsToastShown(id1));
EXPECT_EQ(r0.width(), r1.width());
EXPECT_EQ(r0.height(), r1.height());
EXPECT_LT(r0.y(), r1.y());
EXPECT_EQ(r0.x(), r1.x());
CloseAllToasts();
EXPECT_EQ(0u, GetToastCounts());
WaitForTransitionsDone();
}
TEST_F(MessagePopupCollectionTest, TopDownPositioningWithLeftAndTopTaskbar) {
// If there "seems" to be a taskbar on left and top (like in Unity), it is
// assumed that the actual taskbar is the top one.
// Simulate a taskbar at the top and left.
SetDisplayInfo(gfx::Rect(10, 10, 590, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
gfx::Rect r0 = GetToastRectAt(0);
gfx::Rect r1 = GetToastRectAt(1);
// 2 toasts are shown, equal size, vertical stack.
EXPECT_TRUE(IsToastShown(id0));
EXPECT_TRUE(IsToastShown(id1));
EXPECT_EQ(r0.width(), r1.width());
EXPECT_EQ(r0.height(), r1.height());
EXPECT_LT(r0.y(), r1.y());
EXPECT_EQ(r0.x(), r1.x());
CloseAllToasts();
EXPECT_EQ(0u, GetToastCounts());
WaitForTransitionsDone();
}
TEST_F(MessagePopupCollectionTest, TopDownPositioningWithBottomAndTopTaskbar) {
// If there "seems" to be a taskbar on bottom and top (like in Gnome), it is
// assumed that the actual taskbar is the top one.
// Simulate a taskbar at the top and bottom.
SetDisplayInfo(gfx::Rect(0, 10, 580, 400), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
gfx::Rect r0 = GetToastRectAt(0);
gfx::Rect r1 = GetToastRectAt(1);
// 2 toasts are shown, equal size, vertical stack.
EXPECT_TRUE(IsToastShown(id0));
EXPECT_TRUE(IsToastShown(id1));
EXPECT_EQ(r0.width(), r1.width());
EXPECT_EQ(r0.height(), r1.height());
EXPECT_LT(r0.y(), r1.y());
EXPECT_EQ(r0.x(), r1.x());
CloseAllToasts();
EXPECT_EQ(0u, GetToastCounts());
WaitForTransitionsDone();
}
TEST_F(MessagePopupCollectionTest, LeftPositioningWithLeftTaskbar) {
// Simulate a taskbar at the left.
SetDisplayInfo(gfx::Rect(10, 0, 590, 400), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
gfx::Rect r0 = GetToastRectAt(0);
gfx::Rect r1 = GetToastRectAt(1);
// 2 toasts are shown, equal size, vertical stack.
EXPECT_TRUE(IsToastShown(id0));
EXPECT_TRUE(IsToastShown(id1));
EXPECT_EQ(r0.width(), r1.width());
EXPECT_EQ(r0.height(), r1.height());
EXPECT_GT(r0.y(), r1.y());
EXPECT_EQ(r0.x(), r1.x());
// Ensure that toasts are on the left.
EXPECT_LT(r1.x(), GetWorkArea().CenterPoint().x());
CloseAllToasts();
EXPECT_EQ(0u, GetToastCounts());
WaitForTransitionsDone();
}
TEST_F(MessagePopupCollectionTest, DetectMouseHover) {
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
views::WidgetDelegateView* toast0 = GetToast(id0);
EXPECT_TRUE(toast0 != NULL);
views::WidgetDelegateView* toast1 = GetToast(id1);
EXPECT_TRUE(toast1 != NULL);
ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0);
// Test that mouse detection logic works in presence of out-of-order events.
toast0->OnMouseEntered(event);
EXPECT_TRUE(MouseInCollection());
toast1->OnMouseEntered(event);
EXPECT_TRUE(MouseInCollection());
toast0->OnMouseExited(event);
EXPECT_TRUE(MouseInCollection());
toast1->OnMouseExited(event);
EXPECT_FALSE(MouseInCollection());
// Test that mouse detection logic works in presence of WindowClosing events.
toast0->OnMouseEntered(event);
EXPECT_TRUE(MouseInCollection());
toast1->OnMouseEntered(event);
EXPECT_TRUE(MouseInCollection());
toast0->GetWidget()->CloseNow();
EXPECT_TRUE(MouseInCollection());
toast1->GetWidget()->CloseNow();
EXPECT_FALSE(MouseInCollection());
}
// TODO(dimich): Test repositioning - both normal one and when user is closing
// the toasts.
TEST_F(MessagePopupCollectionTest, DetectMouseHoverWithUserClose) {
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
views::WidgetDelegateView* toast0 = GetToast(id0);
EXPECT_TRUE(toast0 != NULL);
views::WidgetDelegateView* toast1 = GetToast(id1);
ASSERT_TRUE(toast1 != NULL);
ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0);
toast1->OnMouseEntered(event);
static_cast<MessageCenterObserver*>(collection())->OnNotificationRemoved(
id1, true);
EXPECT_FALSE(MouseInCollection());
std::string id2 = AddNotification();
WaitForTransitionsDone();
views::WidgetDelegateView* toast2 = GetToast(id2);
EXPECT_TRUE(toast2 != NULL);
CloseAllToasts();
WaitForTransitionsDone();
}
TEST_F(MessagePopupCollectionTest, ManyPopupNotifications) {
// Add the max visible popup notifications +1, ensure the correct num visible.
size_t notifications_to_add = 3 + 1;
std::vector<std::string> ids(notifications_to_add);
for (size_t i = 0; i < notifications_to_add; ++i) {
ids[i] = AddNotification();
}
WaitForTransitionsDone();
for (size_t i = 0; i < notifications_to_add - 1; ++i) {
EXPECT_TRUE(IsToastShown(ids[i])) << "Should show the " << i << "th ID";
}
EXPECT_FALSE(IsToastShown(ids[notifications_to_add - 1]));
CloseAllToasts();
WaitForTransitionsDone();
}
#if defined(OS_CHROMEOS)
TEST_F(MessagePopupCollectionTest, CloseNonClosableNotifications) {
const char* kNotificationId = "NOTIFICATION1";
std::unique_ptr<Notification> notification(new Notification(
NOTIFICATION_TYPE_BASE_FORMAT, kNotificationId,
base::UTF8ToUTF16("test title"), base::UTF8ToUTF16("test message"),
gfx::Image(), base::string16() /* display_source */, GURL(),
NotifierId(NotifierId::APPLICATION, kNotificationId),
message_center::RichNotificationData(), new NotificationDelegate()));
notification->set_pinned(true);
// Add a pinned notification.
MessageCenter::Get()->AddNotification(std::move(notification));
WaitForTransitionsDone();
// Confirms that there is a toast.
EXPECT_EQ(1u, GetToastCounts());
EXPECT_EQ(1u, MessageCenter::Get()->NotificationCount());
// Close the toast.
views::WidgetDelegateView* toast1 = GetToast(kNotificationId);
ASSERT_TRUE(toast1 != NULL);
ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0);
toast1->OnMouseEntered(event);
static_cast<MessageCenterObserver*>(collection())
->OnNotificationRemoved(kNotificationId, true);
WaitForTransitionsDone();
// Confirms that there is no toast.
EXPECT_EQ(0u, GetToastCounts());
// But the notification still exists.
EXPECT_EQ(1u, MessageCenter::Get()->NotificationCount());
}
#endif // defined(OS_CHROMEOS)
// When notifications were displayed on top, change of notification
// size didn't affect corresponding widget.
TEST_F(MessagePopupCollectionTest, ChangingNotificationSize) {
// Simulate a taskbar at the top.
SetDisplayInfo(gfx::Rect(0, 10, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
struct TestCase {
std::string name;
std::string text;
};
std::vector<TestCase> updates = {
{"shrinking", ""},
{"enlarging", "abc\ndef\nghk\n"},
{"restoring", "abc\ndef\n"},
};
std::vector<std::string> notification_ids;
// adding notifications
{
constexpr int max_visible_popup_notifications = 3;
notification_ids.reserve(max_visible_popup_notifications);
for (int i = 0; i < max_visible_popup_notifications; ++i) {
notification_ids.push_back(std::to_string(i));
auto notification =
CreateTestNotification(notification_ids.back(), updates.back().text);
MessageCenter::Get()->AddNotification(std::move(notification));
}
}
WaitForTransitionsDone();
// updating notifications one by one
for (const std::string& id : notification_ids) {
for (const auto& update : updates) {
MessageCenter::Get()->UpdateNotification(
id, CreateTestNotification(id, update.text));
CheckedAnimationDelegate checked_animation(this);
WaitForTransitionsDone();
EXPECT_FALSE(checked_animation.error_msg())
<< "Animation error, test case: " << id << ' ' << update.name << ":\n"
<< *checked_animation.error_msg();
}
}
CloseAllToasts();
WaitForTransitionsDone();
}
} // namespace test
} // namespace message_center