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