chromium/ios/chrome/browser/supervised_user/model/supervised_user_with_parental_controls_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/test/ios/wait_util.h"
#import "components/policy/policy_constants.h"
#import "components/signin/internal/identity_manager/account_capabilities_constants.h"
#import "components/supervised_user/core/browser/supervised_user_url_filter.h"
#import "components/supervised_user/core/common/features.h"
#import "ios/chrome/browser/metrics/model/metrics_app_interface.h"
#import "ios/chrome/browser/policy/model/policy_app_interface.h"
#import "ios/chrome/browser/policy/model/policy_earl_grey_utils.h"
#import "ios/chrome/browser/policy/model/policy_util.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/signin/model/capabilities_types.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/content_suggestions/content_suggestions_constants.h"
#import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
#import "ios/chrome/browser/ui/settings/google_services/manage_sync_settings_constants.h"
#import "ios/chrome/browser/ui/settings/supervised_user_settings_app_interface.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_matchers_app_interface.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"
#import "ios/testing/earl_grey/matchers.h"
#import "net/base/apple/url_conversions.h"
#import "net/test/embedded_test_server/embedded_test_server.h"
#import "ui/base/l10n/l10n_util.h"
#import "url/gurl.h"

namespace {
static const char* kHost = "a.host";
static const char* kEchoPath = "/echo";
static const char* kEchoContent = "Echo";
static const char* kDefaultPath = "/defaultresponse";
static const char* kDefaultContent = "Default response";
static const char* kInterstitialContent = "Ask your parent";
static const char* kInterstitialWaitingContent = "Waiting for permission";
static const char* kInterstitialBlockReason = "This site is blocked";
static const char* kInterstitialDetails = "Details";
static const char* kInterstitialFirstTimeBanner =
    "Family Link choices for Chrome apply here";
}  // namespace

// Tests the core user journeys of a supervised user with FamilyLink parental
// control restrictions enabled.
@interface SupervisedUserWithParentalControlsTestCase : ChromeTestCase
@end

@implementation SupervisedUserWithParentalControlsTestCase

- (void)signInSupervisedUser {
  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey addFakeIdentity:fakeIdentity
                 withCapabilities:@{
                   @(kIsSubjectToParentalControlsCapabilityName) : @YES,
                 }];

  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
}

- (void)setUp {
  [super setUp];
  bool started = self.testServer->Start();
  GREYAssertTrue(started, @"Test server failed to start.");
  [SupervisedUserSettingsAppInterface setUpTestUrlLoaderFactoryHelper];
}

- (void)tearDown {
  [ChromeEarlGrey closeCurrentTab];
  [SupervisedUserSettingsAppInterface resetSupervisedUserURLFilterBehavior];
  [SupervisedUserSettingsAppInterface resetManualUrlFiltering];
  [SupervisedUserSettingsAppInterface tearDownTestUrlLoaderFactoryHelper];
  [super tearDown];
}

- (void)checkRequestSentMessageVisibility:(BOOL)isVisible {
  NSString* isRequestSentMessageVisible =
      [NSString stringWithFormat:
                    @"%sdocument.getElementById('request-sent-message').hidden",
                    isVisible ? "!" : ""];
  [ChromeEarlGrey waitForJavaScriptCondition:isRequestSentMessageVisible];
}

- (void)checkBlockPageHeaderVisibility:(BOOL)isVisible {
  NSString* isBlockPageHeaderVisible = [NSString
      stringWithFormat:@"%sdocument.getElementById('block-page-header').hidden",
                       isVisible ? "!" : ""];
  [ChromeEarlGrey waitForJavaScriptCondition:isBlockPageHeaderVisible];
}

- (void)checkShowDetailsLinkVisibility:(BOOL)isVisible {
  [self checkElementDisplayStyleVisibility:@"block-reason-show-details-link"
                                 isVisible:isVisible];
}

- (void)checkHideDetailsLinkVisibility:(BOOL)isVisible {
  [self checkElementDisplayStyleVisibility:@"block-reason-hide-details-link"
                                 isVisible:isVisible];
}

- (void)checkElementDisplayStyleVisibility:(NSString*)elementId
                                 isVisible:(BOOL)isVisible {
  NSString* isElementVisible =
      [NSString stringWithFormat:@"getComputedStyle(document.getElementById('%@"
                                 @"')).display %s== \"none\"",
                                 elementId, isVisible ? "!" : "="];
  [ChromeEarlGrey waitForJavaScriptCondition:isElementVisible];
}

- (void)checkInterstitalIsShown {
  [ChromeEarlGrey waitForWebStateContainingText:kInterstitialContent];
  // Originally the "Ask your parent" title is shown and the "Waiting
  // for pemission" message is hidden.
  [self checkBlockPageHeaderVisibility:YES];
  [self checkRequestSentMessageVisibility:NO];
}

- (void)checkInterstitalIsShownInWaitingScreen {
  [ChromeEarlGrey waitForWebStateContainingText:kInterstitialWaitingContent];
  // In waiting screen, the "Ask your parent" title is hidden and the "Waiting
  // for pemission" message is visible instead.
  [self checkBlockPageHeaderVisibility:NO];
  [self checkRequestSentMessageVisibility:YES];
}

- (void)clearBrowsingData {
  // Clear the browsing data.
  [ChromeEarlGreyUI openSettingsMenu];
  [ChromeEarlGreyUI
      tapSettingsMenuButton:chrome_test_util::SettingsMenuPrivacyButton()];
  [ChromeEarlGreyUI
      tapPrivacyMenuButton:chrome_test_util::ClearBrowsingDataCell()];
  // "Browsing history", "Cookies, Site Data" and "Cached Images and Files"
  // are the default checked options when the prefs are registered. No need to
  // modify them.
  [ChromeEarlGreyUI tapClearBrowsingDataMenuButton:
                        chrome_test_util::ClearBrowsingDataButton()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          ConfirmClearBrowsingDataButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
      performAction:grey_tap()];
}

#if !TARGET_IPHONE_SIMULATOR
#define MAYBE_testSupervisedUserSignin DISABLED_testSupervisedUserSignin
#else
#define MAYBE_testSupervisedUserSignin testSupervisedUserSignin
#endif
// TODO(crbug.com/331644931): Re-enable on device when fixed.
// Tests that the user is signed in.
- (void)MAYBE_testSupervisedUserSignin {
  [self signInSupervisedUser];

  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
}

// Tests that the supervised user does not see popular content suggestions.
- (void)testSupervisedUserOpenNewTabPage {
  [self signInSupervisedUser];

  [ChromeEarlGreyUI openNewTab];

  // Assert that the most visited tiles are not visible for supervised users.
  id<GREYMatcher> firstMostVisitedTile = grey_accessibilityID(
      [kContentSuggestionsMostVisitedAccessibilityIdentifierPrefix
          stringByAppendingString:@"0"]);
  [[EarlGrey selectElementWithMatcher:firstMostVisitedTile]
      assertWithMatcher:grey_not(grey_sufficientlyVisible())];
}

#if !TARGET_IPHONE_SIMULATOR
#define MAYBE_testSupervisedUserURLFilteringReloadsOnlyRealizedExistingWebStates \
  DISABLED_testSupervisedUserURLFilteringReloadsOnlyRealizedExistingWebStates
#else
#define MAYBE_testSupervisedUserURLFilteringReloadsOnlyRealizedExistingWebStates \
  testSupervisedUserURLFilteringReloadsOnlyRealizedExistingWebStates
#endif
// TODO(crbug.com/331644931): Re-enable on device when fixed.
// Tests that only realized existing web states will display the interstitial
// when a filtering for them is triggered. Also tests that the filtering logic
// on existing tabs does not force-realize unrealized states. This is a
// regression test for bug: 1486459.
- (void)
    MAYBE_testSupervisedUserURLFilteringReloadsOnlyRealizedExistingWebStates {
  // Signing in the user and allow all sites.
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFilteringToAllowAllSites];

  // Open three tabs, visit a webpage from them and check they are unblocked.
  GURL URL = self.testServer->GetURL(kEchoPath);
  for (int i = 0; i < 3; i++) {
    if (i != 0) {
      [ChromeEarlGreyUI openNewTab];
    }
    [ChromeEarlGrey loadURL:URL];
    [ChromeEarlGrey waitForWebStateContainingText:kEchoContent];
  }

  // Restart the browser (needed to obtain non-realized states).
  AppLaunchConfiguration config;
  config.relaunch_policy = ForceRelaunchByCleanShutdown;
  // Add the switch to make sure that the user stays signed in in the restart.
  config.additional_args.push_back(std::string("-") +
                                   test_switches::kSignInAtStartup);
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  // Check the previously used tabs are maintained.
  [ChromeEarlGrey waitForMainTabCount:3];
  // Set up histogram tracking before changing the filtering behaviour.
  GREYAssertNil([MetricsAppInterface setupHistogramTester],
                @"Failed to set up histogram tester.");
  // Change the filtering setting to block the previously used urls. This
  // results in a new filtering of the existing tabs.
  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];

  // There should be one realized web state (active tab).
  // Check that only one tab displays the intersitial.
  GREYAssertEqual(
      1, [ChromeEarlGrey realizedWebStatesCount],
      @"A single realized web state must exist. The tab reloading filtering"
      @"behaviour should not force web states to become realized.");

  // Wait for one interstitial to appear (on the realized tab).
  GREYAssert(
      base::test::ios::WaitUntilConditionOrTimeout(
          base::test::ios::kWaitForPageLoadTimeout,
          ^bool {
            return
                [SupervisedUserSettingsAppInterface
                    countSupervisedUserIntersitialsForExistingWebStates] == 1;
          }),
      @"Interstitial did not appear.");

  // Out of the 3 tabs, only the active one should have recorded metrics for
  // filtering.
  auto* error =
      [MetricsAppInterface expectTotalCount:1
                               forHistogram:@"ManagedUsers.FilteringResult"];
  if (error) {
    GREYFail([error description]);
  }
}

#if !TARGET_IPHONE_SIMULATOR
#define MAYBE_testSupervisedUserSignedOutOnPolicyChange \
  DISABLED_testSupervisedUserSignedOutOnPolicyChange
#else
#define MAYBE_testSupervisedUserSignedOutOnPolicyChange \
  testSupervisedUserSignedOutOnPolicyChange
#endif
// TODO(crbug.com/331644931): Re-enable on device when fixed.
// Tests that the user is correctly signed out after signin is disabled via
// policy.
- (void)MAYBE_testSupervisedUserSignedOutOnPolicyChange {
  [self signInSupervisedUser];

  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];

  // Disable signin via policy.
  policy_test_utils::SetPolicy(static_cast<int>(BrowserSigninMode::kDisabled),
                               policy::key::kBrowserSignin);

  // Make sure the UI has settled and the last changes are taken into account.
  GREYWaitForAppToIdle(@"App failed to idle");

  NSString* continueLabel =
      l10n_util::GetNSString(IDS_IOS_ENTERPRISE_SIGNED_OUT_CONTINUE);
  [[EarlGrey
      selectElementWithMatcher:grey_allOf(
                                   grey_accessibilityLabel(continueLabel),
                                   grey_accessibilityTrait(
                                       UIAccessibilityTraitButton),
                                   nil)] performAction:grey_tap()];

  [SigninEarlGrey verifySignedOut];
  [PolicyAppInterface clearPolicies];
}

// Tests that the Encryption item is disabled for supervised users.
- (void)testEncryptionItemDisabledForSupervisedUsers {
  [self signInSupervisedUser];
  [ChromeEarlGreyUI openSettingsMenu];
  [ChromeEarlGreyUI
      tapSettingsMenuButton:chrome_test_util::SettingsAccountButton()];

  [[[EarlGrey selectElementWithMatcher:
                  grey_allOf(chrome_test_util::ButtonWithAccessibilityLabelId(
                                 IDS_IOS_MANAGE_SYNC_ENCRYPTION),
                             grey_sufficientlyVisible(), nil)]
         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
      onElementWithMatcher:grey_accessibilityID(
                               kManageSyncTableViewAccessibilityIdentifier)]
      assertWithMatcher:grey_not(grey_userInteractionEnabled())];
}

#pragma mark - Filtering Behaviour

// Tests that users with "Allow Approved" filtering are shown the interstitial
// when they navigate to a non-approved site.
- (void)testSupervisedUserWithAllowApprovedSitesFilteringIsShownInterstitial {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];

  GURL blockedURL = self.testServer->GetURL(kEchoPath);
  [ChromeEarlGrey loadURL:blockedURL];

  [self checkInterstitalIsShown];
}

// Tests that users with "Allow All" filtering are shown the interstitial
// when they navigate to a site that ClassifyUrl classifies as unsafe.
- (void)testSupervisedUserWithAllowAllSitesAndSafeSearchRestricted {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFilteringToAllowAllSites];
  [SupervisedUserSettingsAppInterface
      setDefaultClassifyURLNavigationIsAllowed:NO];

  // When safe search classifies the url as restricted, the user navigation is
  // blocked.
  GURL blockedSafeSearchURL = self.testServer->GetURL(kEchoPath);
  [ChromeEarlGrey loadURL:blockedSafeSearchURL];

  [self checkInterstitalIsShown];
}

// Tests that users with "Allow All" filtering are not blocked
// when they navigate to a website allowed by ClassifyUrl.
- (void)testSupervisedUserWithAllowAllSitesAndSafeSearchAllowed {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFilteringToAllowAllSites];
  // TODO(b/297313665): Instead of a default response, introduce a stack-based
  // approach for the mocked reponses. See `kids_management_api_server_mock.h`.
  [SupervisedUserSettingsAppInterface
      setDefaultClassifyURLNavigationIsAllowed:YES];

  // When safe search classifies the url as allowed, the user can navigate to
  // it.
  GURL allowedSafeSearchURL = self.testServer->GetURL(kEchoPath);
  [ChromeEarlGrey loadURL:allowedSafeSearchURL];

  [ChromeEarlGrey waitForWebStateContainingText:kEchoContent];
}

// Tests that users with "Allow All" filtering are shown the interstitial
// when they navigate to a site from the blocked list.
- (void)
    testSupervisedUserWithAllowAllSitesFilteringIsShownInterstitialOnBlockedSite {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFilteringToAllowAllSites];

  GURL blockedURL = self.testServer->GetURL(kHost, kEchoPath);
  [SupervisedUserSettingsAppInterface
      addWebsiteToBlockList:net::NSURLWithGURL(blockedURL)];

  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShown];
}

// Tests that users with "Allow Approved" filtering are not blocked
// when they navigate to an allow-listed website.
- (void)testSupervisedUserWithAllowApprovedSitesFilteringCanViewAllowedWebages {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];

  GURL URL = self.testServer->GetURL(kEchoPath);
  // The page is originally blocked.
  [ChromeEarlGrey loadURL:URL];
  [self checkInterstitalIsShown];

  // Navigate to another page to change the browser content (otherwise,
  // allow-listing the site will trigger an immediate refresh - not the scope
  // of this test, see
  // `testSupervisedUserWithAllowAllFilteringIsBlockedOnUrlBlockListing`-).
  GURL otherURL = self.testServer->GetURL(kHost, kEchoPath);
  [ChromeEarlGrey loadURL:otherURL];
  [self checkInterstitalIsShown];

  // Allow-list the page and re-visit it. It should now be unblocked.
  [SupervisedUserSettingsAppInterface
      addWebsiteToAllowList:net::NSURLWithGURL(URL)];

  [ChromeEarlGrey loadURL:URL];
  [ChromeEarlGrey waitForWebStateContainingText:kEchoContent];
}

// Tests that when an interstitial is displayed for a blocked site,
// allow-listing it triggers an intestitial refresh and unblocks the page.
- (void)
    testSupervisedUserWithAllowApprovedFilteringIsUnblockedOnURLAllowListing {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];

  GURL URL = self.testServer->GetURL(kEchoPath);
  [ChromeEarlGrey loadURL:URL];
  [self checkInterstitalIsShown];

  [SupervisedUserSettingsAppInterface
      addWebsiteToAllowList:net::NSURLWithGURL(URL)];
  // Ensure that the interstitial is refreshed and the un-blocked page is
  // displayed.
  [ChromeEarlGrey waitForWebStateContainingText:kEchoContent];
}

// Tests that block-listing a url, results in showing immediately the
// interstitial if the user has the url open in a tab.
- (void)testSupervisedUserWithAllowAllFilteringIsBlockedOnURLBlockListing {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFilteringToAllowAllSites];

  GURL URL = self.testServer->GetURL(kEchoPath);
  [ChromeEarlGrey loadURL:URL];
  [ChromeEarlGrey waitForWebStateContainingText:kEchoContent];

  [SupervisedUserSettingsAppInterface
      addWebsiteToBlockList:net::NSURLWithGURL(URL)];
  // Ensure that the interstitial is triggered.
  [self checkInterstitalIsShown];
}

// Tests that users who have the filtering behaviour changed from "Allow all"
// to "Allow approved" websites, will be shown the interstitial as soon as
// the filtering behaviour changes.
- (void)
    testSupervisedUserWithAllowApprovedSitesFilteringIsBlockedOnFilterChange {
  [self signInSupervisedUser];
  GURL safeURL = self.testServer->GetURL(kEchoPath);
  [ChromeEarlGrey loadURL:safeURL];
  [ChromeEarlGrey waitForWebStateContainingText:kEchoContent];

  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];
  [self checkInterstitalIsShown];

  // Reloading the page should not affect the interstitial.
  [ChromeEarlGrey reload];
  [self checkInterstitalIsShown];
}

// Tests that for users who have the filtering behaviour changed from "Allow
// approved" to "Allow all" websites, a blocked pages will be refreshed and
// unblocks as soon as the filtering behaviour changes.
- (void)testSupervisedUserWithAllowAllSitesFilteringIsUnblockedOnFilterChange {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];

  GURL blockedURL = self.testServer->GetURL(kEchoPath);
  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShown];

  [SupervisedUserSettingsAppInterface setFilteringToAllowAllSites];
  // Ensure that the interstitial is refreshed and the un-blocked page is
  // displayed.
  [ChromeEarlGrey waitForWebStateContainingText:kEchoContent];
}

// Tests that users who navigate to a blocked page can request that it is
// unblocked and upon unblocking the page is refreshed and displayed.
- (void)testSupervisedUserWithAllowAllSitesFilteringCanUnblockRequestedWebsite {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFakePermissionCreator];
  [SupervisedUserSettingsAppInterface setFilteringToAllowAllSites];

  GURL blockedURL = self.testServer->GetURL(kEchoPath);
  [SupervisedUserSettingsAppInterface
      addWebsiteToBlockList:net::NSURLWithGURL(blockedURL)];

  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShown];

  // On clicking "Ask in a message" button, the interstitial "Waiting" screen is
  // displayed.
  [ChromeEarlGrey tapWebStateElementWithID:@"remote-approvals-button"];
  [self checkInterstitalIsShownInWaitingScreen];

  // Approving the permission request for the blocked host
  // should refresh the newly unblocked page.
  [SupervisedUserSettingsAppInterface
      approveWebsiteDomain:net::NSURLWithGURL(blockedURL)];
  [ChromeEarlGrey waitForWebStateContainingText:kEchoContent];
}

#pragma mark - Interstitial UI Behaviour

// Checks the behaviour of the "Details" link on click (expand/shrink details).
- (void)testSupervisedUserShowInterstitialDetailsLinkOnClickForNarrowScreen {
#if !TARGET_IPHONE_SIMULATOR
  EARL_GREY_TEST_DISABLED(@"This is an iphone test case only.");
#endif
  // Compact width only.
  if (![ChromeEarlGrey isCompactWidth]) {
    EARL_GREY_TEST_DISABLED(@"This is a narrow screen test case only.");
  }

  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFakePermissionCreator];
  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];

  GURL blockedURL = self.testServer->GetURL(kHost, kEchoPath);
  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShown];
  [ChromeEarlGrey waitForWebStateContainingText:kInterstitialDetails];
  [self checkShowDetailsLinkVisibility:YES];
  [self checkHideDetailsLinkVisibility:NO];

  // Expand the Details link.
  [ChromeEarlGrey tapWebStateElementWithID:@"block-reason-show-details-link"];
  [ChromeEarlGrey waitForWebStateContainingText:"This site is blocked"];
  [self checkShowDetailsLinkVisibility:NO];
  [self checkHideDetailsLinkVisibility:YES];

  // Shrink the Details link.
  [ChromeEarlGrey tapWebStateElementWithID:@"block-reason-hide-details-link"];
  [ChromeEarlGrey waitForWebStateContainingText:kInterstitialDetails];
  [self checkShowDetailsLinkVisibility:YES];
  [self checkHideDetailsLinkVisibility:NO];
}

// Checks that we don't regress to b/290000817: The 'Details' link should
// be absernt from the interstitial 'Waiting' screen bor both existing (updated)
// intersitials and new interstitials for already requested hosts.
- (void)testSupervisedUserShowInterstitialDetailsLinkForNarrowScreen {
#if !TARGET_IPHONE_SIMULATOR
  EARL_GREY_TEST_DISABLED(@"This is an iphone test case only.");
#endif
  // Compact width only.
  if (![ChromeEarlGrey isCompactWidth]) {
    EARL_GREY_TEST_DISABLED(@"This is a narrow screen test case only.");
  }

  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFakePermissionCreator];
  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];

  GURL blockedURL = self.testServer->GetURL(kHost, kEchoPath);
  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShown];

  // Details link must be visible.
  [ChromeEarlGrey waitForWebStateContainingText:kInterstitialDetails];
  [self checkShowDetailsLinkVisibility:YES];
  [self checkHideDetailsLinkVisibility:NO];

  // Case 1: Requested host on present (updated) intersitial:
  // The Details link must not be visible on the
  // "Waiting" screen on the existing interstitial.
  [ChromeEarlGrey tapWebStateElementWithID:@"remote-approvals-button"];
  [self checkInterstitalIsShownInWaitingScreen];
  [self checkShowDetailsLinkVisibility:NO];
  [self checkHideDetailsLinkVisibility:NO];

  // Case 2: Already requested host on a new intersitial case:
  // Tge Details link must not be visible on the
  // "Waiting" screen on the new interstitial.
  GURL otherURL = self.testServer->GetURL("other.host", kEchoPath);
  [ChromeEarlGrey loadURL:otherURL];
  [self checkInterstitalIsShown];

  // Request the original blocked site. The interstitial "Waiting" screen is
  // displayed without the Details.
  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShownInWaitingScreen];
  [self checkShowDetailsLinkVisibility:NO];
  [self checkHideDetailsLinkVisibility:NO];
}

// Tests that the that the Details link / Block reason is displayed on the
// interstitial "Ask your parent" screen depending on the screen width.
- (void)testSupervisedUserInterstitialShowBlockReasonAndDetails {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];

  GURL blockedURL = self.testServer->GetURL(kHost, kEchoPath);

  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShown];

  if ([ChromeEarlGrey isCompactWidth]) {
    // Narrow screen displays "Details" link ("Block reason" is hidden).
    [ChromeEarlGrey waitForWebStateContainingText:kInterstitialDetails];
    [self checkElementDisplayStyleVisibility:@"block-reason-show-details-link"
                                   isVisible:YES];
    [self checkElementDisplayStyleVisibility:@"block-reason" isVisible:NO];
  } else {
    // Wide screen displays "Block reason" ("Details" is hidden).
    [ChromeEarlGrey waitForWebStateContainingText:kInterstitialBlockReason];
    [self checkElementDisplayStyleVisibility:@"block-reason-show-details-link"
                                   isVisible:NO];
    [self checkElementDisplayStyleVisibility:@"block-reason" isVisible:YES];
  }
}

// Tests that the Back Button of the interstitial gets us to the previous page.
- (void)testSupervisedUserInterstitialOnBackButton {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFakePermissionCreator];
  [SupervisedUserSettingsAppInterface setFilteringToAllowAllSites];

  GURL allowedURL = self.testServer->GetURL(kDefaultPath);
  GURL blockedURL = self.testServer->GetURL(kHost, kEchoPath);
  [SupervisedUserSettingsAppInterface
      addWebsiteToBlockList:net::NSURLWithGURL(blockedURL)];

  [ChromeEarlGrey loadURL:allowedURL];
  [ChromeEarlGrey waitForWebStateContainingText:kDefaultContent];

  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShown];

  // On clicking "Ask in a message" button, the interstitial "Waiting" screen is
  // displayed.
  [ChromeEarlGrey tapWebStateElementWithID:@"remote-approvals-button"];
  [self checkInterstitalIsShownInWaitingScreen];

  // On clicking the "Ok" (back) button, the browser goes to the previous
  // (allowed) page.
  [ChromeEarlGrey tapWebStateElementWithID:@"back-button"];
  [ChromeEarlGrey waitForWebStateContainingText:kDefaultContent];
}

// Tests that for already requested for approval urls, the interstitial is shown
// in the waiting screen upon revisiting.
- (void)testSupervisedUserInterstitialForAlreadyRequestedHostShowsWaitScreen {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFakePermissionCreator];
  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];

  GURL blockedURL = self.testServer->GetURL(kHost, kEchoPath);

  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShown];

  // On clicking "Ask in a message" button, the interstitial "Waiting" screen is
  // displayed.
  [ChromeEarlGrey tapWebStateElementWithID:@"remote-approvals-button"];
  [self checkInterstitalIsShownInWaitingScreen];

  // Navigate to another site.
  GURL otherURL = self.testServer->GetURL("other.host", kEchoPath);
  [ChromeEarlGrey loadURL:otherURL];
  [self checkInterstitalIsShown];

  // Navigate to the original blocked site. The interstitial "Waiting" screen is
  // displayed.
  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShownInWaitingScreen];
}

// Tests that users are shown the First Time Banner on the interstitial on their
// first navigation to a blocked page.
- (void)testSupervisedUserInterstitialDisplaysFirstTimeBanner {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface resetFirstTimeBanner];
  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];

  // On the first blocked site the interstitial displays the first time banner.
  GURL blockedURL = self.testServer->GetURL(kEchoPath);
  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShown];
  [ChromeEarlGrey waitForWebStateContainingText:kInterstitialFirstTimeBanner];
  [self checkElementDisplayStyleVisibility:@"banner" isVisible:YES];

  // Navigate to another blocked site. The banner should not be visible anymore.
  blockedURL = self.testServer->GetURL("other.host", kEchoPath);
  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShown];
  [self checkElementDisplayStyleVisibility:@"banner" isVisible:NO];
}

// Tests that the Zoom Text option is available for the interstitial.
- (void)testSupervisedUserInterstitialSupportsZoom {
  [self signInSupervisedUser];
  [SupervisedUserSettingsAppInterface setFilteringToAllowApprovedSites];

  GURL blockedURL = self.testServer->GetURL(kEchoPath);
  [ChromeEarlGrey loadURL:blockedURL];
  [self checkInterstitalIsShown];

  // Verify the Zoom Text button is available and clickable.
  [ChromeEarlGreyUI openToolsMenu];
  [[[EarlGrey
      selectElementWithMatcher:grey_allOf(
                                   grey_accessibilityID(kToolsMenuTextZoom),
                                   grey_sufficientlyVisible(), nil)]
         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
      onElementWithMatcher:chrome_test_util::ToolsMenuView()]
      assertWithMatcher:grey_not(grey_accessibilityTrait(
                            UIAccessibilityTraitNotEnabled))];
}

#pragma mark - Clear Content Behaviour

// Tests that a user in the legacy "syncing" state remains signed in after
// clearing the browsing data (Cookies and BrowsingHistory).
// TODO(crbug.com/40066949): Delete this test after the syncing state is gone.
- (void)testSupervisedUserWithLegacySyncStaysSignedInAfterClearingBrowsingData {
  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey addFakeIdentity:fakeIdentity
                 withCapabilities:@{
                   @(kIsSubjectToParentalControlsCapabilityName) : @YES,
                 }];
  [SigninEarlGrey signinAndEnableLegacySyncFeature:fakeIdentity];
  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];

  [self clearBrowsingData];

  // The user should be still signed in.
  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
}

// Tests that a signed in user remains signed in after clearing the browsing
// data (Cookies and BrowsingHistory).
- (void)testSupervisedUserStaysSignedInAfterClearingBrowsingData {
  [self signInSupervisedUser];
  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];

  [self clearBrowsingData];

  // The user should be still signed in.
  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
}

@end