// Copyright 2024 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/ash/extended_updates/extended_updates_notification.h"
#include <algorithm>
#include <optional>
#include "ash/constants/ash_pref_names.h"
#include "ash/system/extended_updates/extended_updates_metrics.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
#include "chrome/browser/notifications/notification_display_service.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/notifications/notification_handler.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/message_center/public/cpp/notification.h"
namespace ash {
namespace {
using IndexedButton = ExtendedUpdatesNotification::IndexedButton;
using ::testing::Eq;
class TestExtendedUpdatesNotification final
: public ExtendedUpdatesNotification {
public:
explicit TestExtendedUpdatesNotification(Profile* profile)
: ExtendedUpdatesNotification(profile) {}
TestExtendedUpdatesNotification(const TestExtendedUpdatesNotification&) =
delete;
TestExtendedUpdatesNotification& operator=(
const TestExtendedUpdatesNotification&) = delete;
MOCK_METHOD(void, ShowExtendedUpdatesDialog, (), (override));
MOCK_METHOD(void, OpenLearnMoreUrl, (), (override));
private:
~TestExtendedUpdatesNotification() override = default;
};
class ExtendedUpdatesNotificationTest : public testing::Test {
public:
ExtendedUpdatesNotificationTest() = default;
ExtendedUpdatesNotificationTest(const ExtendedUpdatesNotificationTest&) =
delete;
ExtendedUpdatesNotificationTest& operator=(
const ExtendedUpdatesNotificationTest&) = delete;
~ExtendedUpdatesNotificationTest() override = default;
protected:
scoped_refptr<TestExtendedUpdatesNotification> CreateTestNotification(
Profile* profile) {
return base::MakeRefCounted<TestExtendedUpdatesNotification>(profile);
}
// Gets the number of notifications that are currently showing.
int ShowingNotificationCount() {
return std::ranges::count_if(
notification_display_service_tester_.GetDisplayedNotificationsForType(
ExtendedUpdatesNotification::kNotificationType),
[](const message_center::Notification& note) {
return note.id() == ExtendedUpdatesNotification::kNotificationId;
});
}
void ClickNotification(std::optional<IndexedButton> button) {
notification_display_service_tester_.SimulateClick(
ExtendedUpdatesNotification::kNotificationType,
std::string(ExtendedUpdatesNotification::kNotificationId),
button ? std::optional<int>{static_cast<int>(*button)} : std::nullopt,
/*reply=*/std::nullopt);
}
void CloseNotification(bool by_user) {
notification_display_service_tester_.RemoveNotification(
ExtendedUpdatesNotification::kNotificationType,
std::string(ExtendedUpdatesNotification::kNotificationId), by_user,
/*silent=*/false);
}
content::BrowserTaskEnvironment task_environment_;
ScopedTestingCrosSettings cros_settings_;
TestingProfile profile_;
NotificationDisplayServiceTester notification_display_service_tester_{
&profile_};
};
} // namespace
TEST_F(ExtendedUpdatesNotificationTest, ProfileDestroyedBeforeShow) {
auto profile = std::make_unique<TestingProfile>();
auto note = CreateTestNotification(profile.get());
profile.reset();
ExtendedUpdatesNotification::Show(std::move(note));
}
TEST_F(ExtendedUpdatesNotificationTest, ProfileDestroyedAfterShow) {
auto profile = std::make_unique<TestingProfile>();
auto note = CreateTestNotification(profile.get());
ExtendedUpdatesNotification::Show(note);
profile.reset();
note->Close(/*by_user=*/true);
}
TEST_F(ExtendedUpdatesNotificationTest, ClickNoButton) {
ExtendedUpdatesNotification::Show(CreateTestNotification(&profile_));
EXPECT_THAT(ShowingNotificationCount(), Eq(1));
ClickNotification(std::nullopt);
EXPECT_THAT(ShowingNotificationCount(), Eq(1));
CloseNotification(/*by_user=*/false);
EXPECT_THAT(ShowingNotificationCount(), Eq(0));
}
TEST_F(ExtendedUpdatesNotificationTest, ShowExtendedUpdatesDialog) {
base::HistogramTester histogram_tester;
auto note = CreateTestNotification(&profile_);
EXPECT_CALL(*note, ShowExtendedUpdatesDialog()).Times(1);
ExtendedUpdatesNotification::Show(std::move(note));
EXPECT_THAT(ShowingNotificationCount(), Eq(1));
histogram_tester.ExpectBucketCount(
kExtendedUpdatesEntryPointEventMetric,
ExtendedUpdatesEntryPointEvent::kNoArcNotificationShown, 1);
ClickNotification(IndexedButton::kSetUp);
EXPECT_THAT(ShowingNotificationCount(), Eq(0));
EXPECT_FALSE(ExtendedUpdatesNotification::IsNotificationDismissed(&profile_));
histogram_tester.ExpectBucketCount(
kExtendedUpdatesEntryPointEventMetric,
ExtendedUpdatesEntryPointEvent::kNoArcNotificationClicked, 1);
}
TEST_F(ExtendedUpdatesNotificationTest, OpenLearnMoreUrl) {
auto note = CreateTestNotification(&profile_);
EXPECT_CALL(*note, OpenLearnMoreUrl()).Times(1);
ExtendedUpdatesNotification::Show(std::move(note));
EXPECT_THAT(ShowingNotificationCount(), Eq(1));
ClickNotification(IndexedButton::kLearnMore);
EXPECT_THAT(ShowingNotificationCount(), Eq(0));
EXPECT_FALSE(ExtendedUpdatesNotification::IsNotificationDismissed(&profile_));
}
TEST_F(ExtendedUpdatesNotificationTest, UserDismiss) {
ExtendedUpdatesNotification::Show(CreateTestNotification(&profile_));
EXPECT_THAT(ShowingNotificationCount(), Eq(1));
CloseNotification(/*by_user=*/true);
EXPECT_THAT(ShowingNotificationCount(), Eq(0));
EXPECT_TRUE(ExtendedUpdatesNotification::IsNotificationDismissed(&profile_));
}
TEST_F(ExtendedUpdatesNotificationTest, NonUserDismiss) {
ExtendedUpdatesNotification::Show(CreateTestNotification(&profile_));
EXPECT_THAT(ShowingNotificationCount(), Eq(1));
CloseNotification(/*by_user=*/false);
EXPECT_THAT(ShowingNotificationCount(), Eq(0));
EXPECT_FALSE(ExtendedUpdatesNotification::IsNotificationDismissed(&profile_));
}
TEST_F(ExtendedUpdatesNotificationTest, DismissNotificationAfterOptIn) {
ExtendedUpdatesNotification::Show(CreateTestNotification(&profile_));
EXPECT_THAT(ShowingNotificationCount(), Eq(1));
cros_settings_.device_settings()->SetBoolean(kDeviceExtendedAutoUpdateEnabled,
true);
EXPECT_THAT(ShowingNotificationCount(), Eq(0));
}
TEST_F(ExtendedUpdatesNotificationTest,
DismissNotificationAfterOptInWithNoNotificationShowing) {
ExtendedUpdatesNotification::Show(CreateTestNotification(&profile_));
EXPECT_THAT(ShowingNotificationCount(), Eq(1));
CloseNotification(/*by_user=*/false);
EXPECT_THAT(ShowingNotificationCount(), Eq(0));
cros_settings_.device_settings()->SetBoolean(kDeviceExtendedAutoUpdateEnabled,
true);
EXPECT_THAT(ShowingNotificationCount(), Eq(0));
}
} // namespace ash