910e62b5创建于 1月15日历史提交
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <map>
#include <optional>

#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/user_metrics.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "base/version.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/metrics/variations/google_groups_manager_factory.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/hats/hats_service_desktop.h"
#include "chrome/browser/ui/hats/hats_service_factory.h"
#include "chrome/browser/ui/hats/survey_config.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/metrics_services_manager/metrics_services_manager.h"
#include "components/plus_addresses/core/browser/plus_address_hats_utils.h"
#include "components/plus_addresses/core/common/features.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/variations/service/google_groups_manager.h"
#include "components/variations/service/google_groups_manager_prefs.h"
#include "components/variations/variations_seed_processor.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/test/browser_test.h"
#include "net/dns/mock_host_resolver.h"

namespace {

constexpr char kRelevantGroupId[] = "1234";
constexpr base::TimeDelta kCooldownOverride = base::Days(14);

base::test::FeatureRefAndParams probability_zero{
    features::kHappinessTrackingSurveysForDesktopSettings,
    {{"probability", "0.000"}}};
base::test::FeatureRefAndParams probability_one{
    features::kHappinessTrackingSurveysForDesktopSettings,
    {{"probability", "1.000"},
     {"survey", kHatsSurveyTriggerSettings},
     {"en_site_id", "test_site_id"}}};
base::test::FeatureRefAndParams cool_down_period_overriden{
    autofill::features::kPlusAddressAcceptedFirstTimeCreateSurvey,
    {{"probability", "1.000"},
     {"survey", kHatsSurveyTriggerPlusAddressAcceptedFirstTimeCreate},
     {"cooldown-override-days", /*days=*/"14"}}};
base::test::FeatureRefAndParams cool_down_period_overriden_and_group_controlled{
    autofill::features::kPlusAddressAcceptedFirstTimeCreateSurvey,
    {{variations::internal::kGoogleGroupFeatureParamName, kRelevantGroupId},
     {"probability", "1.000"},
     {"survey", kHatsSurveyTriggerPlusAddressAcceptedFirstTimeCreate},
     {"cooldown-override-days", /*days=*/"14"}}};

class ScopedSetMetricsConsent {
 public:
  // Enables or disables metrics consent based off of |consent|.
  explicit ScopedSetMetricsConsent(bool consent) : consent_(consent) {
    ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting(
        &consent_);
  }

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

  ~ScopedSetMetricsConsent() {
    ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting(
        nullptr);
  }

 private:
  const bool consent_;
};

class HatsServiceBrowserTestBase : public policy::PolicyTest {
 public:
  HatsServiceBrowserTestBase(const HatsServiceBrowserTestBase&) = delete;
  HatsServiceBrowserTestBase& operator=(const HatsServiceBrowserTestBase&) =
      delete;

 protected:
  explicit HatsServiceBrowserTestBase(
      std::vector<base::test::FeatureRefAndParams> enabled_features)
      : enabled_features_(enabled_features) {
    scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features_, {});
  }

  HatsServiceBrowserTestBase() = default;
  ~HatsServiceBrowserTestBase() override = default;

  Profile* profile() { return chrome_test_utils::GetProfile(this); }

  HatsServiceDesktop* GetHatsService(Browser* browser = nullptr) {
    Profile* profile =
        browser ? browser->profile() : this->browser()->profile();
    HatsServiceDesktop* service = static_cast<HatsServiceDesktop*>(
        HatsServiceFactory::GetForProfile(profile, true));
    return service;
  }

  void SetMetricsConsent(bool consent) {
    scoped_metrics_consent_.emplace(consent);
  }

  bool HatsNextDialogCreated(Browser* browser = nullptr) {
    return GetHatsService(browser)->hats_next_dialog_exists_for_testing();
  }

  // Mock a survey with a custom requested browser type. The `other_browser`
  // param may be used to mock the survey in another browser too. Returns the
  // trigger to use when launching the survey.
  std::string MockSurveyWithRequestedBrowserType(
      Browser* other_browser,
      hats::SurveyConfig::RequestedBrowserType requested_browser_type) {
    for (HatsServiceDesktop* service :
         {GetHatsService(), GetHatsService(other_browser)}) {
      service
          ->GetSurveyConfigsByTriggersForTesting()[kHatsSurveyTriggerSettings]
          .requested_browser_type = requested_browser_type;
    }
    return kHatsSurveyTriggerSettings;
  }

 private:
  std::optional<ScopedSetMetricsConsent> scoped_metrics_consent_;

  base::test::ScopedFeatureList scoped_feature_list_;

  std::vector<base::test::FeatureRefAndParams> enabled_features_;
};

class HatsServiceProbabilityZero : public HatsServiceBrowserTestBase {
 public:
  HatsServiceProbabilityZero(const HatsServiceProbabilityZero&) = delete;
  HatsServiceProbabilityZero& operator=(const HatsServiceProbabilityZero&) =
      delete;

 protected:
  HatsServiceProbabilityZero()
      : HatsServiceBrowserTestBase({probability_zero}) {}

  ~HatsServiceProbabilityZero() override = default;
};

class HatsServiceProbabilityOne : public HatsServiceBrowserTestBase {
 public:
  HatsServiceProbabilityOne(const HatsServiceProbabilityOne&) = delete;
  HatsServiceProbabilityOne& operator=(const HatsServiceProbabilityOne&) =
      delete;

 protected:
  HatsServiceProbabilityOne() : HatsServiceBrowserTestBase({probability_one}) {}

  ~HatsServiceProbabilityOne() override = default;

 private:
  void SetUpOnMainThread() override {
    host_resolver()->AddRule("*", "127.0.0.1");
    // Set the profile creation time to be old enough to ensure triggering.
    browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
                                                    base::Days(45));
  }

  void TearDownOnMainThread() override {
    GetHatsService()->SetSurveyMetadataForTesting({});
  }
};

class HatsServiceConfigCoolDownPeriodOverriden
    : public HatsServiceBrowserTestBase {
 public:
  HatsServiceConfigCoolDownPeriodOverriden(
      const HatsServiceConfigCoolDownPeriodOverriden&) = delete;
  HatsServiceConfigCoolDownPeriodOverriden& operator=(
      const HatsServiceConfigCoolDownPeriodOverriden&) = delete;

 protected:
  HatsServiceConfigCoolDownPeriodOverriden()
      : HatsServiceBrowserTestBase({cool_down_period_overriden}) {}

  ~HatsServiceConfigCoolDownPeriodOverriden() override = default;
};

class HatsServiceSurveyFeatureControlledByGroup
    : public HatsServiceBrowserTestBase {
 public:
  HatsServiceSurveyFeatureControlledByGroup(
      const HatsServiceSurveyFeatureControlledByGroup&) = delete;
  HatsServiceSurveyFeatureControlledByGroup& operator=(
      const HatsServiceSurveyFeatureControlledByGroup&) = delete;

 protected:
  HatsServiceSurveyFeatureControlledByGroup()
      : HatsServiceBrowserTestBase(
            {cool_down_period_overriden_and_group_controlled}) {}

  ~HatsServiceSurveyFeatureControlledByGroup() override = default;

  void AddProfileToGroup(const std::string& group) {
    base::Value::List pref_groups_list;
    base::Value::Dict group_dict;
    group_dict.Set(variations::kDogfoodGroupsSyncPrefGaiaIdKey, group);
    pref_groups_list.Append(std::move(group_dict));
    profile()->GetPrefs()->SetList(
#if BUILDFLAG(IS_CHROMEOS)
        variations::kOsDogfoodGroupsSyncPrefName,
#else
        variations::kDogfoodGroupsSyncPrefName,
#endif
        std::move(pref_groups_list));
  }
};

}  // namespace

IN_PROC_BROWSER_TEST_F(HatsServiceBrowserTestBase, BubbleNotShownOnDefault) {
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityZero, NoShow) {
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, NoShowConsentNotGiven) {
  SetMetricsConsent(false);
  ASSERT_FALSE(
      g_browser_process->GetMetricsServicesManager()->IsMetricsConsentGiven());
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_FALSE(HatsNextDialogCreated());

  Browser* incognito_browser = CreateIncognitoBrowser();
  GetHatsService(incognito_browser)->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_FALSE(HatsNextDialogCreated(incognito_browser));
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, TriggerMismatchNoShow) {
  SetMetricsConsent(true);
  ASSERT_TRUE(
      g_browser_process->GetMetricsServicesManager()->IsMetricsConsentGiven());
  GetHatsService()->LaunchSurvey("nonexistent-trigger");
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, AlwaysShow) {
  SetMetricsConsent(true);
  ASSERT_TRUE(
      g_browser_process->GetMetricsServicesManager()->IsMetricsConsentGiven());
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_TRUE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       ShowWhenFeedbackSurveyPolicyEnabled) {
  SetMetricsConsent(true);
  policy::PolicyMap policies;
  SetPolicy(&policies, policy::key::kFeedbackSurveysEnabled, base::Value(true));
  UpdateProviderPolicy(policies);
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_TRUE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       NoShowWhenFeedbackSurveyPolicyDisabled) {
  SetMetricsConsent(true);
  policy::PolicyMap policies;
  SetPolicy(&policies, policy::key::kFeedbackSurveysEnabled,
            base::Value(false));
  UpdateProviderPolicy(policies);
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_FALSE(HatsNextDialogCreated());

  Browser* incognito_browser = CreateIncognitoBrowser();
  auto trigger = MockSurveyWithRequestedBrowserType(
      incognito_browser, hats::SurveyConfig::RequestedBrowserType::kIncognito);
  GetHatsService(incognito_browser)->LaunchSurvey(trigger);
  EXPECT_FALSE(HatsNextDialogCreated(incognito_browser));
}

IN_PROC_BROWSER_TEST_F(
    HatsServiceProbabilityOne,
    NeverShowWhenFeedbackSurveyPolicyEnabledWithoutMetricsConsent) {
  SetMetricsConsent(false);
  policy::PolicyMap policies;
  SetPolicy(&policies, policy::key::kFeedbackSurveysEnabled, base::Value(true));
  UpdateProviderPolicy(policies);
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, AlsoShowsSettingsSurvey) {
  SetMetricsConsent(true);
  ASSERT_TRUE(
      g_browser_process->GetMetricsServicesManager()->IsMetricsConsentGiven());
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_TRUE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, SameMajorVersionNoShow) {
  SetMetricsConsent(true);
  base::HistogramTester histogram_tester;
  HatsServiceDesktop::SurveyMetadata metadata;
  metadata.last_major_version = version_info::GetVersion().components()[0];
  GetHatsService()->SetSurveyMetadataForTesting(metadata);
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  histogram_tester.ExpectUniqueSample(
      kHatsShouldShowSurveyReasonHistogram,
      HatsServiceDesktop::ShouldShowSurveyReasons::
          kNoReceivedSurveyInCurrentMilestone,
      1);
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, DifferentMajorVersionShow) {
  SetMetricsConsent(true);
  HatsServiceDesktop::SurveyMetadata metadata;
  metadata.last_major_version = 42;
  ASSERT_NE(42u, version_info::GetVersion().components()[0]);
  GetHatsService()->SetSurveyMetadataForTesting(metadata);
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_TRUE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       SurveyStartedBeforeRequiredElapsedTimeNoShow) {
  SetMetricsConsent(true);
  base::HistogramTester histogram_tester;
  HatsServiceDesktop::SurveyMetadata metadata;
  metadata.last_survey_started_time = base::Time::Now();
  GetHatsService()->SetSurveyMetadataForTesting(metadata);
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  histogram_tester.ExpectUniqueSample(
      kHatsShouldShowSurveyReasonHistogram,
      HatsServiceDesktop::ShouldShowSurveyReasons::kNoLastSurveyTooRecent, 1);
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       SurveyStartedBeforeElapsedTimeBetweenAnySurveys) {
  SetMetricsConsent(true);
  base::HistogramTester histogram_tester;
  HatsServiceDesktop::SurveyMetadata metadata;
  metadata.any_last_survey_started_time = base::Time::Now();
  GetHatsService()->SetSurveyMetadataForTesting(metadata);
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_FALSE(HatsNextDialogCreated());
  histogram_tester.ExpectUniqueSample(
      kHatsShouldShowSurveyReasonHistogram,
      HatsServiceDesktop::ShouldShowSurveyReasons::kNoAnyLastSurveyTooRecent,
      1);
}

IN_PROC_BROWSER_TEST_F(
    HatsServiceConfigCoolDownPeriodOverriden,
    SurveyWithCoolddownOverride_FeatureNotGroupControlled_NoSurvey) {
  base::HistogramTester histogram_tester;
  SetMetricsConsent(true);
  browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
                                                  base::Days(31));

  GoogleGroupsManager* groups_manager =
      GoogleGroupsManagerFactory::GetForBrowserContext(profile());
  EXPECT_FALSE(groups_manager->IsFeatureGroupControlled(
      autofill::features::kPlusAddressAcceptedFirstTimeCreateSurvey));
  EXPECT_TRUE(groups_manager->IsFeatureEnabledForProfile(
      autofill::features::kPlusAddressAcceptedFirstTimeCreateSurvey));
  // The cooldown override for the feature should be set to 14 days.
  EXPECT_EQ(base::FeatureParam<int>(
                &autofill::features::kPlusAddressAcceptedFirstTimeCreateSurvey,
                plus_addresses::hats::kCooldownOverrideDays, 0)
                .Get(),
            14);

  HatsServiceDesktop::SurveyMetadata metadata;
  metadata.any_last_survey_started_time = base::Time::Now();
  metadata.any_last_survey_with_cooldown_override_started_time =
      base::Time::Now() - (kCooldownOverride + base::Days(1));
  GetHatsService()->SetSurveyMetadataForTesting(metadata);
  // kHatsSurveyTriggerPlusAddressAcceptedFirstTimeCreate overrides the cool
  // down period.
  GetHatsService()->LaunchSurvey(
      kHatsSurveyTriggerPlusAddressAcceptedFirstTimeCreate,
      /*success_callback=*/base::DoNothing(),
      /*failure_callback=*/base::DoNothing(), /*product_specific_bits_data=*/{},
      /*product_specific_string_data=*/
      std::map<std::string, std::string>{
          {plus_addresses::hats::kPlusAddressesCount, "0"},
          {plus_addresses::hats::kFirstPlusAddressCreationTime, "0"},
          {plus_addresses::hats::kLastPlusAddressFillingTime, "0"}});
  EXPECT_FALSE(GetHatsService()->hats_next_dialog_exists_for_testing());
  // Since the feature is not group controlled, the cooldown override has no
  // effect. The default cooldown of 180 days is used, so the survey is on
  // cooldown.
  histogram_tester.ExpectUniqueSample(
      kHatsShouldShowSurveyReasonHistogram,
      HatsServiceDesktop::ShouldShowSurveyReasons::kNoAnyLastSurveyTooRecent,
      1);
}

IN_PROC_BROWSER_TEST_F(
    HatsServiceSurveyFeatureControlledByGroup,
    SurveyWithCoolddownOverride_FeatureIsOnCooldown_NoSurvey) {
  base::HistogramTester histogram_tester;
  // Add the profile to the group assigned in the field trial config.
  // `GoogleGroupsManager::IsFeatureGroupControlled()` returns `false` without
  // this call.
  AddProfileToGroup(kRelevantGroupId);
  SetMetricsConsent(true);
  browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
                                                  base::Days(31));

  GoogleGroupsManager* groups_manager =
      GoogleGroupsManagerFactory::GetForBrowserContext(profile());
  EXPECT_TRUE(groups_manager->IsFeatureGroupControlled(
      autofill::features::kPlusAddressAcceptedFirstTimeCreateSurvey));
  EXPECT_TRUE(groups_manager->IsFeatureEnabledForProfile(
      autofill::features::kPlusAddressAcceptedFirstTimeCreateSurvey));
  // The cooldown override for the feature should be set to 14 days.
  EXPECT_EQ(base::FeatureParam<int>(
                &autofill::features::kPlusAddressAcceptedFirstTimeCreateSurvey,
                plus_addresses::hats::kCooldownOverrideDays, 0)
                .Get(),
            14);

  HatsServiceDesktop::SurveyMetadata metadata;
  metadata.any_last_survey_started_time = base::Time::Now() - base::Days(365);
  metadata.any_last_survey_with_cooldown_override_started_time =
      base::Time::Now() - (kCooldownOverride - base::Days(1));
  GetHatsService()->SetSurveyMetadataForTesting(metadata);
  // kHatsSurveyTriggerPlusAddressAcceptedFirstTimeCreate overrides the cool
  // down period.
  GetHatsService()->LaunchSurvey(
      kHatsSurveyTriggerPlusAddressAcceptedFirstTimeCreate,
      /*success_callback=*/base::DoNothing(),
      /*failure_callback=*/base::DoNothing(), /*product_specific_bits_data=*/{},
      /*product_specific_string_data=*/
      std::map<std::string, std::string>{
          {plus_addresses::hats::kPlusAddressesCount, "0"},
          {plus_addresses::hats::kFirstPlusAddressCreationTime, "0"},
          {plus_addresses::hats::kLastPlusAddressFillingTime, "0"}});
  EXPECT_FALSE(GetHatsService()->hats_next_dialog_exists_for_testing());
  // Cooldown period is set, the feature is enabled for profile and group
  // controlled. However, the last survey with cooldown override was shown just
  // 2 days ago, so the survey is on cooldown.
  histogram_tester.ExpectUniqueSample(
      kHatsShouldShowSurveyReasonHistogram,
      HatsServiceDesktop::ShouldShowSurveyReasons::kNoAnyLastSurveyTooRecent,
      1);
}

IN_PROC_BROWSER_TEST_F(
    HatsServiceSurveyFeatureControlledByGroup,
    SurveyWithCoolddownOverride_FeatureIsGroupControlled_StartsSurvey) {
  // Add the profile to the group assigned in the field trial config.
  // `GoogleGroupsManager::IsFeatureGroupControlled()` returns `false` without
  // this call.
  AddProfileToGroup(kRelevantGroupId);
  SetMetricsConsent(true);
  browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
                                                  base::Days(31));

  GoogleGroupsManager* groups_manager =
      GoogleGroupsManagerFactory::GetForBrowserContext(profile());
  EXPECT_TRUE(groups_manager->IsFeatureGroupControlled(
      autofill::features::kPlusAddressAcceptedFirstTimeCreateSurvey));
  EXPECT_TRUE(groups_manager->IsFeatureEnabledForProfile(
      autofill::features::kPlusAddressAcceptedFirstTimeCreateSurvey));
  // The cooldown override for the feature should be set to 14 days.
  EXPECT_EQ(base::FeatureParam<int>(
                &autofill::features::kPlusAddressAcceptedFirstTimeCreateSurvey,
                plus_addresses::hats::kCooldownOverrideDays, 0)
                .Get(),
            14);

  HatsServiceDesktop::SurveyMetadata metadata;
  metadata.any_last_survey_started_time = base::Time::Now();
  metadata.any_last_survey_with_cooldown_override_started_time =
      base::Time::Now() - (kCooldownOverride + base::Days(1));
  GetHatsService()->SetSurveyMetadataForTesting(metadata);
  // kHatsSurveyTriggerPlusAddressAcceptedFirstTimeCreate overrides the cool
  // down period.
  GetHatsService()->LaunchSurvey(
      kHatsSurveyTriggerPlusAddressAcceptedFirstTimeCreate,
      /*success_callback=*/base::DoNothing(),
      /*failure_callback=*/base::DoNothing(), /*product_specific_bits_data=*/{},
      /*product_specific_string_data=*/
      std::map<std::string, std::string>{
          {plus_addresses::hats::kPlusAddressesCount, "0"},
          {plus_addresses::hats::kFirstPlusAddressCreationTime, "0"},
          {plus_addresses::hats::kLastPlusAddressFillingTime, "0"}});
  EXPECT_TRUE(GetHatsService()->hats_next_dialog_exists_for_testing());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, ProfileTooYoungToShow) {
  SetMetricsConsent(true);
  base::HistogramTester histogram_tester;
  // Set creation time to only 15 days.
  browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
                                                  base::Days(15));
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  histogram_tester.ExpectUniqueSample(
      kHatsShouldShowSurveyReasonHistogram,
      HatsServiceDesktop::ShouldShowSurveyReasons::kNoProfileTooNew, 1);
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, ProfileOldEnoughToShow) {
  SetMetricsConsent(true);
  // Set creation time to 31 days. This is just past the threshold.
  browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
                                                  base::Days(31));
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_TRUE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       RegularSurveyInIncognitoNoShow) {
  SetMetricsConsent(true);
  base::HistogramTester histogram_tester;

  // A regular survey should not be shown in incognito
  Browser* incognito_browser = CreateIncognitoBrowser();
  GetHatsService(incognito_browser)->LaunchSurvey(kHatsSurveyTriggerSettings);
  histogram_tester.ExpectUniqueSample(
      kHatsShouldShowSurveyReasonHistogram,
      HatsServiceDesktop::ShouldShowSurveyReasons::kNoWrongBrowserType, 1);
  EXPECT_FALSE(HatsNextDialogCreated(incognito_browser));
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       IncognitoSurveyShownOnlyInIncognito) {
  SetMetricsConsent(true);
  base::HistogramTester histogram_tester;

  Browser* incognito_browser = CreateIncognitoBrowser();
  auto trigger = MockSurveyWithRequestedBrowserType(
      incognito_browser, hats::SurveyConfig::RequestedBrowserType::kIncognito);

  // An incognito survey should not be shown in regular
  GetHatsService()->LaunchSurvey(trigger);
  histogram_tester.ExpectUniqueSample(
      kHatsShouldShowSurveyReasonHistogram,
      HatsServiceDesktop::ShouldShowSurveyReasons::kNoWrongBrowserType, 1);
  EXPECT_FALSE(HatsNextDialogCreated());

  // An incognito survey should be shown in incognito
  GetHatsService(incognito_browser)->LaunchSurvey(trigger);
  EXPECT_TRUE(HatsNextDialogCreated(incognito_browser));
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, IncognitoModeDisabledNoShow) {
  SetMetricsConsent(true);
  // Disable incognito mode for this profile.
  PrefService* pref_service = browser()->profile()->GetPrefs();
  pref_service->SetInteger(
      policy::policy_prefs::kIncognitoModeAvailability,
      static_cast<int>(policy::IncognitoModeAvailability::kDisabled));
  EXPECT_EQ(policy::IncognitoModeAvailability::kDisabled,
            IncognitoModePrefs::GetAvailability(pref_service));

  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, CheckedWithinADayNoShow) {
  SetMetricsConsent(true);
  base::HistogramTester histogram_tester;
  HatsServiceDesktop::SurveyMetadata metadata;
  metadata.last_survey_check_time = base::Time::Now() - base::Hours(23);
  GetHatsService()->SetSurveyMetadataForTesting(metadata);
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  histogram_tester.ExpectUniqueSample(
      kHatsShouldShowSurveyReasonHistogram,
      HatsServiceDesktop::ShouldShowSurveyReasons::kNoLastSurveyCheckTooRecent,
      1);
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, CheckedAfterADayToShow) {
  SetMetricsConsent(true);
  HatsServiceDesktop::SurveyMetadata metadata;
  metadata.last_survey_check_time = base::Time::Now() - base::Days(1);
  GetHatsService()->SetSurveyMetadataForTesting(metadata);
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_TRUE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, SurveyAlreadyFullNoShow) {
  SetMetricsConsent(true);
  HatsServiceDesktop::SurveyMetadata metadata;
  metadata.is_survey_full = true;
  GetHatsService()->SetSurveyMetadataForTesting(metadata);
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, LaunchDelayedSurvey) {
  SetMetricsConsent(true);
  EXPECT_TRUE(
      GetHatsService()->LaunchDelayedSurvey(kHatsSurveyTriggerSettings, 0));
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       LaunchDelayedSurveyForWebContents) {
  SetMetricsConsent(true);
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  EXPECT_TRUE(GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, web_contents, 0));
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, DisallowsEmptyWebContents) {
  SetMetricsConsent(true);
  EXPECT_FALSE(GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, nullptr, 0));
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(
    HatsServiceProbabilityOne,
    AllowsMultipleDelayedSurveyRequestsDifferentWebContents) {
  SetMetricsConsent(true);
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  EXPECT_TRUE(GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, web_contents, 0));
  base::RunLoop().RunUntilIdle();
  chrome::AddTabAt(browser(), GURL(), -1, true);
  EXPECT_TRUE(GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings,
      browser()->tab_strip_model()->GetActiveWebContents(), 0));
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       DisallowsSameDelayedSurveyForWebContentsRequests) {
  SetMetricsConsent(true);
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  EXPECT_TRUE(GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, web_contents, 0));
  EXPECT_FALSE(GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, web_contents, 0));
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       ReleasesPendingTaskAfterFulfilling) {
  SetMetricsConsent(true);
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  EXPECT_TRUE(GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, web_contents, 0));
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(GetHatsService()->HasPendingTasks());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, VisibleWebContentsShow) {
  SetMetricsConsent(true);
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, web_contents, 0);
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, InvisibleWebContentsNoShow) {
  SetMetricsConsent(true);
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, web_contents, 0);
  chrome::AddTabAt(browser(), GURL(), -1, true);
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       NavigatedWebContents_RequireSameOrigin) {
  SetMetricsConsent(true);
  ASSERT_TRUE(embedded_test_server()->Start());

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("a.test", "/empty.html")));

  // As navigating also occurs asynchronously, a long survey delay is use to
  // ensure it completes before the survey tries to run.
  GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, web_contents, 10000, {}, {},
      /*navigation_behavior=*/
      HatsService::NavigationBehavior::REQUIRE_SAME_ORIGIN);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("b.test", "/empty.html")));
  base::RunLoop().RunUntilIdle();

  EXPECT_FALSE(GetHatsService()->HasPendingTasks());
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       NavigatedWebContents_NoRequireSameOrigin) {
  SetMetricsConsent(true);
  ASSERT_TRUE(embedded_test_server()->Start());

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("a.test", "/empty.html")));

  EXPECT_FALSE(GetHatsService()->HasPendingTasks());
  GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, web_contents, 10000, {}, {},
      /*navigation_behavior=*/
      HatsService::NavigationBehavior::ALLOW_ANY);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("b.test", "/empty.html")));
  base::RunLoop().RunUntilIdle();

  // The survey task should still be in the pending task queue.
  EXPECT_TRUE(GetHatsService()->HasPendingTasks());
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       NavigatedWebContents_RequireSameDocument) {
  SetMetricsConsent(true);
  ASSERT_TRUE(embedded_test_server()->Start());

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  const GURL kTestUrl = embedded_test_server()->GetURL("a.test", "/empty.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kTestUrl));

  // As navigating also occurs asynchronously, a long survey delay is use to
  // ensure it completes before the survey tries to run.
  GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, web_contents, 10000, {}, {},
      /*navigation_behavior=*/
      HatsService::NavigationBehavior::REQUIRE_SAME_DOCUMENT);

  // Same-document navigation
  web_contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
      u"document.location='#';", base::NullCallback(),
      content::ISOLATED_WORLD_ID_GLOBAL);
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(GetHatsService()->HasPendingTasks());
  EXPECT_FALSE(HatsNextDialogCreated());

  // Same-origin navigation
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(),
      embedded_test_server()->GetURL("a.test", "/empty_script.html")));

  base::RunLoop().RunUntilIdle();

  EXPECT_FALSE(GetHatsService()->HasPendingTasks());
  EXPECT_FALSE(HatsNextDialogCreated());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, SameOriginNavigation) {
  SetMetricsConsent(true);
  ASSERT_TRUE(embedded_test_server()->Start());

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("a.test", "/empty.html")));

  EXPECT_FALSE(GetHatsService()->HasPendingTasks());
  GetHatsService()->LaunchDelayedSurveyForWebContents(
      kHatsSurveyTriggerSettings, web_contents, 10000, {}, {},
      /*navigation_behavior=*/
      HatsService::NavigationBehavior::REQUIRE_SAME_ORIGIN);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("a.test", "/form.html")));
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(GetHatsService()->HasPendingTasks());
  EXPECT_FALSE(HatsNextDialogCreated());
}

// Check that once a HaTS Next dialog has been created, ShouldShowSurvey
// returns false until the service has been informed the dialog was closed.
IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, SingleHatsNextDialog) {
  SetMetricsConsent(true);
  EXPECT_TRUE(GetHatsService()->ShouldShowSurvey(kHatsSurveyTriggerSettings));
  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);

  // Clear any metadata that would prevent another survey from being displayed.
  GetHatsService()->SetSurveyMetadataForTesting({});

  // At this point a HaTS Next dialog is created and is attempting to contact
  // the wrapper website (which will fail as requests to non-localhost addresses
  // are disallowed in browser tests). Regardless of the outcome of the network
  // request, the dialog waits for a timeout posted to the UI thread before
  // closing itself. Since this test is also on the UI thread, these checks,
  // which rely on the dialog still being open, will not race.
  EXPECT_FALSE(GetHatsService()->ShouldShowSurvey(kHatsSurveyTriggerSettings));

  // Inform the service directly that the dialog has been closed.
  GetHatsService()->HatsNextDialogClosed();
  EXPECT_TRUE(GetHatsService()->ShouldShowSurvey(kHatsSurveyTriggerSettings));
}

// Check that launching a HaTS Next survey records a survey check time
IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, SurveyCheckTimeRecorded) {
  SetMetricsConsent(true);

  // Clear any existing survey metadata.
  GetHatsService()->SetSurveyMetadataForTesting({});

  GetHatsService()->LaunchSurvey(kHatsSurveyTriggerSettings);

  HatsServiceDesktop::SurveyMetadata metadata;
  GetHatsService()->GetSurveyMetadataForTesting(&metadata);
  EXPECT_TRUE(metadata.last_survey_check_time.has_value());
}

// Check that launching a HaTS Next survey records a survey check time even if
// triggered in incognito
IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne,
                       SurveyCheckTimeRecordedIncognito) {
  SetMetricsConsent(true);

  // Clear any existing survey metadata.
  GetHatsService()->SetSurveyMetadataForTesting({});

  Browser* incognito_browser = CreateIncognitoBrowser();
  auto trigger = MockSurveyWithRequestedBrowserType(
      incognito_browser, hats::SurveyConfig::RequestedBrowserType::kIncognito);

  GetHatsService(incognito_browser)->LaunchSurvey(trigger);

  HatsServiceDesktop::SurveyMetadata metadata;
  GetHatsService()->GetSurveyMetadataForTesting(&metadata);
  EXPECT_TRUE(metadata.last_survey_check_time.has_value());
}

IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, DialogDoesNotOutliveBrowser) {
  SetMetricsConsent(true);
  ASSERT_TRUE(
      g_browser_process->GetMetricsServicesManager()->IsMetricsConsentGiven());
  raw_ptr<BrowserWindowInterface> hats_browser = CreateBrowser(GetProfile());

  GetHatsService()->LaunchSurveyForWebContents(
      kHatsSurveyTriggerSettings,
      hats_browser->GetTabStripModel()->GetActiveWebContents(),
      /*product_specific_bits_data=*/{}, /*product_specific_string_data=*/{});
  EXPECT_TRUE(GetHatsService()->hats_next_dialog_exists_for_testing());

  CloseBrowserSynchronously(hats_browser.ExtractAsDangling());
  EXPECT_FALSE(GetHatsService()->hats_next_dialog_exists_for_testing());
}