chromium/ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_screen_egtest.mm

// 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 "ios/chrome/browser/first_run/ui_bundled/first_run_constants.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/signin/model/fake_system_identity.h"
#import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
#import "ios/chrome/common/ui/promo_style/constants.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.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/earl_grey_test.h"
#import "ui/base/l10n/l10n_util.h"

using l10n_util::GetNSString;

namespace {

// Constant for timeout while waiting for asynchronous sync operations.
constexpr base::TimeDelta kSyncOperationTimeout = base::Seconds(10);

// Returns a matcher for the device switcher promo title.
id<GREYMatcher> DeviceSwitcherPromoTitle() {
  return grey_text(GetNSString(
      [ChromeEarlGrey isIPadIdiom]
          ? IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_DEVICE_SWITCHER_TITLE_IPAD
          : IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_DEVICE_SWITCHER_TITLE_IPHONE));
}

// Returns a matcher for the desktop user promo subtitle.
id<GREYMatcher> DesktopUserPromoSubtitle() {
  return grey_text(GetNSString(
      [ChromeEarlGrey isIPadIdiom]
          ? IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_DESKTOP_USER_SUBTITLE_IPAD
          : IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_DESKTOP_USER_SUBTITLE_IPHONE));
}

// Returns a matcher for the android switcher promo subtitle.
id<GREYMatcher> AndroidSwitcherPromoSubtitle() {
  return grey_text(GetNSString(
      [ChromeEarlGrey isIPadIdiom]
          ? IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_ANDROID_SWITCHER_SUBTITLE_IPAD
          : IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_ANDROID_SWITCHER_SUBTITLE_IPHONE));
}

// Returns a matcher for the default promo title.
id<GREYMatcher> DefaultPromoTitle() {
  return grey_text(
      GetNSString([ChromeEarlGrey isIPadIdiom]
                      ? IDS_IOS_FIRST_RUN_DEFAULT_BROWSER_SCREEN_SUBTITLE_IPAD
                      : IDS_IOS_FIRST_RUN_DEFAULT_BROWSER_SCREEN_SUBTITLE));
}

// Returns a matcher for the default promo subtitle.
id<GREYMatcher> DefaultPromoSubtitle() {
  return grey_text(
      GetNSString([ChromeEarlGrey isIPadIdiom]
                      ? IDS_IOS_FIRST_RUN_DEFAULT_BROWSER_SCREEN_TITLE_IPAD
                      : IDS_IOS_FIRST_RUN_DEFAULT_BROWSER_SCREEN_TITLE));
}

}  // namespace

// Test Default Browser promo that appears during the First Run Experience.
@interface DefaultBrowserScreenTestCase : ChromeTestCase
@end

@implementation DefaultBrowserScreenTestCase

#pragma mark - BaseEarlGreyTestCase

+ (void)setUpForTestCase {
  // Leave this empty so that the FRE shows during the first test.
}

- (void)setUp {
  [super setUp];
  [self signIn];
}

- (void)tearDown {
  [self reset];
  [super tearDown];
}

- (AppLaunchConfiguration)appConfigurationForTestCase {
  AppLaunchConfiguration config;
  // Enable Segmented Default Browser promos and iPad tailored Default Browser
  // promo strings.
  config.features_enabled.push_back(kSegmentedDefaultBrowserPromo);
  config.features_enabled.push_back(kDefaultBrowserPromoIPadExperimentalString);
  // Show the First Run UI at startup.
  config.additional_args.push_back("-FirstRunForceEnabled");
  config.additional_args.push_back("true");
  // Relaunch app at each test to rewind the startup state.
  config.relaunch_policy = ForceRelaunchByCleanShutdown;

  if ([self isRunningTest:@selector(testDesktopUserPromoDisplayed)]) {
    config.additional_args.push_back("-ForceExperienceForDeviceSwitcher");
    config.additional_args.push_back("Desktop");
  }
  if ([self isRunningTest:@selector(testAndroidSwitcherPromoDisplayed)]) {
    config.additional_args.push_back("-ForceExperienceForDeviceSwitcher");
    config.additional_args.push_back("AndroidPhone");
  }
  if ([self isRunningTest:@selector(DISABLED_testShopperPromoNotDisplayed)]) {
    config.additional_args.push_back("-ForceExperienceForShopper");
    config.additional_args.push_back("true");
  }
  return config;
}

#pragma mark - Helpers

// Signs in and enables sync.
- (void)signIn {
  [ChromeEarlGrey
      waitForUIElementToAppearWithMatcher:
          grey_accessibilityID(
              first_run::kFirstRunSignInScreenAccessibilityIdentifier)];
  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey addFakeIdentity:fakeIdentity];
  // Sign in.
  [self tapPromoButton:kPromoStylePrimaryActionAccessibilityIdentifier];
  // Enable history/tab sync.
  [ChromeEarlGrey
      waitForUIElementToAppearWithMatcher:
          grey_accessibilityID(kHistorySyncViewAccessibilityIdentifier)];
  [self tapPromoButton:kPromoStylePrimaryActionAccessibilityIdentifier];
  [ChromeEarlGrey
      waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
  [ChromeEarlGrey
      waitForUIElementToAppearWithMatcher:
          grey_accessibilityID(
              first_run::kFirstRunDefaultBrowserScreenAccessibilityIdentifier)];
}

// Signs out and clears sync data.
- (void)reset {
  [SigninEarlGrey signOut];
  [ChromeEarlGrey waitForSyncEngineInitialized:NO
                                   syncTimeout:kSyncOperationTimeout];
  [ChromeEarlGrey clearFakeSyncServerData];
}

// Taps a promo button.
- (void)tapPromoButton:(NSString*)buttonID {
  id<GREYMatcher> buttonMatcher = grey_accessibilityID(buttonID);
  id<GREYMatcher> scrollViewMatcher =
      grey_accessibilityID(kPromoStyleScrollViewAccessibilityIdentifier);
  // Needs to scroll slowly to make sure to not miss a cell if it is not
  // currently on the screen. It should not be bigger than the visible part
  // of the collection view.
  id<GREYAction> searchAction = grey_scrollInDirection(kGREYDirectionDown, 200);
  GREYElementInteraction* element =
      [[EarlGrey selectElementWithMatcher:buttonMatcher]
             usingSearchAction:searchAction
          onElementWithMatcher:scrollViewMatcher];
  [element performAction:grey_tap()];
}

#pragma mark - Tests

// Tests if the desktop user promo is correctly displayed.
- (void)testDesktopUserPromoDisplayed {
  [[EarlGrey selectElementWithMatcher:DeviceSwitcherPromoTitle()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:DesktopUserPromoSubtitle()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests if the android switcher promo is correctly displayed.
- (void)testAndroidSwitcherPromoDisplayed {
  [[EarlGrey selectElementWithMatcher:DeviceSwitcherPromoTitle()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:AndroidSwitcherPromoSubtitle()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests if the default promo is correctly displayed.
- (void)testDefaultPromoDisplayed {
  [[EarlGrey selectElementWithMatcher:DefaultPromoTitle()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:DefaultPromoSubtitle()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that the shopping user promo is not displayed when the Shopper
// experience is forced through experimental settings. Shopper segmentation
// information should not be available during first run.
// TODO(crbug.com/360395573): Enable after modifying segmented default browser
// utils to add this check.
- (void)DISABLED_testShopperPromoNotDisplayed {
  [[EarlGrey selectElementWithMatcher:DefaultPromoTitle()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:DefaultPromoSubtitle()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

@end