chromium/ios/chrome/browser/supervised_user/model/supervised_user_incognito_mode_egtest.mm

// 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/feature_list.h"
#import "components/signin/internal/identity_manager/account_capabilities_constants.h"
#import "components/supervised_user/core/common/features.h"
#import "components/supervised_user/core/common/supervised_user_constants.h"
#import "ios/chrome/browser/policy/model/policy_earl_grey_matchers.h"
#import "ios/chrome/browser/signin/model/fake_system_identity.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h"
#import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_constants.h"
#import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.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/chrome/test/earl_grey/test_switches.h"
#import "ios/testing/earl_grey/app_launch_configuration.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
#import "ios/testing/earl_grey/earl_grey_test.h"

using chrome_test_util::ContainsPartialText;
using chrome_test_util::ShowTabsButton;
using chrome_test_util::TabGridIncognitoTabsPanelButton;
using chrome_test_util::TabGridNewIncognitoTabButton;

namespace {

// Message shown in the disabled incognito tab page for supervised users.
NSString* const kTestSupervisedIncognitoMessage =
    @"Your account is managed by your parent.";

// Label used to find the 'Learn more' link.
NSString* const kTestLearnMoreLabel = @"Learn more";

}  // namespace

// Tests that supervised users have incognito mode disabled.
@interface SupervisedUserIncognitoModeTestCase : ChromeTestCase
@end

@implementation SupervisedUserIncognitoModeTestCase

// Signs in with a supervised account.
- (void)signInWithSupervisedAccount {
  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey addFakeIdentity:fakeIdentity
                 withCapabilities:@{
                   @(kIsSubjectToParentalControlsCapabilityName) : @YES,
                 }];
  [SigninEarlGrey signinWithFakeIdentity:fakeIdentity];
  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
}

// Tests that the tools menu item "New Incognito Tab" is disabled on signin and
// re-enabled on signout.
- (void)testToolsMenuIncognito {
  [self signInWithSupervisedAccount];

  [ChromeEarlGreyUI openToolsMenu];
  policy::AssertOverflowMenuElementEnabled(kToolsMenuNewTabId);
  policy::AssertOverflowMenuElementDisabled(kToolsMenuNewIncognitoTabId);
  [ChromeEarlGreyUI closeToolsMenu];

  [SigninEarlGrey signOut];

  [ChromeEarlGreyUI openToolsMenu];
  policy::AssertOverflowMenuElementEnabled(kToolsMenuNewTabId);
  policy::AssertOverflowMenuElementEnabled(kToolsMenuNewIncognitoTabId);
}

// Tests that the "New Incognito Tab" item in the popup menu (long-pressing the
// tab grid button) is disabled on signin and re-enabled on signout.
- (void)testTabGridButtonLongPressMenuIncognito {
  [self signInWithSupervisedAccount];

  [[EarlGrey selectElementWithMatcher:ShowTabsButton()]
      performAction:grey_longPress()];
  policy::AssertButtonInCollectionEnabled(IDS_IOS_TOOLS_MENU_NEW_TAB);
  policy::AssertButtonInCollectionDisabled(
      IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB);

  // Dismiss the popup menu by tapping anywhere.
  [[EarlGrey selectElementWithMatcher:ShowTabsButton()]
      performAction:grey_tap()];

  [SigninEarlGrey signOut];

  [[EarlGrey selectElementWithMatcher:ShowTabsButton()]
      performAction:grey_longPress()];
  policy::AssertButtonInCollectionEnabled(IDS_IOS_TOOLS_MENU_NEW_TAB);
  policy::AssertButtonInCollectionEnabled(IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB);
}

// Tests that the disabled incognito tab grid shows a link to Family Link.
- (void)testTabGridIncognitoDisabled {
  [self signInWithSupervisedAccount];

  // Open incognito tab grid.
  [ChromeEarlGrey showTabSwitcher];
  [[EarlGrey selectElementWithMatcher:TabGridIncognitoTabsPanelButton()]
      performAction:grey_tap()];

  // New Incognito Tab button `(+)` should be disabled.
  [[EarlGrey selectElementWithMatcher:TabGridNewIncognitoTabButton()]
      assertWithMatcher:grey_accessibilityTrait(
                            UIAccessibilityTraitNotEnabled)];

  // The disabled incognito tab grid should display a message for supervised
  // users.
  [[EarlGrey selectElementWithMatcher:ContainsPartialText(
                                          kTestSupervisedIncognitoMessage)]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Check that the "Learn more" link works.
  [[EarlGrey
      selectElementWithMatcher:grey_allOf(
                                   grey_accessibilityLabel(kTestLearnMoreLabel),
                                   grey_sufficientlyVisible(), nil)]
      performAction:grey_tap()];

  // Wait for the Family Link page to finish loading.
  [ChromeEarlGrey waitForPageToFinishLoading];

  // For testing, there will be a redirect to the main Family Link website and
  // thus we only compare the hostnames.
  std::string expectedHostname =
      GURL(supervised_user::kManagedByParentUiMoreInfoUrl).host();
  GREYAssertEqual([ChromeEarlGrey webStateLastCommittedURL].host(),
                  expectedHostname,
                  @"Did not open the correct Learn more URL with hostname %s",
                  expectedHostname.c_str());
}

// Tests that the incognito tab grid is available after signout.
- (void)testTabGridIncognitoEnabledOnSignout {
  [self signInWithSupervisedAccount];
  [SigninEarlGrey signOut];

  // Open the incognito tab grid.
  [ChromeEarlGrey showTabSwitcher];
  [[EarlGrey selectElementWithMatcher:TabGridIncognitoTabsPanelButton()]
      performAction:grey_tap()];

  // New Incognito Tab button `(+)` should be re-enabled.
  [[EarlGrey selectElementWithMatcher:TabGridNewIncognitoTabButton()]
      assertWithMatcher:grey_not(grey_accessibilityTrait(
                            UIAccessibilityTraitNotEnabled))];

  // The disabled incognito tab should not display any messages from the
  // disabled incognito tab grid.
  [[EarlGrey selectElementWithMatcher:ContainsPartialText(
                                          kTestSupervisedIncognitoMessage)]
      assertWithMatcher:grey_nil()];
}

// Tests that the incognito tab grid is available after signout with restart.
// Regression test for b/360787816.
- (void)testTabGridIncognitoEnabledOnSignoutWithRestart {
  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey addFakeIdentity:fakeIdentity
                 withCapabilities:@{
                   @(kIsSubjectToParentalControlsCapabilityName) : @YES,
                 }];
  [SigninEarlGrey signinWithFakeIdentity:fakeIdentity];
  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];

  // Create the config to relaunch Chrome. Because fake identities are not
  // added on startup, manually configure fake identity cache to include the
  // supervised account.
  AppLaunchConfiguration config;
  config.relaunch_policy = ForceRelaunchByCleanShutdown;
  config.additional_args.push_back(
      base::StrCat({"--", test_switches::kSignInAtStartup}));
  config.additional_args.push_back(base::StrCat({
    "-", test_switches::kAddFakeIdentitiesAtStartup, "=",
        [FakeSystemIdentity encodeIdentitiesToBase64:@[ fakeIdentity ]]
  }));
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  [SigninEarlGrey signOut];

  // Open the incognito tab grid.
  [ChromeEarlGrey showTabSwitcher];
  [[EarlGrey selectElementWithMatcher:TabGridIncognitoTabsPanelButton()]
      performAction:grey_tap()];

  // New Incognito Tab button `(+)` should be re-enabled.
  [[EarlGrey selectElementWithMatcher:TabGridNewIncognitoTabButton()]
      assertWithMatcher:grey_not(grey_accessibilityTrait(
                            UIAccessibilityTraitNotEnabled))];

  // The disabled incognito tab should not display any messages from the
  // disabled incognito tab grid.
  [[EarlGrey selectElementWithMatcher:ContainsPartialText(
                                          kTestSupervisedIncognitoMessage)]
      assertWithMatcher:grey_nil()];
}

// Tests that incognito tabs are destroyed after supervised users sign in.
- (void)testIncognitoTabsDestroyedOnSignin {
  // Create new incognito tabs.
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey openNewIncognitoTab];
  GREYAssertEqual(2, [ChromeEarlGrey incognitoTabCount],
                  @"Incognito tab count should be 2");

  // The latest incognito tab is displayed.
  GREYAssertTrue([ChromeEarlGrey isIncognitoMode],
                 @"Should stay in incognito mode");

  [self signInWithSupervisedAccount];

  // All incognito tabs should be destroyed.
  GREYAssertEqual(0, [ChromeEarlGrey incognitoTabCount],
                  @"Incognito tab count should be 0");

  // If the supervised user was previously on an incognito tab, the disabled
  // incognito tab grid should be displayed.
  [[EarlGrey selectElementWithMatcher:ContainsPartialText(
                                          kTestSupervisedIncognitoMessage)]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Check that the edit button is disabled.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridEditButton()]
      assertWithMatcher:grey_not(grey_enabled())];
}

// Tests that incognito tabs are destroyed while supervised users stay in
// a regular tab.
- (void)testIncognitoTabsDestroyedOnSigninInBackground {
  // Create new incognito tabs.
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey openNewIncognitoTab];
  GREYAssertEqual(2, [ChromeEarlGrey incognitoTabCount],
                  @"Incognito tab count should be 2");

  // Open a new regular tab.
  [ChromeEarlGrey openNewTab];
  GREYAssertFalse([ChromeEarlGrey isIncognitoMode],
                  @"Should stay in regular tab.");

  [self signInWithSupervisedAccount];

  // All incognito tabs should be destroyed.
  GREYAssertEqual(0, [ChromeEarlGrey incognitoTabCount],
                  @"Incognito tab count should be 0");

  // The user should stay on the new tab page.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::NewTabPageOmnibox()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

@end