OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 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 #import <Foundation/Foundation.h> |
| 6 |
| 7 #import "ios/chrome/browser/passwords/js_password_manager.h" |
| 8 #import "ios/web/public/test/web_js_test.h" |
| 9 #import "ios/web/public/test/web_test_with_web_state.h" |
| 10 #include "testing/gtest/include/gtest/gtest.h" |
| 11 #include "testing/gtest_mac.h" |
| 12 |
| 13 // Unit tests for ios/chrome/browser/web/resources/password_controller.js |
| 14 namespace { |
| 15 |
| 16 // Text fixture to test password controller. |
| 17 class PasswordControllerJsTest |
| 18 : public web::WebJsTest<web::WebTestWithWebState> { |
| 19 public: |
| 20 PasswordControllerJsTest() |
| 21 : web::WebJsTest<web::WebTestWithWebState>(@[ @"password_controller" ]) {} |
| 22 }; |
| 23 |
| 24 // IDs used in the Username and Password <input> elements. |
| 25 NSString* const kEmailInputID = @"Email"; |
| 26 NSString* const kPasswordInputID = @"Passwd"; |
| 27 |
| 28 // Returns an autoreleased string of an HTML form that is similar to the |
| 29 // Google Accounts sign in form. |email| may be nil if the form does not |
| 30 // need to be pre-filled with the username. Use |isReadOnly| flag to indicate |
| 31 // if the email field should be read-only. |
| 32 NSString* GAIASignInForm(NSString* formAction, |
| 33 NSString* email, |
| 34 BOOL isReadOnly) { |
| 35 return [NSString |
| 36 stringWithFormat: |
| 37 @"<html><body>" |
| 38 "<form novalidate method=\"post\" action=\"%@\" " |
| 39 "id=\"gaia_loginform\">" |
| 40 " <input name=\"GALX\" type=\"hidden\" value=\"abcdefghij\">" |
| 41 " <input name=\"service\" type=\"hidden\" value=\"mail\">" |
| 42 " <input id=\"%@\" name=\"Email\" type=\"email\" value=\"%@\" %@>" |
| 43 " <input id=\"%@\" name=\"Passwd\" type=\"password\" " |
| 44 " placeholder=\"Password\">" |
| 45 "</form></body></html>", |
| 46 formAction, kEmailInputID, email ? email : @"", |
| 47 isReadOnly ? @"readonly" : @"", kPasswordInputID]; |
| 48 } |
| 49 |
| 50 // Returns an autoreleased string of JSON for a parsed form. |
| 51 NSString* GAIASignInFormData(NSString* formAction) { |
| 52 return [NSString stringWithFormat:@"{" |
| 53 " \"action\":\"%@\"," |
| 54 " \"origin\":\"%@\"," |
| 55 " \"fields\":[" |
| 56 " {\"name\":\"%@\", \"value\":\"\"}," |
| 57 " {\"name\":\"%@\",\"value\":\"\"}" |
| 58 " ]" |
| 59 "}", |
| 60 formAction, formAction, kEmailInputID, |
| 61 kPasswordInputID]; |
| 62 } |
| 63 |
| 64 // Loads a page with a password form containing a username value already. |
| 65 // Checks that an attempt to fill in credentials with the same username |
| 66 // succeeds. |
| 67 TEST_F(PasswordControllerJsTest, |
| 68 FillPasswordFormWithPrefilledUsername_SucceedsWhenUsernameMatches) { |
| 69 NSString* const formAction = @"https://accounts.google.com/ServiceLoginAuth"; |
| 70 NSString* const username = @"john.doe@gmail.com"; |
| 71 NSString* const password = @"super!secret"; |
| 72 LoadHtmlAndInject(GAIASignInForm(formAction, username, YES)); |
| 73 EXPECT_NSEQ(@"true", EvaluateJavaScriptWithFormat( |
| 74 @"__gCrWeb.fillPasswordForm(%@, '%@', '%@', '%@')", |
| 75 GAIASignInFormData(formAction), username, password, |
| 76 formAction)); |
| 77 // Verifies that the sign-in form has been filled with username/password. |
| 78 EvaluateJavaScriptOnElementsAndCheck(@"document.getElementById('%@').value", |
| 79 @[ kEmailInputID, kPasswordInputID ], |
| 80 @[ username, password ]); |
| 81 } |
| 82 |
| 83 // Loads a page with a password form containing a username value already. |
| 84 // Checks that an attempt to fill in credentials with a different username |
| 85 // fails, as long as the field is read-only. |
| 86 TEST_F(PasswordControllerJsTest, |
| 87 FillPasswordFormWithPrefilledUsername_FailsWhenUsernameMismatched) { |
| 88 NSString* const formAction = @"https://accounts.google.com/ServiceLoginAuth"; |
| 89 NSString* const username1 = @"john.doe@gmail.com"; |
| 90 NSString* const username2 = @"jean.dubois@gmail.com"; |
| 91 NSString* const password = @"super!secret"; |
| 92 LoadHtmlAndInject(GAIASignInForm(formAction, username1, YES)); |
| 93 EXPECT_NSEQ(@"false", EvaluateJavaScriptWithFormat( |
| 94 @"__gCrWeb.fillPasswordForm(%@, '%@', '%@', '%@')", |
| 95 GAIASignInFormData(formAction), username2, password, |
| 96 formAction)); |
| 97 // Verifies that the sign-in form has not been filled. |
| 98 EvaluateJavaScriptOnElementsAndCheck(@"document.getElementById('%@').value", |
| 99 @[ kEmailInputID, kPasswordInputID ], |
| 100 @[ username1, @"" ]); |
| 101 } |
| 102 |
| 103 // Loads a page with a password form containing a username value already. |
| 104 // Checks that an attempt to fill in credentials with a different username |
| 105 // succeeds, as long as the field is writeable. |
| 106 TEST_F(PasswordControllerJsTest, |
| 107 FillPasswordFormWithPrefilledUsername_SucceedsByOverridingUsername) { |
| 108 NSString* const formAction = @"https://accounts.google.com/ServiceLoginAuth"; |
| 109 NSString* const username1 = @"john.doe@gmail.com"; |
| 110 NSString* const username2 = @"jane.doe@gmail.com"; |
| 111 NSString* const password = @"super!secret"; |
| 112 LoadHtmlAndInject(GAIASignInForm(formAction, username1, NO)); |
| 113 EXPECT_NSEQ(@"true", EvaluateJavaScriptWithFormat( |
| 114 @"__gCrWeb.fillPasswordForm(%@, '%@', '%@', '%@')", |
| 115 GAIASignInFormData(formAction), username2, password, |
| 116 formAction)); |
| 117 // Verifies that the sign-in form has been filled with the new username |
| 118 // and password. |
| 119 EvaluateJavaScriptOnElementsAndCheck(@"document.getElementById('%@').value", |
| 120 @[ kEmailInputID, kPasswordInputID ], |
| 121 @[ username2, password ]); |
| 122 } |
| 123 |
| 124 // Check that when instructed to fill a form named "bar", a form named "foo" |
| 125 // is not filled with generated password. |
| 126 TEST_F(PasswordControllerJsTest, |
| 127 FillPasswordFormWithGeneratedPassword_FailsWhenFormNotFound) { |
| 128 LoadHtmlAndInject(@"<html>" |
| 129 " <body>" |
| 130 " <form name=\"foo\">" |
| 131 " <input type=\"password\" name=\"ps\">" |
| 132 " </form>" |
| 133 " </body" |
| 134 "</html>"); |
| 135 NSString* const formName = @"bar"; |
| 136 NSString* const password = @"abc"; |
| 137 EXPECT_NSEQ(@"false", |
| 138 EvaluateJavaScriptWithFormat( |
| 139 @"__gCrWeb.fillPasswordFormWithGeneratedPassword('%@', '%@')", |
| 140 formName, password)); |
| 141 } |
| 142 |
| 143 // Check that filling a form without password fields fails. |
| 144 TEST_F(PasswordControllerJsTest, |
| 145 FillPasswordFormWithGeneratedPassword_FailsWhenNoFieldsFilled) { |
| 146 LoadHtmlAndInject(@"<html>" |
| 147 " <body>" |
| 148 " <form name=\"foo\">" |
| 149 " <input type=\"text\" name=\"user\">" |
| 150 " <input type=\"submit\" name=\"go\">" |
| 151 " </form>" |
| 152 " </body" |
| 153 "</html>"); |
| 154 NSString* const formName = @"foo"; |
| 155 NSString* const password = @"abc"; |
| 156 EXPECT_NSEQ(@"false", |
| 157 EvaluateJavaScriptWithFormat( |
| 158 @"__gCrWeb.fillPasswordFormWithGeneratedPassword('%@', '%@')", |
| 159 formName, password)); |
| 160 } |
| 161 |
| 162 // Check that a matching and complete password form is successfully filled |
| 163 // with the generated password. |
| 164 TEST_F(PasswordControllerJsTest, |
| 165 FillPasswordFormWithGeneratedPassword_SucceedsWhenFieldsFilled) { |
| 166 LoadHtmlAndInject(@"<html>" |
| 167 " <body>" |
| 168 " <form name=\"foo\">" |
| 169 " <input type=\"text\" id=\"user\" name=\"user\">" |
| 170 " <input type=\"password\" id=\"ps1\" name=\"ps1\">" |
| 171 " <input type=\"password\" id=\"ps2\" name=\"ps2\">" |
| 172 " <input type=\"submit\" name=\"go\">" |
| 173 " </form>" |
| 174 " </body" |
| 175 "</html>"); |
| 176 NSString* const formName = @"foo"; |
| 177 NSString* const password = @"abc"; |
| 178 EXPECT_NSEQ(@"true", |
| 179 EvaluateJavaScriptWithFormat( |
| 180 @"__gCrWeb.fillPasswordFormWithGeneratedPassword('%@', '%@')", |
| 181 formName, password)); |
| 182 EXPECT_NSEQ(@"true", |
| 183 EvaluateJavaScriptWithFormat( |
| 184 @"document.getElementById('ps1').value == '%@'", password)); |
| 185 EXPECT_NSEQ(@"true", |
| 186 EvaluateJavaScriptWithFormat( |
| 187 @"document.getElementById('ps2').value == '%@'", password)); |
| 188 EXPECT_NSEQ(@"false", |
| 189 EvaluateJavaScriptWithFormat( |
| 190 @"document.getElementById('user').value == '%@'", password)); |
| 191 } |
| 192 |
| 193 // Check that one password form is identified and serialized correctly. |
| 194 TEST_F(PasswordControllerJsTest, |
| 195 FindAndPreparePasswordFormsSingleFrameSingleForm) { |
| 196 LoadHtmlAndInject( |
| 197 @"<html><body>" |
| 198 "<form action='/generic_submit' method='post' name='login_form'>" |
| 199 " Name: <input type='text' name='name'>" |
| 200 " Password: <input type='password' name='password'>" |
| 201 " <input type='submit' value='Submit'>" |
| 202 "</form>" |
| 203 "</body></html>"); |
| 204 |
| 205 const std::string base_url = BaseUrl(); |
| 206 NSString* result = [NSString |
| 207 stringWithFormat: |
| 208 @"[{\"action\":\"/generic_submit\"," |
| 209 "\"method\":\"post\"," |
| 210 "\"name\":\"login_form\"," |
| 211 "\"origin\":\"%s\"," |
| 212 "\"fields\":[{\"element\":\"name\",\"type\":\"text\"}," |
| 213 "{\"element\":\"password\",\"type\":\"password\"}," |
| 214 "{\"element\":\"\",\"type\":\"submit\"}]," |
| 215 "\"usernameElement\":\"name\"," |
| 216 "\"usernameValue\":\"\"," |
| 217 "\"passwords\":[{\"element\":\"password\",\"value\":\"\"}]}]", |
| 218 base_url.c_str()]; |
| 219 EXPECT_NSEQ(result, |
| 220 EvaluateJavaScriptWithFormat(@"__gCrWeb.findPasswordForms()")); |
| 221 }; |
| 222 |
| 223 // Check that multiple password forms are identified and serialized correctly. |
| 224 TEST_F(PasswordControllerJsTest, |
| 225 FindAndPreparePasswordFormsSingleFrameMultipleForms) { |
| 226 LoadHtmlAndInject( |
| 227 @"<html><body>" |
| 228 "<form action='/generic_submit' method='post' id='login_form1'>" |
| 229 " Name: <input type='text' name='name'>" |
| 230 " Password: <input type='password' name='password'>" |
| 231 " <input type='submit' value='Submit'>" |
| 232 "</form>" |
| 233 "<form action='/generic_s2' method='post' name='login_form2'>" |
| 234 " Name: <input type='text' name='name2'>" |
| 235 " Password: <input type='password' name='password2'>" |
| 236 " <input type='submit' value='Submit'>" |
| 237 "</form>" |
| 238 "</body></html>"); |
| 239 |
| 240 const std::string base_url = BaseUrl(); |
| 241 NSString* result = [NSString |
| 242 stringWithFormat: |
| 243 @"[{\"action\":\"/generic_submit\"," |
| 244 "\"method\":\"post\"," |
| 245 "\"name\":\"login_form1\"," |
| 246 "\"origin\":\"%s\"," |
| 247 "\"fields\":[{\"element\":\"name\",\"type\":\"text\"}," |
| 248 "{\"element\":\"password\",\"type\":\"password\"}," |
| 249 "{\"element\":\"\",\"type\":\"submit\"}]," |
| 250 "\"usernameElement\":\"name\"," |
| 251 "\"usernameValue\":\"\"," |
| 252 "\"passwords\":[{\"element\":\"password\",\"value\":\"\"}]}," |
| 253 "{\"action\":\"/generic_s2\"," |
| 254 "\"method\":\"post\"," |
| 255 "\"name\":\"login_form2\"," |
| 256 "\"origin\":\"%s\"," |
| 257 "\"fields\":[{\"element\":\"name2\",\"type\":\"text\"}," |
| 258 "{\"element\":\"password2\",\"type\":\"password\"}," |
| 259 "{\"element\":\"\",\"type\":\"submit\"}]," |
| 260 "\"usernameElement\":\"name2\"," |
| 261 "\"usernameValue\":\"\"," |
| 262 "\"passwords\":[{\"element\":\"password2\",\"value\":\"\"}]}]", |
| 263 base_url.c_str(), base_url.c_str()]; |
| 264 EXPECT_NSEQ(result, |
| 265 EvaluateJavaScriptWithFormat(@"__gCrWeb.findPasswordForms()")); |
| 266 }; |
| 267 |
| 268 // Test serializing of password forms. |
| 269 TEST_F(PasswordControllerJsTest, GetPasswordFormData) { |
| 270 LoadHtmlAndInject( |
| 271 @"<html><body>" |
| 272 "<form name='np' id='np1' action='/generic_submit' method='post'>" |
| 273 " Name: <input type='text' name='name'>" |
| 274 " Password: <input type='password' name='password'>" |
| 275 " <input type='submit' value='Submit'>" |
| 276 "</form>" |
| 277 "</body></html>"); |
| 278 |
| 279 const std::string base_url = BaseUrl(); |
| 280 NSString* parameter = @"window.document.getElementsByTagName('form')[0]"; |
| 281 NSString* result = [NSString |
| 282 stringWithFormat: |
| 283 @"{\"action\":\"/generic_submit\"," |
| 284 "\"method\":\"post\"," |
| 285 "\"name\":\"np\"," |
| 286 "\"origin\":\"%s\"," |
| 287 "\"fields\":[{\"element\":\"name\",\"type\":\"text\"}," |
| 288 "{\"element\":\"password\",\"type\":\"password\"}," |
| 289 "{\"element\":\"\",\"type\":\"submit\"}]," |
| 290 "\"usernameElement\":\"name\"," |
| 291 "\"usernameValue\":\"\"," |
| 292 "\"passwords\":[{\"element\":\"password\",\"value\":\"\"}]}", |
| 293 base_url.c_str()]; |
| 294 EXPECT_NSEQ( |
| 295 result, |
| 296 EvaluateJavaScriptWithFormat( |
| 297 @"__gCrWeb.stringify(__gCrWeb.getPasswordFormData(%@))", parameter)); |
| 298 }; |
| 299 |
| 300 } // namespace |
OLD | NEW |