chromium/ios/chrome/browser/web/model/http_auth_egtest.mm

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <memory>

#import "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "base/threading/platform_thread.h"
#import "base/time/time.h"
#import "components/strings/grit/components_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_matchers.h"
#import "ios/chrome/test/earl_grey/web_http_server_chrome_test_case.h"
#import "ios/chrome/test/scoped_eg_synchronization_disabler.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#import "ios/web/public/test/http_server/http_auth_response_provider.h"
#import "ios/web/public/test/http_server/http_server.h"
#import "ios/web/public/test/http_server/http_server_util.h"
#import "ui/base/l10n/l10n_util_mac.h"
#import "url/gurl.h"

using base::test::ios::WaitUntilConditionOrTimeout;
using base::test::ios::kWaitForPageLoadTimeout;

namespace {

// Returns matcher for HTTP Authentication dialog.
id<GREYMatcher> HttpAuthDialog() {
  NSString* title = l10n_util::GetNSStringWithFixup(IDS_LOGIN_DIALOG_TITLE);
  return chrome_test_util::StaticTextWithAccessibilityLabel(title);
}

// Returns matcher for Username text field.
id<GREYMatcher> UsernameField() {
  return grey_accessibilityValue(l10n_util::GetNSStringWithFixup(
      IDS_IOS_HTTP_LOGIN_DIALOG_USERNAME_PLACEHOLDER));
}

// Returns matcher for Password text field.
id<GREYMatcher> PasswordField() {
  return grey_accessibilityValue(l10n_util::GetNSStringWithFixup(
      IDS_IOS_HTTP_LOGIN_DIALOG_PASSWORD_PLACEHOLDER));
}

// Returns matcher for Login button.
id<GREYMatcher> LoginButton() {
  return chrome_test_util::ButtonWithAccessibilityLabelId(
      IDS_LOGIN_DIALOG_OK_BUTTON_LABEL);
}

// Waits until static text with IDS_LOGIN_DIALOG_TITLE label is displayed.
void WaitForHttpAuthDialog() {
  BOOL dialog_shown = WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{
    NSError* error = nil;
    [[EarlGrey selectElementWithMatcher:HttpAuthDialog()]
        assertWithMatcher:grey_notNil()
                    error:&error];
    return !error;
  });
  GREYAssert(dialog_shown, @"HTTP Authentication dialog was not shown");
}

}  // namespace

// Test case for HTTP Authentication flow.
@interface HTTPAuthTestCase : WebHttpServerChromeTestCase
@end

@implementation HTTPAuthTestCase

// Tests Basic HTTP Authentication with correct username and password.
- (void)testSuccessfullBasicAuth {
  if ([ChromeEarlGrey isIPadIdiom]) {
    // EG does not allow interactions with HTTP Dialog when loading spinner is
    // animated. TODO(crbug.com/41294580): Enable this test on iPad when
    // EarlGrey allows tapping dialog buttons with active page load spinner.
    EARL_GREY_TEST_DISABLED(@"Tab Title not displayed on handset.");
  }

  // EG synchronization disabled block.
  {
    // EG synchronizes with WKWebView. Disable synchronization for EG interation
    // during when page is loading.
    ScopedSynchronizationDisabler disabler;
    GURL URL = web::test::HttpServer::MakeUrl("http://good-auth");
    web::test::SetUpHttpServer(std::make_unique<web::HttpAuthResponseProvider>(
        URL, "GoodRealm", "gooduser", "goodpass"));
    [ChromeEarlGrey loadURL:URL waitForCompletion:NO];
    WaitForHttpAuthDialog();

    [[EarlGrey selectElementWithMatcher:UsernameField()]
        performAction:grey_tap()];

    // Wait for the keyboard to be shown.
    base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.5));

    // Enter valid username and password.
    [[EarlGrey selectElementWithMatcher:UsernameField()]
        performAction:grey_replaceText(@"gooduser")];
    [[EarlGrey selectElementWithMatcher:PasswordField()]
        performAction:grey_replaceText(@"goodpass")];
    [[EarlGrey selectElementWithMatcher:LoginButton()]
        performAction:grey_tap()];
  }  // EG synchronization disabled block.

  const std::string pageText = web::HttpAuthResponseProvider::page_text();
  [ChromeEarlGrey waitForWebStateContainingText:pageText];
}

// Tests Basic HTTP Authentication with incorrect username and password.
- (void)testUnsuccessfullBasicAuth {
  if ([ChromeEarlGrey isIPadIdiom]) {
    // EG does not allow interactions with HTTP Dialog when loading spinner is
    // animated. TODO(crbug.com/41294580): Enable this test on iPad when
    // EarlGrey allows tapping dialog buttons with active page load spinner.
    EARL_GREY_TEST_DISABLED(@"Tab Title not displayed on handset.");
  }

  // EG synchronization disabled block.
  {
    // EG synchronizes with WKWebView. Disable synchronization for EG interation
    // during when page is loading.
    ScopedSynchronizationDisabler disabler;
    GURL URL = web::test::HttpServer::MakeUrl("http://bad-auth");
    web::test::SetUpHttpServer(std::make_unique<web::HttpAuthResponseProvider>(
        URL, "BadRealm", "baduser", "badpass"));
    [ChromeEarlGrey loadURL:URL waitForCompletion:NO];
    WaitForHttpAuthDialog();

    // Enter invalid username and password.
    [[EarlGrey selectElementWithMatcher:UsernameField()]
        performAction:grey_replaceText(@"gooduser")];
    [[EarlGrey selectElementWithMatcher:PasswordField()]
        performAction:grey_replaceText(@"goodpass")];
    [[EarlGrey selectElementWithMatcher:LoginButton()]
        performAction:grey_tap()];

    // Ensure first dialog is dismissed before waiting for the second one.
    base::PlatformThread::Sleep(base::Seconds(1));
    // Verifies that authentication was requested again.
    WaitForHttpAuthDialog();
    [[EarlGrey selectElementWithMatcher:chrome_test_util::CancelButton()]
        performAction:grey_tap()];
  }  // EG synchronization disabled block.

  [[EarlGrey selectElementWithMatcher:HttpAuthDialog()]
      assertWithMatcher:grey_nil()];
}

// Tests Cancelling Basic HTTP Authentication.
- (void)testCancellingBasicAuth {
  if ([ChromeEarlGrey isIPadIdiom]) {
    // EG does not allow interactions with HTTP Dialog when loading spinner is
    // animated. TODO(crbug.com/41294580): Enable this test on iPad when
    // EarlGrey allows tapping dialog buttons with active page load spinner.
    EARL_GREY_TEST_DISABLED(@"Tab Title not displayed on handset.");
  }

  // EG synchronization disabled block.
  {
    // EG synchronizes with WKWebView. Disable synchronization for EG interation
    // during when page is loading.
    ScopedSynchronizationDisabler disabler;
    GURL URL = web::test::HttpServer::MakeUrl("http://cancel-auth");
    web::test::SetUpHttpServer(std::make_unique<web::HttpAuthResponseProvider>(
        URL, "CancellingRealm", "", ""));
    [ChromeEarlGrey loadURL:URL waitForCompletion:NO];
    WaitForHttpAuthDialog();

    id<GREYMatcher> cancelButtonMatcher = grey_allOf(
        chrome_test_util::CancelButton(),
        grey_not(grey_accessibilityTrait(UIAccessibilityTraitNotEnabled)), nil);
    // Wait for element to become enabled because auth dialog buttons are
    // initially disabled. See crbug.com/341353783
    [ChromeEarlGrey waitForUIElementToAppearWithMatcher:cancelButtonMatcher];

    [[EarlGrey selectElementWithMatcher:cancelButtonMatcher]
        performAction:grey_tap()];

  }  // EG synchronization disabled block.

  [[EarlGrey selectElementWithMatcher:HttpAuthDialog()]
      assertWithMatcher:grey_nil()];
}

@end