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.
5 #include "chrome/browser/ui/webui/options/options_ui_browsertest.h"
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"
30 #if !defined(OS_CHROMEOS)
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"
47 using content::MessageLoopRunner;
53 class SignOutWaiter : public SigninManagerBase::Observer {
55 explicit SignOutWaiter(SigninManagerBase* signin_manager)
56 : seen_(false), running_(false), scoped_observer_(this) {
57 scoped_observer_.Add(signin_manager);
59 virtual ~SignOutWaiter() {}
66 message_loop_runner_ = new MessageLoopRunner;
67 message_loop_runner_->Run();
71 virtual void GoogleSignedOut(const std::string& username) OVERRIDE {
76 message_loop_runner_->Quit();
83 ScopedObserver<SigninManagerBase, SignOutWaiter> scoped_observer_;
84 scoped_refptr<MessageLoopRunner> message_loop_runner_;
87 #if !defined(OS_CHROMEOS)
88 void RunClosureWhenProfileInitialized(const base::Closure& closure,
90 Profile::CreateStatus status) {
91 if (status == Profile::CREATE_STATUS_INITIALIZED)
96 bool FrameHasSettingsSourceHost(content::RenderFrameHost* frame) {
97 return frame->GetLastCommittedURL().DomainIs(
98 chrome::kChromeUISettingsFrameHost);
103 OptionsUIBrowserTest::OptionsUIBrowserTest() {
106 void OptionsUIBrowserTest::NavigateToSettings() {
107 NavigateToSettingsSubpage("");
110 void 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);
115 content::WebContents* web_contents =
116 browser()->tab_strip_model()->GetActiveWebContents();
117 ASSERT_TRUE(web_contents);
118 ASSERT_TRUE(web_contents->GetWebUI());
120 content::WebUIController* controller =
121 web_contents->GetWebUI()->GetController();
122 #if !defined(OS_CHROMEOS)
123 controller = static_cast<UberUI*>(controller)->
124 GetSubpage(chrome::kChromeUISettingsFrameURL)->GetController();
126 OptionsUI* options_ui = static_cast<OptionsUI*>(controller);
128 // It is not possible to subscribe to the OnFinishedLoading event before the
129 // call to NavigateToURL(), because the WebUI does not yet exist at that time.
130 // However, it is safe to subscribe afterwards, because the event will always
131 // be posted asynchronously to the message loop.
132 scoped_refptr<MessageLoopRunner> message_loop_runner(new MessageLoopRunner);
133 scoped_ptr<OptionsUI::OnFinishedLoadingCallbackList::Subscription>
134 subscription = options_ui->RegisterOnFinishedLoadingCallback(
135 message_loop_runner->QuitClosure());
136 message_loop_runner->Run();
138 // The OnFinishedLoading event, which indicates that all WebUI initialization
139 // methods have been called on the JS side, is temporally unrelated to whether
140 // or not the WebContents considers itself to have finished loading. We want
141 // to wait for this too, however, because, e.g. this is a sufficient condition
142 // to get the focus properly placed on a form element.
143 content::WaitForLoadStop(web_contents);
146 void OptionsUIBrowserTest::NavigateToSettingsFrame() {
147 const GURL& url = GURL(chrome::kChromeUISettingsFrameURL);
148 ui_test_utils::NavigateToURL(browser(), url);
151 void OptionsUIBrowserTest::VerifyNavbar() {
152 bool navbar_exist = false;
153 #if defined(OS_CHROMEOS)
154 bool should_navbar_exist = false;
156 bool should_navbar_exist = true;
158 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
159 browser()->tab_strip_model()->GetActiveWebContents(),
160 "domAutomationController.send("
161 " !!document.getElementById('navigation'))",
163 EXPECT_EQ(should_navbar_exist, navbar_exist);
166 void OptionsUIBrowserTest::VerifyTitle() {
167 base::string16 title =
168 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle();
169 base::string16 expected_title = l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE);
170 EXPECT_NE(title.find(expected_title), base::string16::npos);
173 content::RenderFrameHost* OptionsUIBrowserTest::GetSettingsFrame() {
174 // NB: The utility function content::FrameHasSourceUrl can't be used because
175 // the settings frame navigates itself to chrome://settings-frame/settings
176 // to indicate that it's showing the top-level settings. Therefore, just
178 return content::FrameMatchingPredicate(
179 browser()->tab_strip_model()->GetActiveWebContents(),
180 base::Bind(&FrameHasSettingsSourceHost));
183 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, LoadOptionsByURL) {
184 NavigateToSettings();
189 // Flaky on win_rel when the profile is deleted crbug.com/103355
190 // Also related to crbug.com/104851
192 #define MAYBE_VerifyManagedSignout DISABLED_VerifyManagedSignout
194 #define MAYBE_VerifyManagedSignout VerifyManagedSignout
197 #if !defined(OS_CHROMEOS)
198 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, MAYBE_VerifyManagedSignout) {
199 SigninManager* signin =
200 SigninManagerFactory::GetForProfile(browser()->profile());
201 signin->OnExternalSigninCompleted("test@example.com");
202 signin->ProhibitSignout(true);
204 NavigateToSettingsFrame();
206 // This script simulates a click on the "Disconnect your Google Account"
207 // button and returns true if the hidden flag of the appropriate dialog gets
210 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
211 browser()->tab_strip_model()->GetActiveWebContents(),
212 "var dialog = $('manage-profile-overlay-disconnect-managed');"
213 "var original_status = dialog.hidden;"
214 "var original = ManageProfileOverlay.showDisconnectManagedProfileDialog;"
215 "var teststub = function(event) {"
217 " domAutomationController.send(original_status && !dialog.hidden);"
219 "ManageProfileOverlay.showDisconnectManagedProfileDialog = teststub;"
220 "$('start-stop-sync').click();",
225 base::FilePath profile_dir = browser()->profile()->GetPath();
226 ProfileInfoCache& profile_info_cache =
227 g_browser_process->profile_manager()->GetProfileInfoCache();
229 EXPECT_TRUE(DirectoryExists(profile_dir));
230 EXPECT_TRUE(profile_info_cache.GetIndexOfProfileWithPath(profile_dir) !=
233 content::WindowedNotificationObserver wait_for_profile_deletion(
234 chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
235 content::NotificationService::AllSources());
237 // TODO(kaliamoorthi): Get the macos problem fixed and remove this code.
238 // Deleting the Profile also destroys all browser windows of that Profile.
239 // Wait for the current browser to close before resuming, otherwise
240 // the browser_tests shutdown code will be confused on the Mac.
241 content::WindowedNotificationObserver wait_for_browser_closed(
242 chrome::NOTIFICATION_BROWSER_CLOSED,
243 content::NotificationService::AllSources());
245 ASSERT_TRUE(content::ExecuteScript(
246 browser()->tab_strip_model()->GetActiveWebContents(),
247 "$('disconnect-managed-profile-ok').click();"));
249 wait_for_profile_deletion.Wait();
251 EXPECT_TRUE(profile_info_cache.GetIndexOfProfileWithPath(profile_dir) ==
254 wait_for_browser_closed.Wait();
257 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, VerifyUnmanagedSignout) {
258 SigninManager* signin =
259 SigninManagerFactory::GetForProfile(browser()->profile());
260 const std::string user = "test@example.com";
261 signin->OnExternalSigninCompleted(user);
263 NavigateToSettingsFrame();
265 // This script simulates a click on the "Disconnect your Google Account"
266 // button and returns true if the hidden flag of the appropriate dialog gets
269 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
270 browser()->tab_strip_model()->GetActiveWebContents(),
271 "var dialog = $('sync-setup-stop-syncing');"
272 "var original_status = dialog.hidden;"
273 "$('start-stop-sync').click();"
274 "domAutomationController.send(original_status && !dialog.hidden);",
279 SignOutWaiter sign_out_waiter(signin);
281 ASSERT_TRUE(content::ExecuteScript(
282 browser()->tab_strip_model()->GetActiveWebContents(),
283 "$('stop-syncing-ok').click();"));
285 sign_out_waiter.Wait();
287 EXPECT_TRUE(browser()->profile()->GetProfileName() != user);
288 EXPECT_TRUE(signin->GetAuthenticatedUsername().empty());
291 // Regression test for http://crbug.com/301436, excluded on Chrome OS because
292 // profile management in the settings UI exists on desktop platforms only.
293 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, NavigateBackFromOverlayDialog) {
294 NavigateToSettingsFrame();
296 // Click a button that opens an overlay dialog.
297 content::WebContents* contents =
298 browser()->tab_strip_model()->GetActiveWebContents();
299 ASSERT_TRUE(content::ExecuteScript(
300 contents, "$('manage-default-search-engines').click();"));
302 // Go back to the settings page.
303 content::TestNavigationObserver observer(contents);
304 chrome::GoBack(browser(), CURRENT_TAB);
307 // Verify that the settings page lists one profile.
308 const char javascript[] =
309 "domAutomationController.send("
310 " document.querySelectorAll('list#profiles-list > div[role=listitem]')"
313 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
314 contents, javascript, &profiles));
315 EXPECT_EQ(1, profiles);
317 // Create a second profile.
318 ProfileManager* profile_manager = g_browser_process->profile_manager();
319 const base::FilePath profile_path =
320 profile_manager->GenerateNextProfileDirectoryPath();
322 base::RunLoop run_loop;
323 profile_manager->CreateProfileAsync(
324 profile_manager->GenerateNextProfileDirectoryPath(),
325 base::Bind(&RunClosureWhenProfileInitialized,
326 run_loop.QuitClosure()),
332 // Verify that the settings page has updated and lists two profiles.
333 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
334 contents, javascript, &profiles));
335 EXPECT_EQ(2, profiles);
339 } // namespace options