[ios][TipSNotifications] Update DefaultBrowser notification
This change updates the Default Browser Tips notification to lead to the
Default Browser settings screen (as recently discussed in the design
doc).
It also adds EG tests to verify the flow when the user interacts
with any of the Tips notifications.
Fixed: 323943933
Change-Id: Idd8eb03d430ea697d97649e01e9126e4dd70509c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5273024
Commit-Queue: Scott Yoder <scottyoder@google.com>
Reviewed-by: Gauthier Ambard <gambard@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1257989}
diff --git a/ios/chrome/browser/default_browser/model/promo_source.h b/ios/chrome/browser/default_browser/model/promo_source.h
index 18557228..5943a33 100644
--- a/ios/chrome/browser/default_browser/model/promo_source.h
+++ b/ios/chrome/browser/default_browser/model/promo_source.h
@@ -10,14 +10,15 @@
// LINT.IfChange
enum class DefaultBrowserPromoSource {
kSettings = 0,
- kOmnibox,
- kExternalIntent,
- kSetUpList,
+ kOmnibox = 1,
+ kExternalIntent = 2,
+ kSetUpList = 3,
// kExternalAction refers to Chrome being opened with a "ChromeExternalAction"
// host.
- kExternalAction,
- kMaxValue = kExternalAction,
+ kExternalAction = 4,
+ kTipsNotification = 5,
+ kMaxValue = kTipsNotification,
};
-// LINT.ThenChange(//tools/metrics/histograms/metadata/ios/enums.xml)
+// LINT.ThenChange(tools/metrics/histograms/metadata/settings/enums.xml)
#endif // IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_PROMO_SOURCE_H_
diff --git a/ios/chrome/browser/tips_notifications/model/OWNERS b/ios/chrome/browser/tips_notifications/OWNERS
similarity index 100%
rename from ios/chrome/browser/tips_notifications/model/OWNERS
rename to ios/chrome/browser/tips_notifications/OWNERS
diff --git a/ios/chrome/browser/tips_notifications/eg_test/BUILD.gn b/ios/chrome/browser/tips_notifications/eg_test/BUILD.gn
new file mode 100644
index 0000000..0292bfc
--- /dev/null
+++ b/ios/chrome/browser/tips_notifications/eg_test/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("eg2_tests") {
+ configs += [ "//build/config/ios:xctest_config" ]
+ testonly = true
+ sources = [ "tips_notifications_egtest.mm" ]
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//ios/chrome/browser/shared/model/prefs:pref_names",
+ "//ios/chrome/browser/shared/public/features",
+ "//ios/chrome/browser/signin/model:fake_system_identity",
+ "//ios/chrome/browser/tips_notifications/model:utils",
+ "//ios/chrome/browser/ui/authentication:eg_test_support+eg2",
+ "//ios/chrome/browser/ui/authentication/signin:constants",
+ "//ios/chrome/browser/ui/content_suggestions:eg_test_support+eg2",
+ "//ios/chrome/browser/ui/content_suggestions/set_up_list:constants",
+ "//ios/chrome/browser/ui/push_notification:test_support",
+ "//ios/chrome/common/ui/confirmation_alert:constants",
+ "//ios/chrome/test/earl_grey:eg_test_support+eg2",
+ "//ios/testing/earl_grey:eg_test_support+eg2",
+ ]
+}
diff --git a/ios/chrome/browser/tips_notifications/eg_test/tips_notifications_egtest.mm b/ios/chrome/browser/tips_notifications/eg_test/tips_notifications_egtest.mm
new file mode 100644
index 0000000..91bd0006
--- /dev/null
+++ b/ios/chrome/browser/tips_notifications/eg_test/tips_notifications_egtest.mm
@@ -0,0 +1,168 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "base/strings/stringprintf.h"
+#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
+#import "ios/chrome/browser/shared/public/features/features.h"
+#import "ios/chrome/browser/signin/model/fake_system_identity.h"
+#import "ios/chrome/browser/tips_notifications/model/utils.h"
+#import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
+#import "ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h"
+#import "ios/chrome/browser/ui/content_suggestions/set_up_list/constants.h"
+#import "ios/chrome/common/ui/confirmation_alert/constants.h"
+#import "ios/chrome/test/earl_grey/chrome_actions.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
+#import "ios/chrome/test/earl_grey/chrome_matchers.h"
+#import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#import "ios/testing/earl_grey/app_launch_manager.h"
+#import "ios/testing/earl_grey/earl_grey_test.h"
+
+namespace {
+
+// Long presses the view with the given `accessibility_id`.
+void LongPressView(NSString* accessibility_id) {
+ id<GREYMatcher> matcher = grey_accessibilityID(accessibility_id);
+ [[EarlGrey selectElementWithMatcher:matcher] performAction:grey_longPress()];
+}
+
+// Wait for a view that contains a partial match to the given `text`, then tap
+// it.
+void WaitForThenTapText(NSString* text) {
+ id item = chrome_test_util::ContainsPartialText(text);
+ [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:item];
+ [[EarlGrey selectElementWithMatcher:item] performAction:grey_tap()];
+}
+
+// Taps a view containing a partial match to the given `text`.
+void TapText(NSString* text) {
+ id item = grey_allOf(chrome_test_util::ContainsPartialText(text),
+ grey_sufficientlyVisible(), nil);
+ [[EarlGrey selectElementWithMatcher:item] performAction:grey_tap()];
+}
+
+// Taps "Allow" on notification permissions alert, if it appears.
+void MaybeTapAllowNotifications() {
+ XCUIApplication* springboardApplication = [[XCUIApplication alloc]
+ initWithBundleIdentifier:@"com.apple.springboard"];
+ auto button = springboardApplication.buttons[@"Allow"];
+ if ([button waitForExistenceWithTimeout:2]) {
+ [button tap];
+ [ChromeEarlGreyUI waitForAppToIdle];
+ }
+}
+
+void TapNotification() {
+ XCUIApplication* springboardApplication = [[XCUIApplication alloc]
+ initWithBundleIdentifier:@"com.apple.springboard"];
+ auto notification =
+ springboardApplication.otherElements[@"Notification"].firstMatch;
+ GREYAssert([notification waitForExistenceWithTimeout:4],
+ @"A notification did not appear");
+ [notification tap];
+ [ChromeEarlGreyUI waitForAppToIdle];
+}
+
+} // namespace
+
+// Test case for Tips Notifications.
+@interface TipsNotificationsTestCase : ChromeTestCase
+@end
+
+@implementation TipsNotificationsTestCase
+
+- (AppLaunchConfiguration)appConfigurationForTestCase {
+ AppLaunchConfiguration config;
+ // config.features_enabled.push_back(kIOSTipsNotifications);
+ config.features_enabled.push_back(kMagicStack);
+
+ // Enable Tips Notifications with 1s trigger time.
+ std::string enableFeatures = base::StringPrintf(
+ "--enable-features=%s:%s/%s", kIOSTipsNotifications.name,
+ kIOSTipsNotificationsTriggerTimeParam, "1s");
+ config.additional_args.push_back(enableFeatures);
+ return config;
+}
+
++ (void)setUpForTestCase {
+ [super setUpForTestCase];
+
+ [ChromeEarlGreyAppInterface writeFirstRunSentinel];
+ [ChromeEarlGreyAppInterface clearDefaultBrowserPromoData];
+ [ChromeEarlGrey resetDataForLocalStatePref:
+ prefs::kIosCredentialProviderPromoLastActionTaken];
+ [NewTabPageAppInterface resetSetUpListPrefs];
+}
+
+- (void)optInToTipsNotifications {
+ // Long press the SetUpList module.
+ LongPressView(set_up_list::kDefaultBrowserItemID);
+
+ // Tap the menu item to enable notifications.
+ TapText(@"Turn on Notifications");
+ MaybeTapAllowNotifications();
+
+ // Tap the confirmation snackbar.
+ WaitForThenTapText(@"notifications turned on");
+}
+
+// Tests triggering and interacting with each of the Tips notifications.
+- (void)testTriggerNotifications {
+ XCUIApplication* app = [[XCUIApplication alloc] init];
+ [self optInToTipsNotifications];
+
+ // Trigger the Default Browser Notification.
+ [XCUIDevice.sharedDevice pressButton:XCUIDeviceButtonHome];
+ [ChromeEarlGrey resetDataForLocalStatePref:kTipsNotificationsSentPref];
+ [app activate];
+ TapNotification();
+
+ // Verify that the Default Browser Promo is visible.
+ id<GREYMatcher> defaultBrowserView =
+ chrome_test_util::DefaultBrowserSettingsTableViewMatcher();
+ [[EarlGrey selectElementWithMatcher:defaultBrowserView]
+ assertWithMatcher:grey_sufficientlyVisible()];
+
+ // Tap "cancel".
+ [[EarlGrey
+ selectElementWithMatcher:chrome_test_util::NavigationBarCancelButton()]
+ performAction:grey_tap()];
+
+ // Trigger the What's New notification.
+ [XCUIDevice.sharedDevice pressButton:XCUIDeviceButtonHome];
+ [app activate];
+ TapNotification();
+
+ // Verify that the What's New screen is showing.
+ id<GREYMatcher> whatsNewView = grey_accessibilityID(@"kWhatsNewListViewId");
+ [[EarlGrey selectElementWithMatcher:whatsNewView]
+ assertWithMatcher:grey_sufficientlyVisible()];
+
+ // Dismiss the What's New screen.
+ id<GREYMatcher> whatsNewDoneButton =
+ grey_accessibilityID(@"kWhatsNewTableViewNavigationDismissButtonId");
+ [[EarlGrey selectElementWithMatcher:whatsNewDoneButton]
+ performAction:grey_tap()];
+
+ // Trigger the Signin notification.
+ [XCUIDevice.sharedDevice pressButton:XCUIDeviceButtonHome];
+ [app activate];
+ [SigninEarlGrey addFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
+ TapNotification();
+
+ // Verify the signin screen is showing
+ id<GREYMatcher> signinView =
+ grey_accessibilityID(kWebSigninAccessibilityIdentifier);
+ [[EarlGrey selectElementWithMatcher:signinView]
+ assertWithMatcher:grey_sufficientlyVisible()];
+
+ // Dismiss Signin.
+ [[EarlGrey
+ selectElementWithMatcher:chrome_test_util::NavigationBarCancelButton()]
+ performAction:grey_tap()];
+}
+
+@end
diff --git a/ios/chrome/browser/tips_notifications/model/BUILD.gn b/ios/chrome/browser/tips_notifications/model/BUILD.gn
index f3a978d..fbdd06e 100644
--- a/ios/chrome/browser/tips_notifications/model/BUILD.gn
+++ b/ios/chrome/browser/tips_notifications/model/BUILD.gn
@@ -11,6 +11,7 @@
":utils",
"//base",
"//components/feature_engagement/public",
+ "//ios/chrome/browser/default_browser/model",
"//ios/chrome/browser/default_browser/model:utils",
"//ios/chrome/browser/feature_engagement/model",
"//ios/chrome/browser/push_notification/model:push_notification_client",
@@ -23,6 +24,7 @@
"//ios/chrome/browser/signin/model",
"//ios/chrome/browser/ui/authentication:signin_presenter",
]
+ frameworks = [ "UserNotifications.framework" ]
}
source_set("utils") {
@@ -36,6 +38,7 @@
"//ios/chrome/browser/shared/public/features",
"//ui/base",
]
+ frameworks = [ "UserNotifications.framework" ]
}
source_set("unit_tests") {
@@ -46,6 +49,7 @@
":utils",
"//base",
"//base/test:test_support",
+ "//ios/chrome/browser/default_browser/model",
"//ios/chrome/browser/default_browser/model:test_support",
"//ios/chrome/browser/default_browser/model:utils",
"//ios/chrome/browser/first_run/model",
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
index f79ae19..9836abe 100644
--- a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
@@ -10,6 +10,7 @@
#import "components/prefs/pref_registry_simple.h"
#import "components/prefs/pref_service.h"
#import "components/sync/base/features.h"
+#import "ios/chrome/browser/default_browser/model/promo_source.h"
#import "ios/chrome/browser/default_browser/model/utils.h"
#import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
@@ -21,7 +22,7 @@
#import "ios/chrome/browser/shared/model/utils/first_run_util.h"
#import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
-#import "ios/chrome/browser/shared/public/commands/promos_manager_commands.h"
+#import "ios/chrome/browser/shared/public/commands/settings_commands.h"
#import "ios/chrome/browser/shared/public/commands/show_signin_command.h"
#import "ios/chrome/browser/signin/model/authentication_service.h"
#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
@@ -268,8 +269,10 @@
void TipsNotificationClient::ShowDefaultBrowserPromo() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Browser* browser = GetSceneLevelForegroundActiveBrowser();
- [HandlerForProtocol(browser->GetCommandDispatcher(), PromosManagerCommands)
- maybeDisplayDefaultBrowserPromo];
+ [HandlerForProtocol(browser->GetCommandDispatcher(), SettingsCommands)
+ showDefaultBrowserSettingsFromViewController:nil
+ sourceForUMA:DefaultBrowserPromoSource::
+ kTipsNotification];
}
void TipsNotificationClient::ShowWhatsNew() {
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm
index a28593b..2c335ee 100644
--- a/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm
@@ -8,6 +8,7 @@
#import "base/test/task_environment.h"
#import "base/threading/thread_restrictions.h"
+#import "ios/chrome/browser/default_browser/model/promo_source.h"
#import "ios/chrome/browser/default_browser/model/utils.h"
#import "ios/chrome/browser/default_browser/model/utils_test_support.h"
#import "ios/chrome/browser/first_run/model/first_run.h"
@@ -19,7 +20,7 @@
#import "ios/chrome/browser/shared/model/browser_state/test_chrome_browser_state_manager.h"
#import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
-#import "ios/chrome/browser/shared/public/commands/promos_manager_commands.h"
+#import "ios/chrome/browser/shared/public/commands/settings_commands.h"
#import "ios/chrome/browser/tips_notifications/model/utils.h"
#import "ios/chrome/test/testing_application_context.h"
#import "ios/testing/scoped_block_swizzler.h"
@@ -182,11 +183,14 @@
// Tests that the client handles a Default Browser notification response.
TEST_F(TipsNotificationClientTest, DefaultBrowserHandle) {
- id mock_handler = OCMProtocolMock(@protocol(PromosManagerCommands));
- OCMExpect([mock_handler maybeDisplayDefaultBrowserPromo]);
+ id mock_handler = OCMProtocolMock(@protocol(SettingsCommands));
+ OCMExpect([mock_handler
+ showDefaultBrowserSettingsFromViewController:nil
+ sourceForUMA:DefaultBrowserPromoSource::
+ kTipsNotification]);
[browser_->GetCommandDispatcher()
startDispatchingToTarget:mock_handler
- forProtocol:@protocol(PromosManagerCommands)];
+ forProtocol:@protocol(SettingsCommands)];
id mock_response = MockRequestResponse(TipsNotificationType::kDefaultBrowser);
client_->HandleNotificationInteraction(mock_response);
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index 561f9d63..996f08b 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -98,6 +98,7 @@
"//ios/chrome/browser/ssl/model:eg2_tests",
"//ios/chrome/browser/supervised_user/model:eg2_tests",
"//ios/chrome/browser/sync/model/prefs:eg2_tests",
+ "//ios/chrome/browser/tips_notifications/eg_test:eg2_tests",
"//ios/chrome/browser/ui/autofill:eg2_tests",
"//ios/chrome/browser/ui/autofill/branding:eg2_tests",
"//ios/chrome/browser/ui/autofill/manual_fill:eg2_tests",
diff --git a/tools/metrics/histograms/metadata/settings/enums.xml b/tools/metrics/histograms/metadata/settings/enums.xml
index 4044e5b3..8ebcbf4a 100644
--- a/tools/metrics/histograms/metadata/settings/enums.xml
+++ b/tools/metrics/histograms/metadata/settings/enums.xml
@@ -58,6 +58,7 @@
<int value="2" label="External Intent"/>
<int value="3" label="Set Up List"/>
<int value="4" label="External Action"/>
+ <int value="5" label="Tips Notification"/>
</enum>
<enum name="LockScreenProgress">