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 |