// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_TEST_UI_TEST_UTILS_H_ #define CHROME_TEST_UI_TEST_UTILS_H_ #pragma once #include #include #include #include "app/keyboard_codes.h" #include "base/basictypes.h" #include "base/message_loop.h" #include "base/scoped_temp_dir.h" #include "base/string16.h" #include "chrome/browser/view_ids.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" #include "chrome/test/automation/dom_element_proxy.h" #include "gfx/native_widget_types.h" class AppModalDialog; class BookmarkModel; class BookmarkNode; class Browser; class CommandLine; class DownloadManager; class ExtensionAction; class FilePath; class GURL; class MessageLoop; class NavigationController; class NotificationType; class Profile; class RenderViewHost; class ScopedTempDir; class TabContents; class Value; // A collections of functions designed for use with InProcessBrowserTest. namespace ui_test_utils { // Turns on nestable tasks, runs the message loop, then resets nestable tasks // to what they were originally. Prefer this over MessageLoop::Run for in // process browser tests that need to block until a condition is met. void RunMessageLoop(); // Turns on nestable tasks, runs all pending tasks in the message loop, // then resets nestable tasks to what they were originally. Prefer this // over MessageLoop::RunAllPending for in process browser tests to run // all pending tasks. void RunAllPendingInMessageLoop(); // Puts the current tab title in |title|. Returns true on success. bool GetCurrentTabTitle(const Browser* browser, string16* title); // Waits for the current tab to complete the navigation. Returns true on // success. bool WaitForNavigationInCurrentTab(Browser* browser); // Waits for the current tab to complete the specified number of navigations. // Returns true on success. bool WaitForNavigationsInCurrentTab(Browser* browser, int number_of_navigations); // Waits for |controller| to complete a navigation. This blocks until // the navigation finishes. void WaitForNavigation(NavigationController* controller); // Waits for |controller| to complete a navigation. This blocks until // the specified number of navigations complete. void WaitForNavigations(NavigationController* controller, int number_of_navigations); // Waits for a new tab to be added to |browser|. void WaitForNewTab(Browser* browser); // Waits for a |browser_action| to be updated. void WaitForBrowserActionUpdated(ExtensionAction* browser_action); // Waits for a load stop for the specified |controller|. void WaitForLoadStop(NavigationController* controller); // Waits for a new browser to be created, returning the browser. Browser* WaitForNewBrowser(); // Opens |url| in an incognito browser window with the off the record profile of // |profile|, blocking until the navigation finishes. This will create a new // browser if a browser with the off the record profile does not exist. void OpenURLOffTheRecord(Profile* profile, const GURL& url); // Navigates the selected tab of |browser| to |url|, blocking until the // navigation finishes. void NavigateToURL(Browser* browser, const GURL& url); // Navigates the selected tab of |browser| to |url|, blocking until the // number of navigations specified complete. void NavigateToURLBlockUntilNavigationsComplete(Browser* browser, const GURL& url, int number_of_navigations); // Gets the DOMDocument for the active tab in |browser|. // Returns a NULL reference on failure. DOMElementProxyRef GetActiveDOMDocument(Browser* browser); // Executes the passed |script| in the frame pointed to by |frame_xpath| (use // empty string for main frame). The |script| should not invoke // domAutomationController.send(); otherwise, your test will hang or be flaky. // If you want to extract a result, use one of the below functions. // Returns true on success. bool ExecuteJavaScript(RenderViewHost* render_view_host, const std::wstring& frame_xpath, const std::wstring& script) WARN_UNUSED_RESULT; // The following methods executes the passed |script| in the frame pointed to by // |frame_xpath| (use empty string for main frame) and sets |result| to the // value returned by the script evaluation. // They return true on success, false if the script evaluation failed or did not // evaluate to the expected type. // Note: In order for the domAutomationController to work, you must call // EnableDOMAutomation() in your test first. bool ExecuteJavaScriptAndExtractInt(RenderViewHost* render_view_host, const std::wstring& frame_xpath, const std::wstring& script, int* result) WARN_UNUSED_RESULT; bool ExecuteJavaScriptAndExtractBool(RenderViewHost* render_view_host, const std::wstring& frame_xpath, const std::wstring& script, bool* result) WARN_UNUSED_RESULT; bool ExecuteJavaScriptAndExtractString(RenderViewHost* render_view_host, const std::wstring& frame_xpath, const std::wstring& script, std::string* result) WARN_UNUSED_RESULT; // Generate the file path for testing a particular test. // The file for the tests is all located in // test_root_directory/dir/ // The returned path is FilePath format. FilePath GetTestFilePath(const FilePath& dir, const FilePath& file); // Generate the URL for testing a particular test. // HTML for the tests is all located in // test_root_directory/dir/ // The returned path is GURL format. GURL GetTestUrl(const FilePath& dir, const FilePath& file); // Creates an observer that waits for |download_manager| to report that it // has a total of |count| downloads that have been handles void WaitForDownloadCount(DownloadManager* download_manager, size_t count); // Blocks until an application modal dialog is showns and returns it. AppModalDialog* WaitForAppModalDialog(); // Causes the specified tab to crash. Blocks until it is crashed. void CrashTab(TabContents* tab); // Waits for the focus to change in the specified RenderViewHost. void WaitForFocusChange(RenderViewHost* rvh); // Waits for the renderer to return focus to the browser (happens through tab // traversal). void WaitForFocusInBrowser(Browser* browser); // Performs a find in the page of the specified tab. Returns the number of // matches found. |ordinal| is an optional parameter which is set to the index // of the current match. int FindInPage(TabContents* tab, const string16& search_string, bool forward, bool case_sensitive, int* ordinal); // Returns true if the View is focused. bool IsViewFocused(const Browser* browser, ViewID vid); // Simulates a mouse click on a View in the browser. void ClickOnView(const Browser* browser, ViewID vid); // Blocks until a notification for given |type| is received. void WaitForNotification(NotificationType type); // Register |observer| for the given |type| and |source| and run // the message loop until the observer posts a quit task. void RegisterAndWait(NotificationObserver* observer, NotificationType type, const NotificationSource& source); // Blocks until |model| finishes loading. void WaitForBookmarkModelToLoad(BookmarkModel* model); // Blocks until the |browser|'s history finishes loading. void WaitForHistoryToLoad(Browser* browser); // Puts the native window for |browser| in |native_window|. Returns true on // success. bool GetNativeWindow(const Browser* browser, gfx::NativeWindow* native_window) WARN_UNUSED_RESULT; // Brings the native window for |browser| to the foreground. Returns true on // success. bool BringBrowserWindowToFront(const Browser* browser) WARN_UNUSED_RESULT; // Sends a key press, blocking until the key press is received or the test times // out. This uses ui_controls::SendKeyPress, see it for details. Returns true // if the event was successfully sent and received. bool SendKeyPressSync(const Browser* browser, app::KeyboardCode key, bool control, bool shift, bool alt, bool command) WARN_UNUSED_RESULT; // Sends a key press, blocking until both the key press and a notification from // |source| of type |type| are received, or until the test times out. This uses // ui_controls::SendKeyPress, see it for details. Returns true if the event was // successfully sent and both the event and notification were received. bool SendKeyPressAndWait(const Browser* browser, app::KeyboardCode key, bool control, bool shift, bool alt, bool command, NotificationType type, const NotificationSource& source) WARN_UNUSED_RESULT; // Run a message loop only for the specified amount of time. class TimedMessageLoopRunner { public: // Create new MessageLoopForUI and attach to it. TimedMessageLoopRunner(); // Attach to an existing message loop. explicit TimedMessageLoopRunner(MessageLoop* loop) : loop_(loop), owned_(false), quit_loop_invoked_(false) {} ~TimedMessageLoopRunner(); // Run the message loop for ms milliseconds. void RunFor(int ms); // Post Quit task to the message loop. void Quit(); // Post delayed Quit task to the message loop. void QuitAfter(int ms); bool WasTimedOut() const { return !quit_loop_invoked_; } private: MessageLoop* loop_; bool owned_; bool quit_loop_invoked_; DISALLOW_COPY_AND_ASSIGN(TimedMessageLoopRunner); }; // This is a utility class for running a python websocket server // during tests. The server is started during the construction of the // object, and is stopped when the destructor is called. Note that // because of the underlying script that is used: // // third_paty/WebKit/WebKitTools/Scripts/new-run-webkit-websocketserver // // Only *_wsh.py handlers found under "http/tests/websocket/tests" from the // |root_directory| will be found and active while running the test // server. class TestWebSocketServer { public: // Creates and starts a python websocket server with |root_directory|. explicit TestWebSocketServer(const FilePath& root_directory); // Destroys and stops the server. ~TestWebSocketServer(); private: // Sets up PYTHONPATH to run websocket_server.py. void SetPythonPath(); // Creates a CommandLine for invoking the python interpreter. CommandLine* CreatePythonCommandLine(); // Creates a CommandLine for invoking the python websocker server. CommandLine* CreateWebSocketServerCommandLine(); // A Scoped temporary directory for holding the python pid file. ScopedTempDir temp_dir_; // Used to close the same python interpreter when server falls out // scope. FilePath websocket_pid_file_; DISALLOW_COPY_AND_ASSIGN(TestWebSocketServer); }; // A notification observer which quits the message loop when a notification // is received. It also records the source and details of the notification. class TestNotificationObserver : public NotificationObserver { public: TestNotificationObserver() : source_(NotificationService::AllSources()) { } virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { source_ = source; details_ = details; MessageLoopForUI::current()->Quit(); } const NotificationSource& source() const { return source_; } const NotificationDetails& details() const { return details_; } private: NotificationSource source_; NotificationDetails details_; }; // A WindowedNotificationObserver allows code to watch for a notification // over a window of time. Typically testing code will need to do something // like this: // PerformAction() // WaitForCompletionNotification() // This leads to flakiness as there's a window between PerformAction returning // and the observers getting registered, where a notification will be missed. // // Rather, one can do this: // WindowedNotificationObserver signal(...) // PerformAction() // signal.Wait() class WindowedNotificationObserver : public NotificationObserver { public: /* Register to listen for notifications of the given type from either * a specific source, or from all sources if |source| is * NotificationService::AllSources(). */ WindowedNotificationObserver(NotificationType notification_type, const NotificationSource& source); /* Wait until the specified notification occurs. You must have specified a * source in the arguments to the constructor in order to use this function. * Otherwise, you should use WaitFor. */ void Wait(); /* WaitFor waits until the given notification type is received from the * given object. If the notification was emitted between the construction of * this object and this call then it returns immediately. * * Beware that this is inheriently plagued by ABA issues. Consider: * WindowedNotificationObserver is created, listening for notifications from * all sources * Object A is created with address x and fires a notification * Object A is freed * Object B is created with the same address * WaitFor is called with the address of B * * In this case, WaitFor will return immediately because of the * notification from A (because they shared an address), despite being * different objects. */ void WaitFor(const NotificationSource& source); virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details); private: bool seen_; bool running_; std::set sources_seen_; NotificationSource waiting_for_; NotificationRegistrar registrar_; DISALLOW_COPY_AND_ASSIGN(WindowedNotificationObserver); }; // Similar to WindowedNotificationObserver but also provides a way of retrieving // the details associated with the notification. // Note that in order to use that class the details class should be copiable, // which is the case with most notifications. template class WindowedNotificationObserverWithDetails : public WindowedNotificationObserver { public: WindowedNotificationObserverWithDetails(NotificationType notification_type, const T* source) : WindowedNotificationObserver(notification_type, Source(source)) {} // Fills |details| with the details of the notification received for |source|. bool GetDetailsFor(T* source, U* details) { typename std::map::const_iterator iter = details_.find(source); if (iter == details_.end()) return false; *details = iter->second; return true; } virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { details_[Source(source).ptr()] = *Details(details).ptr(); WindowedNotificationObserver::Observe(type, source, details); } private: std::map details_; DISALLOW_COPY_AND_ASSIGN(WindowedNotificationObserverWithDetails); }; // Hide a native window. void HideNativeWindow(gfx::NativeWindow window); // Show and focus a native window. void ShowAndFocusNativeWindow(gfx::NativeWindow window); } // namespace ui_test_utils #endif // CHROME_TEST_UI_TEST_UTILS_H_