blob: a4fdfbc951c35c1fa33d89cbb9b2420c40642f4b [file] [log] [blame]
// Copyright 2018 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 "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/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/models/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,
base::ASCIIToUTF16("Test Web Notification"),
base::ASCIIToUTF16("Notification message body."), gfx::Image(),
base::ASCIIToUTF16("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::MENU_SOURCE_TYPE_LAST,
base::OnceClosure()) {}
private:
// AppMenuModelAdapter overrides:
void RecordHistogramOnMenuClosed() override {}
DISALLOW_COPY_AND_ASSIGN(TestAppMenuModelAdapter);
};
} // namespace
class NotificationMenuControllerTest : public AshTestBase {
public:
NotificationMenuControllerTest() = default;
~NotificationMenuControllerTest() override {}
// Overridden from AshTestBase:
void TearDown() override {
// 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, base::ASCIIToUTF16("item 0"));
test_app_menu_model_adapter_->model()->AddItem(
1, base::ASCIIToUTF16("item 1"));
root_menu_item_view_ =
new views::MenuItemView(test_app_menu_model_adapter_.get());
host_view_ = std::make_unique<views::View>();
host_view_->AddChildView(root_menu_item_view_);
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_; }
private:
// The root MenuItemView. Owned by |host_view_|.
views::MenuItemView* root_menu_item_view_ = nullptr;
// Allows the dtor to access the restricted views::MenuItemView dtor.
std::unique_ptr<views::View> host_view_;
std::unique_ptr<NotificationMenuController> notification_menu_controller_;
std::unique_ptr<TestAppMenuModelAdapter> test_app_menu_model_adapter_;
DISALLOW_COPY_AND_ASSIGN(NotificationMenuControllerTest);
};
// 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(2, root_menu_item_view()->GetSubmenu()->child_count());
// 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(3, root_menu_item_view()->GetSubmenu()->child_count());
}
// 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(3, root_menu_item_view()->GetSubmenu()->child_count());
// Remove the notification, this should result in the NotificationMenuView
// container being removed.
message_center::MessageCenter::Get()->RemoveNotification(notification_id,
true);
EXPECT_EQ(2, root_menu_item_view()->GetSubmenu()->child_count());
// Add the same notification.
BuildAndSendNotification(kTestAppId, notification_id);
// The container MenuItemView should be added again.
EXPECT_EQ(3, root_menu_item_view()->GetSubmenu()->child_count());
}
// 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(3, root_menu_item_view()->GetSubmenu()->child_count());
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(3, root_menu_item_view()->GetSubmenu()->child_count());
// Remove the final notification.
message_center->RemoveNotification(notification_id_1, true);
// The container should be removed.
EXPECT_EQ(2, root_menu_item_view()->GetSubmenu()->child_count());
}
} // namespace ash