blob: dd38db74b69e35eb0f924c4d46117d0bf6219f11 [file] [log] [blame]
// Copyright 2017 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 "chrome/browser/notifications/notification_template_builder.h"
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/notifications/mock_notification_image_retainer.h"
#include "chrome/grit/chromium_strings.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/notification.h"
using message_center::Notification;
using message_center::NotifierId;
using message_center::RichNotificationData;
namespace {
const char kContextMenuLabel[] = "settings";
const char kEncodedId[] = "0|Default|0|https://example.com/|notification_id";
const char kNotificationId[] = "notification_id";
const char kNotificationTitle[] = "My Title";
const char kNotificationMessage[] = "My Message";
const char kNotificationOrigin[] = "https://example.com";
const char kProfileId[] = "Default";
bool FixedTime(base::Time* time) {
base::Time::Exploded exploded = {0};
exploded.year = 1998;
exploded.month = 9;
exploded.day_of_month = 4;
exploded.hour = 1;
exploded.minute = 2;
exploded.second = 3;
return base::Time::FromUTCExploded(exploded, time);
}
} // namespace
class NotificationTemplateBuilderTest : public ::testing::Test {
public:
NotificationTemplateBuilderTest() = default;
~NotificationTemplateBuilderTest() override = default;
void SetUp() override {
NotificationTemplateBuilder::OverrideContextMenuLabelForTesting(
kContextMenuLabel);
}
void TearDown() override {
NotificationTemplateBuilder::OverrideContextMenuLabelForTesting(nullptr);
}
protected:
// Builds a notification object and initializes it to default values.
std::unique_ptr<message_center::Notification> InitializeBasicNotification() {
GURL origin_url(kNotificationOrigin);
auto notification = std::make_unique<message_center::Notification>(
message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
base::UTF8ToUTF16(kNotificationTitle),
base::UTF8ToUTF16(kNotificationMessage), gfx::Image() /* icon */,
base::string16() /* display_source */, origin_url,
NotifierId(origin_url), RichNotificationData(), nullptr /* delegate */);
// Set a fixed timestamp, to avoid having to test against current timestamp.
base::Time timestamp;
if (!FixedTime(&timestamp))
return nullptr;
notification->set_timestamp(timestamp);
return notification;
}
// Converts the notification data to XML and verifies it is as expected. Calls
// must be wrapped in ASSERT_NO_FATAL_FAILURE().
void VerifyXml(const message_center::Notification& notification,
const base::string16& xml_template) {
MockNotificationImageRetainer image_retainer;
template_ = NotificationTemplateBuilder::Build(&image_retainer, kEncodedId,
kProfileId, notification);
ASSERT_TRUE(template_);
EXPECT_EQ(template_->GetNotificationTemplate(), xml_template);
}
protected:
std::unique_ptr<NotificationTemplateBuilder> template_;
private:
DISALLOW_COPY_AND_ASSIGN(NotificationTemplateBuilderTest);
};
TEST_F(NotificationTemplateBuilderTest, SimpleToast) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">example.com</text>
</binding>
</visual>
<actions>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, Buttons) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
std::vector<message_center::ButtonInfo> buttons;
buttons.emplace_back(base::ASCIIToUTF16("Button1"));
buttons.emplace_back(base::ASCIIToUTF16("Button2"));
notification->set_buttons(buttons);
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">example.com</text>
</binding>
</visual>
<actions>
<action activationType="foreground" content="Button1" arguments="buttonIndex=0"/>
<action activationType="foreground" content="Button2" arguments="buttonIndex=1"/>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, InlineReplies) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
std::vector<message_center::ButtonInfo> buttons;
message_center::ButtonInfo button1(base::ASCIIToUTF16("Button1"));
button1.type = message_center::ButtonType::TEXT;
button1.placeholder = base::ASCIIToUTF16("Reply here");
buttons.emplace_back(button1);
buttons.emplace_back(base::ASCIIToUTF16("Button2"));
notification->set_buttons(buttons);
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">example.com</text>
</binding>
</visual>
<actions>
<input id="userResponse" type="text" placeHolderContent="Reply here"/>
<action activationType="foreground" content="Button1" arguments="buttonIndex=0"/>
<action activationType="foreground" content="Button2" arguments="buttonIndex=1"/>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, InlineRepliesDoubleInput) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
std::vector<message_center::ButtonInfo> buttons;
message_center::ButtonInfo button1(base::ASCIIToUTF16("Button1"));
button1.type = message_center::ButtonType::TEXT;
button1.placeholder = base::ASCIIToUTF16("Reply here");
buttons.emplace_back(button1);
message_center::ButtonInfo button2(base::ASCIIToUTF16("Button2"));
button2.type = message_center::ButtonType::TEXT;
button2.placeholder = base::ASCIIToUTF16("Should not appear");
buttons.emplace_back(button2);
notification->set_buttons(buttons);
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">example.com</text>
</binding>
</visual>
<actions>
<input id="userResponse" type="text" placeHolderContent="Reply here"/>
<action activationType="foreground" content="Button1" arguments="buttonIndex=0"/>
<action activationType="foreground" content="Button2" arguments="buttonIndex=1"/>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, InlineRepliesTextTypeNotFirst) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
std::vector<message_center::ButtonInfo> buttons;
buttons.emplace_back(base::ASCIIToUTF16("Button1"));
message_center::ButtonInfo button2(base::ASCIIToUTF16("Button2"));
button2.type = message_center::ButtonType::TEXT;
button2.placeholder = base::ASCIIToUTF16("Reply here");
buttons.emplace_back(button2);
notification->set_buttons(buttons);
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">example.com</text>
</binding>
</visual>
<actions>
<input id="userResponse" type="text" placeHolderContent="Reply here"/>
<action activationType="foreground" content="Button1" arguments="buttonIndex=0"/>
<action activationType="foreground" content="Button2" arguments="buttonIndex=1"/>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, Silent) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
notification->set_silent(true);
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">example.com</text>
</binding>
</visual>
<actions>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
<audio silent="true"/>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, RequireInteraction) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
std::vector<message_center::ButtonInfo> buttons;
buttons.emplace_back(base::ASCIIToUTF16("Button1"));
notification->set_buttons(buttons);
notification->set_never_timeout(true);
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" scenario="reminder" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">example.com</text>
</binding>
</visual>
<actions>
<action activationType="foreground" content="Button1" arguments="buttonIndex=0"/>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, NullTimestamp) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
base::Time timestamp;
notification->set_timestamp(timestamp);
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">example.com</text>
</binding>
</visual>
<actions>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, LocalizedContextMenu) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
// Disable overriding context menu label.
NotificationTemplateBuilder::OverrideContextMenuLabelForTesting(nullptr);
const wchar_t kExpectedXmlTemplate[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">example.com</text>
</binding>
</visual>
<actions>
<action content="%ls" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
base::string16 settings_msg = l10n_util::GetStringUTF16(
IDS_WIN_NOTIFICATION_SETTINGS_CONTEXT_MENU_ITEM_NAME);
base::string16 expected_xml =
base::StringPrintf(kExpectedXmlTemplate, settings_msg.c_str());
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, expected_xml));
}
TEST_F(NotificationTemplateBuilderTest, Images) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
SkBitmap icon;
icon.allocN32Pixels(64, 64);
icon.eraseARGB(255, 100, 150, 200);
notification->set_icon(gfx::Image::CreateFrom1xBitmap(icon));
notification->set_image(gfx::Image::CreateFrom1xBitmap(icon));
std::vector<message_center::ButtonInfo> buttons;
message_center::ButtonInfo button(base::ASCIIToUTF16("Button1"));
button.type = message_center::ButtonType::TEXT;
button.placeholder = base::ASCIIToUTF16("Reply here");
button.icon = gfx::Image::CreateFrom1xBitmap(icon);
buttons.emplace_back(button);
notification->set_buttons(buttons);
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">example.com</text>
<image placement="appLogoOverride" src="c:\temp\img0.tmp" hint-crop="none"/>
<image placement="hero" src="c:\temp\img1.tmp"/>
</binding>
</visual>
<actions>
<input id="userResponse" type="text" placeHolderContent="Reply here"/>
<action activationType="foreground" content="Button1" arguments="buttonIndex=0" imageUri="c:\temp\img2.tmp"/>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, ContextMessage) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
notification->set_context_message(L"context_message");
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">context_message</text>
</binding>
</visual>
<actions>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, ExtensionNoContextMessage) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
// Explicitly not setting context message to ensure attribution is not added.
// Explicitly set origin url to something non http/https to ensure that origin
// is not used as attribution.
notification->set_origin_url(
GURL("chrome-extension://bfojpkhoiegeigfifhdnbeobmhlahdle/"));
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
</binding>
</visual>
<actions>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, ProgressBar) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
notification->set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
notification->set_progress(30);
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>My Message</text>
<text placement="attribution">example.com</text>
<progress status="" value="0.30"/>
</binding>
</visual>
<actions>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}
TEST_F(NotificationTemplateBuilderTest, ListEntries) {
std::unique_ptr<message_center::Notification> notification =
InitializeBasicNotification();
notification->set_type(message_center::NOTIFICATION_TYPE_MULTIPLE);
std::vector<message_center::NotificationItem> items;
items.emplace_back(L"title1", L"message1");
items.emplace_back(L"title2", L"message2");
items.emplace_back(L"title3", L"message3");
items.emplace_back(L"title4", L"message4");
items.emplace_back(L"title5", L"message5"); // Will be truncated.
notification->set_items(items);
const wchar_t kExpectedXml[] =
LR"(<toast launch="0|Default|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
<visual>
<binding template="ToastGeneric">
<text>My Title</text>
<text>title1 - message1
title2 - message2
title3 - message3
title4 - message4
</text>
<text placement="attribution">example.com</text>
</binding>
</visual>
<actions>
<action content="settings" placement="contextMenu" activationType="foreground" arguments="notificationSettings"/>
</actions>
</toast>
)";
ASSERT_NO_FATAL_FAILURE(VerifyXml(*notification, kExpectedXml));
}