// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "base/test/simple_test_clock.h"
#import "base/test/task_environment.h"
#import "components/feature_engagement/public/event_constants.h"
#import "components/feature_engagement/public/feature_constants.h"
#import "components/feature_engagement/public/tracker.h"
#import "components/feature_engagement/test/scoped_iph_feature_list.h"
#import "components/feature_engagement/test/test_tracker.h"
#import "testing/platform_test.h"
// Unittests related to the triggering of default browser promos via feature
// engagement.
class DefaultBrowserFeatureEngagementTest : public PlatformTest {
public:
DefaultBrowserFeatureEngagementTest() {
test_clock_.SetNow(base::Time::Now());
}
~DefaultBrowserFeatureEngagementTest() override {}
base::RepeatingCallback<void(bool)> BoolArgumentQuitClosure() {
return base::IgnoreArgs<bool>(run_loop_.QuitClosure());
}
std::unique_ptr<feature_engagement::Tracker> CreateAndInitTracker() {
std::unique_ptr<feature_engagement::Tracker> tracker =
feature_engagement::CreateTestTracker();
base::Time initial_time = test_clock_.Now();
tracker->SetClockForTesting(test_clock_, initial_time);
// Make sure tracker is initialized.
tracker->AddOnInitializedCallback(BoolArgumentQuitClosure());
run_loop_.Run();
return tracker;
}
void SatisfyChromeOpenCondition(feature_engagement::Tracker* tracker) {
// Promos can be displayed only after Chrome opened 7 times.
for (int i = 0; i < 7; i++) {
tracker->NotifyEvent(feature_engagement::events::kChromeOpened);
}
}
protected:
base::test::TaskEnvironment task_environment_;
base::SimpleTestClock test_clock_;
base::RunLoop run_loop_;
};
// Tests that the Remind Me Later promo only triggers after the corresponding
// event has been fired.
TEST_F(DefaultBrowserFeatureEngagementTest, TestRemindMeLater) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures(
{feature_engagement::kIPHiOSPromoDefaultBrowserReminderFeature});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// At first, the user has not chosen to be reminded later, so it should not
// trigger.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoDefaultBrowserReminderFeature));
// The user taps remind me later.
tracker->NotifyEvent(
feature_engagement::events::kDefaultBrowserPromoRemindMeLater);
// The reminder should still not trigger, as not enough time has passed since
// the user saw it first.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoDefaultBrowserReminderFeature));
// After a day, the reminder should trigger.
test_clock_.Advance(base::Days(1));
EXPECT_TRUE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoDefaultBrowserReminderFeature));
tracker->Dismissed(
feature_engagement::kIPHiOSPromoDefaultBrowserReminderFeature);
// After another day, the reminder should not trigger again
test_clock_.Advance(base::Days(1));
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoDefaultBrowserReminderFeature));
}
// Basic test for the generic default browser promo.
TEST_F(DefaultBrowserFeatureEngagementTest, DefaultBrowserBasicTest) {
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Promo shouldn't trigger because the preconditions are not satistfied.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
// Make sure the preconditions are satisfied.
tracker->NotifyEvent("generic_default_browser_promo_conditions_met");
// Promos can be displayed only after Chrome opened 7 times.
SatisfyChromeOpenCondition(tracker.get());
// The promo should trigger because all the preconditions are now satisfied.
EXPECT_TRUE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
tracker->Dismissed(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature);
// It shouldn't trigger the second time.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
}
// Basic test for the All Tabs default browser promo.
TEST_F(DefaultBrowserFeatureEngagementTest, AllTabsPromoBasicTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures({feature_engagement::kIPHiOSPromoAllTabsFeature});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Promo shouldn't trigger because the preconditions are not satistfied.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoAllTabsFeature));
// Make sure the preconditions are satisfied.
tracker->NotifyEvent("all_tabs_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
// The promo should trigger because all the preconditions are now satisfied.
EXPECT_TRUE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoAllTabsFeature));
tracker->Dismissed(feature_engagement::kIPHiOSPromoAllTabsFeature);
// It shouldn't trigger the second time.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoAllTabsFeature));
// After a month it still shouldn't trigger
test_clock_.Advance(base::Days(30));
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoAllTabsFeature));
}
// Basic test for the Made for iOS default browser promo.
TEST_F(DefaultBrowserFeatureEngagementTest, MadeForIOSPromoBasicTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures(
{feature_engagement::kIPHiOSPromoMadeForIOSFeature});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Promo shouldn't trigger because the preconditions are not satistfied.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoMadeForIOSFeature));
// Make sure the preconditions are satisfied.
tracker->NotifyEvent("made_for_ios_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
// The promo should trigger because all the preconditions are now satisfied.
EXPECT_TRUE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoMadeForIOSFeature));
tracker->Dismissed(feature_engagement::kIPHiOSPromoMadeForIOSFeature);
// It shouldn't trigger the second time.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoMadeForIOSFeature));
// After a month it still shouldn't trigger
test_clock_.Advance(base::Days(30));
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoMadeForIOSFeature));
}
// Basic test for the Stay Safe default browser promo.
TEST_F(DefaultBrowserFeatureEngagementTest, StaySafePromoBasicTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures({feature_engagement::kIPHiOSPromoStaySafeFeature});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Promo shouldn't trigger because the preconditions are not satistfied.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
// Make sure the preconditions are satisfied.
tracker->NotifyEvent("stay_safe_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
// The promo should trigger because all the preconditions are now satisfied.
EXPECT_TRUE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
tracker->Dismissed(feature_engagement::kIPHiOSPromoStaySafeFeature);
// It shouldn't trigger the second time.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
// After a month it still shouldn't trigger
test_clock_.Advance(base::Days(30));
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
}
// Basic test for the generic default browser promo.
TEST_F(DefaultBrowserFeatureEngagementTest, GenericPromoBasicTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures(
{feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Promo shouldn't trigger because the preconditions are not satistfied.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
// Make sure the preconditions are satisfied.
tracker->NotifyEvent("generic_default_browser_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
// The promo should trigger because all the preconditions are now satisfied.
EXPECT_TRUE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
tracker->Dismissed(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature);
// It shouldn't trigger the second time.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
// After a year it still shouldn't trigger
test_clock_.Advance(base::Days(366));
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
}
// Verify that promo conditions expire.
TEST_F(DefaultBrowserFeatureEngagementTest,
AllTabsPromoConditionExpirationTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures({feature_engagement::kIPHiOSPromoAllTabsFeature});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Make sure the preconditions are satisfied.
tracker->NotifyEvent("all_tabs_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
// Verify that promo would trigger.
ASSERT_TRUE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoAllTabsFeature));
// Conditions should expire after 21 days.
test_clock_.Advance(base::Days(21));
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoAllTabsFeature));
}
// Verify that promo conditions expire.
TEST_F(DefaultBrowserFeatureEngagementTest,
MadeForIOSPromoConditionExpirationTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures(
{feature_engagement::kIPHiOSPromoMadeForIOSFeature});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Make sure the preconditions are satisfied.
tracker->NotifyEvent("made_for_ios_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
// Verify that promo would trigger.
ASSERT_TRUE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoMadeForIOSFeature));
// Conditions should expire after 21 days.
test_clock_.Advance(base::Days(21));
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoMadeForIOSFeature));
}
// Verify that promo conditions expire.
TEST_F(DefaultBrowserFeatureEngagementTest,
StaySafePromoConditionExpirationTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures({feature_engagement::kIPHiOSPromoStaySafeFeature});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Make sure the preconditions are satisfied.
tracker->NotifyEvent("stay_safe_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
// Verify that promo would trigger.
ASSERT_TRUE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
// Conditions should expire after 21 days.
test_clock_.Advance(base::Days(21));
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
}
// Verify that promo conditions expire.
TEST_F(DefaultBrowserFeatureEngagementTest,
GenericPromoConditionExpirationTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures(
{feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Make sure the preconditions are satisfied.
tracker->NotifyEvent("generic_default_browser_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
// Verify that promo would trigger.
ASSERT_TRUE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
// Conditions should expire after 21 days.
test_clock_.Advance(base::Days(21));
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
}
// Test for default browser group configuration.
TEST_F(DefaultBrowserFeatureEngagementTest, DefaultBrowserGroupTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures({feature_engagement::kIPHiOSPromoStaySafeFeature});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Promo shouldn't trigger because the preconditions are not satistfied.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
// Make sure the preconditions are satisfied for the Stay Safe promo.
tracker->NotifyEvent("stay_safe_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
// Mark one of the group promos as displayed.
tracker->NotifyEvent("default_browser_promos_group_trigger");
// The promo cannot be triggered because after one default browser promo we
// need to wait 14 days.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
// After 14 days it should trigger another promo.
test_clock_.Advance(base::Days(14));
EXPECT_TRUE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
tracker->Dismissed(feature_engagement::kIPHiOSPromoStaySafeFeature);
}
// Verify generic promo triggers with sliding window experiment.
TEST_F(DefaultBrowserFeatureEngagementTest,
GenericPromoSlidingWindowExperimentTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures(
{feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature,
feature_engagement::kDefaultBrowserEligibilitySlidingWindow});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Make sure the preconditions are satisfied.
tracker->NotifyEvent("generic_default_browser_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
// The promo should trigger because all the preconditions are now satisfied.
ASSERT_TRUE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
// Mark promo displayed without calling ```ShouldTriggerHelpUI``` to avoid
// session rate limitations.
tracker->NotifyEvent("generic_default_browser_promo_trigger");
// It shouldn't trigger the second time.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
// After a month it still shouldn't trigger
test_clock_.Advance(base::Days(30));
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
// After 365 days it should show again.
test_clock_.Advance(base::Days(366));
// Still need to satisfy the preconditions.
tracker->NotifyEvent("generic_default_browser_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
EXPECT_TRUE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
}
// Verify generic promo triggers without satisfying all conditions when in
// experiment.
TEST_F(DefaultBrowserFeatureEngagementTest,
GenericPromoTriggerCriteriaExperimentTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures(
{feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature,
feature_engagement::kDefaultBrowserTriggerCriteriaExperiment});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
tracker->NotifyEvent(feature_engagement::events::
kDefaultBrowserPromoTriggerCriteriaConditionsMet);
// Promo shouldn't trigger because the group preconditions are not satistfied.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
// Make sure the group preconditions are satisfied.
SatisfyChromeOpenCondition(tracker.get());
// The promo should trigger because all the preconditions are now satisfied.
EXPECT_TRUE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
tracker->Dismissed(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature);
// It shouldn't trigger the second time.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
// After a year and satisfying all the conditions again it still shouldn't
// trigger.
test_clock_.Advance(base::Days(366));
tracker->NotifyEvent("generic_default_browser_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoGenericDefaultBrowserFeature));
}
// Test only one of the tailored promos will show.
TEST_F(DefaultBrowserFeatureEngagementTest, TailoredDefaultBrowserGroupTest) {
feature_engagement::test::ScopedIphFeatureList list;
list.InitAndEnableFeatures(
{feature_engagement::kIPHiOSPromoStaySafeFeature,
feature_engagement::kIPHiOSPromoMadeForIOSFeature});
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Promo shouldn't trigger because the preconditions are not satistfied.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
// Make sure the preconditions are satisfied for the Stay Safe promo.
tracker->NotifyEvent("stay_safe_promo_conditions_met");
SatisfyChromeOpenCondition(tracker.get());
// Make sure that the promo would have triggered before another tailored promo
// is shown.
EXPECT_TRUE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
// Mark one of the tailored group promos as displayed.
tracker->NotifyEvent("made_for_ios_promo_trigger");
tracker->NotifyEvent("tailored_default_browser_promos_group_trigger");
// The promo cannot be triggered because another tailored promo already was
// shown.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
// After a year it should still not show trigger another promo.
test_clock_.Advance(base::Days(365));
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoStaySafeFeature));
}
// Test that blue dot keeps necessary cooldown period from FRE
TEST_F(DefaultBrowserFeatureEngagementTest,
BlueDotOverflowMenuFeatureFRECooldown) {
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Promo shouldn't trigger because the preconditions are not satistfied.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSDefaultBrowserOverflowMenuBadgeFeature));
// Make sure the preconditions are satisfied for the blue dot promo.
SatisfyChromeOpenCondition(tracker.get());
// If user seen the FRE the blue dot promo shouldn't trigger.
tracker->NotifyEvent("default_browser_fre_shown");
EXPECT_FALSE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSDefaultBrowserOverflowMenuBadgeFeature));
// After 15 days it should still not trigger.
test_clock_.Advance(base::Days(15));
EXPECT_FALSE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSDefaultBrowserOverflowMenuBadgeFeature));
// After another 7 days it should trigger.
test_clock_.Advance(base::Days(7));
EXPECT_TRUE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSDefaultBrowserOverflowMenuBadgeFeature));
}
// Test that blue dot keeps necessary cooldown period from default browser
// promos.
TEST_F(DefaultBrowserFeatureEngagementTest,
BlueDotSettingsFeatureFullscreenPromoCooldown) {
std::unique_ptr<feature_engagement::Tracker> tracker = CreateAndInitTracker();
// Promo shouldn't trigger because the preconditions are not satistfied.
EXPECT_FALSE(tracker->ShouldTriggerHelpUI(
feature_engagement::kIPHiOSDefaultBrowserOverflowMenuBadgeFeature));
// Make sure the preconditions are satisfied for the blue dot promo.
SatisfyChromeOpenCondition(tracker.get());
// If user seen any of the fullscreen promos then the blue dot promo shouldn't
// trigger.
tracker->NotifyEvent("default_browser_promos_group_trigger");
EXPECT_FALSE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSDefaultBrowserOverflowMenuBadgeFeature));
// After 5 days it should still not trigger.
test_clock_.Advance(base::Days(5));
EXPECT_FALSE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSDefaultBrowserOverflowMenuBadgeFeature));
// After another 10 days it should trigger.
test_clock_.Advance(base::Days(10));
EXPECT_TRUE(tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSDefaultBrowserOverflowMenuBadgeFeature));
}