blob: f5be4bc338f567df5290b21a39a1e48054b7b106 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <EarlGrey/EarlGrey.h>
#import <XCTest/XCTest.h>
#import "base/ios/block_types.h"
#include "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#include "base/test/scoped_feature_list.h"
#include "components/unified_consent/feature.h"
#import "ios/chrome/app/main_controller.h"
#include "ios/chrome/browser/system_flags.h"
#import "ios/chrome/browser/tabs/tab_model.h"
#import "ios/chrome/browser/ui/authentication/cells/signin_promo_view.h"
#import "ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
#import "ios/chrome/browser/ui/commands/application_commands.h"
#import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
#import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_constants.h"
#import "ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest_util.h"
#include "ios/chrome/browser/ui/util/ui_util.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/app/chrome_test_util.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/scoped_eg_synchronization_disabler.h"
#import "ios/public/provider/chrome/browser/signin/fake_chrome_identity.h"
#import "ios/public/provider/chrome/browser/signin/fake_chrome_identity_service.h"
#import "ios/web/public/web_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using chrome_test_util::BookmarksNavigationBarDoneButton;
using chrome_test_util::PrimarySignInButton;
using chrome_test_util::SecondarySignInButton;
using chrome_test_util::SettingsDoneButton;
using chrome_test_util::SyncSettingsConfirmButton;
typedef NS_ENUM(NSInteger, OpenSigninMethod) {
OpenSigninMethodFromSettings,
OpenSigninMethodFromBookmarks,
OpenSigninMethodFromRecentTabs,
OpenSigninMethodFromTabSwitcher,
};
namespace {
// Taps on the primary sign-in button in recent tabs, and scroll first, if
// necessary.
void TapOnPrimarySignInButtonInRecentTabs() {
id<GREYMatcher> matcher =
grey_allOf(PrimarySignInButton(), grey_sufficientlyVisible(), nil);
const CGFloat kPixelsToScroll = 300;
id<GREYAction> searchAction =
grey_scrollInDirection(kGREYDirectionDown, kPixelsToScroll);
GREYElementInteraction* interaction =
[[EarlGrey selectElementWithMatcher:matcher]
usingSearchAction:searchAction
onElementWithMatcher:
grey_accessibilityID(
kRecentTabsTableViewControllerAccessibilityIdentifier)];
[interaction performAction:grey_tap()];
}
// Removes all browsing data.
void RemoveBrowsingData() {
__block BOOL browsing_data_removed = NO;
[chrome_test_util::GetMainController()
removeBrowsingDataForBrowserState:chrome_test_util::
GetOriginalBrowserState()
timePeriod:browsing_data::TimePeriod::ALL_TIME
removeMask:BrowsingDataRemoveMask::REMOVE_ALL
completionBlock:^{
browsing_data_removed = YES;
}];
GREYCondition* condition =
[GREYCondition conditionWithName:@"Wait for removing browsing data."
block:^BOOL {
return browsing_data_removed;
}];
GREYAssert(
[condition
waitWithTimeout:base::test::ios::kWaitForClearBrowsingDataTimeout],
@"Browsing data was not removed.");
}
} // namespace
// Sign-in interaction tests that work both with Unified Consent enabled or
// disabled.
@interface SigninInteractionControllerTestCase : ChromeTestCase
@end
@implementation SigninInteractionControllerTestCase
- (void)setUp {
[super setUp];
// Remove closed tab history to make sure the sign-in promo is always visible
// in recent tabs.
RemoveBrowsingData();
}
// Tests that opening the sign-in screen from the Settings and signing in works
// correctly when there is already an identity on the device.
- (void)testSignInOneUser {
// Set up a fake identity.
ChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
identity);
[SigninEarlGreyUI signinWithIdentity:identity];
// Check |identity| is signed-in.
[SigninEarlGreyUtils checkSignedInWithIdentity:identity];
}
// Tests that signing out from the Settings works correctly.
- (void)testSignInDisconnectFromChrome {
ChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
identity);
// Sign in to |identity|.
[SigninEarlGreyUI signinWithIdentity:identity];
// Sign out.
[SigninEarlGreyUI signOutWithManagedAccount:NO];
}
// Tests that signing out of a managed account from the Settings works
// correctly.
- (void)testSignInDisconnectFromChromeManaged {
ChromeIdentity* identity = [SigninEarlGreyUtils fakeManagedIdentity];
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
identity);
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
[SigninEarlGreyUI selectIdentityWithEmail:identity.userEmail];
[SigninEarlGreyUI confirmSigninConfirmationDialog];
{
// Synchronization off due to an infinite spinner.
ScopedSynchronizationDisabler disabler;
WaitForMatcher(chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_MANAGED_SIGNIN_ACCEPT_BUTTON));
TapButtonWithLabelId(IDS_IOS_MANAGED_SIGNIN_ACCEPT_BUTTON);
}
[SigninEarlGreyUtils checkSignedInWithIdentity:identity];
[[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
performAction:grey_tap()];
// Sign out.
[SigninEarlGreyUI signOutWithManagedAccount:YES];
// Check that there is no signed in user.
[SigninEarlGreyUtils checkSignedOut];
}
// Tests that signing in, tapping the Settings link on the confirmation screen
// and closing the Settings correctly leaves the user signed in without any
// Settings shown.
- (void)testSignInOpenSettings {
ChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
identity);
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
[SigninEarlGreyUI selectIdentityWithEmail:identity.userEmail];
// Wait until the next screen appears.
[SigninEarlGreyUI tapSettingsLink];
if (unified_consent::IsUnifiedConsentFeatureEnabled()) {
[[EarlGrey selectElementWithMatcher:SyncSettingsConfirmButton()]
performAction:grey_tap()];
} else {
[[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
performAction:grey_tap()];
}
// All Settings should be gone and user signed in.
id<GREYMatcher> settings_matcher =
chrome_test_util::StaticTextWithAccessibilityLabelId(
IDS_IOS_SETTINGS_TITLE);
[[EarlGrey selectElementWithMatcher:settings_matcher]
assertWithMatcher:grey_notVisible()];
[SigninEarlGreyUtils checkSignedInWithIdentity:identity];
}
// Opens the sign in screen and then cancel it by opening a new tab. Ensures
// that the sign in screen is correctly dismissed. crbug.com/462200
- (void)testSignInCancelIdentityPicker {
// Add an identity to avoid arriving on the Add Account screen when opening
// sign-in.
ChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
identity);
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
// Open new tab to cancel sign-in.
OpenNewTabCommand* command =
[OpenNewTabCommand commandWithURLFromChrome:GURL("about:blank")];
[chrome_test_util::DispatcherForActiveBrowserViewController()
openURLInNewTab:command];
// Wait until the page is opened.
[[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
// Re-open the sign-in screen. If it wasn't correctly dismissed previously,
// this will fail.
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
if (unified_consent::IsUnifiedConsentFeatureEnabled())
[SigninEarlGreyUI selectIdentityWithEmail:identity.userEmail];
VerifyChromeSigninViewVisible();
// Close sign-in screen and Settings.
TapButtonWithLabelId(IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_SKIP_BUTTON);
[[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
performAction:grey_tap()];
}
// Starts an authentication flow and cancel it by opening a new tab. Ensures
// that the authentication flow is correctly canceled and dismissed.
// crbug.com/462202
- (void)testSignInCancelAuthenticationFlow {
// Set up the fake identities.
ios::FakeChromeIdentityService* identity_service =
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider();
ChromeIdentity* identity1 = [SigninEarlGreyUtils fakeIdentity1];
ChromeIdentity* identity2 = [SigninEarlGreyUtils fakeIdentity2];
identity_service->AddIdentity(identity1);
identity_service->AddIdentity(identity2);
// This signs in |identity2| first, ensuring that the "Clear Data Before
// Syncing" dialog is shown during the second sign-in. This dialog will
// effectively block the authentication flow, ensuring that the authentication
// flow is always still running when the sign-in is being cancelled.
[SigninEarlGreyUI signinWithIdentity:identity2];
// Sign out.
[SigninEarlGreyUI signOutWithManagedAccount:NO];
// Sign in with |identity1|.
[ChromeEarlGreyUI openSettingsMenu];
[[EarlGrey selectElementWithMatcher:SecondarySignInButton()]
performAction:grey_tap()];
[SigninEarlGreyUI selectIdentityWithEmail:identity1.userEmail];
if (unified_consent::IsUnifiedConsentFeatureEnabled()) {
// With unified consent, the authentication flow is only created when the
// confirm button is selected. Note that authentication flow actually
/// blocks as the "Clear Browsing Before Syncing" dialog is presented.
[SigninEarlGreyUI confirmSigninConfirmationDialog];
}
// Open new tab to cancel sign-in.
OpenNewTabCommand* command =
[OpenNewTabCommand commandWithURLFromChrome:GURL("about:blank")];
[chrome_test_util::DispatcherForActiveBrowserViewController()
openURLInNewTab:command];
// Wait until the page is opened.
[[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
// Re-open the sign-in screen. If it wasn't correctly dismissed previously,
// this will fail.
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
if (unified_consent::IsUnifiedConsentFeatureEnabled())
[SigninEarlGreyUI selectIdentityWithEmail:identity1.userEmail];
VerifyChromeSigninViewVisible();
// Close sign-in screen and Settings.
TapButtonWithLabelId(IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_SKIP_BUTTON);
[[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
performAction:grey_tap()];
[SigninEarlGreyUtils checkSignedOut];
}
// Opens the sign in screen from the bookmarks and then cancel it by tapping on
// done. Ensures that the sign in screen is correctly dismissed.
// Regression test for crbug.com/596029.
- (void)testSignInCancelFromBookmarks {
ChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
identity);
// Open Bookmarks and tap on Sign In promo button.
[ChromeEarlGreyUI openToolsMenu];
[ChromeEarlGreyUI tapToolsMenuButton:chrome_test_util::BookmarksMenuButton()];
[ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
// Assert sign-in screen was shown.
if (unified_consent::IsUnifiedConsentFeatureEnabled())
[SigninEarlGreyUI selectIdentityWithEmail:identity.userEmail];
VerifyChromeSigninViewVisible();
// Open new tab to cancel sign-in.
OpenNewTabCommand* command =
[OpenNewTabCommand commandWithURLFromChrome:GURL("about:blank")];
[chrome_test_util::DispatcherForActiveBrowserViewController()
openURLInNewTab:command];
// Wait until the page is opened.
[[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
// Re-open the sign-in screen. If it wasn't correctly dismissed previously,
// this will fail.
[ChromeEarlGreyUI openToolsMenu];
[ChromeEarlGreyUI tapToolsMenuButton:chrome_test_util::BookmarksMenuButton()];
[ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
if (unified_consent::IsUnifiedConsentFeatureEnabled())
[SigninEarlGreyUI selectIdentityWithEmail:identity.userEmail];
VerifyChromeSigninViewVisible();
// Close sign-in screen and Bookmarks.
TapButtonWithLabelId(IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_SKIP_BUTTON);
[[EarlGrey selectElementWithMatcher:BookmarksNavigationBarDoneButton()]
performAction:grey_tap()];
}
#pragma mark - Dismiss tests
// Tests to dismiss sign-in by opening an URL from another app.
// Sign-in opened from: setting menu.
// Interrupted at: user consent.
- (void)testDismissSigninFromSettings {
[self assertOpenURLWhenSigninFromView:OpenSigninMethodFromSettings
tapSettingsLink:NO];
}
// Tests to dismiss sign-in by opening an URL from another app.
// Sign-in opened from: setting menu.
// Interrupted at: advanced sign-in.
- (void)testDismissAdvancedSigninSettingsFromAdvancedSigninSettings {
[self assertOpenURLWhenSigninFromView:OpenSigninMethodFromSettings
tapSettingsLink:YES];
}
// Tests to dismiss sign-in by opening an URL from another app.
// Sign-in opened from: bookmark view.
// Interrupted at: user consent.
- (void)testDismissSigninFromBookmarks {
[self assertOpenURLWhenSigninFromView:OpenSigninMethodFromBookmarks
tapSettingsLink:NO];
}
// Tests to dismiss sign-in by opening an URL from another app.
// Sign-in opened from: bookmark view.
// Interrupted at: advanced sign-in.
- (void)testDismissAdvancedSigninBookmarksFromAdvancedSigninSettings {
[self assertOpenURLWhenSigninFromView:OpenSigninMethodFromBookmarks
tapSettingsLink:YES];
}
// Tests to dismiss sign-in by opening an URL from another app.
// Sign-in opened from: recent tabs.
// Interrupted at: user consent.
- (void)testDismissSigninFromRecentTabs {
[self assertOpenURLWhenSigninFromView:OpenSigninMethodFromRecentTabs
tapSettingsLink:NO];
}
// Tests to dismiss sign-in by opening an URL from another app.
// Sign-in opened from: recent tabs.
// Interrupted at: advanced sign-in.
- (void)testDismissSigninFromRecentTabsFromAdvancedSigninSettings {
[self assertOpenURLWhenSigninFromView:OpenSigninMethodFromRecentTabs
tapSettingsLink:YES];
}
// Tests to dismiss sign-in by opening an URL from another app.
// Sign-in opened from: tab switcher.
// Interrupted at: user consent.
- (void)testDismissSigninFromTabSwitcher {
[self assertOpenURLWhenSigninFromView:OpenSigninMethodFromTabSwitcher
tapSettingsLink:NO];
}
// Tests to dismiss sign-in by opening an URL from another app.
// Sign-in opened from: tab switcher.
// Interrupted at: advanced sign-in.
- (void)testDismissSigninFromTabSwitcherFromAdvancedSigninSettings {
[self assertOpenURLWhenSigninFromView:OpenSigninMethodFromTabSwitcher
tapSettingsLink:YES];
}
#pragma mark - Utils
// Starts the sign-in workflow, and simulates opening an URL from another app.
// |openSigninMethod| is the way to start the sign-in.
// |tapSettingsLink| if YES, the setting link is tapped before opening the URL.
- (void)assertOpenURLWhenSigninFromView:(OpenSigninMethod)openSigninMethod
tapSettingsLink:(BOOL)tapSettingsLink {
ChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
identity);
switch (openSigninMethod) {
case OpenSigninMethodFromSettings:
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI tapSettingsMenuButton:PrimarySignInButton()];
break;
case OpenSigninMethodFromBookmarks:
[ChromeEarlGreyUI openToolsMenu];
[ChromeEarlGreyUI
tapToolsMenuButton:chrome_test_util::BookmarksMenuButton()];
[[EarlGrey selectElementWithMatcher:PrimarySignInButton()]
performAction:grey_tap()];
break;
case OpenSigninMethodFromRecentTabs:
[ChromeEarlGreyUI openToolsMenu];
[ChromeEarlGreyUI
tapToolsMenuButton:chrome_test_util::RecentTabsMenuButton()];
TapOnPrimarySignInButtonInRecentTabs();
break;
case OpenSigninMethodFromTabSwitcher:
[[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridOpenButton()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::
TabGridOtherDevicesPanelButton()]
performAction:grey_tap()];
TapOnPrimarySignInButtonInRecentTabs();
break;
}
if (tapSettingsLink) {
[[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
[SigninEarlGreyUI tapSettingsLink];
}
[[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
// Open the URL as if it was opened from another app.
UIApplication* application = UIApplication.sharedApplication;
id<UIApplicationDelegate> applicationDelegate = application.delegate;
NSURL* url = [NSURL URLWithString:@"http://www.example.com/"];
[applicationDelegate application:application openURL:url options:@{}];
[[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
// Check if the URL was opened.
web::WebState* webState = chrome_test_util::GetMainController()
.interfaceProvider.mainInterface.tabModel
.webStateList->GetActiveWebState();
GURL expectedString(url.absoluteString.UTF8String);
GREYAssertEqual(expectedString, webState->GetVisibleURL(), @"url not loaded");
if (tapSettingsLink) {
// Should be signed in.
[SigninEarlGreyUtils checkSignedInWithIdentity:identity];
} else {
// Should be not signed in.
[SigninEarlGreyUtils checkSignedOut];
}
}
@end