| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 "chrome/browser/ui/webui/options/options_ui_browsertest.h" | |
| 6 | |
| 7 #include "base/scoped_observer.h" | |
| 8 #include "base/strings/string16.h" | |
| 9 #include "base/strings/utf_string_conversions.h" | |
| 10 #include "build/build_config.h" | |
| 11 #include "chrome/browser/chrome_notification_types.h" | |
| 12 #include "chrome/browser/signin/account_tracker_service_factory.h" | |
| 13 #include "chrome/browser/signin/signin_manager_factory.h" | |
| 14 #include "chrome/browser/ui/browser.h" | |
| 15 #include "chrome/browser/ui/chrome_pages.h" | |
| 16 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
| 17 #include "chrome/browser/ui/webui/options/options_ui.h" | |
| 18 #include "chrome/browser/ui/webui/uber/uber_ui.h" | |
| 19 #include "chrome/common/chrome_features.h" | |
| 20 #include "chrome/common/url_constants.h" | |
| 21 #include "chrome/grit/generated_resources.h" | |
| 22 #include "chrome/test/base/ui_test_utils.h" | |
| 23 #include "components/prefs/pref_service.h" | |
| 24 #include "components/signin/core/browser/account_tracker_service.h" | |
| 25 #include "components/signin/core/browser/signin_manager.h" | |
| 26 #include "components/strings/grit/components_strings.h" | |
| 27 #include "content/public/browser/notification_service.h" | |
| 28 #include "content/public/browser/render_frame_host.h" | |
| 29 #include "content/public/browser/web_contents.h" | |
| 30 #include "content/public/test/browser_test_utils.h" | |
| 31 #include "content/public/test/test_utils.h" | |
| 32 #include "ui/base/l10n/l10n_util.h" | |
| 33 | |
| 34 #if !defined(OS_CHROMEOS) | |
| 35 #include <string> | |
| 36 | |
| 37 #include "base/bind.h" | |
| 38 #include "base/callback.h" | |
| 39 #include "base/files/file_path.h" | |
| 40 #include "base/run_loop.h" | |
| 41 #include "chrome/browser/browser_process.h" | |
| 42 #include "chrome/browser/profiles/profile.h" | |
| 43 #include "chrome/browser/profiles/profile_attributes_storage.h" | |
| 44 #include "chrome/browser/profiles/profile_manager.h" | |
| 45 #include "chrome/browser/ui/browser_commands.h" | |
| 46 #include "content/public/test/test_navigation_observer.h" | |
| 47 #include "ui/base/window_open_disposition.h" | |
| 48 #include "url/gurl.h" | |
| 49 #endif | |
| 50 | |
| 51 using content::MessageLoopRunner; | |
| 52 | |
| 53 namespace options { | |
| 54 | |
| 55 namespace { | |
| 56 | |
| 57 class SignOutWaiter : public SigninManagerBase::Observer { | |
| 58 public: | |
| 59 explicit SignOutWaiter(SigninManagerBase* signin_manager) | |
| 60 : seen_(false), running_(false), scoped_observer_(this) { | |
| 61 scoped_observer_.Add(signin_manager); | |
| 62 } | |
| 63 ~SignOutWaiter() override {} | |
| 64 | |
| 65 void Wait() { | |
| 66 if (seen_) | |
| 67 return; | |
| 68 | |
| 69 running_ = true; | |
| 70 message_loop_runner_ = new MessageLoopRunner; | |
| 71 message_loop_runner_->Run(); | |
| 72 EXPECT_TRUE(seen_); | |
| 73 } | |
| 74 | |
| 75 void GoogleSignedOut(const std::string& account_id, | |
| 76 const std::string& username) override { | |
| 77 seen_ = true; | |
| 78 if (!running_) | |
| 79 return; | |
| 80 | |
| 81 message_loop_runner_->Quit(); | |
| 82 running_ = false; | |
| 83 } | |
| 84 | |
| 85 private: | |
| 86 bool seen_; | |
| 87 bool running_; | |
| 88 ScopedObserver<SigninManagerBase, SignOutWaiter> scoped_observer_; | |
| 89 scoped_refptr<MessageLoopRunner> message_loop_runner_; | |
| 90 }; | |
| 91 | |
| 92 #if !defined(OS_CHROMEOS) | |
| 93 void RunClosureWhenProfileInitialized(const base::Closure& closure, | |
| 94 Profile* profile, | |
| 95 Profile::CreateStatus status) { | |
| 96 if (status == Profile::CREATE_STATUS_INITIALIZED) | |
| 97 closure.Run(); | |
| 98 } | |
| 99 #endif | |
| 100 | |
| 101 bool FrameHasSettingsSourceHost(content::RenderFrameHost* frame) { | |
| 102 return frame->GetLastCommittedURL().DomainIs( | |
| 103 chrome::kChromeUISettingsFrameHost); | |
| 104 } | |
| 105 | |
| 106 } // namespace | |
| 107 | |
| 108 OptionsUIBrowserTest::OptionsUIBrowserTest() { | |
| 109 } | |
| 110 | |
| 111 void OptionsUIBrowserTest::SetUpInProcessBrowserTestFixture() { | |
| 112 InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); | |
| 113 disable_md_settings_.InitAndDisableFeature(features::kMaterialDesignSettings); | |
| 114 } | |
| 115 | |
| 116 void OptionsUIBrowserTest::NavigateToSettings() { | |
| 117 NavigateToSettingsSubpage(""); | |
| 118 } | |
| 119 | |
| 120 void OptionsUIBrowserTest::NavigateToSettingsSubpage( | |
| 121 const std::string& sub_page) { | |
| 122 const GURL& url = chrome::GetSettingsUrl(sub_page); | |
| 123 ui_test_utils::NavigateToURLWithDisposition( | |
| 124 browser(), url, WindowOpenDisposition::CURRENT_TAB, 0); | |
| 125 | |
| 126 content::WebContents* web_contents = | |
| 127 browser()->tab_strip_model()->GetActiveWebContents(); | |
| 128 ASSERT_TRUE(web_contents); | |
| 129 ASSERT_TRUE(web_contents->GetWebUI()); | |
| 130 | |
| 131 content::WebUIController* controller = | |
| 132 web_contents->GetWebUI()->GetController(); | |
| 133 #if !defined(OS_CHROMEOS) | |
| 134 controller = static_cast<UberUI*>(controller)-> | |
| 135 GetSubpage(chrome::kChromeUISettingsFrameURL)->GetController(); | |
| 136 #endif | |
| 137 OptionsUI* options_ui = static_cast<OptionsUI*>(controller); | |
| 138 | |
| 139 // It is not possible to subscribe to the OnFinishedLoading event before the | |
| 140 // call to NavigateToURL(), because the WebUI does not yet exist at that time. | |
| 141 // However, it is safe to subscribe afterwards, because the event will always | |
| 142 // be posted asynchronously to the message loop. | |
| 143 scoped_refptr<MessageLoopRunner> message_loop_runner(new MessageLoopRunner); | |
| 144 std::unique_ptr<OptionsUI::OnFinishedLoadingCallbackList::Subscription> | |
| 145 subscription = options_ui->RegisterOnFinishedLoadingCallback( | |
| 146 message_loop_runner->QuitClosure()); | |
| 147 message_loop_runner->Run(); | |
| 148 | |
| 149 // The OnFinishedLoading event, which indicates that all WebUI initialization | |
| 150 // methods have been called on the JS side, is temporally unrelated to whether | |
| 151 // or not the WebContents considers itself to have finished loading. We want | |
| 152 // to wait for this too, however, because, e.g. this is a sufficient condition | |
| 153 // to get the focus properly placed on a form element. | |
| 154 content::WaitForLoadStop(web_contents); | |
| 155 } | |
| 156 | |
| 157 void OptionsUIBrowserTest::NavigateToSettingsFrame() { | |
| 158 const GURL& url = GURL(chrome::kChromeUISettingsFrameURL); | |
| 159 ui_test_utils::NavigateToURL(browser(), url); | |
| 160 } | |
| 161 | |
| 162 void OptionsUIBrowserTest::VerifyNavbar() { | |
| 163 bool navbar_exist = false; | |
| 164 #if defined(OS_CHROMEOS) | |
| 165 bool should_navbar_exist = false; | |
| 166 #else | |
| 167 bool should_navbar_exist = true; | |
| 168 #endif | |
| 169 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( | |
| 170 browser()->tab_strip_model()->GetActiveWebContents(), | |
| 171 "domAutomationController.send(" | |
| 172 " !!document.getElementById('navigation'))", | |
| 173 &navbar_exist)); | |
| 174 EXPECT_EQ(should_navbar_exist, navbar_exist); | |
| 175 } | |
| 176 | |
| 177 void OptionsUIBrowserTest::VerifyTitle() { | |
| 178 base::string16 title = | |
| 179 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle(); | |
| 180 base::string16 expected_title = l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE); | |
| 181 EXPECT_NE(title.find(expected_title), base::string16::npos); | |
| 182 } | |
| 183 | |
| 184 content::RenderFrameHost* OptionsUIBrowserTest::GetSettingsFrame() { | |
| 185 // NB: The utility function content::FrameHasSourceUrl can't be used because | |
| 186 // the settings frame navigates itself to chrome://settings-frame/settings | |
| 187 // to indicate that it's showing the top-level settings. Therefore, just | |
| 188 // match the host. | |
| 189 return content::FrameMatchingPredicate( | |
| 190 browser()->tab_strip_model()->GetActiveWebContents(), | |
| 191 base::Bind(&FrameHasSettingsSourceHost)); | |
| 192 } | |
| 193 | |
| 194 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, LoadOptionsByURL) { | |
| 195 NavigateToSettings(); | |
| 196 VerifyTitle(); | |
| 197 VerifyNavbar(); | |
| 198 } | |
| 199 | |
| 200 // Flaky on Linux, Mac and Win: http://crbug.com/469113 | |
| 201 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) | |
| 202 #define MAYBE_VerifyManagedSignout DISABLED_VerifyManagedSignout | |
| 203 #else | |
| 204 #define MAYBE_VerifyManagedSignout VerifyManagedSignout | |
| 205 #endif | |
| 206 | |
| 207 #if !defined(OS_CHROMEOS) | |
| 208 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, MAYBE_VerifyManagedSignout) { | |
| 209 SigninManager* signin = | |
| 210 SigninManagerFactory::GetForProfile(browser()->profile()); | |
| 211 signin->OnExternalSigninCompleted("test@example.com"); | |
| 212 signin->ProhibitSignout(true); | |
| 213 | |
| 214 NavigateToSettingsFrame(); | |
| 215 | |
| 216 // This script simulates a click on the "Disconnect your Google Account" | |
| 217 // button and returns true if the hidden flag of the appropriate dialog gets | |
| 218 // flipped. | |
| 219 bool result = false; | |
| 220 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( | |
| 221 browser()->tab_strip_model()->GetActiveWebContents(), | |
| 222 "var dialog = $('manage-profile-overlay-disconnect-managed');" | |
| 223 "var original_status = dialog.hidden;" | |
| 224 "var original = ManageProfileOverlay.showDisconnectManagedProfileDialog;" | |
| 225 "var teststub = function(event) {" | |
| 226 " original(event);" | |
| 227 " domAutomationController.send(original_status && !dialog.hidden);" | |
| 228 "};" | |
| 229 "ManageProfileOverlay.showDisconnectManagedProfileDialog = teststub;" | |
| 230 "$('start-stop-sync').click();", | |
| 231 &result)); | |
| 232 | |
| 233 EXPECT_TRUE(result); | |
| 234 | |
| 235 base::FilePath profile_dir = browser()->profile()->GetPath(); | |
| 236 ProfileAttributesStorage& storage = | |
| 237 g_browser_process->profile_manager()->GetProfileAttributesStorage(); | |
| 238 ProfileAttributesEntry* entry; | |
| 239 | |
| 240 EXPECT_TRUE(DirectoryExists(profile_dir)); | |
| 241 EXPECT_TRUE(storage.GetProfileAttributesWithPath(profile_dir, &entry)); | |
| 242 | |
| 243 // TODO(kaliamoorthi): Get the macos problem fixed and remove this code. | |
| 244 // Deleting the Profile also destroys all browser windows of that Profile. | |
| 245 // Wait for the current browser to close before resuming, otherwise | |
| 246 // the browser_tests shutdown code will be confused on the Mac. | |
| 247 content::WindowedNotificationObserver wait_for_browser_closed( | |
| 248 chrome::NOTIFICATION_BROWSER_CLOSED, | |
| 249 content::NotificationService::AllSources()); | |
| 250 | |
| 251 ASSERT_TRUE(content::ExecuteScript( | |
| 252 browser()->tab_strip_model()->GetActiveWebContents(), | |
| 253 "$('disconnect-managed-profile-ok').click();")); | |
| 254 | |
| 255 EXPECT_TRUE(storage.GetProfileAttributesWithPath(profile_dir, &entry)); | |
| 256 | |
| 257 wait_for_browser_closed.Wait(); | |
| 258 } | |
| 259 | |
| 260 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, VerifyUnmanagedSignout) { | |
| 261 const std::string user = "test@example.com"; | |
| 262 AccountTrackerServiceFactory::GetForProfile(browser()->profile()) | |
| 263 ->SeedAccountInfo("12345", user); | |
| 264 SigninManager* signin = | |
| 265 SigninManagerFactory::GetForProfile(browser()->profile()); | |
| 266 signin->OnExternalSigninCompleted(user); | |
| 267 | |
| 268 NavigateToSettingsFrame(); | |
| 269 | |
| 270 // This script simulates a click on the "Disconnect your Google Account" | |
| 271 // button and returns true if the hidden flag of the appropriate dialog gets | |
| 272 // flipped. | |
| 273 bool result = false; | |
| 274 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( | |
| 275 browser()->tab_strip_model()->GetActiveWebContents(), | |
| 276 "var dialog = $('sync-setup-stop-syncing');" | |
| 277 "var original_status = dialog.hidden;" | |
| 278 "$('start-stop-sync').click();" | |
| 279 "domAutomationController.send(original_status && !dialog.hidden);", | |
| 280 &result)); | |
| 281 | |
| 282 EXPECT_TRUE(result); | |
| 283 | |
| 284 SignOutWaiter sign_out_waiter(signin); | |
| 285 | |
| 286 ASSERT_TRUE(content::ExecuteScript( | |
| 287 browser()->tab_strip_model()->GetActiveWebContents(), | |
| 288 "$('stop-syncing-ok').click();")); | |
| 289 | |
| 290 sign_out_waiter.Wait(); | |
| 291 | |
| 292 EXPECT_TRUE(browser()->profile()->GetProfileUserName() != user); | |
| 293 EXPECT_FALSE(signin->IsAuthenticated()); | |
| 294 } | |
| 295 | |
| 296 // Regression test for http://crbug.com/301436, excluded on Chrome OS because | |
| 297 // profile management in the settings UI exists on desktop platforms only. | |
| 298 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, NavigateBackFromOverlayDialog) { | |
| 299 NavigateToSettingsFrame(); | |
| 300 | |
| 301 // Click a button that opens an overlay dialog. | |
| 302 content::WebContents* contents = | |
| 303 browser()->tab_strip_model()->GetActiveWebContents(); | |
| 304 ASSERT_TRUE(content::ExecuteScript( | |
| 305 contents, "$('manage-default-search-engines').click();")); | |
| 306 | |
| 307 // Go back to the settings page. | |
| 308 content::TestNavigationObserver observer(contents); | |
| 309 chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB); | |
| 310 observer.Wait(); | |
| 311 | |
| 312 // Verify that the settings page lists one profile. | |
| 313 const char javascript[] = | |
| 314 "domAutomationController.send(" | |
| 315 " document.querySelectorAll('list#profiles-list > div[role=listitem]')" | |
| 316 " .length);"; | |
| 317 int profiles; | |
| 318 ASSERT_TRUE(content::ExecuteScriptAndExtractInt( | |
| 319 contents, javascript, &profiles)); | |
| 320 EXPECT_EQ(1, profiles); | |
| 321 | |
| 322 // Create a second profile. | |
| 323 ProfileManager* profile_manager = g_browser_process->profile_manager(); | |
| 324 const base::FilePath profile_path = | |
| 325 profile_manager->GenerateNextProfileDirectoryPath(); | |
| 326 | |
| 327 base::RunLoop run_loop; | |
| 328 profile_manager->CreateProfileAsync( | |
| 329 profile_manager->GenerateNextProfileDirectoryPath(), | |
| 330 base::Bind(&RunClosureWhenProfileInitialized, | |
| 331 run_loop.QuitClosure()), | |
| 332 base::string16(), | |
| 333 std::string(), | |
| 334 std::string()); | |
| 335 run_loop.Run(); | |
| 336 | |
| 337 // Verify that the settings page has updated and lists two profiles. | |
| 338 ASSERT_TRUE(content::ExecuteScriptAndExtractInt( | |
| 339 contents, javascript, &profiles)); | |
| 340 EXPECT_EQ(2, profiles); | |
| 341 } | |
| 342 #endif | |
| 343 | |
| 344 } // namespace options | |
| OLD | NEW |