// Copyright 2018 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/chrome/browser/ui/settings/password/password_manager_view_controller.h"
#import "base/apple/foundation_util.h"
#import "base/functional/bind.h"
#import "base/location.h"
#import "base/strings/sys_string_conversions.h"
#import "base/strings/utf_string_conversions.h"
#import "base/test/bind.h"
#import "base/test/ios/wait_util.h"
#import "base/test/metrics/histogram_tester.h"
#import "components/affiliations/core/browser/fake_affiliation_service.h"
#import "components/feature_engagement/public/feature_constants.h"
#import "components/keyed_service/core/service_access_type.h"
#import "components/password_manager/core/browser/leak_detection/mock_bulk_leak_check_service.h"
#import "components/password_manager/core/browser/password_form.h"
#import "components/password_manager/core/browser/password_manager_test_utils.h"
#import "components/password_manager/core/browser/password_store/test_password_store.h"
#import "ios/chrome/browser/affiliations/model/ios_chrome_affiliation_service_factory.h"
#import "ios/chrome/browser/favicon/model/favicon_loader.h"
#import "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_bulk_leak_check_service_factory.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_password_check_manager.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.h"
#import "ios/chrome/browser/passwords/model/password_check_observer_bridge.h"
#import "ios/chrome/browser/passwords/model/save_passwords_consumer.h"
#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_text_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_item.h"
#import "ios/chrome/browser/shared/ui/table_view/legacy_chrome_table_view_controller_test.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/signin/model/chrome_account_manager_service_factory.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"
#import "ios/chrome/browser/ui/settings/cells/inline_promo_cell.h"
#import "ios/chrome/browser/ui/settings/cells/inline_promo_item.h"
#import "ios/chrome/browser/ui/settings/cells/settings_check_item.h"
#import "ios/chrome/browser/ui/settings/password/password_manager_view_controller+Testing.h"
#import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
#import "ios/chrome/browser/ui/settings/password/passwords_mediator.h"
#import "ios/chrome/browser/ui/settings/password/passwords_settings_commands.h"
#import "ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/app/sync_test_util.h"
#import "ios/chrome/test/scoped_key_window.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/gmock/include/gmock/gmock.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/gtest_support.h"
#import "ui/base/l10n/l10n_util.h"
#import "ui/base/l10n/l10n_util_mac.h"
using password_manager::InsecureType;
using password_manager::MockBulkLeakCheckService;
using password_manager::PasswordForm;
using password_manager::TestPasswordStore;
using ::testing::Return;
// TODO(crbug.com/40839348): Remove this double and uses TestSyncUserSettings
@interface TestPasswordsMediator : PasswordsMediator
@property(nonatomic) OnDeviceEncryptionState encryptionState;
@end
@implementation TestPasswordsMediator
- (OnDeviceEncryptionState)onDeviceEncryptionState {
return self.encryptionState;
}
@end
namespace {
// Use this test suite for tests that verify behaviors of
// PasswordManagerViewController before loading the passwords for the first time
// has finished. All other tests should go in PasswordManagerViewControllerTest.
class PasswordManagerViewControllerTest
: public LegacyChromeTableViewControllerTest {
protected:
PasswordManagerViewControllerTest() = default;
void SetUp() override {
LegacyChromeTableViewControllerTest::SetUp();
TestChromeBrowserState::Builder builder;
builder.AddTestingFactory(
IOSChromeProfilePasswordStoreFactory::GetInstance(),
base::BindRepeating(
&password_manager::BuildPasswordStore<web::BrowserState,
TestPasswordStore>));
builder.AddTestingFactory(
IOSChromeBulkLeakCheckServiceFactory::GetInstance(),
base::BindRepeating(base::BindLambdaForTesting([](web::BrowserState*) {
return std::unique_ptr<KeyedService>(
std::make_unique<MockBulkLeakCheckService>());
})));
builder.AddTestingFactory(
IOSChromeAffiliationServiceFactory::GetInstance(),
base::BindRepeating(base::BindLambdaForTesting([](web::BrowserState*) {
return std::unique_ptr<KeyedService>(
std::make_unique<affiliations::FakeAffiliationService>());
})));
browser_state_ = std::move(builder).Build();
browser_ = std::make_unique<TestBrowser>(browser_state_.get());
CreateController();
ChromeBrowserState* browserState = browser_->GetBrowserState();
mediator_ = [[TestPasswordsMediator alloc]
initWithPasswordCheckManager:IOSChromePasswordCheckManagerFactory::
GetForBrowserState(browserState)
faviconLoader:IOSChromeFaviconLoaderFactory::
GetForBrowserState(browserState)
syncService:SyncServiceFactory::GetForBrowserState(
browserState)
prefService:browserState->GetPrefs()];
mediator_.encryptionState = OnDeviceEncryptionStateNotShown;
// Inject some fake passwords to pass the loading state.
PasswordManagerViewController* passwords_controller =
GetPasswordManagerViewController();
passwords_controller.delegate = mediator_;
mediator_.consumer = passwords_controller;
passwords_settings_commands_strict_mock_ =
OCMStrictProtocolMock(@protocol(PasswordsSettingsCommands));
passwords_controller.handler = passwords_settings_commands_strict_mock_;
// Show the Password Manager widget promo.
passwords_controller.shouldShowPasswordManagerWidgetPromo = YES;
WaitForPasswordsLoadingCompletion();
}
int GetSectionIndex(PasswordSectionIdentifier section) {
return [GetPasswordManagerViewController().tableViewModel
sectionForSectionIdentifier:section];
}
TestPasswordStore& GetTestStore() {
return *static_cast<TestPasswordStore*>(
IOSChromeProfilePasswordStoreFactory::GetForBrowserState(
browser_->GetBrowserState(), ServiceAccessType::EXPLICIT_ACCESS)
.get());
}
MockBulkLeakCheckService& GetMockPasswordCheckService() {
return *static_cast<MockBulkLeakCheckService*>(
IOSChromeBulkLeakCheckServiceFactory::GetForBrowserState(
browser_->GetBrowserState()));
}
LegacyChromeTableViewController* InstantiateController() override {
ChromeAccountManagerService* account_manager_service =
ChromeAccountManagerServiceFactory::GetForBrowserState(
browser_state_.get());
return [[PasswordManagerViewController alloc]
initWithChromeAccountManagerService:account_manager_service
prefService:browser_state_.get()->GetPrefs()
shouldOpenInSearchMode:NO];
}
PasswordManagerViewController* GetPasswordManagerViewController() {
return static_cast<PasswordManagerViewController*>(controller());
}
PasswordManagerViewController* CreateControllerForPasswordSearch() {
ChromeAccountManagerService* account_manager_service =
ChromeAccountManagerServiceFactory::GetForBrowserState(
browser_state_.get());
PasswordManagerViewController* passwords_controller =
[[PasswordManagerViewController alloc]
initWithChromeAccountManagerService:account_manager_service
prefService:browser_state_.get()->GetPrefs()
shouldOpenInSearchMode:YES];
passwords_controller.delegate = mediator_;
mediator_.consumer = passwords_controller;
passwords_controller.handler = passwords_settings_commands_strict_mock_;
passwords_controller.shouldShowPasswordManagerWidgetPromo = YES;
// Wait for passwords loading completion.
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForUIElementTimeout, ^bool {
return [passwords_controller didReceivePasswords];
}));
return passwords_controller;
}
void ChangePasswordCheckState(PasswordCheckUIState state) {
PasswordManagerViewController* passwords_controller =
GetPasswordManagerViewController();
NSInteger insecure_count = 0;
for (const auto& signon_realm_forms : GetTestStore().stored_passwords()) {
insecure_count += base::ranges::count_if(
signon_realm_forms.second, [](const PasswordForm& form) {
return !form.password_issues.empty();
});
}
[passwords_controller setPasswordCheckUIState:state
insecurePasswordsCount:insecure_count];
}
// Adds a form to PasswordManagerViewController.
void AddPasswordForm(std::unique_ptr<password_manager::PasswordForm> form) {
form->in_store = password_manager::PasswordForm::Store::kProfileStore;
GetTestStore().AddLogin(*form);
RunUntilIdle();
}
// Creates a form.
std::unique_ptr<password_manager::PasswordForm> CreateForm(
std::u16string username_value) {
auto form = std::make_unique<password_manager::PasswordForm>();
form->url = GURL("http://www.example.com/accounts/LoginAuth");
form->action = GURL("http://www.example.com/accounts/Login");
form->username_element = u"Email";
form->username_value = username_value;
form->password_element = u"Passwd";
form->password_value = u"test";
form->submit_element = u"signIn";
form->signon_realm = "http://www.example.com/";
form->scheme = password_manager::PasswordForm::Scheme::kHtml;
form->blocked_by_user = false;
return form;
}
// Created and adds a saved password form.
void AddSavedForm1(std::u16string username_value = u"[email protected]") {
auto form = CreateForm(username_value);
AddPasswordForm(std::move(form));
}
// Creates and adds a saved password form.
void AddSavedForm2() {
auto form = std::make_unique<password_manager::PasswordForm>();
form->url = GURL("http://www.example2.com/accounts/LoginAuth");
form->action = GURL("http://www.example2.com/accounts/Login");
form->username_element = u"Email";
form->username_value = u"[email protected]";
form->password_element = u"Passwd";
form->password_value = u"test";
form->submit_element = u"signIn";
form->signon_realm = "http://www.example2.com/";
form->scheme = password_manager::PasswordForm::Scheme::kHtml;
form->blocked_by_user = false;
AddPasswordForm(std::move(form));
}
// Creates and adds a blocked site form to never offer to save
// user's password to those sites.
void AddBlockedForm1() {
auto form = std::make_unique<password_manager::PasswordForm>();
form->url = GURL("http://www.secret.com/login");
form->signon_realm = "http://www.secret.com/";
form->scheme = password_manager::PasswordForm::Scheme::kHtml;
form->blocked_by_user = true;
AddPasswordForm(std::move(form));
}
// Creates and adds another blocked site form to never offer to save
// user's password to those sites.
void AddBlockedForm2() {
auto form = std::make_unique<password_manager::PasswordForm>();
form->url = GURL("http://www.secret2.com/login");
form->signon_realm = "http://www.secret2.com/";
form->scheme = password_manager::PasswordForm::Scheme::kHtml;
form->blocked_by_user = true;
AddPasswordForm(std::move(form));
}
// Creates and adds a saved insecure password form.
void AddSavedInsecureForm(
InsecureType insecure_type,
bool is_muted = false,
std::u16string username_value = u"[email protected]") {
auto form = CreateForm(username_value);
form->password_issues = {
{insecure_type,
password_manager::InsecurityMetadata(
base::Time::Now(), password_manager::IsMuted(is_muted),
password_manager::TriggerBackendNotification(false))}};
AddPasswordForm(std::move(form));
}
// Deletes the item at (row, section) and wait util idle.
void deleteItemAndWait(int section, int row) {
[GetPasswordManagerViewController()
deleteItems:@[ [NSIndexPath indexPathForRow:row inSection:section] ]];
RunUntilIdle();
}
void CheckDetailItemTextWithPluralIds(int expected_text_id,
int expected_detail_text_id,
int count,
int section,
int item) {
SettingsCheckItem* cell =
static_cast<SettingsCheckItem*>(GetTableViewItem(section, item));
EXPECT_NSEQ(l10n_util::GetNSString(expected_text_id), [cell text]);
EXPECT_NSEQ(base::SysUTF16ToNSString(l10n_util::GetPluralStringFUTF16(
expected_detail_text_id, count)),
[cell detailText]);
}
void CheckDetailItemTextWithPlaceholder(int expected_text_id,
int expected_detail_text_id,
std::u16string placeholder,
int section,
int item) {
SettingsCheckItem* cell =
static_cast<SettingsCheckItem*>(GetTableViewItem(section, item));
EXPECT_NSEQ(l10n_util::GetNSString(expected_text_id), [cell text]);
EXPECT_NSEQ(l10n_util::GetNSStringF(expected_detail_text_id, placeholder),
[cell detailText]);
}
// Enables/Disables the edit mode based on `editing`.
void SetEditing(bool editing) {
[GetPasswordManagerViewController() setEditing:editing animated:NO];
}
// Blocks the test until passwords have been set for the first time and the
// loading spinner was removed from the View Controller.
void WaitForPasswordsLoadingCompletion() {
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForUIElementTimeout, ^bool {
return [GetPasswordManagerViewController() didReceivePasswords];
}));
}
// Selects a cell in the table view.
void SelectCell(NSInteger item, NSInteger sectionIndex) {
PasswordManagerViewController* passwords_controller =
GetPasswordManagerViewController();
[passwords_controller
tableView:passwords_controller.tableView
didSelectRowAtIndexPath:[NSIndexPath indexPathForItem:item
inSection:sectionIndex]];
}
void RunUntilIdle() { task_environment_.RunUntilIdle(); }
web::WebTaskEnvironment task_environment_;
std::unique_ptr<TestChromeBrowserState> browser_state_;
std::unique_ptr<TestBrowser> browser_;
TestPasswordsMediator* mediator_;
ScopedKeyWindow scoped_window_;
UIViewController* root_view_controller_ = nil;
id passwords_settings_commands_strict_mock_;
};
// Tests default case has no saved sites and no blocked sites.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_TestInitialization) {
CheckController();
EXPECT_EQ(0, NumberOfSections()); // Empty state.
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Tests adding one item in saved password section.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_AddSavedPasswords) {
AddSavedForm1();
EXPECT_EQ(5, NumberOfSections());
EXPECT_EQ(1, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Tests adding one item in blocked password section.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_AddBlockedPasswords) {
AddBlockedForm1();
EXPECT_EQ(5, NumberOfSections());
EXPECT_EQ(1,
NumberOfItemsInSection(GetSectionIndex(SectionIdentifierBlocked)));
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Tests adding one item in saved password section, and two items in blocked
// password section.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_AddSavedAndBlocked) {
AddSavedForm1();
AddBlockedForm1();
AddBlockedForm2();
// There should be two sections added.
EXPECT_EQ(6, NumberOfSections());
// There should be 1 row in saved password section.
EXPECT_EQ(1, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
// There should be 2 rows in blocked password section.
EXPECT_EQ(2,
NumberOfItemsInSection(GetSectionIndex(SectionIdentifierBlocked)));
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Tests the order in which the saved passwords are displayed.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_TestSavedPasswordsOrder) {
AddSavedForm2();
CheckURLCellTitleAndDetailText(
@"example2.com", @"", GetSectionIndex(SectionIdentifierSavedPasswords),
0);
AddSavedForm1();
CheckURLCellTitleAndDetailText(
@"example.com", @"", GetSectionIndex(SectionIdentifierSavedPasswords), 0);
CheckURLCellTitleAndDetailText(
@"example2.com", @"", GetSectionIndex(SectionIdentifierSavedPasswords),
1);
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Tests the order in which the blocked passwords are displayed.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_TestBlockedPasswordsOrder) {
AddBlockedForm2();
CheckURLCellTitle(@"secret2.com", GetSectionIndex(SectionIdentifierBlocked),
/* item= */ 0);
AddBlockedForm1();
CheckURLCellTitle(@"secret.com", GetSectionIndex(SectionIdentifierBlocked),
/* item= */ 0);
CheckURLCellTitle(@"secret2.com", GetSectionIndex(SectionIdentifierBlocked),
/* item= */ 1);
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Tests displaying passwords in the saved passwords section when there are
// duplicates in the password store.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_AddSavedDuplicates) {
AddSavedForm1();
AddSavedForm1();
EXPECT_EQ(5, NumberOfSections());
EXPECT_EQ(1, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Tests displaying passwords in the blocked passwords section when there
// are duplicates in the password store.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_AddBlockedDuplicates) {
AddBlockedForm1();
AddBlockedForm1();
EXPECT_EQ(5, NumberOfSections());
EXPECT_EQ(1,
NumberOfItemsInSection(GetSectionIndex(SectionIdentifierBlocked)));
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Tests deleting items from saved passwords (as affiliated groups) and blocked
// passwords sections.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest,
DISABLED_DeleteAffiliatedGroupsAndBlockedPasswords) {
AddSavedForm1();
AddSavedForm1(u"[email protected]");
AddSavedForm2();
AddBlockedForm1();
AddBlockedForm2();
// 2 affiliated groups in the Saved Passwords section and 2 blocked passwords
// in the Blocked section.
EXPECT_EQ(2, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
EXPECT_EQ(2,
NumberOfItemsInSection(GetSectionIndex(SectionIdentifierBlocked)));
// Expect password delete dialog and delete first affiliated group in Saved
// Passwords section.
OCMExpect([passwords_settings_commands_strict_mock_
showPasswordDeleteDialogWithOrigins:@[ @"example.com" ]
completion:[OCMArg isNotNil]])
.andDo(^(NSInvocation* invocation) {
[GetPasswordManagerViewController() deleteItemAtIndexPathsForTesting:@[
[NSIndexPath
indexPathForRow:0
inSection:GetSectionIndex(SectionIdentifierSavedPasswords)]
]];
});
deleteItemAndWait(GetSectionIndex(SectionIdentifierSavedPasswords), 0);
EXPECT_OCMOCK_VERIFY(passwords_settings_commands_strict_mock_);
// Should only have 1 affiliated group left in this section.
EXPECT_EQ(1, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
// Blocked section unaffected.
EXPECT_EQ(2,
NumberOfItemsInSection(GetSectionIndex(SectionIdentifierBlocked)));
// Delete item in blocked passwords section.
deleteItemAndWait(GetSectionIndex(SectionIdentifierBlocked), 0);
EXPECT_EQ(1,
NumberOfItemsInSection(GetSectionIndex(SectionIdentifierBlocked)));
// Saved Passwords section unaffected.
EXPECT_EQ(1, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
// There should be no password sections remaining and no search bar.
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Tests that the password manager is updated when passwords change while in
// search mode.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest,
DISABLED_TestChangePasswordsWhileSearching) {
root_view_controller_ = [[UIViewController alloc] init];
scoped_window_.Get().rootViewController = root_view_controller_;
PasswordManagerViewController* passwords_controller =
GetPasswordManagerViewController();
// Add a saved password so the empty state isn't shown.
AddSavedForm1();
// Present the view controller.
__block bool presentation_finished = NO;
UINavigationController* navigation_controller =
[[UINavigationController alloc]
initWithRootViewController:passwords_controller];
[root_view_controller_ presentViewController:navigation_controller
animated:NO
completion:^{
presentation_finished = YES;
}];
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForUIElementTimeout, ^bool {
return presentation_finished;
}));
EXPECT_EQ(5, NumberOfSections());
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierAddPasswordButton]);
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierPasswordCheck]);
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierSavedPasswords]);
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierManageAccountHeader]);
passwords_controller.navigationItem.searchController.active = YES;
// Add a password update.
AddSavedForm2();
EXPECT_EQ(1, NumberOfSections());
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierSavedPasswords]);
EXPECT_EQ(2, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
passwords_controller.navigationItem.searchController.active = NO;
// Sections are restored after search is over.
EXPECT_EQ(5, NumberOfSections());
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierAddPasswordButton]);
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierPasswordCheck]);
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierSavedPasswords]);
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierManageAccountHeader]);
// Dismiss the view controller and wait for the dismissal to finish.
__block bool dismissal_finished = NO;
[passwords_controller settingsWillBeDismissed];
[root_view_controller_ dismissViewControllerAnimated:NO
completion:^{
dismissal_finished = YES;
}];
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForUIElementTimeout, ^bool {
return dismissal_finished;
}));
}
// Tests that dismissing the Search Controller multiple times without presenting
// it again doesn't cause a crash.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest,
DISABLED_TestDismissingSearchControllerMultipleTimesDoesntCrash) {
root_view_controller_ = [[UIViewController alloc] init];
scoped_window_.Get().rootViewController = root_view_controller_;
PasswordManagerViewController* passwords_controller =
GetPasswordManagerViewController();
// Present the view controller.
__block bool presentation_finished = NO;
UINavigationController* navigation_controller =
[[UINavigationController alloc]
initWithRootViewController:passwords_controller];
[root_view_controller_ presentViewController:navigation_controller
animated:NO
completion:^{
presentation_finished = YES;
}];
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForUIElementTimeout, ^bool {
return presentation_finished;
}));
// Present and dismiss the search controller twice to validate that the
// PasswordController doesn't try to update itself after the second dismissal
// which would cause a crash.
passwords_controller.navigationItem.searchController.active = YES;
passwords_controller.navigationItem.searchController.active = NO;
passwords_controller.navigationItem.searchController.active = NO;
// Dismiss the view controller and wait for the dismissal to finish.
__block bool dismissal_finished = NO;
[passwords_controller settingsWillBeDismissed];
[root_view_controller_ dismissViewControllerAnimated:NO
completion:^{
dismissal_finished = YES;
}];
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForUIElementTimeout, ^bool {
return dismissal_finished;
}));
}
// Tests that opening the PasswordManagerViewController in search mode shows the
// expected content.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_TestOpenInSearchMode) {
// Call `settingsWillBeDismissed` on the initial view controller so that its
// observers are reset.
[GetPasswordManagerViewController() settingsWillBeDismissed];
root_view_controller_ = [[UIViewController alloc] init];
scoped_window_.Get().rootViewController = root_view_controller_;
PasswordManagerViewController* passwords_controller =
CreateControllerForPasswordSearch();
// Add a saved password so the empty state isn't shown.
AddSavedForm1();
// Present the view controller.
__block bool presentation_finished = NO;
UINavigationController* navigation_controller =
[[UINavigationController alloc]
initWithRootViewController:passwords_controller];
[root_view_controller_ presentViewController:navigation_controller
animated:NO
completion:^{
presentation_finished = YES;
}];
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForUIElementTimeout, ^bool {
return presentation_finished;
}));
// Verify that the search controller is active and that the content of table
// view model is as expected in search mode.
RunUntilIdle();
EXPECT_TRUE(passwords_controller.navigationItem.searchController.active);
EXPECT_EQ(1, [[passwords_controller tableViewModel] numberOfSections]);
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierSavedPasswords]);
int savedPasswordsSectionIdentifier = [passwords_controller.tableViewModel
sectionForSectionIdentifier:SectionIdentifierSavedPasswords];
EXPECT_EQ(1, [[passwords_controller tableViewModel]
numberOfItemsInSection:savedPasswordsSectionIdentifier]);
passwords_controller.navigationItem.searchController.active = NO;
// Verify that the content of table view model is as expected after leaving
// search mode.
EXPECT_EQ(5, [[passwords_controller tableViewModel] numberOfSections]);
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierAddPasswordButton]);
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierPasswordCheck]);
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierSavedPasswords]);
EXPECT_TRUE([passwords_controller.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierManageAccountHeader]);
// Dismiss the view controller and wait for the dismissal to finish.
__block bool dismissal_finished = NO;
[passwords_controller settingsWillBeDismissed];
[root_view_controller_ dismissViewControllerAnimated:NO
completion:^{
dismissal_finished = YES;
}];
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForUIElementTimeout, ^bool {
return dismissal_finished;
}));
}
// Tests that the "Check Now" button is greyed out in edit mode.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest,
DISABLED_TestCheckPasswordButtonDisabledEditMode) {
PasswordManagerViewController* passwords_controller =
GetPasswordManagerViewController();
AddSavedForm1();
// Switch to default state so that the button is present.
ChangePasswordCheckState(PasswordCheckStateDefault);
TableViewDetailTextItem* checkPasswordButton =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1);
CheckTextCellTextWithId(IDS_IOS_CHECK_PASSWORDS_NOW_BUTTON,
GetSectionIndex(SectionIdentifierPasswordCheck), 1);
[passwords_controller setEditing:YES animated:NO];
EXPECT_NSEQ([UIColor colorNamed:kTextSecondaryColor],
checkPasswordButton.textColor);
EXPECT_TRUE(checkPasswordButton.accessibilityTraits &
UIAccessibilityTraitNotEnabled);
[passwords_controller setEditing:NO animated:NO];
EXPECT_NSEQ([UIColor colorNamed:kBlueColor], checkPasswordButton.textColor);
EXPECT_FALSE(checkPasswordButton.accessibilityTraits &
UIAccessibilityTraitNotEnabled);
[passwords_controller settingsWillBeDismissed];
}
// Tests filtering of items.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_FilterItems) {
AddSavedForm1();
AddSavedForm2();
AddBlockedForm1();
AddBlockedForm2();
EXPECT_EQ(6, NumberOfSections());
PasswordManagerViewController* passwords_controller =
GetPasswordManagerViewController();
UISearchBar* bar =
passwords_controller.navigationItem.searchController.searchBar;
// Force the initial data to be rendered into view first, before doing any
// new filtering (avoids mismatch when reloadSections is called).
[passwords_controller searchBar:bar textDidChange:@""];
// Search item in save passwords section.
[passwords_controller searchBar:bar textDidChange:@"example.com"];
// Only one item in saved passwords should remain.
EXPECT_EQ(1, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
EXPECT_EQ(0,
NumberOfItemsInSection(GetSectionIndex(SectionIdentifierBlocked)));
CheckURLCellTitleAndDetailText(
@"example.com", @"", GetSectionIndex(SectionIdentifierSavedPasswords), 0);
[passwords_controller searchBar:bar textDidChange:@"secret"];
// Only two blocked items should remain.
EXPECT_EQ(0, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
EXPECT_EQ(2,
NumberOfItemsInSection(GetSectionIndex(SectionIdentifierBlocked)));
CheckURLCellTitle(@"secret.com", GetSectionIndex(SectionIdentifierBlocked),
/* item= */ 0);
CheckURLCellTitle(@"secret2.com", GetSectionIndex(SectionIdentifierBlocked),
/* item= */ 1);
[passwords_controller searchBar:bar textDidChange:@""];
// All items should be back.
EXPECT_EQ(2, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
EXPECT_EQ(2,
NumberOfItemsInSection(GetSectionIndex(SectionIdentifierBlocked)));
[passwords_controller settingsWillBeDismissed];
}
// Tests filtering of items when group with multiple password entries is
// present.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_FilterGroupsOfPasswords) {
GURL mUrl = GURL("http://www.m.me");
GURL facebookUrl = GURL("http://www.facebook.com");
{
auto form = CreateForm(u"username1");
form->url = mUrl;
form->action = facebookUrl;
form->signon_realm = "http://www.facebook.com";
AddPasswordForm(std::move(form));
}
{
auto form = CreateForm(u"username2");
form->url = facebookUrl;
form->action = facebookUrl;
form->signon_realm = "http://www.facebook.com";
AddPasswordForm(std::move(form));
}
EXPECT_EQ(5, NumberOfSections());
PasswordManagerViewController* passwords_controller =
GetPasswordManagerViewController();
UISearchBar* bar =
passwords_controller.navigationItem.searchController.searchBar;
// Force the initial data to be rendered into view first, before doing any
// new filtering (avoids mismatch when reloadSections is called).
[passwords_controller searchBar:bar textDidChange:@""];
// Password entries are groups as one item.
EXPECT_EQ(1, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
// Search using on of the urls of the group.
[passwords_controller searchBar:bar textDidChange:@"facebook.com"];
// Password entries are groups as one item.
EXPECT_EQ(1, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
[passwords_controller settingsWillBeDismissed];
}
// Test verifies disabled state of password check cell.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_PasswordCheckStateDisabled) {
AddSavedForm1();
ChangePasswordCheckState(PasswordCheckStateDisabled);
// Check button should be shown.
EXPECT_TRUE(
HasTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1));
CheckTextCellTextWithId(IDS_IOS_CHECK_PASSWORDS_NOW_BUTTON,
GetSectionIndex(SectionIdentifierPasswordCheck), 1);
CheckDetailItemTextWithIds(
IDS_IOS_PASSWORD_CHECKUP, IDS_IOS_PASSWORD_CHECKUP_DESCRIPTION,
GetSectionIndex(SectionIdentifierPasswordCheck), 0);
SettingsCheckItem* checkPassword =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 0);
EXPECT_FALSE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_FALSE(checkPassword.trailingImage);
EXPECT_FALSE(checkPassword.accessoryType);
TableViewTextItem* checkNowButton =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1);
EXPECT_FALSE(checkNowButton.enabled);
SetEditing(true);
EXPECT_FALSE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_FALSE(checkPassword.trailingImage);
EXPECT_FALSE(checkPassword.accessoryType);
SetEditing(false);
SelectCell(/*item=*/0,
/*sectionIndex=*/GetSectionIndex(SectionIdentifierPasswordCheck));
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Test verifies default state of password check cell.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_PasswordCheckStateDefault) {
AddSavedForm1();
ChangePasswordCheckState(PasswordCheckStateDefault);
// Check button should be shown.
EXPECT_TRUE(
HasTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1));
CheckTextCellTextWithId(IDS_IOS_CHECK_PASSWORDS_NOW_BUTTON,
GetSectionIndex(SectionIdentifierPasswordCheck), 1);
CheckDetailItemTextWithIds(
IDS_IOS_PASSWORD_CHECKUP, IDS_IOS_PASSWORD_CHECKUP_DESCRIPTION,
GetSectionIndex(SectionIdentifierPasswordCheck), 0);
SettingsCheckItem* checkPassword =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 0);
EXPECT_TRUE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_FALSE(checkPassword.trailingImage);
EXPECT_FALSE(checkPassword.accessoryType);
TableViewTextItem* checkNowButton =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1);
EXPECT_TRUE(checkNowButton.enabled);
SetEditing(true);
EXPECT_FALSE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_FALSE(checkPassword.trailingImage);
EXPECT_FALSE(checkPassword.accessoryType);
SetEditing(false);
SelectCell(/*item=*/0,
/*sectionIndex=*/GetSectionIndex(SectionIdentifierPasswordCheck));
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Test verifies safe state of password check cell.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_PasswordCheckStateSafe) {
AddSavedForm1();
ChangePasswordCheckState(PasswordCheckStateSafe);
// Check button should be hidden.
EXPECT_FALSE(
HasTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1));
SettingsCheckItem* checkPassword =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 0);
EXPECT_NSEQ(l10n_util::GetNSString(IDS_IOS_PASSWORD_CHECKUP),
[checkPassword text]);
EXPECT_NSEQ([GetPasswordManagerViewController()
.delegate formattedElapsedTimeSinceLastCheck],
[checkPassword detailText]);
EXPECT_TRUE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_TRUE(checkPassword.trailingImage);
EXPECT_TRUE([checkPassword.trailingImageTintColor
isEqual:[UIColor colorNamed:kGreen500Color]]);
EXPECT_TRUE(checkPassword.accessoryType);
SetEditing(true);
EXPECT_FALSE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_TRUE(checkPassword.trailingImage);
EXPECT_TRUE(checkPassword.accessoryType);
SetEditing(false);
OCMExpect([passwords_settings_commands_strict_mock_ showPasswordCheckup]);
SelectCell(/*item=*/0,
/*sectionIndex=*/GetSectionIndex(SectionIdentifierPasswordCheck));
EXPECT_OCMOCK_VERIFY(passwords_settings_commands_strict_mock_);
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Test verifies compromised state of password check cell.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest,
DISABLED_PasswordCheckStateUnmutedCompromisedPasswords) {
AddSavedInsecureForm(InsecureType::kLeaked);
ChangePasswordCheckState(PasswordCheckStateUnmutedCompromisedPasswords);
// Check button should be hidden.
EXPECT_FALSE(
HasTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1));
CheckDetailItemTextWithPluralIds(
IDS_IOS_PASSWORD_CHECKUP, IDS_IOS_PASSWORD_CHECKUP_COMPROMISED_COUNT, 1,
GetSectionIndex(SectionIdentifierPasswordCheck), 0);
SettingsCheckItem* checkPassword =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 0);
EXPECT_TRUE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_TRUE(checkPassword.trailingImage);
EXPECT_TRUE([checkPassword.trailingImageTintColor
isEqual:[UIColor colorNamed:kRed500Color]]);
EXPECT_TRUE(checkPassword.accessoryType);
SetEditing(true);
EXPECT_FALSE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_TRUE(checkPassword.trailingImage);
EXPECT_TRUE(checkPassword.accessoryType);
[GetPasswordManagerViewController() settingsWillBeDismissed];
SetEditing(false);
OCMExpect([passwords_settings_commands_strict_mock_ showPasswordCheckup]);
SelectCell(/*item=*/0,
/*sectionIndex=*/GetSectionIndex(SectionIdentifierPasswordCheck));
EXPECT_OCMOCK_VERIFY(passwords_settings_commands_strict_mock_);
}
// Test verifies reused state of password check cell.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest,
DISABLED_PasswordCheckStateReusedPasswords) {
AddSavedInsecureForm(InsecureType::kReused);
AddSavedInsecureForm(InsecureType::kReused, /*is_muted=*/false,
/*username_value=*/u"[email protected]");
ChangePasswordCheckState(PasswordCheckStateReusedPasswords);
// Check button should be hidden.
EXPECT_FALSE(
HasTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1));
CheckDetailItemTextWithPlaceholder(
IDS_IOS_PASSWORD_CHECKUP, IDS_IOS_PASSWORD_CHECKUP_REUSED_COUNT,
base::NumberToString16(2),
GetSectionIndex(SectionIdentifierPasswordCheck), 0);
SettingsCheckItem* checkPassword =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 0);
EXPECT_TRUE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_TRUE(checkPassword.trailingImage);
EXPECT_TRUE([checkPassword.trailingImageTintColor
isEqual:[UIColor colorNamed:kYellow500Color]]);
EXPECT_TRUE(checkPassword.accessoryType);
SetEditing(true);
EXPECT_FALSE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_TRUE(checkPassword.trailingImage);
EXPECT_TRUE(checkPassword.accessoryType);
[GetPasswordManagerViewController() settingsWillBeDismissed];
SetEditing(false);
OCMExpect([passwords_settings_commands_strict_mock_ showPasswordCheckup]);
SelectCell(/*item=*/0,
/*sectionIndex=*/GetSectionIndex(SectionIdentifierPasswordCheck));
EXPECT_OCMOCK_VERIFY(passwords_settings_commands_strict_mock_);
}
// Test verifies weak state of password check cell.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest,
DISABLED_PasswordCheckStateWeakPasswords) {
AddSavedInsecureForm(InsecureType::kWeak);
ChangePasswordCheckState(PasswordCheckStateWeakPasswords);
// Check button should be hidden.
EXPECT_FALSE(
HasTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1));
CheckDetailItemTextWithPluralIds(
IDS_IOS_PASSWORD_CHECKUP, IDS_IOS_PASSWORD_CHECKUP_WEAK_COUNT, 1,
GetSectionIndex(SectionIdentifierPasswordCheck), 0);
SettingsCheckItem* checkPassword =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 0);
EXPECT_TRUE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_TRUE(checkPassword.trailingImage);
EXPECT_TRUE([checkPassword.trailingImageTintColor
isEqual:[UIColor colorNamed:kYellow500Color]]);
EXPECT_TRUE(checkPassword.accessoryType);
SetEditing(true);
EXPECT_FALSE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_TRUE(checkPassword.trailingImage);
EXPECT_TRUE(checkPassword.accessoryType);
[GetPasswordManagerViewController() settingsWillBeDismissed];
SetEditing(false);
OCMExpect([passwords_settings_commands_strict_mock_ showPasswordCheckup]);
SelectCell(/*item=*/0,
/*sectionIndex=*/GetSectionIndex(SectionIdentifierPasswordCheck));
EXPECT_OCMOCK_VERIFY(passwords_settings_commands_strict_mock_);
}
// Test verifies dismissed state of password check cell.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest,
DISABLED_PasswordCheckStateDismissedPasswords) {
AddSavedInsecureForm(InsecureType::kLeaked, /*is_muted=*/true);
ChangePasswordCheckState(PasswordCheckStateDismissedWarnings);
// Check button should be hidden.
EXPECT_FALSE(
HasTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1));
CheckDetailItemTextWithPluralIds(
IDS_IOS_PASSWORD_CHECKUP, IDS_IOS_PASSWORD_CHECKUP_DISMISSED_COUNT, 1,
GetSectionIndex(SectionIdentifierPasswordCheck), 0);
SettingsCheckItem* checkPassword =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 0);
EXPECT_TRUE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_TRUE(checkPassword.trailingImage);
EXPECT_TRUE([checkPassword.trailingImageTintColor
isEqual:[UIColor colorNamed:kYellow500Color]]);
EXPECT_TRUE(checkPassword.accessoryType);
SetEditing(true);
EXPECT_FALSE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_TRUE(checkPassword.trailingImage);
EXPECT_TRUE(checkPassword.accessoryType);
[GetPasswordManagerViewController() settingsWillBeDismissed];
SetEditing(false);
OCMExpect([passwords_settings_commands_strict_mock_ showPasswordCheckup]);
SelectCell(/*item=*/0,
/*sectionIndex=*/GetSectionIndex(SectionIdentifierPasswordCheck));
EXPECT_OCMOCK_VERIFY(passwords_settings_commands_strict_mock_);
}
// Test verifies running state of password check cell.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_PasswordCheckStateRunning) {
AddSavedForm1();
ChangePasswordCheckState(PasswordCheckStateRunning);
// Check button should be hidden.
EXPECT_FALSE(
HasTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1));
CheckDetailItemTextWithPluralIds(
IDS_IOS_PASSWORD_CHECKUP_ONGOING,
IDS_IOS_PASSWORD_CHECKUP_SITES_AND_APPS_COUNT, 1,
GetSectionIndex(SectionIdentifierPasswordCheck), 0);
SettingsCheckItem* checkPassword =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 0);
EXPECT_TRUE(checkPassword.enabled);
EXPECT_FALSE(checkPassword.indicatorHidden);
EXPECT_FALSE(checkPassword.trailingImage);
EXPECT_FALSE(checkPassword.accessoryType);
SetEditing(true);
EXPECT_FALSE(checkPassword.enabled);
EXPECT_FALSE(checkPassword.indicatorHidden);
EXPECT_FALSE(checkPassword.trailingImage);
EXPECT_FALSE(checkPassword.accessoryType);
SetEditing(false);
SelectCell(/*item=*/0,
/*sectionIndex=*/GetSectionIndex(SectionIdentifierPasswordCheck));
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Test verifies error state of password check cell.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_PasswordCheckStateError) {
AddSavedForm1();
ChangePasswordCheckState(PasswordCheckStateError);
// Check button should be shown.
EXPECT_TRUE(
HasTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1));
CheckTextCellTextWithId(IDS_IOS_CHECK_PASSWORDS_NOW_BUTTON,
GetSectionIndex(SectionIdentifierPasswordCheck), 1);
CheckDetailItemTextWithIds(
IDS_IOS_PASSWORD_CHECKUP, IDS_IOS_PASSWORD_CHECKUP_ERROR,
GetSectionIndex(SectionIdentifierPasswordCheck), 0);
SettingsCheckItem* checkPassword =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 0);
EXPECT_TRUE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_FALSE(checkPassword.trailingImage);
EXPECT_FALSE(checkPassword.infoButtonHidden);
EXPECT_FALSE(checkPassword.accessoryType);
TableViewTextItem* checkNowButton =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1);
EXPECT_TRUE(checkNowButton.enabled);
SetEditing(true);
EXPECT_FALSE(checkPassword.enabled);
EXPECT_TRUE(checkPassword.indicatorHidden);
EXPECT_FALSE(checkPassword.trailingImage);
EXPECT_FALSE(checkPassword.infoButtonHidden);
EXPECT_FALSE(checkPassword.accessoryType);
SetEditing(false);
SelectCell(/*item=*/0,
/*sectionIndex=*/GetSectionIndex(SectionIdentifierPasswordCheck));
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Test verifies that the "Check Now" button is unavailable when the user is
// signed out.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest,
DISABLED_PasswordCheckStateSignedOutError) {
AddSavedForm1();
ChangePasswordCheckState(PasswordCheckStateSignedOut);
// Check button should be shown.
EXPECT_TRUE(
HasTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1));
TableViewTextItem* checkNowButton =
GetTableViewItem(GetSectionIndex(SectionIdentifierPasswordCheck), 1);
EXPECT_FALSE(checkNowButton.enabled);
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Test verifies tapping start triggers correct function in service.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_StartPasswordCheck) {
AddSavedForm1();
RunUntilIdle();
// Switch to default state so that the button is present.
ChangePasswordCheckState(PasswordCheckStateDefault);
PasswordManagerViewController* passwords_controller =
GetPasswordManagerViewController();
EXPECT_CALL(GetMockPasswordCheckService(), CheckUsernamePasswordPairs);
SelectCell(/*item=*/1,
/*sectionIndex=*/GetSectionIndex(SectionIdentifierPasswordCheck));
[passwords_controller settingsWillBeDismissed];
}
// Test verifies changes to the password store are reflected on UI.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_PasswordStoreListener) {
AddSavedForm1();
EXPECT_EQ(1, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
AddSavedForm2();
EXPECT_EQ(2, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
auto password =
GetTestStore().stored_passwords().at("http://www.example.com/").at(0);
GetTestStore().RemoveLogin(FROM_HERE, password);
RunUntilIdle();
EXPECT_EQ(1, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Test verifies the content of the widget promo cell.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest, DISABLED_WidgetPromo) {
AddSavedForm1();
GetPasswordManagerViewController().shouldShowPasswordManagerWidgetPromo = YES;
[GetPasswordManagerViewController() reloadData];
EXPECT_EQ(1, NumberOfItemsInSection(
GetSectionIndex(SectionIdentifierSavedPasswords)));
InlinePromoItem* item = static_cast<InlinePromoItem*>(
GetTableViewItem(GetSectionIndex(SectionIdentifierWidgetPromo), 0));
EXPECT_NSEQ(item.promoImage, [UIImage imageNamed:WidgetPromoImageName()]);
EXPECT_NSEQ(item.promoText, l10n_util::GetNSString(
IDS_IOS_PASSWORD_MANAGER_WIDGET_PROMO_TEXT));
EXPECT_NSEQ(item.moreInfoButtonTitle,
l10n_util::GetNSString(
IDS_IOS_PASSWORD_MANAGER_WIDGET_PROMO_BUTTON_TITLE));
EXPECT_TRUE(item.shouldShowCloseButton);
EXPECT_FALSE(GetPasswordManagerViewController().editing);
EXPECT_TRUE(item.enabled);
SetEditing(true);
EXPECT_FALSE(item.enabled);
EXPECT_NSEQ(item.promoImage,
[UIImage imageNamed:WidgetPromoDisabledImageName()]);
SetEditing(false);
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Tests that the right metric is logged when tapping the widget promo's close
// button.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest,
DISABLED_WidgetPromoCloseButtonMetric) {
AddSavedForm1();
// Make Password Manager show the promo.
GetPasswordManagerViewController().shouldShowPasswordManagerWidgetPromo = YES;
[GetPasswordManagerViewController() reloadData];
// Bucket count should be zero.
base::HistogramTester histogram_tester;
histogram_tester.ExpectBucketCount(kPasswordManagerWidgetPromoActionHistogram,
PasswordManagerWidgetPromoAction::kClose,
0);
NSIndexPath* index_path = [NSIndexPath
indexPathForRow:0
inSection:GetSectionIndex(SectionIdentifierWidgetPromo)];
InlinePromoCell* cell = base::apple::ObjCCastStrict<InlinePromoCell>(
[GetPasswordManagerViewController() tableView:controller().tableView
cellForRowAtIndexPath:index_path]);
// Simulate tap on promo's close button.
[cell.closeButton sendActionsForControlEvents:UIControlEventTouchUpInside];
// Bucket count should now be one.
histogram_tester.ExpectBucketCount(kPasswordManagerWidgetPromoActionHistogram,
PasswordManagerWidgetPromoAction::kClose,
1);
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
// Tests that the right metric is logged when tapping the widget promo's more
// info button.
// TODO(crbug.com/347688002): re-enable when they pass locally as part of
// the full ios_chrome_unittests app (i.e. without --gtest_filter=).
TEST_F(PasswordManagerViewControllerTest,
DISABLED_WidgetPromoMoreInfoButtonMetric) {
AddSavedForm1();
// Make Password Manager show the promo.
GetPasswordManagerViewController().shouldShowPasswordManagerWidgetPromo = YES;
[GetPasswordManagerViewController() reloadData];
// Bucket count should be zero.
base::HistogramTester histogram_tester;
histogram_tester.ExpectBucketCount(
kPasswordManagerWidgetPromoActionHistogram,
PasswordManagerWidgetPromoAction::kOpenInstructions, 0);
NSIndexPath* index_path = [NSIndexPath
indexPathForRow:0
inSection:GetSectionIndex(SectionIdentifierWidgetPromo)];
InlinePromoCell* cell = base::apple::ObjCCastStrict<InlinePromoCell>(
[GetPasswordManagerViewController() tableView:controller().tableView
cellForRowAtIndexPath:index_path]);
// Simulate tap on promo's more info button.
[cell.moreInfoButton sendActionsForControlEvents:UIControlEventTouchUpInside];
// Bucket count should now be one.
histogram_tester.ExpectBucketCount(
kPasswordManagerWidgetPromoActionHistogram,
PasswordManagerWidgetPromoAction::kOpenInstructions, 1);
[GetPasswordManagerViewController() settingsWillBeDismissed];
}
} // namespace