chromium/ios/chrome/browser/policy/model/policy_incognito_mode_availability_egtest.mm

// 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.

#import "ios/testing/earl_grey/earl_grey_test.h"

#import "base/json/json_string_value_serializer.h"
#import "base/strings/sys_string_conversions.h"
#import "components/policy/core/common/policy_loader_ios_constants.h"
#import "components/policy/policy_constants.h"
#import "ios/chrome/browser/policy/model/policy_app_interface.h"
#import "ios/chrome/browser/policy/model/policy_earl_grey_matchers.h"
#import "ios/chrome/browser/ui/popup_menu/popup_menu_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/testing/earl_grey/app_launch_configuration.h"
#import "ios/testing/earl_grey/app_launch_manager.h"

using chrome_test_util::ToolsMenuView;
using policy::AssertContextMenuItemDisabled;
using policy::AssertContextMenuItemEnabled;
using policy::AssertOverflowMenuElementDisabled;
using policy::AssertOverflowMenuElementEnabled;

namespace {

// Values of the incognito mode availability.
enum class IncognitoAvailability {
  kAvailable = 0,
  kDisabled = 1,
  kOnly = 2,
  kMaxValue = kOnly,
};

// Sets the incognito mode availability.
void SetIncognitoAvailabiliy(IncognitoAvailability availability) {
  [PolicyAppInterface
      setPolicyValue:[NSString stringWithFormat:@"%d",
                                                static_cast<int>(availability),
                                                nil]
              forKey:base::SysUTF8ToNSString(
                         policy::key::kIncognitoModeAvailability)];
}

// Returns a matcher for the tab grid button.
id<GREYMatcher> TabGridButton() {
  return chrome_test_util::ButtonWithAccessibilityLabelId(
      IDS_IOS_TOOLBAR_SHOW_TABS);
}

}  // namespace

// Test case to verify that the IncognitoModeAvailability policy is set and
// respected.
@interface PolicyIncognitoModeAvailabilityTestCase : ChromeTestCase
@end

@implementation PolicyIncognitoModeAvailabilityTestCase

- (AppLaunchConfiguration)appConfigurationForTestCase {
  // Use commandline args to insert fake policy data into NSUserDefaults. To the
  // app, this policy data will appear under the
  // "com.apple.configuration.managed" key.
  AppLaunchConfiguration config;
  config.relaunch_policy = NoForceRelaunchAndResetState;
  return config;
}

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

// When the IncognitoModeAvailability policy is set to available, the tools
// menu item "New Tab" and "New Incognito Tab" should be enabled.
- (void)testToolsMenuWhenIncognitoAvailable {
  SetIncognitoAvailabiliy(IncognitoAvailability::kAvailable);
  [ChromeEarlGreyUI openToolsMenu];

  AssertOverflowMenuElementEnabled(kToolsMenuNewTabId);
  AssertOverflowMenuElementEnabled(kToolsMenuNewIncognitoTabId);
}

// When the IncognitoModeAvailability policy is set to disabled, the tools menu
// item "New Incognito Tab" should be disabled.
- (void)testToolsMenuWhenIncognitoDisabled {
  SetIncognitoAvailabiliy(IncognitoAvailability::kDisabled);
  [ChromeEarlGreyUI openToolsMenu];

  AssertOverflowMenuElementEnabled(kToolsMenuNewTabId);
  AssertOverflowMenuElementDisabled(kToolsMenuNewIncognitoTabId);
}

// When the IncognitoModeAvailability policy is set to forced, the tools menu
// item "New Tab" should be disabled.
- (void)testToolsMenuWhenIncognitoOnly {
  SetIncognitoAvailabiliy(IncognitoAvailability::kOnly);
  [ChromeEarlGreyUI openToolsMenu];

  AssertOverflowMenuElementDisabled(kToolsMenuNewTabId);
  AssertOverflowMenuElementEnabled(kToolsMenuNewIncognitoTabId);
}

// When the IncognitoModeAvailability policy is set to available, the "New Tab"
// and "New Incognito Tab" items should be enabled in the popup menu triggered
// by long-pressing the tab grid button.
- (void)testTabGridButtonLongPressMenuWhenIncognitoAvailable {
  SetIncognitoAvailabiliy(IncognitoAvailability::kAvailable);
  // Long press the tab grid button.
  [[EarlGrey selectElementWithMatcher:TabGridButton()]
      performAction:grey_longPress()];

  AssertContextMenuItemEnabled(IDS_IOS_TOOLS_MENU_NEW_TAB);
  AssertContextMenuItemEnabled(IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB);
}

// When the IncognitoModeAvailability policy is set to disabled, the "New
// Incognito Tab" item should be disabled in the popup menu triggered by
// long-pressing the tab grid button.
- (void)testTabGridButtonLongPressMenuWhenIncognitoDisabled {
  SetIncognitoAvailabiliy(IncognitoAvailability::kDisabled);
  // Long press the tab grid button.
  [[EarlGrey selectElementWithMatcher:TabGridButton()]
      performAction:grey_longPress()];

  AssertContextMenuItemEnabled(IDS_IOS_TOOLS_MENU_NEW_TAB);
  AssertContextMenuItemDisabled(IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB);
}

// When the IncognitoModeAvailability policy is set to forced, the "New Tab"
// item should be disabled in the popup menu triggered by long-pressing the tab
// grid button.
- (void)testTabGridButtonLongPressMenuWhenIncognitoOnly {
  SetIncognitoAvailabiliy(IncognitoAvailability::kOnly);
  // Long press the tab grid button.
  [[EarlGrey selectElementWithMatcher:TabGridButton()]
      performAction:grey_longPress()];

  AssertContextMenuItemDisabled(IDS_IOS_TOOLS_MENU_NEW_TAB);
  AssertContextMenuItemEnabled(IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB);
}

// Tests that when the IncognitoModeAvailability policy is set to forced, the
// "New Tab" keyboard shortcut action is disabled and can't open a new regular
// tab. This doesn't verify the tab grid UI.
- (void)testOpenNewTab_FromPhysicalKeyboard_ForcedIncognito {
  // Restart the app with the incognito policy.
  AppLaunchConfiguration config;
  config.relaunch_policy = ForceRelaunchByCleanShutdown;
  // Configure the policy to force sign-in.
  config.additional_args.push_back(
      "-" + base::SysNSStringToUTF8(kPolicyLoaderIOSConfigurationKey));
  config.additional_args.push_back(
      "<dict><key>IncognitoModeAvailability</key><integer>2</integer></dict>");
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  // Use the `CMD + n` keyboard shorcut to try opening a regular tab.
  [ChromeEarlGrey simulatePhysicalKeyboardEvent:@"n"
                                          flags:UIKeyModifierCommand];

  // Verify that the browser view is still in incognito mode.
  GREYAssertTrue([ChromeEarlGrey isIncognitoMode],
                 @"should stay in incognito mode");
}

// Tests that when the IncognitoModeAvailability policy is set to disabled, the
// "New Incognito Tab" keyboard shortcut action is disabled and can't open a new
// incognito tab. This doesn't verify the tab grid UI.
- (void)testOpenNewTab_FromPhysicalKeyboard__DisabledIncognito {
  // Restart the app to take into consideration the policy value.
  AppLaunchConfiguration config;
  config.relaunch_policy = ForceRelaunchByCleanShutdown;
  // Configure the policy to force sign-in.
  config.additional_args.push_back(
      "-" + base::SysNSStringToUTF8(kPolicyLoaderIOSConfigurationKey));
  config.additional_args.push_back(
      "<dict><key>IncognitoModeAvailability</key><integer>1</integer></dict>");
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  // Use the `CMD + SHIFT + n` keyboard shorcut to try opening an incognito tab.
  [ChromeEarlGrey
      simulatePhysicalKeyboardEvent:@"n"
                              flags:UIKeyModifierCommand | UIKeyModifierShift];

  GREYAssertFalse([ChromeEarlGrey isIncognitoMode],
                  @"should stay in regular mode");
}

// TODO(crbug.com/40163908): Add test to new tab long-press menu.

@end