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