Home | History | Annotate | Download | only in options
      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 
     47 using content::MessageLoopRunner;
     48 
     49 namespace options {
     50 
     51 namespace {
     52 
     53 class 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)
     88 void 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 
     96 bool FrameHasSettingsSourceHost(content::RenderFrameHost* frame) {
     97   return frame->GetLastCommittedURL().DomainIs(
     98       chrome::kChromeUISettingsFrameHost);
     99 }
    100 
    101 }  // namespace
    102 
    103 OptionsUIBrowserTest::OptionsUIBrowserTest() {
    104 }
    105 
    106 void OptionsUIBrowserTest::NavigateToSettings() {
    107   NavigateToSettingsSubpage("");
    108 }
    109 
    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);
    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   // The OnFinishedLoading event, which indicates that all WebUI initialization
    135   // methods have been called on the JS side, is temporally unrelated to whether
    136   // or not the WebContents considers itself to have finished loading. We want
    137   // to wait for this too, however, because, e.g. this is a sufficient condition
    138   // to get the focus properly placed on a form element.
    139   content::WaitForLoadStop(web_contents);
    140 }
    141 
    142 void OptionsUIBrowserTest::NavigateToSettingsFrame() {
    143   const GURL& url = GURL(chrome::kChromeUISettingsFrameURL);
    144   ui_test_utils::NavigateToURL(browser(), url);
    145 }
    146 
    147 void OptionsUIBrowserTest::VerifyNavbar() {
    148   bool navbar_exist = false;
    149   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
    150       browser()->tab_strip_model()->GetActiveWebContents(),
    151       "domAutomationController.send("
    152       "    !!document.getElementById('navigation'))",
    153       &navbar_exist));
    154   EXPECT_EQ(true, navbar_exist);
    155 }
    156 
    157 void OptionsUIBrowserTest::VerifyTitle() {
    158   base::string16 title =
    159       browser()->tab_strip_model()->GetActiveWebContents()->GetTitle();
    160   base::string16 expected_title = l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE);
    161   EXPECT_NE(title.find(expected_title), base::string16::npos);
    162 }
    163 
    164 content::RenderFrameHost* OptionsUIBrowserTest::GetSettingsFrame() {
    165   // NB: The utility function content::FrameHasSourceUrl can't be used because
    166   // the settings frame navigates itself to chrome://settings-frame/settings
    167   // to indicate that it's showing the top-level settings. Therefore, just
    168   // match the host.
    169   return content::FrameMatchingPredicate(
    170       browser()->tab_strip_model()->GetActiveWebContents(),
    171       base::Bind(&FrameHasSettingsSourceHost));
    172 }
    173 
    174 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, LoadOptionsByURL) {
    175   NavigateToSettings();
    176   VerifyTitle();
    177   VerifyNavbar();
    178 }
    179 
    180 // Flaky on win_rel when the profile is deleted crbug.com/103355
    181 // Also related to crbug.com/104851
    182 #if defined(OS_WIN)
    183 #define MAYBE_VerifyManagedSignout DISABLED_VerifyManagedSignout
    184 #else
    185 #define MAYBE_VerifyManagedSignout VerifyManagedSignout
    186 #endif
    187 
    188 #if !defined(OS_CHROMEOS)
    189 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, MAYBE_VerifyManagedSignout) {
    190   SigninManager* signin =
    191       SigninManagerFactory::GetForProfile(browser()->profile());
    192   signin->OnExternalSigninCompleted("test (at) example.com");
    193   signin->ProhibitSignout(true);
    194 
    195   NavigateToSettingsFrame();
    196 
    197   // This script simulates a click on the "Disconnect your Google Account"
    198   // button and returns true if the hidden flag of the appropriate dialog gets
    199   // flipped.
    200   bool result = false;
    201   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    202       browser()->tab_strip_model()->GetActiveWebContents(),
    203       "var dialog = $('manage-profile-overlay-disconnect-managed');"
    204       "var original_status = dialog.hidden;"
    205       "var original = ManageProfileOverlay.showDisconnectManagedProfileDialog;"
    206       "var teststub = function(event) {"
    207       "  original(event);"
    208       "  domAutomationController.send(original_status && !dialog.hidden);"
    209       "};"
    210       "ManageProfileOverlay.showDisconnectManagedProfileDialog = teststub;"
    211       "$('start-stop-sync').click();",
    212       &result));
    213 
    214   EXPECT_TRUE(result);
    215 
    216   base::FilePath profile_dir = browser()->profile()->GetPath();
    217   ProfileInfoCache& profile_info_cache =
    218       g_browser_process->profile_manager()->GetProfileInfoCache();
    219 
    220   EXPECT_TRUE(DirectoryExists(profile_dir));
    221   EXPECT_TRUE(profile_info_cache.GetIndexOfProfileWithPath(profile_dir) !=
    222               std::string::npos);
    223 
    224   content::WindowedNotificationObserver wait_for_profile_deletion(
    225       chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
    226       content::NotificationService::AllSources());
    227 
    228   // TODO(kaliamoorthi): Get the macos problem fixed and remove this code.
    229   // Deleting the Profile also destroys all browser windows of that Profile.
    230   // Wait for the current browser to close before resuming, otherwise
    231   // the browser_tests shutdown code will be confused on the Mac.
    232   content::WindowedNotificationObserver wait_for_browser_closed(
    233       chrome::NOTIFICATION_BROWSER_CLOSED,
    234       content::NotificationService::AllSources());
    235 
    236   ASSERT_TRUE(content::ExecuteScript(
    237       browser()->tab_strip_model()->GetActiveWebContents(),
    238       "$('disconnect-managed-profile-ok').click();"));
    239 
    240   wait_for_profile_deletion.Wait();
    241 
    242   EXPECT_TRUE(profile_info_cache.GetIndexOfProfileWithPath(profile_dir) ==
    243               std::string::npos);
    244 
    245   wait_for_browser_closed.Wait();
    246 }
    247 
    248 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, VerifyUnmanagedSignout) {
    249   SigninManager* signin =
    250       SigninManagerFactory::GetForProfile(browser()->profile());
    251   const std::string user = "test (at) example.com";
    252   signin->OnExternalSigninCompleted(user);
    253 
    254   NavigateToSettingsFrame();
    255 
    256   // This script simulates a click on the "Disconnect your Google Account"
    257   // button and returns true if the hidden flag of the appropriate dialog gets
    258   // flipped.
    259   bool result = false;
    260   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    261       browser()->tab_strip_model()->GetActiveWebContents(),
    262       "var dialog = $('sync-setup-stop-syncing');"
    263       "var original_status = dialog.hidden;"
    264       "$('start-stop-sync').click();"
    265       "domAutomationController.send(original_status && !dialog.hidden);",
    266       &result));
    267 
    268   EXPECT_TRUE(result);
    269 
    270   SignOutWaiter sign_out_waiter(signin);
    271 
    272   ASSERT_TRUE(content::ExecuteScript(
    273       browser()->tab_strip_model()->GetActiveWebContents(),
    274       "$('stop-syncing-ok').click();"));
    275 
    276   sign_out_waiter.Wait();
    277 
    278   EXPECT_TRUE(browser()->profile()->GetProfileName() != user);
    279   EXPECT_TRUE(signin->GetAuthenticatedUsername().empty());
    280 }
    281 
    282 // Regression test for http://crbug.com/301436, excluded on Chrome OS because
    283 // profile management in the settings UI exists on desktop platforms only.
    284 IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, NavigateBackFromOverlayDialog) {
    285   NavigateToSettingsFrame();
    286 
    287   // Click a button that opens an overlay dialog.
    288   content::WebContents* contents =
    289       browser()->tab_strip_model()->GetActiveWebContents();
    290   ASSERT_TRUE(content::ExecuteScript(
    291       contents, "$('manage-default-search-engines').click();"));
    292 
    293   // Go back to the settings page.
    294   content::TestNavigationObserver observer(contents);
    295   chrome::GoBack(browser(), CURRENT_TAB);
    296   observer.Wait();
    297 
    298   // Verify that the settings page lists one profile.
    299   const char javascript[] =
    300       "domAutomationController.send("
    301       "    document.querySelectorAll('list#profiles-list > div[role=listitem]')"
    302       "        .length);";
    303   int profiles;
    304   ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
    305       contents, javascript, &profiles));
    306   EXPECT_EQ(1, profiles);
    307 
    308   // Create a second profile.
    309   ProfileManager* profile_manager = g_browser_process->profile_manager();
    310   const base::FilePath profile_path =
    311       profile_manager->GenerateNextProfileDirectoryPath();
    312 
    313   base::RunLoop run_loop;
    314   profile_manager->CreateProfileAsync(
    315       profile_manager->GenerateNextProfileDirectoryPath(),
    316       base::Bind(&RunClosureWhenProfileInitialized,
    317                  run_loop.QuitClosure()),
    318                  base::string16(),
    319                  base::string16(),
    320                  std::string());
    321   run_loop.Run();
    322 
    323   // Verify that the settings page has updated and lists two profiles.
    324   ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
    325       contents, javascript, &profiles));
    326   EXPECT_EQ(2, profiles);
    327 }
    328 #endif
    329 
    330 }  // namespace options
    331