Commit 953d0f88 authored by Mohamed Adel's avatar Mohamed Adel Committed by Commit Bot

Adds tests for NPB Mac UNNotification

This adds tests for the different methods in the bridge, and the
different scenarios that can happen when displaying a notification. It
also tests features specific to the API.

Change-Id: Id58fe2a63c19baf2b1cb6a83cc488d3c060692cd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2517466Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarRichard Knoll <knollr@chromium.org>
Reviewed-by: default avatarRayan Kanso <rayankans@chromium.org>
Commit-Queue: Mohamed Adel <adelm@google.com>
Cr-Commit-Position: refs/heads/master@{#825848}
parent e996374e
// Copyright 2020 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.
#import <UserNotifications/UserNotifications.h>
#include "base/bind.h"
#include "base/mac/scoped_nsobject.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "chrome/browser/notifications/notification_platform_bridge_mac_unnotification.h"
#include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h"
#include "chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.h"
#include "chrome/browser/ui/cocoa/notifications/unnotification_response_builder_mac.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "ui/message_center/public/cpp/notification.h"
#include "url/gurl.h"
// TODO(crbug/1146412): Move the mock classes to a separate file to avoid name
// clashes.
API_AVAILABLE(macosx(10.14))
@interface FakeNotification : NSObject
@property(nonatomic, retain) UNNotificationRequest* request;
@end
@implementation FakeNotification
@synthesize request;
@end
API_AVAILABLE(macosx(10.14))
@interface FakeUNUserNotificationCenter : NSObject
- (instancetype)init;
// Need to provide a nop implementation of setDelegate as it is
// used during the setup of the bridge.
- (void)setDelegate:(id<UNUserNotificationCenterDelegate>)delegate;
- (void)removeAllDeliveredNotifications;
- (void)setNotificationCategories:(NSSet<UNNotificationCategory*>*)categories;
- (void)replaceContentForRequestWithIdentifier:(NSString*)requestIdentifier
replacementContent:
(UNMutableNotificationContent*)content
completionHandler:
(void (^)(NSError* _Nullable error))
notificationDelivered;
- (void)addNotificationRequest:(UNNotificationRequest*)request
withCompletionHandler:(void (^)(NSError* error))completionHandler;
- (void)getDeliveredNotificationsWithCompletionHandler:
(void (^)(NSArray<UNNotification*>* notifications))completionHandler;
- (void)getNotificationCategoriesWithCompletionHandler:
(void (^)(NSSet<UNNotificationCategory*>* categories))completionHandler;
- (void)requestAuthorizationWithOptions:(UNAuthorizationOptions)options
completionHandler:(void (^)(BOOL granted, NSError* error))
completionHandler;
- (void)removeDeliveredNotificationsWithIdentifiers:
(NSArray<NSString*>*)identifiers;
@end
@implementation FakeUNUserNotificationCenter {
base::scoped_nsobject<NSMutableArray> _banners;
base::scoped_nsobject<NSSet> _categories;
}
- (instancetype)init {
if ((self = [super init])) {
_banners.reset([[NSMutableArray alloc] init]);
_categories.reset([[NSSet alloc] init]);
}
return self;
}
- (void)setDelegate:(id<UNUserNotificationCenterDelegate>)delegate {
}
- (void)removeAllDeliveredNotifications {
[_banners removeAllObjects];
}
- (void)setNotificationCategories:(NSSet<UNNotificationCategory*>*)categories {
_categories.reset([categories copy]);
}
- (void)replaceContentForRequestWithIdentifier:(NSString*)requestIdentifier
replacementContent:
(UNMutableNotificationContent*)content
completionHandler:
(void (^)(NSError* _Nullable error))
notificationDelivered {
UNNotificationRequest* request =
[UNNotificationRequest requestWithIdentifier:requestIdentifier
content:content
trigger:nil];
base::scoped_nsobject<FakeNotification> notification(
[[FakeNotification alloc] init]);
[notification setRequest:request];
[_banners addObject:notification];
notificationDelivered(/*error=*/nil);
}
- (void)addNotificationRequest:(UNNotificationRequest*)request
withCompletionHandler:(void (^)(NSError* error))completionHandler {
base::scoped_nsobject<FakeNotification> notification(
[[FakeNotification alloc] init]);
[notification setRequest:request];
[_banners addObject:notification];
completionHandler(/*error=*/nil);
}
- (void)getDeliveredNotificationsWithCompletionHandler:
(void (^)(NSArray<UNNotification*>* notifications))completionHandler {
completionHandler([[_banners copy] autorelease]);
}
- (void)getNotificationCategoriesWithCompletionHandler:
(void (^)(NSSet<UNNotificationCategory*>* categories))completionHandler {
completionHandler([[_categories copy] autorelease]);
}
- (void)requestAuthorizationWithOptions:(UNAuthorizationOptions)options
completionHandler:(void (^)(BOOL granted, NSError* error))
completionHandler {
completionHandler(/*granted=*/YES, /*error=*/nil);
}
- (void)removeDeliveredNotificationsWithIdentifiers:
(NSArray<NSString*>*)identifiers {
[_banners filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
UNNotification* notification,
NSDictionary* bindings) {
NSString* toastId = [[[[notification request] content] userInfo]
objectForKey:notification_constants::kNotificationId];
return ![identifiers containsObject:toastId];
}]];
}
@end
using message_center::Notification;
class UNNotificationPlatformBridgeMacTest : public testing::Test {
public:
UNNotificationPlatformBridgeMacTest()
: manager_(TestingBrowserProcess::GetGlobal()) {}
void SetUp() override {
ASSERT_TRUE(manager_.SetUp());
profile_ = manager_.CreateTestingProfile("Moe");
if (@available(macOS 10.14, *)) {
center_.reset([[FakeUNUserNotificationCenter alloc] init]);
bridge_ = std::make_unique<NotificationPlatformBridgeMacUNNotification>(
static_cast<UNUserNotificationCenter*>(center_.get()));
}
}
protected:
Notification CreateNotification(const std::string& notificationId = "id1") {
GURL url("https://gmail.com");
Notification notification(
message_center::NOTIFICATION_TYPE_SIMPLE, notificationId,
base::UTF8ToUTF16("Title"), base::UTF8ToUTF16("Context"), gfx::Image(),
base::UTF8ToUTF16("Notifier's Name"), url,
message_center::NotifierId(url), message_center::RichNotificationData(),
base::MakeRefCounted<message_center::NotificationDelegate>());
return notification;
}
API_AVAILABLE(macosx(10.14))
base::scoped_nsobject<FakeUNUserNotificationCenter> center_;
API_AVAILABLE(macosx(10.14))
std::unique_ptr<NotificationPlatformBridgeMacUNNotification> bridge_;
TestingProfile* profile_ = nullptr;
private:
content::BrowserTaskEnvironment task_environment_;
TestingProfileManager manager_;
};
TEST_F(UNNotificationPlatformBridgeMacTest, TestDisplay) {
if (@available(macOS 10.14, *)) {
Notification notification = CreateNotification();
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
notification, nullptr);
[center_ getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification*>* _Nonnull notifications) {
ASSERT_EQ(1u, [notifications count]);
UNNotification* delivered_notification = [notifications objectAtIndex:0];
UNNotificationContent* delivered_content =
[[delivered_notification request] content];
EXPECT_NSEQ(@"Title", [delivered_content title]);
EXPECT_NSEQ(@"Context", [delivered_content body]);
EXPECT_NSEQ(@"gmail.com", [delivered_content subtitle]);
}];
[center_ getNotificationCategoriesWithCompletionHandler:^(
NSSet<UNNotificationCategory*>* categories) {
EXPECT_EQ(1u, [categories count]);
}];
}
}
TEST_F(UNNotificationPlatformBridgeMacTest, TestNotificationHasIcon) {
if (@available(macOS 10.14, *)) {
Notification notification = CreateNotification();
SkBitmap icon;
icon.allocN32Pixels(64, 64);
icon.eraseARGB(255, 100, 150, 200);
notification.set_icon(gfx::Image::CreateFrom1xBitmap(icon));
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
notification, nullptr);
[center_ getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification*>* _Nonnull notifications) {
ASSERT_EQ(1u, [notifications count]);
UNNotification* delivered_notification = [notifications objectAtIndex:0];
UNNotificationContent* delivered_content =
[[delivered_notification request] content];
ASSERT_EQ(1u, [[delivered_content attachments] count]);
EXPECT_NSEQ(@"id1", [[[delivered_content attachments] objectAtIndex:0]
identifier]);
}];
}
}
TEST_F(UNNotificationPlatformBridgeMacTest, TestNotificationNoIcon) {
if (@available(macOS 10.14, *)) {
Notification notification = CreateNotification();
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
notification, nullptr);
[center_ getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification*>* _Nonnull notifications) {
ASSERT_EQ(1u, [notifications count]);
UNNotification* delivered_notification = [notifications objectAtIndex:0];
UNNotificationContent* delivered_content =
[[delivered_notification request] content];
EXPECT_EQ(0u, [[delivered_content attachments] count]);
}];
}
}
TEST_F(UNNotificationPlatformBridgeMacTest, TestCloseNotification) {
if (@available(macOS 10.14, *)) {
Notification notification = CreateNotification();
[center_ getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification*>* _Nonnull notifications) {
EXPECT_EQ(0u, [notifications count]);
}];
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
notification, nullptr);
[center_ getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification*>* _Nonnull notifications) {
EXPECT_EQ(1u, [notifications count]);
}];
bridge_->Close(profile_, "id1");
// RunLoop is used here to ensure that Close has finished executing before
// the notification count is checked below. Since Close executes
// asynchronous calls the order is not guaranteed by nature.
base::RunLoop().RunUntilIdle();
[center_ getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification*>* _Nonnull notifications) {
EXPECT_EQ(0u, [notifications count]);
}];
}
}
TEST_F(UNNotificationPlatformBridgeMacTest, TestGetDisplayed) {
if (@available(macOS 10.14, *)) {
Notification notification = CreateNotification();
[center_ getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification*>* _Nonnull notifications) {
EXPECT_EQ(0u, [notifications count]);
}];
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
notification, nullptr);
[center_ getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification*>* _Nonnull notifications) {
EXPECT_EQ(1u, [notifications count]);
}];
base::RunLoop run_loop;
int notification_count = -1;
bridge_->GetDisplayed(
profile_,
base::BindLambdaForTesting([&](std::set<std::string> notifications,
bool supports_synchronization) {
notification_count = notifications.size();
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(1, notification_count);
}
}
TEST_F(UNNotificationPlatformBridgeMacTest,
TestGetDisplayedMultipleNotifications) {
if (@available(macOS 10.14, *)) {
Notification first_notification = CreateNotification("id1");
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
first_notification, nullptr);
Notification second_notification = CreateNotification("id2");
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
second_notification, nullptr);
base::RunLoop run_loop;
int notification_count = -1;
bridge_->GetDisplayed(
profile_,
base::BindLambdaForTesting([&](std::set<std::string> notifications,
bool supports_synchronization) {
notification_count = notifications.size();
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(2, notification_count);
[center_ getNotificationCategoriesWithCompletionHandler:^(
NSSet<UNNotificationCategory*>* categories) {
EXPECT_EQ(2u, [categories count]);
}];
}
}
TEST_F(UNNotificationPlatformBridgeMacTest, TestQuitRemovesNotifications) {
if (@available(macOS 10.14, *)) {
Notification notification = CreateNotification();
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
notification, nullptr);
[center_ getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification*>* _Nonnull notifications) {
EXPECT_EQ(1u, [notifications count]);
}];
bridge_.reset();
// The destructor of the bridge_ will call removeAllDeliveredNotifications.
[center_ getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification*>* _Nonnull notifications) {
EXPECT_EQ(0u, [notifications count]);
}];
}
}
TEST_F(UNNotificationPlatformBridgeMacTest, TestNotificationNoButtons) {
if (@available(macOS 10.14, *)) {
Notification notification = CreateNotification();
notification.set_settings_button_handler(
message_center::SettingsButtonHandler::DELEGATE);
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
notification, nullptr);
[center_ getNotificationCategoriesWithCompletionHandler:^(
NSSet<UNNotificationCategory*>* categories) {
ASSERT_EQ(1u, [categories count]);
UNNotificationCategory* category = [categories anyObject];
// If this selector from the private API is available the close button
// will be set in alernateAction, and the other buttons will be set in
// actions. Otherwise, all buttons will be setting in actions which causes
// the total count to differ by one.
if ([category respondsToSelector:@selector(alternateAction)]) {
ASSERT_EQ(1ul, [[category actions] count]);
EXPECT_NSEQ(@"Close",
[[category valueForKey:@"_alternateAction"] title]);
EXPECT_NSEQ(@"Settings", [[[category actions] lastObject] title]);
} else {
ASSERT_EQ(2ul, [[category actions] count]);
EXPECT_NSEQ(@"Close", [[[category actions] lastObject] title]);
EXPECT_NSEQ(@"Settings", [[[category actions] firstObject] title]);
}
}];
}
}
TEST_F(UNNotificationPlatformBridgeMacTest, TestNotificationNoSettingsButton) {
if (@available(macOS 10.14, *)) {
Notification notification = CreateNotification();
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
notification, nullptr);
[center_ getNotificationCategoriesWithCompletionHandler:^(
NSSet<UNNotificationCategory*>* categories) {
ASSERT_EQ(1u, [categories count]);
UNNotificationCategory* category = [categories anyObject];
if ([category respondsToSelector:@selector(alternateAction)])
EXPECT_EQ(0ul, [[category actions] count]);
else
EXPECT_EQ(1ul, [[category actions] count]);
}];
}
}
TEST_F(UNNotificationPlatformBridgeMacTest, TestNotificationWithButtons) {
if (@available(macOS 10.14, *)) {
Notification notification = CreateNotification();
notification.set_settings_button_handler(
message_center::SettingsButtonHandler::DELEGATE);
std::vector<message_center::ButtonInfo> buttons = {
message_center::ButtonInfo(base::UTF8ToUTF16("Button 1")),
message_center::ButtonInfo(base::UTF8ToUTF16("Button 2"))};
notification.set_buttons(buttons);
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
notification, nullptr);
[center_ getNotificationCategoriesWithCompletionHandler:^(
NSSet<UNNotificationCategory*>* categories) {
ASSERT_EQ(1u, [categories count]);
UNNotificationCategory* category = [categories anyObject];
if ([category respondsToSelector:@selector(alternateAction)]) {
EXPECT_NSEQ(@"Button 1", [[category actions][0] title]);
EXPECT_NSEQ(@"Button 2", [[category actions][1] title]);
EXPECT_EQ(3ul, [[category actions] count]);
} else {
EXPECT_NSEQ(@"Button 1", [[category actions][1] title]);
EXPECT_NSEQ(@"Button 2", [[category actions][2] title]);
EXPECT_EQ(4ul, [[category actions] count]);
}
}];
}
}
TEST_F(UNNotificationPlatformBridgeMacTest,
TestNotificationCategoryIdentifier) {
if (@available(macOS 10.14, *)) {
Notification notification = CreateNotification();
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
notification, nullptr);
[center_ getNotificationCategoriesWithCompletionHandler:^(
NSSet<UNNotificationCategory*>* categories) {
ASSERT_EQ(1u, [categories count]);
EXPECT_NSEQ(@"id1", [[categories anyObject] identifier]);
}];
}
}
TEST_F(UNNotificationPlatformBridgeMacTest, TestCloseRemovesCategory) {
if (@available(macOS 10.14, *)) {
Notification first_notification = CreateNotification();
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
first_notification, nullptr);
[center_ getNotificationCategoriesWithCompletionHandler:^(
NSSet<UNNotificationCategory*>* categories) {
EXPECT_EQ(1u, [categories count]);
}];
bridge_->Close(profile_, "id1");
// RunLoop is used here to ensure that Close has finished executing before
// the notification count is checked below. Since Close executes
// asynchronous calls the order is not guaranteed by nature.
base::RunLoop().RunUntilIdle();
// Categories get updated during the next display call, so we call display
// to make sure that the category has been removed.
Notification second_notification = CreateNotification("id2");
bridge_->Display(NotificationHandler::Type::WEB_PERSISTENT, profile_,
second_notification, nullptr);
[center_ getNotificationCategoriesWithCompletionHandler:^(
NSSet<UNNotificationCategory*>* categories) {
ASSERT_EQ(1u, [categories count]);
EXPECT_NSEQ(@"id2", [[categories anyObject] identifier]);
}];
}
}
...@@ -32,6 +32,7 @@ base::scoped_nsobject<UNNotificationBuilder> NewTestBuilder( ...@@ -32,6 +32,7 @@ base::scoped_nsobject<UNNotificationBuilder> NewTestBuilder(
[builder setProfileId:@"profileId"]; [builder setProfileId:@"profileId"];
[builder setIncognito:false]; [builder setIncognito:false];
[builder setCreatorPid:@1]; [builder setCreatorPid:@1];
[builder setShowSettingsButton:NO];
[builder setNotificationType:[NSNumber numberWithInt:static_cast<int>(type)]]; [builder setNotificationType:[NSNumber numberWithInt:static_cast<int>(type)]];
return builder; return builder;
} }
......
...@@ -3908,6 +3908,7 @@ test("unit_tests") { ...@@ -3908,6 +3908,7 @@ test("unit_tests") {
"../browser/mac/exception_processor_unittest.mm", "../browser/mac/exception_processor_unittest.mm",
"../browser/mac/keystone_glue_unittest.mm", "../browser/mac/keystone_glue_unittest.mm",
"../browser/notifications/notification_platform_bridge_mac_unittest.mm", "../browser/notifications/notification_platform_bridge_mac_unittest.mm",
"../browser/notifications/notification_platform_bridge_mac_unnotification_unittest.mm",
"../browser/notifications/notification_platform_bridge_mac_utils_unittest.mm", "../browser/notifications/notification_platform_bridge_mac_utils_unittest.mm",
"../browser/notifications/stub_alert_dispatcher_mac.h", "../browser/notifications/stub_alert_dispatcher_mac.h",
"../browser/notifications/stub_alert_dispatcher_mac.mm", "../browser/notifications/stub_alert_dispatcher_mac.mm",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment