| // Copyright 2021 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/ui/hats/trust_safety_sentiment_service.h" |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/extensions/api/settings_private/generated_pref.h" |
| #include "chrome/browser/ui/hats/hats_service.h" |
| #include "chrome/browser/ui/hats/hats_service_factory.h" |
| #include "chrome/browser/ui/hats/mock_hats_service.h" |
| #include "chrome/browser/ui/hats/trust_safety_sentiment_service_factory.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/webui_url_constants.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/browsing_data/core/browsing_data_utils.h" |
| #include "components/content_settings/core/test/content_settings_mock_provider.h" |
| #include "components/content_settings/core/test/content_settings_test_utils.h" |
| #include "components/privacy_sandbox/tracking_protection_prefs.h" |
| #include "components/signin/public/base/signin_pref_names.h" |
| #include "components/sync_preferences/testing_pref_service_syncable.h" |
| #include "components/unified_consent/pref_names.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/test_renderer_host.h" |
| #include "content/public/test/web_contents_tester.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| using ::testing::_; |
| |
| class TrustSafetySentimentServiceTest : public testing::Test { |
| public: |
| TrustSafetySentimentServiceTest() { |
| mock_hats_service_ = static_cast<MockHatsService*>( |
| HatsServiceFactory::GetInstance()->SetTestingFactoryAndUse( |
| profile(), base::BindRepeating(&BuildMockHatsService))); |
| EXPECT_CALL(*mock_hats_service(), CanShowAnySurvey(_)) |
| .WillRepeatedly(testing::Return(true)); |
| } |
| |
| void SetUp() override { |
| metrics::DesktopSessionDurationTracker::Initialize(); |
| } |
| |
| void TearDown() override { |
| TrustSafetySentimentServiceFactory::GetInstance()->ShutDownForTesting( |
| profile()); |
| metrics::DesktopSessionDurationTracker::CleanupForTesting(); |
| } |
| |
| protected: |
| struct FeatureParams { |
| std::string privacy_settings_time = "20s"; |
| std::string min_time_to_prompt = "2m"; |
| std::string max_time_to_prompt = "60m"; |
| std::string ntp_visits_min_range = "2"; |
| std::string ntp_visits_max_range = "4"; |
| std::string privacy_settings_probability = "0.6"; |
| std::string trusted_surface_probability = "0.4"; |
| std::string transactions_probability = "0.05"; |
| std::string privacy_sandbox_4_consent_accept_probability = "0.01"; |
| std::string privacy_sandbox_4_consent_decline_probability = "0.1"; |
| std::string privacy_sandbox_4_notice_ok_probability = "0.1"; |
| std::string privacy_sandbox_4_notice_settings_probability = "0.1"; |
| std::string privacy_settings_trigger_id = "privacy-settings-test"; |
| std::string trusted_surface_trigger_id = "trusted-surface-test"; |
| std::string transactions_trigger_id = "transactions-test"; |
| std::string transactions_password_manager_time = "20s"; |
| }; |
| |
| // Skip deprecated PrivacySandbox3 and 4 values. |
| bool IsDeprecatedFeatureArea(int enum_value) { |
| return ((enum_value >= 4 && enum_value <= 9) || |
| (enum_value >= 15 && enum_value <= 18)); |
| } |
| |
| void SetupFeatureParameters(FeatureParams params) { |
| feature_list()->InitWithFeaturesAndParameters( |
| /*enabled_features=*/ |
| {base::test::FeatureRefAndParams( |
| features::kTrustSafetySentimentSurvey, |
| { |
| {"privacy-settings-time", params.privacy_settings_time}, |
| {"min-time-to-prompt", params.min_time_to_prompt}, |
| {"max-time-to-prompt", params.max_time_to_prompt}, |
| {"ntp-visits-min-range", params.ntp_visits_min_range}, |
| {"ntp-visits-max-range", params.ntp_visits_max_range}, |
| {"privacy-settings-probability", |
| params.privacy_settings_probability}, |
| {"trusted-surface-probability", |
| params.trusted_surface_probability}, |
| {"transactions-probability", params.transactions_probability}, |
| {"privacy-settings-trigger-id", |
| params.privacy_settings_trigger_id}, |
| {"trusted-surface-trigger-id", |
| params.trusted_surface_trigger_id}, |
| {"transactions-trigger-id", params.transactions_trigger_id}, |
| {"transactions-password-manager-time", |
| params.transactions_password_manager_time}, |
| })}, |
| /*disabled_features=*/ |
| {features::kTrustSafetySentimentSurveyV2}); |
| } |
| |
| struct FeatureParamsV2 { |
| std::string min_time_to_prompt = "2m"; |
| std::string max_time_to_prompt = "60m"; |
| std::string ntp_visits_min_range = "2"; |
| std::string ntp_visits_max_range = "4"; |
| std::string min_session_time = "30s"; |
| std::string trusted_surface_time = "5s"; |
| std::string browsing_data_probability = "0.4"; |
| std::string control_group_probability = "0.4"; |
| std::string download_warning_ui_probability = "0.0"; |
| std::string password_check_probability = "0.4"; |
| std::string password_protection_ui_probability = "0.0"; |
| std::string safety_check_probability = "0.4"; |
| std::string safety_hub_notification_probability = "0.4"; |
| std::string safety_hub_interaction_probability = "0.4"; |
| std::string trusted_surface_probability = "0.4"; |
| std::string privacy_guide_probability = "0.4"; |
| std::string privacy_sandbox_4_consent_accept_probability = "0.01"; |
| std::string privacy_sandbox_4_consent_decline_probability = "0.1"; |
| std::string privacy_sandbox_4_notice_ok_probability = "0.1"; |
| std::string privacy_sandbox_4_notice_settings_probability = "0.1"; |
| std::string safe_browsing_interstitial_probability = "0.4"; |
| std::string browsing_data_trigger_id = "browsing-data-test"; |
| std::string control_group_trigger_id = "control-group-test"; |
| std::string download_warning_ui_trigger_id = "download-warning-ui-test"; |
| std::string password_check_trigger_id = "password-check-test"; |
| std::string password_protection_ui_trigger_id = |
| "password-protection-ui-test"; |
| std::string safety_check_trigger_id = "safety-check-test"; |
| std::string trusted_surface_trigger_id = "trusted-surface-test"; |
| std::string privacy_guide_trigger_id = "privacy-guide-test"; |
| std::string safe_browsing_interstitial_trigger_id = |
| "safe-browsing-interstitial"; |
| }; |
| |
| void SetupFeatureParametersV2(FeatureParamsV2 params) { |
| feature_list()->InitAndEnableFeatureWithParameters( |
| features::kTrustSafetySentimentSurveyV2, |
| { |
| {"min-time-to-prompt", params.min_time_to_prompt}, |
| {"max-time-to-prompt", params.max_time_to_prompt}, |
| {"ntp-visits-min-range", params.ntp_visits_min_range}, |
| {"ntp-visits-max-range", params.ntp_visits_max_range}, |
| {"min-session-time", params.min_session_time}, |
| {"trusted-surface-time", params.trusted_surface_time}, |
| {"browsing-data-probability", params.browsing_data_probability}, |
| {"control-group-probability", params.control_group_probability}, |
| {"download-warning-ui-probability", |
| params.download_warning_ui_probability}, |
| {"password-check-probability", params.password_check_probability}, |
| {"password-protection-ui-probability", |
| params.password_protection_ui_probability}, |
| {"safety-check-probability", params.safety_check_probability}, |
| {"safety-hub-notification-probability", |
| params.safety_hub_notification_probability}, |
| {"safety-hub-interaction-probability", |
| params.safety_hub_interaction_probability}, |
| {"trusted-surface-probability", params.trusted_surface_probability}, |
| {"privacy-guide-probability", params.privacy_guide_probability}, |
| {"safe-browsing-interstitial-probability", |
| params.safe_browsing_interstitial_probability}, |
| {"browsing-data-trigger-id", params.browsing_data_trigger_id}, |
| {"control-group-trigger-id", params.control_group_trigger_id}, |
| {"download-warning-ui-trigger-id", |
| params.download_warning_ui_trigger_id}, |
| {"password-check-trigger-id", params.password_check_trigger_id}, |
| {"password-protection-ui-trigger-id", |
| params.password_protection_ui_trigger_id}, |
| {"safety-check-trigger-id", params.safety_check_trigger_id}, |
| {"trusted-surface-trigger-id", params.trusted_surface_trigger_id}, |
| {"privacy-guide-trigger-id", params.privacy_guide_trigger_id}, |
| {"safe-browsing-interstitial-trigger-id", |
| params.safe_browsing_interstitial_trigger_id}, |
| }); |
| } |
| |
| void CheckHistograms( |
| const std::set<TrustSafetySentimentService::FeatureArea>& triggered_areas, |
| const std::set<TrustSafetySentimentService::FeatureArea>& |
| surveyed_areas) { |
| std::map<std::string, std::set<TrustSafetySentimentService::FeatureArea>> |
| histogram_to_expected = { |
| {"Feedback.TrustSafetySentiment.TriggerOccurred", triggered_areas}, |
| {"Feedback.TrustSafetySentiment.SurveyRequested", surveyed_areas}}; |
| |
| for (const auto& histogram_expected : histogram_to_expected) { |
| const auto& histogram_name = histogram_expected.first; |
| const auto& expected = histogram_expected.second; |
| histogram_tester()->ExpectTotalCount(histogram_name, expected.size()); |
| for (auto area : expected) { |
| histogram_tester()->ExpectBucketCount(histogram_name, area, 1); |
| } |
| } |
| } |
| |
| void CheckCallTriggerOccurredHistogram( |
| const std::map<TrustSafetySentimentService::FeatureArea, int>& |
| triggered_area_counts) { |
| int total_count = 0; |
| const std::string histogram_name = |
| "Feedback.TrustSafetySentiment.CallTriggerOccurred"; |
| for (const auto& histogram_expected : triggered_area_counts) { |
| const auto& feature_area = histogram_expected.first; |
| int expected_count = histogram_expected.second; |
| histogram_tester()->ExpectBucketCount(histogram_name, feature_area, |
| expected_count); |
| total_count += expected_count; |
| } |
| histogram_tester()->ExpectTotalCount(histogram_name, total_count); |
| } |
| |
| TrustSafetySentimentService* service() { |
| return TrustSafetySentimentServiceFactory::GetForProfile(profile()); |
| } |
| content::BrowserTaskEnvironment* task_environment() { |
| return &task_environment_; |
| } |
| base::test::ScopedFeatureList* feature_list() { return &feature_list_; } |
| base::HistogramTester* histogram_tester() { return &histogram_tester_; } |
| MockHatsService* mock_hats_service() { return mock_hats_service_; } |
| TestingProfile* profile() { return &profile_; } |
| |
| private: |
| content::BrowserTaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| content::RenderViewHostTestEnabler render_view_host_test_enabler_; |
| TestingProfile profile_; |
| base::test::ScopedFeatureList feature_list_; |
| base::HistogramTester histogram_tester_; |
| raw_ptr<MockHatsService> mock_hats_service_; |
| }; |
| |
| TEST_F(TrustSafetySentimentServiceTest, Eligibility_NtpOpens) { |
| // A survey should not be shown if not enough NTPs have been opened since |
| // the most recent trigger action. |
| FeatureParams params; |
| params.privacy_settings_probability = "1.0"; |
| params.trusted_surface_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "2"; |
| params.ntp_visits_max_range = "2"; |
| SetupFeatureParameters(params); |
| |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| |
| service()->TriggerOccurred( |
| TrustSafetySentimentService::FeatureArea::kPrivacySettings, {}); |
| service()->OpenedNewTabPage(); |
| service()->TriggerOccurred( |
| TrustSafetySentimentService::FeatureArea::kTrustedSurface, {}); |
| |
| // The Trusted Surface trigger should prevent a survey from being shown, as |
| // it still has 1 NTP to open. |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kPrivacySettings, |
| TrustSafetySentimentService::FeatureArea::kTrustedSurface}, |
| {}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kPrivacySettings, 1}, |
| {TrustSafetySentimentService::FeatureArea::kTrustedSurface, 1}}); |
| |
| // The next NTP should be eligible for a survey. |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey); |
| service()->OpenedNewTabPage(); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, Eligibility_Time) { |
| // A survey should not be shown if any trigger action occurred to recently, or |
| // if all trigger actions occurred too long ago. |
| FeatureParams params; |
| params.privacy_settings_probability = "1.0"; |
| params.trusted_surface_probability = "1.0"; |
| params.min_time_to_prompt = "1m"; |
| params.max_time_to_prompt = "10m"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParameters(params); |
| |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| service()->TriggerOccurred( |
| TrustSafetySentimentService::FeatureArea::kPrivacySettings, {}); |
| service()->OpenedNewTabPage(); |
| |
| task_environment()->AdvanceClock(base::Minutes(2)); |
| service()->TriggerOccurred( |
| TrustSafetySentimentService::FeatureArea::kTrustedSurface, {}); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| // Moving the clock forward such that only the trusted surface trigger is |
| // within the window should guarantee it is the survey shown. |
| task_environment()->AdvanceClock(base::Minutes(9)); |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyTrustedSurface, _, _, _, |
| _, _, _)); |
| service()->OpenedNewTabPage(); |
| |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kPrivacySettings, |
| TrustSafetySentimentService::FeatureArea::kTrustedSurface}, |
| {TrustSafetySentimentService::FeatureArea::kTrustedSurface}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kPrivacySettings, 1}, |
| {TrustSafetySentimentService::FeatureArea::kTrustedSurface, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, TriggerProbability) { |
| // Triggers which fail the probability check should not be considered. |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| FeatureParams params; |
| params.trusted_surface_probability = "0.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| SetupFeatureParameters(params); |
| |
| service()->TriggerOccurred( |
| TrustSafetySentimentService::FeatureArea::kTrustedSurface, {}); |
| service()->OpenedNewTabPage(); |
| CheckHistograms({}, {}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kTrustedSurface, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, TriggersClearOnLaunch) { |
| // Check that all active triggers are cleared when a survey is launched. |
| FeatureParams params; |
| params.trusted_surface_probability = "1.0"; |
| params.privacy_settings_probability = "1.0"; |
| params.transactions_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParameters(params); |
| |
| service()->TriggerOccurred( |
| TrustSafetySentimentService::FeatureArea::kPrivacySettings, {}); |
| service()->TriggerOccurred( |
| TrustSafetySentimentService::FeatureArea::kTrustedSurface, {}); |
| |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kPrivacySettings, |
| TrustSafetySentimentService::FeatureArea::kTrustedSurface}, |
| {}); |
| |
| // The launched survey will be randomly selected from the two triggers. |
| std::string requested_survey_trigger; |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey) |
| .WillOnce(testing::SaveArg<0>(&requested_survey_trigger)); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| auto surveyed_feature_area = |
| requested_survey_trigger == kHatsSurveyTriggerTrustSafetyPrivacySettings |
| ? TrustSafetySentimentService::FeatureArea::kPrivacySettings |
| : TrustSafetySentimentService::FeatureArea::kTrustedSurface; |
| |
| // The trigger which did not result in a survey should no longer be |
| // considered. |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| // Repeated triggers post survey launch should however be considered. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyTransactions, _, _, _, |
| _, _, _)); |
| service()->TriggerOccurred( |
| TrustSafetySentimentService::FeatureArea::kTransactions, {}); |
| service()->OpenedNewTabPage(); |
| |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kPrivacySettings, |
| TrustSafetySentimentService::FeatureArea::kTrustedSurface, |
| TrustSafetySentimentService::FeatureArea::kTransactions}, |
| {surveyed_feature_area, |
| TrustSafetySentimentService::FeatureArea::kTransactions}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kPrivacySettings, 1}, |
| {TrustSafetySentimentService::FeatureArea::kTrustedSurface, 1}, |
| {TrustSafetySentimentService::FeatureArea::kTransactions, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, SettingsWatcher_PrivacySettings) { |
| FeatureParams params; |
| params.privacy_settings_probability = "1.0"; |
| params.privacy_settings_time = "10s"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParameters(params); |
| |
| // Create and navigate a test web contents to settings. |
| auto web_contents = |
| content::WebContentsTester::CreateTestWebContents(profile(), nullptr); |
| content::WebContentsTester::For(web_contents.get()) |
| ->SetLastCommittedURL(GURL(chrome::kChromeUISettingsURL)); |
| |
| // Interacting with setting shouldn't causes a survey to be immediately |
| // displayed, but should require the user to stay on settings for some time. |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| service()->InteractedWithPrivacySettings(web_contents.get()); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| // Once the user has spent the appropriate amount of time on settings, they |
| // should be eligible for a survey. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| _, _, _, _)); |
| task_environment()->AdvanceClock(base::Seconds(20)); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| // Leaving settings before the required time should disqualify the user from |
| // receiving a survey. |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| service()->InteractedWithPrivacySettings(web_contents.get()); |
| task_environment()->AdvanceClock(base::Seconds(5)); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| |
| content::WebContentsTester::For(web_contents.get()) |
| ->SetLastCommittedURL(GURL("http://unrelated.com")); |
| task_environment()->AdvanceClock(base::Seconds(15)); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, SettingsWatcher_PasswordManager) { |
| FeatureParams params; |
| params.transactions_probability = "1.0"; |
| params.transactions_password_manager_time = "10s"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParameters(params); |
| |
| // Check that after being informed of a visit to the password manager page, |
| // the service correctly watches the provided WebContents to check if the |
| // user stays on settings. |
| auto web_contents = |
| content::WebContentsTester::CreateTestWebContents(profile(), nullptr); |
| content::WebContentsTester::For(web_contents.get()) |
| ->SetLastCommittedURL(GURL(chrome::kChromeUISettingsURL)); |
| |
| // A survey should not be shown unless the user spends at least the required |
| // time on settings after opening password manager. |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| service()->OpenedPasswordManager(web_contents.get()); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| // Once the user has spent sufficient time on settings after visiting the |
| // password manager, they should be eligible for the Transactions copy of the |
| // survey. |
| SurveyBitsData expected_psd = {{"Saved password", false}}; |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyTransactions, _, _, |
| expected_psd, _, _, _)); |
| |
| task_environment()->AdvanceClock(base::Seconds(20)); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| // Leaving settings before the required time should not make the user |
| // eligible. |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| service()->OpenedPasswordManager(web_contents.get()); |
| task_environment()->AdvanceClock(base::Seconds(5)); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| |
| content::WebContentsTester::For(web_contents.get()) |
| ->SetLastCommittedURL(GURL("http://unrelated.com")); |
| task_environment()->AdvanceClock(base::Seconds(15)); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, RanSafetyCheck) { |
| // Running the safety check is considered a trigger, and should make a user |
| // eligible to receive a survey. |
| FeatureParams params; |
| params.privacy_settings_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParameters(params); |
| |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| _, _, _, _)); |
| service()->RanSafetyCheck(); |
| service()->OpenedNewTabPage(); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, SavedPassword) { |
| // Saving a password is considered a trigger, and should make a user eligible |
| // to receive a survey. |
| FeatureParams params; |
| params.transactions_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParameters(params); |
| |
| SurveyBitsData expected_psd = {{"Saved password", true}}; |
| |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyTransactions, _, _, |
| expected_psd, _, _, _)); |
| service()->SavedPassword(); |
| service()->OpenedNewTabPage(); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, SavedCard) { |
| // Saving a card is considered a trigger, and should make a user eligible |
| // to receive a survey. |
| FeatureParams params; |
| params.transactions_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParameters(params); |
| |
| SurveyBitsData expected_psd = {{"Saved password", false}}; |
| |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyTransactions, _, _, |
| expected_psd, _, _, _)); |
| service()->SavedCard(); |
| service()->OpenedNewTabPage(); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, PrivacySettingsProductSpecificData) { |
| // Check the product specific data accompanying surveys for the Privacy |
| // Settings feature area correctly records whether the user has a non default |
| // privacy setting. |
| FeatureParams params; |
| params.privacy_settings_probability = "1.0"; |
| params.privacy_settings_time = "0s"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParameters(params); |
| |
| auto web_contents = |
| content::WebContentsTester::CreateTestWebContents(profile(), nullptr); |
| content::WebContentsTester::For(web_contents.get()) |
| ->SetLastCommittedURL(GURL(chrome::kChromeUISettingsURL)); |
| |
| SurveyBitsData expected_psd = {{"Non default setting", false}, |
| {"Ran safety check", false}}; |
| |
| // By default, a user should have no non-default settings. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| expected_psd, _, _, _)); |
| service()->InteractedWithPrivacySettings(web_contents.get()); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| expected_psd["Ran safety check"] = true; |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| expected_psd, _, _, _)); |
| service()->RanSafetyCheck(); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| // Check that default content settings are considered. |
| expected_psd["Non default setting"] = true; |
| auto* content_settings = |
| HostContentSettingsMapFactory::GetForProfile(profile()); |
| content_settings->SetDefaultContentSetting( |
| ContentSettingsType::SOUND, ContentSetting::CONTENT_SETTING_BLOCK); |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| expected_psd, _, _, _)); |
| service()->RanSafetyCheck(); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| content_settings->SetDefaultContentSetting( |
| ContentSettingsType::SOUND, ContentSetting::CONTENT_SETTING_DEFAULT); |
| |
| // Check that preferences are considered. |
| expected_psd["Ran safety check"] = false; |
| profile()->GetTestingPrefService()->SetUserPref( |
| prefs::kEnableDoNotTrack, std::make_unique<base::Value>(true)); |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| expected_psd, _, _, _)); |
| service()->InteractedWithPrivacySettings(web_contents.get()); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| profile()->GetPrefs()->ClearPref(prefs::kEnableDoNotTrack); |
| |
| // Check that sync state defaults are handled correctly. |
| profile()->GetTestingPrefService()->SetUserPref( |
| unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, |
| std::make_unique<base::Value>(true)); |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| expected_psd, _, _, _)); |
| service()->InteractedWithPrivacySettings(web_contents.get()); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| // UKM is only non default while no sync consent is present. |
| expected_psd["Non default setting"] = false; |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| expected_psd, _, _, _)); |
| profile()->GetTestingPrefService()->SetUserPref( |
| prefs::kGoogleServicesConsentedToSync, |
| std::make_unique<base::Value>(true)); |
| service()->InteractedWithPrivacySettings(web_contents.get()); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| |
| // A preference or content setting changed via policy should not be considered |
| // as non-default. |
| profile()->GetTestingPrefService()->SetManagedPref( |
| prefs::kEnableDoNotTrack, std::make_unique<base::Value>(true)); |
| auto managed_provider = std::make_unique<content_settings::MockProvider>(); |
| managed_provider->SetWebsiteSetting( |
| ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(), |
| ContentSettingsType::COOKIES, |
| base::Value(ContentSetting::CONTENT_SETTING_BLOCK), /*constraints=*/{}); |
| content_settings::TestUtils::OverrideProvider( |
| content_settings, std::move(managed_provider), |
| content_settings::ProviderType::kPolicyProvider); |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| expected_psd, _, _, _)); |
| service()->InteractedWithPrivacySettings(web_contents.get()); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, ActiveIncognitoPreventsSurvey) { |
| // Check that when an incognito profile exists that a survey is not shown. |
| FeatureParams params; |
| params.privacy_settings_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParameters(params); |
| |
| auto* otr_profile = |
| profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true); |
| |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| service()->RanSafetyCheck(); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| profile()->DestroyOffTheRecordProfile(otr_profile); |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| _, _, _, _)); |
| service()->OpenedNewTabPage(); |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kPrivacySettings, |
| TrustSafetySentimentService::FeatureArea::kIneligible}, |
| {TrustSafetySentimentService::FeatureArea::kPrivacySettings}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kSafetyCheck, 1}, |
| {TrustSafetySentimentService::FeatureArea::kPrivacySettings, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, ClosingIncognitoDelaysSurvey) { |
| // Check that closing an incognito session delays when a survey is shown |
| // by the minimum time to prompt, and the maximum of the range of NTP visits. |
| FeatureParams params; |
| params.privacy_settings_probability = "1.0"; |
| params.min_time_to_prompt = "1m"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "2"; |
| SetupFeatureParameters(params); |
| |
| auto* otr_profile = |
| profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true); |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| service()->RanSafetyCheck(); |
| |
| // Record 2 visits to the NTP so regardless of the random NTP count chosen, |
| // the Privacy Settings trigger will be eligible, but currently blocked by |
| // the presence of an incognito profile. |
| for (int i = 0; i < 2; i++) { |
| service()->OpenedNewTabPage(); |
| } |
| |
| profile()->DestroyOffTheRecordProfile(otr_profile); |
| |
| // The first NTP opened after closing the incognito session should never |
| // result in a survey, as the maximum of the range is 2. |
| service()->OpenedNewTabPage(); |
| |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kPrivacySettings, |
| TrustSafetySentimentService::FeatureArea::kIneligible}, |
| {}); |
| |
| // The second visit to the NTP should not trigger a survey if it takes place |
| // less than the minimum time to prompt after closing an incognito session. |
| task_environment()->AdvanceClock(base::Seconds(30)); |
| service()->OpenedNewTabPage(); |
| |
| // Up to this point no attempt to show any survey should have been made. |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| _, _, _, _)); |
| |
| // The next tab open which occurs after the required number of opens, and the |
| // minimum time has passed, should trigger a survey. |
| task_environment()->AdvanceClock(base::Minutes(1)); |
| service()->OpenedNewTabPage(); |
| |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kPrivacySettings, |
| TrustSafetySentimentService::FeatureArea::kIneligible}, |
| {TrustSafetySentimentService::FeatureArea::kPrivacySettings}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kSafetyCheck, 1}, |
| {TrustSafetySentimentService::FeatureArea::kPrivacySettings, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, AllFeatureAreasHaveTriggers) { |
| // Assert that for every feature area there is the correct version(s) and |
| // survey trigger id. |
| FeatureParams paramsv1; |
| SetupFeatureParameters(paramsv1); |
| for (int enum_value = 0; |
| enum_value <= |
| static_cast<int>(TrustSafetySentimentService::FeatureArea::kMaxValue); |
| ++enum_value) { |
| if (IsDeprecatedFeatureArea(enum_value)) { |
| continue; |
| } |
| auto feature_area = |
| static_cast<TrustSafetySentimentService::FeatureArea>(enum_value); |
| if (TrustSafetySentimentService::VersionCheck(feature_area)) { |
| EXPECT_NE("", TrustSafetySentimentService::GetHatsTriggerForFeatureArea( |
| feature_area)); |
| } |
| } |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, V2_AllFeatureAreasHaveTriggers) { |
| // Assert that for every feature area there is the correct version(s) and |
| // survey trigger id. |
| FeatureParamsV2 paramsv2; |
| SetupFeatureParametersV2(paramsv2); |
| for (int enum_value = 0; |
| enum_value <= |
| static_cast<int>(TrustSafetySentimentService::FeatureArea::kMaxValue); |
| ++enum_value) { |
| if (IsDeprecatedFeatureArea(enum_value)) { |
| continue; |
| } |
| auto feature_area = |
| static_cast<TrustSafetySentimentService::FeatureArea>(enum_value); |
| if (TrustSafetySentimentService::VersionCheck(feature_area)) { |
| EXPECT_NE("", TrustSafetySentimentService::GetHatsTriggerForFeatureArea( |
| feature_area)); |
| } |
| } |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, AllFeatureAreasHaveProbabilities) { |
| // Check that for every feature with a probability of 1 and the correct |
| // version, the dice roll always succeeds. |
| FeatureParams params; |
| params.privacy_settings_probability = "1.0"; |
| params.trusted_surface_probability = "1.0"; |
| params.transactions_probability = "1.0"; |
| params.privacy_sandbox_4_consent_accept_probability = "1.0"; |
| params.privacy_sandbox_4_consent_decline_probability = "1.0"; |
| params.privacy_sandbox_4_notice_ok_probability = "1.0"; |
| params.privacy_sandbox_4_notice_settings_probability = "1.0"; |
| |
| SetupFeatureParameters(params); |
| for (int enum_value = 0; |
| enum_value <= |
| static_cast<int>(TrustSafetySentimentService::FeatureArea::kMaxValue); |
| ++enum_value) { |
| if (IsDeprecatedFeatureArea(enum_value)) { |
| continue; |
| } |
| auto feature_area = |
| static_cast<TrustSafetySentimentService::FeatureArea>(enum_value); |
| if (TrustSafetySentimentService::VersionCheck(feature_area)) { |
| EXPECT_TRUE(TrustSafetySentimentService::ProbabilityCheck(feature_area)) |
| << "Feature area: " << static_cast<int>(feature_area); |
| } |
| } |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, V2_AllFeatureAreasHaveProbabilities) { |
| // Check that for every feature with a probability of 1 and the correct |
| // version, the dice roll always succeeds. |
| FeatureParamsV2 params; |
| params.browsing_data_probability = "1.0"; |
| params.control_group_probability = "1.0"; |
| params.download_warning_ui_probability = "1.0"; |
| params.password_check_probability = "1.0"; |
| params.password_protection_ui_probability = "1.0"; |
| params.safety_check_probability = "1.0"; |
| params.trusted_surface_probability = "1.0"; |
| params.privacy_guide_probability = "1.0"; |
| params.privacy_sandbox_4_consent_accept_probability = "1.0"; |
| params.privacy_sandbox_4_consent_decline_probability = "1.0"; |
| params.privacy_sandbox_4_notice_ok_probability = "1.0"; |
| params.privacy_sandbox_4_notice_settings_probability = "1.0"; |
| params.safe_browsing_interstitial_probability = "1.0"; |
| params.safety_hub_notification_probability = "1.0"; |
| params.safety_hub_interaction_probability = "1.0"; |
| |
| SetupFeatureParametersV2(params); |
| for (int enum_value = 0; |
| enum_value <= |
| static_cast<int>(TrustSafetySentimentService::FeatureArea::kMaxValue); |
| ++enum_value) { |
| if (IsDeprecatedFeatureArea(enum_value)) { |
| continue; |
| } |
| auto feature_area = |
| static_cast<TrustSafetySentimentService::FeatureArea>(enum_value); |
| if (TrustSafetySentimentService::VersionCheck(feature_area)) { |
| EXPECT_TRUE(TrustSafetySentimentService::ProbabilityCheck(feature_area)) |
| << "Feature area: " << static_cast<int>(feature_area); |
| } |
| } |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, Eligibility_V1FeatureWhileV2Enabled) { |
| // A survey from V1 only is not shown because V2 is enabled. |
| FeatureParams params; |
| params.privacy_settings_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| feature_list()->InitWithFeaturesAndParameters( |
| {{features::kTrustSafetySentimentSurvey, |
| { |
| {"min-time-to-prompt", params.min_time_to_prompt}, |
| {"max-time-to-prompt", params.max_time_to_prompt}, |
| {"ntp-visits-min-range", params.ntp_visits_min_range}, |
| {"ntp-visits-max-range", params.ntp_visits_max_range}, |
| {"trusted-surface-probability", params.trusted_surface_probability}, |
| {"trusted-surface-trigger-id", params.trusted_surface_trigger_id}, |
| }}, |
| {features::kTrustSafetySentimentSurveyV2, {}}}, |
| {}); |
| |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| service()->TriggerOccurred( |
| TrustSafetySentimentService::FeatureArea::kPrivacySettings, {}); |
| |
| service()->OpenedNewTabPage(); |
| |
| // Survey should not shown be shown as triggered because v2 enabled. |
| CheckHistograms({}, {}); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| // Disable V2 and now the same trigger should work. |
| feature_list()->Reset(); |
| SetupFeatureParameters(params); |
| |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| _, _, _, _)); |
| service()->TriggerOccurred( |
| TrustSafetySentimentService::FeatureArea::kPrivacySettings, {}); |
| service()->OpenedNewTabPage(); |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kPrivacySettings}, |
| {TrustSafetySentimentService::FeatureArea::kPrivacySettings}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kPrivacySettings, 2}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, V2_TrustedSurface) { |
| // A survey from version 2 is only shown if the right conditions are met. |
| FeatureParamsV2 params; |
| params.trusted_surface_probability = "1.0"; |
| params.min_time_to_prompt = "2m"; |
| params.max_time_to_prompt = "4m"; |
| params.ntp_visits_min_range = "2"; |
| params.ntp_visits_max_range = "2"; |
| SetupFeatureParametersV2(params); |
| |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| service()->TriggerOccurred( |
| TrustSafetySentimentService::FeatureArea::kTrustedSurface, {}); |
| |
| service()->OpenedNewTabPage(); |
| service()->OpenedNewTabPage(); |
| |
| // Survey should not shown because although the ntp visits condition is met, |
| // the time is not. |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kTrustedSurface}, |
| {}); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| task_environment()->AdvanceClock(base::Minutes(3)); |
| // Assert the V2 survey is called and not the V1. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyTrustedSurface, _, _, _, |
| _, _, _)) |
| .Times(0); |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2TrustedSurface, _, _, |
| _, _, _, _)); |
| |
| // A survey should be shown because we are now within the right time. |
| service()->OpenedNewTabPage(); |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kTrustedSurface}, |
| {TrustSafetySentimentService::FeatureArea::kTrustedSurface}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kTrustedSurface, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, V2_SafetyCheck) { |
| // Running the safety check is considered a trigger, and should make a user |
| // eligible to receive a survey. |
| FeatureParamsV2 params; |
| params.safety_check_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| // Running safety check was previously part of PrivacySettings, so assure only |
| // the correct histograms and survey are triggered for V2. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyPrivacySettings, _, _, |
| _, _, _, _)) |
| .Times(0); |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2SafetyCheck, _, _, _, |
| _, _, _)); |
| service()->RanSafetyCheck(); |
| service()->OpenedNewTabPage(); |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kSafetyCheck}, |
| {TrustSafetySentimentService::FeatureArea::kSafetyCheck}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kSafetyCheck, 1}, |
| {TrustSafetySentimentService::FeatureArea::kPrivacySettings, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, V2_PasswordCheck) { |
| // Running password check should make a user eligible to receive a survey. |
| FeatureParamsV2 params; |
| params.password_check_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| // The correct survey should be launched. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2PasswordCheck, _, _, |
| _, _, _, _)); |
| service()->RanPasswordCheck(); |
| service()->OpenedNewTabPage(); |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kPasswordCheck}, |
| {TrustSafetySentimentService::FeatureArea::kPasswordCheck}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kPasswordCheck, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, V2_BrowsingData) { |
| // Clearing history through CBD should make user eligible to receive a survey. |
| FeatureParamsV2 params; |
| params.browsing_data_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| std::vector<std::pair<browsing_data::BrowsingDataType, SurveyBitsData>> |
| datatypes = {{browsing_data::BrowsingDataType::HISTORY, |
| {{"Deleted history", true}, |
| {"Deleted downloads", false}, |
| {"Deleted autofill form data", false}}}, |
| {browsing_data::BrowsingDataType::DOWNLOADS, |
| {{"Deleted history", false}, |
| {"Deleted downloads", true}, |
| {"Deleted autofill form data", false}}}, |
| {browsing_data::BrowsingDataType::FORM_DATA, |
| {{"Deleted history", false}, |
| {"Deleted downloads", false}, |
| {"Deleted autofill form data", true}}}}; |
| |
| for (const auto& datatype : datatypes) { |
| // The correct survey should be launched. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2BrowsingData, _, _, |
| datatype.second, _, _, _)); |
| service()->ClearedBrowsingData(datatype.first); |
| service()->OpenedNewTabPage(); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| } |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, V2_BrowsingData_NotInterested) { |
| // Clearing a BrowsingDataType that we are not interested in should not |
| // trigger a survey. |
| FeatureParamsV2 params; |
| params.browsing_data_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| // No browsing data survey should be launched. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2BrowsingData, _, _, _, |
| _, _, _)) |
| .Times(0); |
| service()->ClearedBrowsingData(browsing_data::BrowsingDataType::SITE_DATA); |
| service()->OpenedNewTabPage(); |
| CheckHistograms({}, {}); |
| CheckCallTriggerOccurredHistogram({}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, V2_PrivacyGuide) { |
| // Finishing the privacy guide is considered a trigger, and should make a user |
| // eligible to receive a survey. |
| FeatureParamsV2 params; |
| params.privacy_guide_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| // The correct survey should be launched. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2PrivacyGuide, _, _, _, |
| _, _, _)); |
| service()->FinishedPrivacyGuide(); |
| service()->OpenedNewTabPage(); |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kPrivacyGuide}, |
| {TrustSafetySentimentService::FeatureArea::kPrivacyGuide}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kPrivacyGuide, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, V2_ControlGroup) { |
| // If a user is in the control group, they should be eligible to see a survey |
| // if they reached a certain threshold of session length, and only once. |
| FeatureParamsV2 params; |
| params.control_group_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| params.min_session_time = "30s"; |
| SetupFeatureParametersV2(params); |
| |
| // A survey should not be launched because the session threshold was not |
| // surpassed. |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| base::TimeTicks session_start = base::TimeTicks::Now(); |
| service()->OnSessionStarted(session_start); |
| task_environment()->AdvanceClock(base::Seconds(10)); |
| base::TimeTicks session_end = base::TimeTicks::Now(); |
| service()->OnSessionEnded(session_end - session_start, session_end); |
| service()->OpenedNewTabPage(); |
| CheckHistograms({}, {}); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| // A survey should be launched because the session threshold was surpassed. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2ControlGroup, _, _, _, |
| _, _, _)); |
| session_start = base::TimeTicks::Now(); |
| service()->OnSessionStarted(session_start); |
| task_environment()->AdvanceClock(base::Seconds(40)); |
| session_end = base::TimeTicks::Now(); |
| service()->OnSessionEnded(session_end - session_start, session_end); |
| service()->OpenedNewTabPage(); |
| CheckHistograms({TrustSafetySentimentService::FeatureArea::kControlGroup}, |
| {TrustSafetySentimentService::FeatureArea::kControlGroup}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kControlGroup, 1}}); |
| testing::Mock::VerifyAndClearExpectations(mock_hats_service()); |
| |
| // A second valid trigger should not launch a survey because we have already |
| // performed one dice roll for this user. |
| EXPECT_CALL(*mock_hats_service(), LaunchSurvey).Times(0); |
| session_start = base::TimeTicks::Now(); |
| service()->OnSessionStarted(session_start); |
| task_environment()->AdvanceClock(base::Seconds(40)); |
| session_end = base::TimeTicks::Now(); |
| service()->OnSessionEnded(session_end - session_start, session_end); |
| service()->OpenedNewTabPage(); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, V2_SafeBrowsingInterstitial) { |
| // Making a final decision on a safe browsing interstitial is considered a |
| // trigger, and should make a user eligible to receive a survey. |
| FeatureParamsV2 params; |
| params.safe_browsing_interstitial_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| // The correct survey should be launched. |
| EXPECT_CALL( |
| *mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2SafeBrowsingInterstitial, _, |
| _, _, _, _, _)); |
| service()->InteractedWithSafeBrowsingInterstitial( |
| true, safe_browsing::SBThreatType::SB_THREAT_TYPE_URL_PHISHING); |
| service()->OpenedNewTabPage(); |
| CheckHistograms( |
| {TrustSafetySentimentService::FeatureArea::kSafeBrowsingInterstitial}, |
| {TrustSafetySentimentService::FeatureArea::kSafeBrowsingInterstitial}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kSafeBrowsingInterstitial, |
| 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, V2_DownloadWarningUI) { |
| // Making a final decision on a download warning is considered a |
| // trigger, and should make a user eligible to receive a survey. |
| FeatureParamsV2 params; |
| params.download_warning_ui_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| // The correct survey should be launched. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2DownloadWarningUI, _, |
| _, _, _, _, _)); |
| service()->InteractedWithDownloadWarningUI( |
| DownloadItemWarningData::WarningSurface::BUBBLE_MAINPAGE, |
| DownloadItemWarningData::WarningAction::PROCEED); |
| service()->OpenedNewTabPage(); |
| CheckHistograms( |
| {TrustSafetySentimentService::FeatureArea::kDownloadWarningUI}, |
| {TrustSafetySentimentService::FeatureArea::kDownloadWarningUI}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kDownloadWarningUI, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, PasswordProtectionUINonPasswordChange) { |
| // Making a final decision on a password protection UI is considered a |
| // trigger, and should make a user eligible to receive a survey. |
| FeatureParamsV2 params; |
| params.password_protection_ui_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| // The correct survey should be launched. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2PasswordProtectionUI, |
| _, _, _, _, _, _)); |
| service()->PhishedPasswordUpdateNotClicked( |
| PasswordProtectionUIType::PAGE_INFO, |
| PasswordProtectionUIAction::IGNORE_WARNING); |
| service()->OpenedNewTabPage(); |
| CheckHistograms( |
| {TrustSafetySentimentService::FeatureArea::kPasswordProtectionUI}, |
| {TrustSafetySentimentService::FeatureArea::kPasswordProtectionUI}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kPasswordProtectionUI, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, |
| PasswordProtectionUIPasswordChangeClickedNotCompleted) { |
| // Making a final decision on a password protection UI is considered a |
| // trigger, and should make a user eligible to receive a survey. |
| FeatureParamsV2 params; |
| params.password_protection_ui_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| // The correct survey should be launched. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2PasswordProtectionUI, |
| _, _, _, _, _, _)); |
| service()->ProtectResetOrCheckPasswordClicked( |
| PasswordProtectionUIType::PAGE_INFO); |
| task_environment()->AdvanceClock(kPasswordChangeInactivity); |
| task_environment()->RunUntilIdle(); |
| service()->OpenedNewTabPage(); |
| |
| CheckHistograms( |
| {TrustSafetySentimentService::FeatureArea::kPasswordProtectionUI}, |
| {TrustSafetySentimentService::FeatureArea::kPasswordProtectionUI}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kPasswordProtectionUI, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, |
| PasswordProtectionUIPasswordChangeClickedAndCompleted) { |
| // Making a final decision on a password protection UI is considered a |
| // trigger, and should make a user eligible to receive a survey. |
| FeatureParamsV2 params; |
| params.password_protection_ui_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| // The correct survey should be launched. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2PasswordProtectionUI, |
| _, _, _, _, _, _)); |
| service()->ProtectResetOrCheckPasswordClicked( |
| PasswordProtectionUIType::PAGE_INFO); |
| service()->PhishedPasswordUpdateFinished(); |
| service()->OpenedNewTabPage(); |
| CheckHistograms( |
| {TrustSafetySentimentService::FeatureArea::kPasswordProtectionUI}, |
| {TrustSafetySentimentService::FeatureArea::kPasswordProtectionUI}); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kPasswordProtectionUI, 1}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, |
| PasswordProtectionUIPasswordChangeThenNonPasswordChange) { |
| // Making a final decision on a password protection UI is considered a |
| // trigger, and should make a user eligible to receive a survey. |
| FeatureParamsV2 params; |
| params.password_protection_ui_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| // The correct survey should be launched. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2PasswordProtectionUI, |
| _, _, _, _, _, _)); |
| service()->ProtectResetOrCheckPasswordClicked( |
| PasswordProtectionUIType::PAGE_INFO); |
| service()->PhishedPasswordUpdateNotClicked( |
| PasswordProtectionUIType::PAGE_INFO, PasswordProtectionUIAction::CLOSE); |
| service()->PhishedPasswordUpdateFinished(); |
| service()->OpenedNewTabPage(); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kPasswordProtectionUI, 2}}); |
| } |
| |
| TEST_F(TrustSafetySentimentServiceTest, |
| PasswordProtectionUIPasswordChangeThenNonPasswordChange2) { |
| // Making a final decision on a password protection UI is considered a |
| // trigger, and should make a user eligible to receive a survey. |
| FeatureParamsV2 params; |
| params.password_protection_ui_probability = "1.0"; |
| params.min_time_to_prompt = "0s"; |
| params.ntp_visits_min_range = "0"; |
| params.ntp_visits_max_range = "0"; |
| SetupFeatureParametersV2(params); |
| |
| // The correct survey should be launched. |
| EXPECT_CALL(*mock_hats_service(), |
| LaunchSurvey(kHatsSurveyTriggerTrustSafetyV2PasswordProtectionUI, |
| _, _, _, _, _, _)); |
| service()->ProtectResetOrCheckPasswordClicked( |
| PasswordProtectionUIType::PAGE_INFO); |
| service()->PhishedPasswordUpdateNotClicked( |
| PasswordProtectionUIType::PAGE_INFO, PasswordProtectionUIAction::CLOSE); |
| service()->PhishedPasswordUpdateFinished(); |
| task_environment()->AdvanceClock(kPasswordChangeInactivity); |
| service()->OpenedNewTabPage(); |
| CheckCallTriggerOccurredHistogram( |
| {{TrustSafetySentimentService::FeatureArea::kPasswordProtectionUI, 2}}); |
| } |