chromium/chrome/browser/ash/eol/eol_notification_browsertest.cc

// Copyright 2023 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/eol/eol_notification.h"

#include <memory>
#include <string>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "ash/public/cpp/ash_view_ids.h"
#include "ash/public/cpp/system_tray_test_api.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "chrome/browser/ash/login/session/user_session_manager.h"
#include "chrome/browser/ash/login/test/device_state_mixin.h"
#include "chrome/browser/ash/login/test/guest_session_mixin.h"
#include "chrome/browser/ash/login/test/logged_in_user_mixin.h"
#include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/notifications/notification_test_util.h"
#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_manager_observer.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
#include "chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h"
#include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/user_manager.h"
#include "content/public/test/browser_test.h"

namespace ash {

namespace {

constexpr char kEolNotificationId[] = "chrome://product_eol";

enum class TestCase {
  kIncentivesDisabled,
  kIncentivesWithoutOffer,
  kIncentivesWithOffer,
  kIncentivesWithOfferAndAUEWarning,
};

// Returns the "incentive_type" feature param to be used for a test case.
std::string GetTestFeatureParamString(TestCase test_case) {
  switch (test_case) {
    case TestCase::kIncentivesDisabled:
      NOTREACHED_IN_MIGRATION();
      return "";
    case TestCase::kIncentivesWithoutOffer:
      return "no_offer";
    case TestCase::kIncentivesWithOffer:
      return "offer";
    case TestCase::kIncentivesWithOfferAndAUEWarning:
      return "offer_with_warning";
  }
}

// Sets up a test's scoped feature list to run enable the provided test case.
void SetUpScopedFeatureListForTestCase(
    TestCase test_case,
    base::test::ScopedFeatureList* scoped_feature_list) {
  if (test_case == TestCase::kIncentivesDisabled) {
    scoped_feature_list->InitAndDisableFeature(features::kEolIncentive);
  } else {
    scoped_feature_list->InitAndEnableFeatureWithParameters(
        features::kEolIncentive,
        {{"incentive_type", GetTestFeatureParamString(test_case)}});
  }
}

// Possible results for `EolStatusMixin::SetUpTime()`.
enum class TimeSetupResult {
  kSuccess,
  kInvalidNow,
  kInvalidEol,
  kInvalidProfileCreation
};

// A mixin that injects a stub notification display service that tracks set of
// notifications for the primary profile. Expected to be used with test that go
// through login flow, and the primary user profile is different than the
// browser test profile (`browser()->profile()`).
class NotificationDisplayServiceMixin : public InProcessBrowserTestMixin,
                                        public ProfileManagerObserver {
 public:
  explicit NotificationDisplayServiceMixin(
      InProcessBrowserTestMixinHost* mixin_host)
      : InProcessBrowserTestMixin(mixin_host) {}

  NotificationDisplayServiceMixin(const NotificationDisplayServiceMixin&) =
      delete;
  NotificationDisplayServiceMixin& operator=(
      const NotificationDisplayServiceMixin&) = delete;

  ~NotificationDisplayServiceMixin() override = default;

  // InProcessBrowserTestMixin:
  void SetUpOnMainThread() override {
    // The mixin observes profile manager, and initializes the test notification
    // service when the primary user profile gets added. If primary user (and
    // profile) have already been created at this point, the mixin will not be
    // able to detect profile addition.
    ASSERT_FALSE(user_manager::UserManager::Get()->GetPrimaryUser());
    profile_waiter_ = std::make_unique<base::RunLoop>();
    profile_manager_observer_.Observe(g_browser_process->profile_manager());
  }

  void TearDownOnMainThread() override {
    profile_manager_observer_.Reset();
    profile_waiter_.reset();
    display_service_.reset();
  }

  // ProfileManagerObserver:
  void OnProfileAdded(Profile* profile) override {
    if (!user_manager::UserManager::Get()->IsPrimaryUser(
            BrowserContextHelper::Get()->GetUserByBrowserContext(profile))) {
      return;
    }
    profile_manager_observer_.Reset();
    display_service_ =
        std::make_unique<NotificationDisplayServiceTester>(profile);
    profile_waiter_->Quit();
  }

  NotificationDisplayServiceTester* WaitForDisplayService() {
    if (!display_service_ && !profile_manager_observer_.IsObserving()) {
      return nullptr;
    }

    profile_waiter_->Run();
    return display_service_.get();
  }

 private:
  base::ScopedObservation<ProfileManager, ProfileManagerObserver>
      profile_manager_observer_{this};
  std::unique_ptr<NotificationDisplayServiceTester> display_service_;
  std::unique_ptr<base::RunLoop> profile_waiter_;
};

// Mixin that sets up session state to indicate certain EOL status:
// It can override EOL date provided returned by update engine, the current time
// used by EOL notification handler, and profile creation time.
class EolStatusMixin : public InProcessBrowserTestMixin {
 public:
  explicit EolStatusMixin(InProcessBrowserTestMixinHost* mixin_host)
      : InProcessBrowserTestMixin(mixin_host) {}

  EolStatusMixin(const EolStatusMixin&) = delete;
  EolStatusMixin& operator=(const EolStatusMixin&) = delete;

  ~EolStatusMixin() override = default;

  // InProcessBrowserTestMixin:
  void SetUpInProcessBrowserTestFixture() override {
    InProcessBrowserTestMixin::SetUpInProcessBrowserTestFixture();
    update_engine_client_ = UpdateEngineClient::InitializeFakeForTest();
  }

  void SetUpOnMainThread() override {
    InProcessBrowserTestMixin::SetUpOnMainThread();
    UserSessionManager::GetInstance()
        ->SetEolNotificationHandlerFactoryForTesting(
            base::BindRepeating(&EolStatusMixin::CreateEolNotificationHandler,
                                base::Unretained(this)));
  }
  void TearDownOnMainThread() override {
    UserSessionManager::GetInstance()
        ->SetEolNotificationHandlerFactoryForTesting(
            UserSessionManager::EolNotificationHandlerFactoryCallback());

    InProcessBrowserTestMixin::TearDownOnMainThread();
  }

  // Sets up times relevant to calculating EOL notification status.
  // `now_string` - The time used by EOL notification handler.
  // `eol_string` - The time reportted by fake update engine as device EOL.
  // `profile_creation_string` - The time used to override user profile creation
  // when EOL notification handler gets created.
  // Callers should verify that the method returned kSuccess.
  [[nodiscard]] TimeSetupResult SetUpTime(const char* now_string,
                                          const char* eol_string,
                                          const char* profile_creation_string) {
    base::Time now;
    if (!base::Time::FromUTCString(now_string, &now)) {
      return TimeSetupResult::kInvalidNow;
    }

    base::Time eol;
    if (!base::Time::FromUTCString(eol_string, &eol)) {
      return TimeSetupResult::kInvalidEol;
    }

    if (!base::Time::FromUTCString(profile_creation_string,
                                   &profile_creation_time_)) {
      return TimeSetupResult::kInvalidProfileCreation;
    }

    clock_.SetNow(now);
    update_engine_client_->set_eol_date(eol);
    return TimeSetupResult::kSuccess;
  }

 private:
  std::unique_ptr<EolNotification> CreateEolNotificationHandler(
      Profile* profile) {
    auto eol_notification = std::make_unique<EolNotification>(profile);
    eol_notification->OverrideClockForTesting(&clock_);
    if (!profile_creation_time_.is_null()) {
      profile->SetCreationTimeForTesting(profile_creation_time_);
    }
    return eol_notification;
  }

  raw_ptr<FakeUpdateEngineClient, DanglingUntriaged> update_engine_client_ =
      nullptr;
  base::SimpleTestClock clock_;
  base::Time profile_creation_time_;
};

}  // namespace

// Tests that verify EOL notifications for regular users on non-managed devices.
class EolNotificationTest : public MixinBasedInProcessBrowserTest,
                            public ::testing::WithParamInterface<TestCase> {
 public:
  EolNotificationTest() {
    SetUpScopedFeatureListForTestCase(GetParam(), &scoped_feature_list_);
  }

  bool NotificationHasClaimButton() {
    return GetParam() != TestCase::kIncentivesDisabled;
  }

  std::u16string GetEolApproachingNotificationTitle(
      const std::u16string& eol_month) const {
    switch (GetParam()) {
      case TestCase::kIncentivesDisabled:
        return u"Updates end " + eol_month;
      case TestCase::kIncentivesWithoutOffer:
        return u"Security updates end soon. Upgrade to a new Chromebook.";
      case TestCase::kIncentivesWithOffer:
        return u"Save $50 or more on a new Chromebook, when you upgrade today";
      case TestCase::kIncentivesWithOfferAndAUEWarning:
        return u"Security updates end soon. Save $50 or more on a new "
               u"Chromebook.";
    }
  }

  std::u16string GetEolApproachingNotificationMessage(
      const std::string& eol_month) const {
    switch (GetParam()) {
      case TestCase::kIncentivesDisabled:
        return u"You'll still be able to use this Chrome device after that "
               u"time, but it will no longer get automatic software and "
               u"security updates";
      case TestCase::kIncentivesWithoutOffer:
        return base::UTF8ToUTF16(base::StringPrintf(
            "You will stop getting security and software updates for this "
            "Chromebook in %s. Upgrade your Chromebook for the best "
            "experience.",
            eol_month.c_str()));
      case TestCase::kIncentivesWithOffer:
      case TestCase::kIncentivesWithOfferAndAUEWarning:
        return base::UTF8ToUTF16(
            base::StringPrintf("You will stop getting security updates for "
                               "this Chromebook in %s. Time to upgrade for "
                               "the latest security and software. Offer "
                               "terms apply.",
                               eol_month.c_str()));
    }
  }

  std::u16string GetRecentEolNotificationTitle() const {
    switch (GetParam()) {
      case TestCase::kIncentivesDisabled:
        return u"Final software update";
      case TestCase::kIncentivesWithoutOffer:
        return u"Security updates have ended. Upgrade to a new Chromebook.";
      case TestCase::kIncentivesWithOffer:
        return u"Save $50 or more on a new Chromebook, when you upgrade today";
      case TestCase::kIncentivesWithOfferAndAUEWarning:
        return u"Security updates have ended. Save $50 or more on a new "
               u"Chromebook.";
    }
  }

  std::u16string GetRecentEolNotificationMessage() const {
    switch (GetParam()) {
      case TestCase::kIncentivesDisabled:
        return u"This is the last automatic software and security update for "
               u"this Chrome device. To get future updates, upgrade to a "
               u"newer model.";
      case TestCase::kIncentivesWithoutOffer:
        return u"Your Chromebook is no longer receiving security and software "
               u"updates. Upgrade your Chromebook for the best experience.";
      case TestCase::kIncentivesWithOffer:
      case TestCase::kIncentivesWithOfferAndAUEWarning:
        return u"Your Chromebook is no longer receiving security updates. "
               u"Time to upgrade for the latest security and software. "
               u"Offer terms apply.";
    }
  }

  bool ShowsNotificationWithOffer() const {
    switch (GetParam()) {
      case TestCase::kIncentivesDisabled:
      case TestCase::kIncentivesWithoutOffer:
        return false;
      case TestCase::kIncentivesWithOffer:
      case TestCase::kIncentivesWithOfferAndAUEWarning:
        return true;
    }
  }

 protected:
  EolStatusMixin eol_status_mixin_{&mixin_host_};

  NotificationDisplayServiceMixin notifications_mixin_{&mixin_host_};

  ash::LoggedInUserMixin logged_in_user_mixin_{
      &mixin_host_, /*test_base=*/this, embedded_test_server(),
      LoggedInUserMixin::LogInType::kConsumer};

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

INSTANTIATE_TEST_SUITE_P(
    All,
    EolNotificationTest,
    ::testing::Values(TestCase::kIncentivesDisabled,
                      TestCase::kIncentivesWithoutOffer,
                      TestCase::kIncentivesWithOffer,
                      TestCase::kIncentivesWithOfferAndAUEWarning));

// Tests that verify EOL notifications are not shown on managed devices.
class ManagedDeviceEolNotificationTest
    : public MixinBasedInProcessBrowserTest,
      public ::testing::WithParamInterface<TestCase> {
 public:
  ManagedDeviceEolNotificationTest() {
    SetUpScopedFeatureListForTestCase(GetParam(), &scoped_feature_list_);
  }

 protected:
  EolStatusMixin eol_status_mixin_{&mixin_host_};

  NotificationDisplayServiceMixin notifications_mixin_{&mixin_host_};

  ash::DeviceStateMixin device_state_{
      &mixin_host_,
      ash::DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};

  ash::LoggedInUserMixin logged_in_user_mixin_{
      &mixin_host_, /*test_base=*/this, embedded_test_server(),
      LoggedInUserMixin::LogInType::kManaged};

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

INSTANTIATE_TEST_SUITE_P(
    All,
    ManagedDeviceEolNotificationTest,
    ::testing::Values(TestCase::kIncentivesDisabled,
                      TestCase::kIncentivesWithoutOffer,
                      TestCase::kIncentivesWithOffer,
                      TestCase::kIncentivesWithOfferAndAUEWarning));

// Tests that verify EOL notifications with incentives are not shown for child
// users.
class ChildUserEolNotificationTest
    : public MixinBasedInProcessBrowserTest,
      public ::testing::WithParamInterface<TestCase> {
 public:
  ChildUserEolNotificationTest() {
    SetUpScopedFeatureListForTestCase(GetParam(), &scoped_feature_list_);
  }

 protected:
  EolStatusMixin eol_status_mixin_{&mixin_host_};

  NotificationDisplayServiceMixin notifications_mixin_{&mixin_host_};

  ash::LoggedInUserMixin logged_in_user_mixin_{
      &mixin_host_, /*test_base=*/this, embedded_test_server(),
      LoggedInUserMixin::LogInType::kChild};

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

INSTANTIATE_TEST_SUITE_P(
    All,
    ChildUserEolNotificationTest,
    ::testing::Values(TestCase::kIncentivesDisabled,
                      TestCase::kIncentivesWithoutOffer,
                      TestCase::kIncentivesWithOffer,
                      TestCase::kIncentivesWithOfferAndAUEWarning));

class SuppressedNotificationTest : public MixinBasedInProcessBrowserTest,
                                   public ::testing::WithParamInterface<bool> {
 public:
  SuppressedNotificationTest() {
    scoped_feature_list_.InitWithFeatureState(
        features::kSuppressFirstEolWarning, SuppressFirstWarningEnabled());
  }

  bool SuppressFirstWarningEnabled() { return GetParam(); }

 protected:
  EolStatusMixin eol_status_mixin_{&mixin_host_};
  NotificationDisplayServiceMixin notifications_mixin_{&mixin_host_};
  ash::LoggedInUserMixin logged_in_user_mixin_{
      &mixin_host_, /*test_base=*/this, embedded_test_server(),
      LoggedInUserMixin::LogInType::kConsumer};

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

INSTANTIATE_TEST_SUITE_P(All, SuppressedNotificationTest, testing::Bool());

IN_PROC_BROWSER_TEST_P(EolNotificationTest, ShowNotificationForEolApproaching) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"12 May 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  ASSERT_TRUE(notification);

  EXPECT_EQ(GetEolApproachingNotificationTitle(u"June 2023"),
            notification->title());
  EXPECT_EQ(GetEolApproachingNotificationMessage("June 2023"),
            notification->message());

  if (NotificationHasClaimButton()) {
    notification_display_service->SimulateClick(
        NotificationHandler::Type::TRANSIENT, notification->id(),
        /*action_index=*/0, /*reply=*/std::nullopt);
    content::WebContents* active_contents =
        chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
    ASSERT_TRUE(active_contents);
    EXPECT_EQ(ShowsNotificationWithOffer()
                  ? chrome::kEolIncentiveNotificationOfferURL
                  : chrome::kEolIncentiveNotificationNoOfferURL,
              active_contents->GetVisibleURL());
  }
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest, NoTrayNoticeWhenEolApproaches) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"12 May 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));
  logged_in_user_mixin_.LogInUser();
  base::RunLoop().RunUntilIdle();

  EXPECT_FALSE(SystemTrayTestApi().IsBubbleViewVisible(
      VIEW_ID_QS_EOL_NOTICE_BUTTON, /*open_tray=*/true));
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest,
                       PRE_EolApproachingNotificationNotReshown) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"12 May 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  ASSERT_TRUE(notification);
  notification_display_service->SimulateClick(
      NotificationHandler::Type::TRANSIENT, notification->id(),
      /*action_index=*/0, /*reply=*/std::nullopt);
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest,
                       EolApproachingNotificationNotReshown) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"12 May 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  EXPECT_FALSE(notification);
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest,
                       ShowEolApproachingNotificationForNewUsers) {
  ASSERT_EQ(
      TimeSetupResult::kSuccess,
      eol_status_mixin_.SetUpTime(/*now_string=*/"12 May 2023",
                                  /*eol_string=*/"01 June 2023",
                                  /*profile_creation_string=*/"01 April 2023"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  ASSERT_TRUE(notification);

  // Users that were created recently are not eligible for incentive
  // notifications.
  EXPECT_EQ(u"Updates end June 2023", notification->title());

  notification_display_service->SimulateClick(
      NotificationHandler::Type::TRANSIENT, notification->id(),
      /*action_index=*/0, /*reply=*/std::nullopt);
  content::WebContents* active_contents =
      chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(active_contents);
  EXPECT_EQ(GURL(chrome::kAutoUpdatePolicyURL),
            active_contents->GetVisibleURL());
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest, ShowRecentEolNotification) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  ASSERT_TRUE(notification);

  EXPECT_EQ(GetRecentEolNotificationTitle(), notification->title());
  EXPECT_EQ(GetRecentEolNotificationMessage(), notification->message());

  if (NotificationHasClaimButton()) {
    notification_display_service->SimulateClick(
        NotificationHandler::Type::TRANSIENT, notification->id(),
        /*action_index=*/0, /*reply=*/std::nullopt);
    content::WebContents* active_contents =
        chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
    ASSERT_TRUE(active_contents);
    EXPECT_EQ(ShowsNotificationWithOffer()
                  ? chrome::kEolIncentiveNotificationOfferURL
                  : chrome::kEolIncentiveNotificationNoOfferURL,
              active_contents->GetVisibleURL());
  }
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest,
                       PRE_RecentEolNotificationNotReshown) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  ASSERT_TRUE(notification);

  notification_display_service->SimulateClick(
      NotificationHandler::Type::TRANSIENT, notification->id(),
      /*action_index=*/0, /*reply=*/std::nullopt);

  // Verify quick settings notice still shows.
  EXPECT_EQ(
      GetParam() != TestCase::kIncentivesDisabled,
      SystemTrayTestApi().IsBubbleViewVisible(VIEW_ID_QS_EOL_NOTICE_BUTTON,
                                              /*open_tray=*/true));
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest, RecentEolNotificationNotReshown) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  EXPECT_EQ(GetParam() == TestCase::kIncentivesDisabled, !!notification);
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest, PRE_ShowTrayNoticeSoonAfterEol) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));
  logged_in_user_mixin_.LogInUser();
  base::RunLoop().RunUntilIdle();

  const bool incentives_enabled = GetParam() != TestCase::kIncentivesDisabled;
  SystemTrayTestApi tray_test_api;
  ASSERT_EQ(incentives_enabled,
            tray_test_api.IsBubbleViewVisible(VIEW_ID_QS_EOL_NOTICE_BUTTON,
                                              /*open_tray=*/true));
  if (!incentives_enabled) {
    return;
  }

  tray_test_api.ClickBubbleView(VIEW_ID_QS_EOL_NOTICE_BUTTON);

  content::WebContents* active_contents =
      chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(active_contents);
  EXPECT_EQ(ShowsNotificationWithOffer()
                ? chrome::kEolIncentiveNotificationOfferURL
                : chrome::kEolIncentiveNotificationNoOfferURL,
            active_contents->GetVisibleURL());
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest, ShowTrayNoticeSoonAfterEol) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));
  logged_in_user_mixin_.LogInUser();
  base::RunLoop().RunUntilIdle();

  SystemTrayTestApi tray_test_api;
  EXPECT_EQ(GetParam() != TestCase::kIncentivesDisabled,
            tray_test_api.IsBubbleViewVisible(VIEW_ID_QS_EOL_NOTICE_BUTTON,
                                              /*open_tray=*/true));
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest, NoTrayNoticeOnLockScreen) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));
  logged_in_user_mixin_.LogInUser();
  base::RunLoop().RunUntilIdle();

  SessionManagerClient::Get()->RequestLockScreen();
  SessionStateWaiter(session_manager::SessionState::LOCKED).Wait();

  SystemTrayTestApi tray_test_api;
  EXPECT_FALSE(tray_test_api.IsBubbleViewVisible(VIEW_ID_QS_EOL_NOTICE_BUTTON,
                                                 /*open_tray=*/true));
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest, NoTrayNoticeBeforeLogin) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));
  base::RunLoop().RunUntilIdle();

  SystemTrayTestApi tray_test_api;
  EXPECT_FALSE(tray_test_api.IsBubbleViewVisible(VIEW_ID_QS_EOL_NOTICE_BUTTON,
                                                 /*open_tray=*/true));
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest, NoTrayNoticeForNewUsers) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 May 2023"));
  logged_in_user_mixin_.LogInUser();
  base::RunLoop().RunUntilIdle();

  SystemTrayTestApi tray_test_api;
  EXPECT_FALSE(tray_test_api.IsBubbleViewVisible(VIEW_ID_QS_EOL_NOTICE_BUTTON,
                                                 /*open_tray=*/true));
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest,
                       ShowRecentEolNotificationForNewUsers) {
  ASSERT_EQ(
      TimeSetupResult::kSuccess,
      eol_status_mixin_.SetUpTime(/*now_string=*/"03 June 2023",
                                  /*eol_string=*/"01 June 2023",
                                  /*profile_creation_string=*/"05 May 2023"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  ASSERT_TRUE(notification);

  // Recently created users are not eligible for incentives notifications.
  EXPECT_EQ(u"Final software update", notification->title());

  notification_display_service->SimulateClick(
      NotificationHandler::Type::TRANSIENT, notification->id(),
      /*action_index=*/0, /*reply=*/std::nullopt);
  content::WebContents* active_contents =
      chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(active_contents);
  EXPECT_EQ(GURL(chrome::kEolNotificationURL),
            active_contents->GetVisibleURL());
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest, ShowNonRecentEolNotification) {
  ASSERT_EQ(
      TimeSetupResult::kSuccess,
      eol_status_mixin_.SetUpTime(/*now_string=*/"03 July 2023",
                                  /*eol_string=*/"01 June 2023",
                                  /*profile_creation_string=*/"05 May 2020"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  ASSERT_TRUE(notification);

  // Users should not see incentivized notification if they log in long after
  // EOL.
  EXPECT_EQ(u"Final software update", notification->title());

  notification_display_service->SimulateClick(
      NotificationHandler::Type::TRANSIENT, notification->id(),
      /*action_index=*/0, /*reply=*/std::nullopt);
  content::WebContents* active_contents =
      chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(active_contents);
  EXPECT_EQ(GURL(chrome::kEolNotificationURL),
            active_contents->GetVisibleURL());
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest, PRE_ShowTrayNoticeLongAfterEol) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 August 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));
  logged_in_user_mixin_.LogInUser();
  base::RunLoop().RunUntilIdle();

  const bool incentives_enabled = GetParam() != TestCase::kIncentivesDisabled;
  SystemTrayTestApi tray_test_api;
  ASSERT_EQ(incentives_enabled,
            tray_test_api.IsBubbleViewVisible(VIEW_ID_QS_EOL_NOTICE_BUTTON,
                                              /*open_tray=*/true));
  if (!incentives_enabled) {
    return;
  }

  tray_test_api.ClickBubbleView(VIEW_ID_QS_EOL_NOTICE_BUTTON);

  content::WebContents* active_contents =
      chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(active_contents);
  EXPECT_EQ(chrome::kEolIncentiveNotificationNoOfferURL,
            active_contents->GetVisibleURL());
}

IN_PROC_BROWSER_TEST_P(EolNotificationTest, ShowTrayNoticeLongAfterEol) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 August 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));
  logged_in_user_mixin_.LogInUser();
  base::RunLoop().RunUntilIdle();

  SystemTrayTestApi tray_test_api;
  EXPECT_EQ(GetParam() != TestCase::kIncentivesDisabled,
            tray_test_api.IsBubbleViewVisible(VIEW_ID_QS_EOL_NOTICE_BUTTON,
                                              /*open_tray=*/true));
}

IN_PROC_BROWSER_TEST_P(ManagedDeviceEolNotificationTest,
                       NoEolApproachingNotification) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"12 May 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  EXPECT_FALSE(notification);
}

IN_PROC_BROWSER_TEST_P(ManagedDeviceEolNotificationTest,
                       NoEolPassedNotification) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  EXPECT_FALSE(notification);
}

IN_PROC_BROWSER_TEST_P(ManagedDeviceEolNotificationTest, NoTrayNotice) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));
  logged_in_user_mixin_.LogInUser();
  base::RunLoop().RunUntilIdle();

  EXPECT_FALSE(SystemTrayTestApi().IsBubbleViewVisible(
      VIEW_ID_QS_EOL_NOTICE_BUTTON, /*open_tray=*/true));
}

IN_PROC_BROWSER_TEST_P(ChildUserEolNotificationTest,
                       NoEolApproachingNotification) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"12 May 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  ASSERT_TRUE(notification);

  EXPECT_EQ(u"Updates end June 2023", notification->title());
}

IN_PROC_BROWSER_TEST_P(ChildUserEolNotificationTest, NoEolPassedNotification) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  ASSERT_TRUE(notification);

  EXPECT_EQ(u"Final software update", notification->title());
}

IN_PROC_BROWSER_TEST_P(ChildUserEolNotificationTest, NoTrayNotice) {
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"03 June 2023", /*eol_string=*/"01 June 2023",
                /*profile_creation_string=*/"05 December 2021"));
  logged_in_user_mixin_.LogInUser();
  base::RunLoop().RunUntilIdle();

  EXPECT_FALSE(SystemTrayTestApi().IsBubbleViewVisible(
      VIEW_ID_QS_EOL_NOTICE_BUTTON, /*open_tray=*/true));
}

// Test that the eol notification is not shown when just within 180 days, when
// first eol warning is suppressed.
IN_PROC_BROWSER_TEST_P(SuppressedNotificationTest,
                       EolNotificationSupressed180DaysBefore) {
  // Set eol date to be 173 days in the future.
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"12 May 2023", /*eol_string=*/"1 November 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  EXPECT_EQ(SuppressFirstWarningEnabled(), !notification);
}

// Test that the eol notification is shown when just within 90 days.
IN_PROC_BROWSER_TEST_P(SuppressedNotificationTest,
                       EolNotificationShown90DaysBefore) {
  // Set eol date to be 83 days in the future.
  ASSERT_EQ(TimeSetupResult::kSuccess,
            eol_status_mixin_.SetUpTime(
                /*now_string=*/"12 May 2023", /*eol_string=*/"3 August 2023",
                /*profile_creation_string=*/"05 December 2021"));

  logged_in_user_mixin_.LogInUser();

  NotificationDisplayServiceTester* notification_display_service =
      notifications_mixin_.WaitForDisplayService();
  ASSERT_TRUE(notification_display_service);

  base::RunLoop().RunUntilIdle();

  std::optional<message_center::Notification> notification =
      notification_display_service->GetNotification(kEolNotificationId);
  EXPECT_TRUE(notification);
}

}  // namespace ash