OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/callback.h" | |
6 #include "base/mac/foundation_util.h" | |
7 #include "base/memory/ref_counted.h" | |
8 #include "base/strings/utf_string_conversions.h" | |
9 #include "base/time/time.h" | |
10 #include "components/autofill/core/common/password_form.h" | |
11 #include "components/keyed_service/core/service_access_type.h" | |
12 #include "components/password_manager/core/browser/password_store.h" | |
13 #include "components/password_manager/core/common/password_manager_pref_names.h" | |
14 #include "components/prefs/pref_service.h" | |
15 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" | |
16 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h" | |
17 #import "ios/chrome/browser/ui/settings/password_details_collection_view_control ler_for_testing.h" | |
18 #import "ios/chrome/browser/ui/settings/reauthentication_module.h" | |
19 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h" | |
20 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h" | |
21 #import "ios/chrome/browser/ui/util/top_view_controller.h" | |
22 #include "ios/chrome/grit/ios_strings.h" | |
23 #import "ios/chrome/test/app/chrome_test_util.h" | |
24 #include "ios/chrome/test/earl_grey/accessibility_util.h" | |
25 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h" | |
26 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h" | |
27 #import "ios/chrome/test/earl_grey/chrome_matchers.h" | |
28 #import "ios/chrome/test/earl_grey/chrome_test_case.h" | |
29 #include "ui/base/l10n/l10n_util.h" | |
30 #include "url/gurl.h" | |
31 | |
32 #if !defined(__has_feature) || !__has_feature(objc_arc) | |
33 #error "This file requires ARC support." | |
34 #endif | |
35 | |
36 // This test complements | |
37 // password_details_collection_view_controller_unittest.mm. Very simple | |
38 // integration tests and features which are not currently unittestable should | |
39 // go here, the rest into the unittest. | |
40 // This test only uses the new UI which allows viewing passwords. | |
41 // TODO(crbug.com/159166): Remove the above sentence once the new UI is the | |
42 // default one. | |
43 | |
44 using autofill::PasswordForm; | |
45 using chrome_test_util::ButtonWithAccessibilityLabel; | |
46 using chrome_test_util::ButtonWithAccessibilityLabelId; | |
47 using chrome_test_util::NavigationBarDoneButton; | |
48 | |
49 namespace { | |
50 | |
51 // Matcher for the Settings button in the tools menu. | |
52 id<GREYMatcher> SettingsButton() { | |
sdefresne
2017/05/02 15:30:08
baxley: are those helper methods (used only once)
baxley
2017/05/02 16:20:41
I think some of these fall under the category of a
vabr (Chromium)
2017/05/03 05:41:37
Thanks for raising this and for the detailed answe
| |
53 return grey_accessibilityID(kToolsMenuSettingsId); | |
54 } | |
55 | |
56 // Matcher for the Save Passwords cell on the main Settings screen. | |
57 id<GREYMatcher> PasswordsButton() { | |
58 return ButtonWithAccessibilityLabelId(IDS_IOS_SAVE_PASSWORDS); | |
59 } | |
60 | |
61 // Matcher for a password entry for |username|. | |
62 id<GREYMatcher> Entry(NSString* username) { | |
63 return ButtonWithAccessibilityLabel(username); | |
64 } | |
65 | |
66 // Matcher for the Edit button in Save Passwords view. | |
67 id<GREYMatcher> EditButton() { | |
68 return ButtonWithAccessibilityLabelId(IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON); | |
69 } | |
70 | |
71 // Matcher for the Copy password button in Password Details view. | |
72 id<GREYMatcher> CopyPasswordButton() { | |
73 return ButtonWithAccessibilityLabelId(IDS_IOS_SETTINGS_PASSWORD_COPY_BUTTON); | |
74 } | |
75 | |
76 } // namespace | |
77 | |
78 @interface MockReauthenticationModule : NSObject<ReauthenticationProtocol> | |
79 | |
80 @property(nonatomic, assign) BOOL shouldSucceed; | |
81 | |
82 @end | |
83 | |
84 @implementation MockReauthenticationModule | |
85 | |
86 @synthesize shouldSucceed = _shouldSucceed; | |
87 | |
88 - (BOOL)canAttemptReauth { | |
89 return YES; | |
90 } | |
91 | |
92 - (void)attemptReauthWithLocalizedReason:(NSString*)localizedReason | |
93 handler:(void (^)(BOOL success)) | |
94 showCopyPasswordsHandler { | |
95 showCopyPasswordsHandler(_shouldSucceed); | |
96 } | |
97 | |
98 @end | |
99 | |
100 // Various tests for the Save Passwords section of the settings. | |
101 @interface PasswordsSettingsTestCase : ChromeTestCase | |
102 @end | |
103 | |
104 @implementation PasswordsSettingsTestCase | |
105 | |
106 // Return pref for saving passwords back to the passed value and restores the | |
107 // experimental flag for viewing passwords. | |
108 - (void)passwordsTearDown:(BOOL)defaultPasswordManagementSetting | |
109 :(NSString*)oldExperiment { | |
110 ios::ChromeBrowserState* browserState = | |
111 chrome_test_util::GetOriginalBrowserState(); | |
112 PrefService* preferences = browserState->GetPrefs(); | |
113 preferences->SetBoolean( | |
114 password_manager::prefs::kPasswordManagerSavingEnabled, | |
115 defaultPasswordManagementSetting); | |
116 | |
117 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; | |
118 [defaults setObject:oldExperiment forKey:@"EnableViewCopyPasswords"]; | |
119 } | |
120 | |
121 // Sets the preference to allow saving passwords and activates the flag to use | |
122 // the new UI for viewing passwords in settings. Also, ensures that original | |
123 // state is restored after the test ends. | |
124 - (void)scopedEnablePasswordManagementAndViewingUI { | |
125 // Retrieve the original preference state. | |
126 ios::ChromeBrowserState* browserState = | |
127 chrome_test_util::GetOriginalBrowserState(); | |
128 PrefService* preferences = browserState->GetPrefs(); | |
129 bool defaultPasswordManagerSavingPref = preferences->GetBoolean( | |
130 password_manager::prefs::kPasswordManagerSavingEnabled); | |
131 | |
132 // Retrieve the experiment setting. | |
133 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; | |
134 NSString* oldSetting = [defaults stringForKey:@"EnableViewCopyPasswords"]; | |
135 | |
136 // Ensure restoring that on tear-down. | |
137 __weak PasswordsSettingsTestCase* weakSelf = self; | |
138 [self setTearDownHandler:^{ | |
139 [weakSelf passwordsTearDown:defaultPasswordManagerSavingPref:oldSetting]; | |
140 }]; | |
141 | |
142 // Enable saving. | |
143 preferences->SetBoolean( | |
144 password_manager::prefs::kPasswordManagerSavingEnabled, true); | |
145 | |
146 // Enable viewing passwords in settings. | |
147 [defaults setObject:@"Enabled" forKey:@"EnableViewCopyPasswords"]; | |
148 } | |
149 | |
150 - (scoped_refptr<password_manager::PasswordStore>)passwordStore { | |
151 // ServiceAccessType governs behaviour in Incognito: only modifications with | |
152 // EXPLICIT_ACCESS, which correspond to user's explicit gesture, succeed. | |
153 // This test does not deal with Incognito, so the value of the argument is | |
154 // irrelevant. | |
155 return IOSChromePasswordStoreFactory::GetForBrowserState( | |
156 chrome_test_util::GetOriginalBrowserState(), | |
157 ServiceAccessType::EXPLICIT_ACCESS); | |
158 } | |
159 | |
160 // Saves an example form in the store. | |
161 - (void)saveExamplePasswordForm { | |
162 PasswordForm example; | |
163 example.username_value = base::ASCIIToUTF16("user"); | |
164 example.password_value = base::ASCIIToUTF16("password"); | |
165 example.origin = GURL("https://example.com"); | |
166 example.signon_realm = example.origin.spec(); | |
167 | |
168 [self passwordStore]->AddLogin(example); | |
169 // Allow the PasswordStore to process this on the DB thread. | |
170 [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; | |
171 } | |
172 | |
173 // Removes all credentials stored. | |
174 - (void)clearPasswordStore { | |
175 [self passwordStore]->RemoveLoginsCreatedBetween(base::Time(), base::Time(), | |
176 base::Closure()); | |
177 // Allow the PasswordStore to process this on the DB thread. | |
178 [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; | |
179 } | |
180 | |
181 // Opens the passwords page from the NTP. It requires no menus to be open. | |
182 - (void)openPasswordSettings { | |
183 // Open settings and verify data in the view controller. | |
184 [ChromeEarlGreyUI openToolsMenu]; | |
185 [[EarlGrey selectElementWithMatcher:SettingsButton()] | |
186 performAction:grey_tap()]; | |
187 [[EarlGrey selectElementWithMatcher:PasswordsButton()] | |
188 performAction:grey_tap()]; | |
189 | |
190 // Wait for UI components to finish loading. | |
191 [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; | |
192 } | |
193 | |
194 // Tap back arrow, to get one level higher in settings. | |
195 - (void)tapBackArrow { | |
196 [[EarlGrey | |
197 selectElementWithMatcher:grey_allOf( | |
198 grey_accessibilityID(@"ic_arrow_back"), | |
199 grey_accessibilityTrait( | |
200 UIAccessibilityTraitButton), | |
201 nil)] performAction:grey_tap()]; | |
202 | |
203 // Wait for UI components to finish loading. | |
204 [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; | |
205 } | |
206 | |
207 // Tap Edit in any settings view. | |
208 - (void)tapEdit { | |
209 [[EarlGrey selectElementWithMatcher:EditButton()] performAction:grey_tap()]; | |
210 | |
211 // Wait for UI components to finish loading. | |
212 [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; | |
213 } | |
214 | |
215 // Tap Done in any settings view. | |
216 - (void)tapDone { | |
217 [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()] | |
218 performAction:grey_tap()]; | |
219 | |
220 // Wait for UI components to finish loading. | |
221 [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; | |
222 } | |
223 | |
224 // Verifies the UI elements are accessible on the Passwords page. | |
225 // TODO(crbug.com/159166): This differs from testAccessibilityOnPasswords in | |
226 // settings_egtest.mm in that here this tests the new UI (for viewing | |
227 // passwords), where in settings_egtest.mm the default (old) UI is tested. | |
228 // Once the new is the default, just remove the test in settings_egtest.mm. | |
229 - (void)testAccessibilityOnPasswords { | |
230 [self scopedEnablePasswordManagementAndViewingUI]; | |
231 | |
232 // Saving a form is needed for using the "password details" view. | |
233 [self saveExamplePasswordForm]; | |
234 | |
235 [self openPasswordSettings]; | |
236 chrome_test_util::VerifyAccessibilityForCurrentScreen(); | |
237 | |
238 [self tapEdit]; | |
239 chrome_test_util::VerifyAccessibilityForCurrentScreen(); | |
240 [self tapDone]; | |
241 | |
242 // Inspect "password details" view. | |
243 [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")] | |
244 performAction:grey_tap()]; | |
245 chrome_test_util::VerifyAccessibilityForCurrentScreen(); | |
246 [self tapBackArrow]; | |
247 | |
248 [self tapBackArrow]; | |
249 [self tapDone]; | |
250 [self clearPasswordStore]; | |
251 } | |
252 | |
253 // Checks that attempts to copy a password provide appropriate feedback, | |
254 // both when reauthentication succeeds and when it fails. | |
255 - (void)testCopyPasswordToast { | |
256 [self scopedEnablePasswordManagementAndViewingUI]; | |
257 | |
258 // Saving a form is needed for using the "password details" view. | |
259 [self saveExamplePasswordForm]; | |
260 | |
261 [self openPasswordSettings]; | |
262 | |
263 [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")] | |
264 performAction:grey_tap()]; | |
265 | |
266 // Get the PasswordDetailsCollectionViewController and replace the | |
267 // reauthentication module with a fake one to avoid being blocked with a | |
268 // reauth prompt. | |
269 MockReauthenticationModule* reauth = | |
sdefresne
2017/05/02 15:30:08
I think that the styleguide discourage uses of abb
vabr (Chromium)
2017/05/03 05:41:37
Good point, done.
| |
270 [[MockReauthenticationModule alloc] init]; | |
271 SettingsNavigationController* snc = | |
272 base::mac::ObjCCastStrict<SettingsNavigationController>( | |
273 top_view_controller::TopPresentedViewController()); | |
274 PasswordDetailsCollectionViewController* pdcvc = | |
275 base::mac::ObjCCastStrict<PasswordDetailsCollectionViewController>( | |
276 snc.topViewController); | |
277 [pdcvc setReauthenticationModule:reauth]; | |
278 | |
279 // Check the snackbar in case of successful reauthentication. | |
280 reauth.shouldSucceed = YES; | |
281 [[EarlGrey selectElementWithMatcher:CopyPasswordButton()] | |
282 performAction:grey_tap()]; | |
283 [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; | |
284 NSString* snackbarLabel = | |
285 l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_WAS_COPIED_MESSAGE); | |
286 [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarLabel)] | |
287 assertWithMatcher:grey_notNil()]; | |
288 | |
289 // Check the snackbar in case of failed reauthentication. | |
290 reauth.shouldSucceed = NO; | |
291 [[EarlGrey selectElementWithMatcher:CopyPasswordButton()] | |
292 performAction:grey_tap()]; | |
293 [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; | |
294 snackbarLabel = | |
295 l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_WAS_NOT_COPIED_MESSAGE); | |
296 [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarLabel)] | |
297 assertWithMatcher:grey_notNil()]; | |
298 | |
299 [self tapBackArrow]; | |
300 [self tapBackArrow]; | |
301 [self tapDone]; | |
302 [self clearPasswordStore]; | |
303 } | |
304 | |
305 @end | |
OLD | NEW |