blob: 45684bd45b2f57ecae507835cfb4bdf5bbde82e0 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/hid/hid_pinned_notification.h"
#include <string>
#include "chrome/browser/hid/hid_connection_tracker.h"
#include "chrome/browser/hid/hid_connection_tracker_factory.h"
#include "chrome/browser/hid/hid_system_tray_icon_unittest.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/notifications/system_notification_helper.h"
#include "chrome/test/base/testing_browser_process.h"
#include "extensions/common/constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
namespace {
std::u16string GetExpectedMessage(
const std::vector<HidSystemTrayIconTestBase::OriginItem>& origin_items) {
auto sorted_origin_items = origin_items;
// Sort the |origin_items| by origin. This is necessary because the origin
// items for each profile in the pinned notification are created by iterating
// through a structure of flat_map<url::Origin, ...>.
base::ranges::sort(sorted_origin_items);
#if BUILDFLAG(ENABLE_EXTENSIONS)
std::vector<std::string> extension_names;
for (const auto& [origin, connection_count, name] : sorted_origin_items) {
CHECK(origin.scheme() == extensions::kExtensionScheme);
extension_names.push_back(name);
}
CHECK(!extension_names.empty());
if (extension_names.size() == 1) {
return base::UTF8ToUTF16(base::StringPrintf("%s is accessing HID devices",
extension_names[0].c_str()));
} else if (extension_names.size() == 2) {
return base::UTF8ToUTF16(base::StringPrintf(
"Extensions accessing devices: %s, %s", extension_names[0].c_str(),
extension_names[1].c_str()));
}
return base::UTF8ToUTF16(
base::StringPrintf("Extensions accessing devices: %s, %s +%zu more",
extension_names[0].c_str(), extension_names[1].c_str(),
extension_names.size() - 2));
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
NOTREACHED_NORETURN();
}
} // namespace
class HidPinnedNotificationTest : public HidSystemTrayIconTestBase {
public:
void SetUp() override {
HidSystemTrayIconTestBase::SetUp();
TestingBrowserProcess::GetGlobal()->SetSystemNotificationHelper(
std::make_unique<SystemNotificationHelper>());
display_service_ =
std::make_unique<NotificationDisplayServiceTester>(/*profile=*/nullptr);
}
void TearDown() override {
TestingBrowserProcess::GetGlobal()->SetSystemNotificationHelper(nullptr);
HidSystemTrayIconTestBase::TearDown();
}
void CheckIcon(const std::vector<HidSystemTrayIconTestBase::ProfileItem>&
profile_connection_counts) override {
EXPECT_FALSE(display_service_
->GetDisplayedNotificationsForType(
NotificationHandler::Type::TRANSIENT)
.empty());
// Check each button label and behavior of clicking the button.
for (const auto& [profile, origin_items] : profile_connection_counts) {
size_t total_connection_count = 0;
for (const auto& [origin, connection_count, name] : origin_items) {
total_connection_count += connection_count;
}
auto* hid_connection_tracker = static_cast<MockHidConnectionTracker*>(
HidConnectionTrackerFactory::GetForProfile(profile,
/*create=*/false));
EXPECT_TRUE(hid_connection_tracker);
auto maybe_notification = display_service_->GetNotification(
HidPinnedNotification::GetNotificationId(profile));
ASSERT_TRUE(maybe_notification);
EXPECT_EQ(maybe_notification->title(),
GetExpectedTitle(origin_items.size(), total_connection_count));
EXPECT_EQ(maybe_notification->message(),
GetExpectedMessage(origin_items));
EXPECT_EQ(maybe_notification->priority(), message_center::LOW_PRIORITY);
EXPECT_EQ(maybe_notification->rich_notification_data().buttons.size(),
1u);
EXPECT_EQ(maybe_notification->rich_notification_data().buttons[0].title,
u"HID settings");
EXPECT_TRUE(maybe_notification->delegate());
EXPECT_CALL(*hid_connection_tracker, ShowContentSettingsExceptions());
SimulateButtonClick(profile);
}
}
void CheckIconHidden() override {
EXPECT_TRUE(display_service_
->GetDisplayedNotificationsForType(
NotificationHandler::Type::TRANSIENT)
.empty());
}
private:
void SimulateButtonClick(Profile* profile) {
display_service_->SimulateClick(
NotificationHandler::Type::TRANSIENT,
HidPinnedNotification::GetNotificationId(profile),
/*action_index=*/0, /*reply=*/absl::nullopt);
}
std::unique_ptr<NotificationDisplayServiceTester> display_service_;
};
#if BUILDFLAG(ENABLE_EXTENSIONS)
TEST_F(HidPinnedNotificationTest, SingleProfileEmptyNameExtensionOrigins) {
// Current TestingProfileManager can't support empty profile name as it uses
// profile name for profile path. Passing empty would result in a failure in
// ProfileManager::IsAllowedProfilePath(). Changing the way
// TestingProfileManager creating profile path like adding "profile" prefix
// doesn't work either as some tests are written in a way that takes
// assumption of testing profile path pattern. Hence it creates testing
// profile with non-empty name and then change the profile name to empty which
// can still achieve what this file wants to test.
profile()->set_profile_name("");
TestSingleProfileExtentionOrigins();
}
TEST_F(HidPinnedNotificationTest, BounceConnectionExtensionOrigins) {
TestBounceConnectionExtensionOrigins();
}
TEST_F(HidPinnedNotificationTest, SingleProfileNonEmptyNameExtentionOrigins) {
TestSingleProfileExtentionOrigins();
}
TEST_F(HidPinnedNotificationTest, MultipleProfilesExtentionOrigins) {
TestMultipleProfilesExtensionOrigins();
}
TEST_F(HidPinnedNotificationTest, ExtensionRemoval) {
TestExtensionRemoval();
}
// Test message in pinned notification for up to 5 extensions.
TEST_F(HidPinnedNotificationTest, MultipleExtensionsNotificationMessage) {
Profile* profile = CreateTestingProfile("user");
HidConnectionTracker* hid_connection_tracker =
HidConnectionTrackerFactory::GetForProfile(profile, /*create=*/true);
std::vector<HidSystemTrayIconTestBase::OriginItem> origin_items;
for (size_t idx = 0; idx < 5; idx++) {
auto extension =
CreateExtensionWithName(base::StringPrintf("Test Extension %zu", idx));
AddExtensionToProfile(profile, extension.get());
hid_connection_tracker->IncrementConnectionCount(extension.get()->origin());
origin_items.emplace_back(extension->origin(), 1, extension->name());
CheckIcon({{profile, origin_items}});
}
}
#endif // BUILDFLAG(ENABLE_EXTENSIONS)