diff options
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/automation/automation_provider_observers.cc | 24 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider_observers.h | 22 | ||||
-rw-r--r-- | chrome/browser/automation/testing_automation_provider.cc | 88 | ||||
-rw-r--r-- | chrome/browser/automation/testing_automation_provider.h | 18 | ||||
-rw-r--r-- | chrome/browser/notifications/balloon_collection.cc | 8 | ||||
-rw-r--r-- | chrome/browser/notifications/balloon_collection.h | 11 | ||||
-rw-r--r-- | chrome/test/data/notifications/notification_tester.html | 98 | ||||
-rw-r--r-- | chrome/test/functional/PYAUTO_TESTS | 1 | ||||
-rw-r--r-- | chrome/test/functional/notifications.py | 337 | ||||
-rw-r--r-- | chrome/test/pyautolib/pyauto.py | 63 |
10 files changed, 670 insertions, 0 deletions
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc index 5bc5f1c..aeabd00 100644 --- a/chrome/browser/automation/automation_provider_observers.cc +++ b/chrome/browser/automation/automation_provider_observers.cc @@ -4,7 +4,10 @@ #include "chrome/browser/automation/automation_provider_observers.h" +#include <deque> + #include "base/basictypes.h" +#include "base/callback.h" #include "base/json/json_writer.h" #include "base/scoped_ptr.h" #include "base/string_util.h" @@ -23,6 +26,8 @@ #include "chrome/browser/extensions/extension_updater.h" #include "chrome/browser/login_prompt.h" #include "chrome/browser/metrics/metric_event_duration_details.h" +#include "chrome/browser/notifications/balloon.h" +#include "chrome/browser/notifications/balloon_collection.h" #include "chrome/browser/printing/print_job.h" #include "chrome/browser/profile.h" #include "chrome/browser/search_engines/template_url_model.h" @@ -1346,3 +1351,22 @@ void AutocompleteEditFocusedObserver::Observe( delete this; } +OnNotificationBalloonCountObserver::OnNotificationBalloonCountObserver( + AutomationProvider* provider, + IPC::Message* reply_message, + BalloonCollection* collection, + int count) + : reply_(provider, reply_message), + collection_(collection), + count_(count) { + collection->set_on_collection_changed_callback(NewCallback( + this, &OnNotificationBalloonCountObserver::OnBalloonCollectionChanged)); +} + +void OnNotificationBalloonCountObserver::OnBalloonCollectionChanged() { + if (static_cast<int>(collection_->GetActiveBalloons().size()) == count_) { + collection_->set_on_collection_changed_callback(NULL); + reply_.SendSuccess(NULL); + delete this; + } +} diff --git a/chrome/browser/automation/automation_provider_observers.h b/chrome/browser/automation/automation_provider_observers.h index 5c7b77b..de20990 100644 --- a/chrome/browser/automation/automation_provider_observers.h +++ b/chrome/browser/automation/automation_provider_observers.h @@ -10,6 +10,7 @@ #include <map> #include <set> +#include "chrome/browser/automation/automation_provider_json.h" #include "chrome/browser/bookmarks/bookmark_model_observer.h" #include "chrome/browser/browsing_data_remover.h" #include "chrome/browser/download/download_item.h" @@ -27,6 +28,7 @@ class AutocompleteEditModel; class AutomationProvider; +class BalloonCollection; class Browser; class Extension; class ExtensionProcessManager; @@ -888,4 +890,24 @@ class AutocompleteEditFocusedObserver : public NotificationObserver { DISALLOW_COPY_AND_ASSIGN(AutocompleteEditFocusedObserver); }; +// Allows the automation provider to wait for a given number of +// notification balloons. +class OnNotificationBalloonCountObserver { + public: + OnNotificationBalloonCountObserver(AutomationProvider* provider, + IPC::Message* reply_message, + BalloonCollection* collection, + int count); + + void OnBalloonCollectionChanged(); + + private: + AutomationJSONReply reply_; + BalloonCollection* collection_; + int count_; + + DISALLOW_COPY_AND_ASSIGN(OnNotificationBalloonCountObserver); +}; + + #endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_OBSERVERS_H_ diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc index 75e064f..e62caae 100644 --- a/chrome/browser/automation/testing_automation_provider.cc +++ b/chrome/browser/automation/testing_automation_provider.cc @@ -59,6 +59,10 @@ #include "chrome/common/net/url_request_context_getter.h" #include "chrome/common/notification_service.h" #include "chrome/common/url_constants.h" +#include "chrome/browser/notifications/balloon.h" +#include "chrome/browser/notifications/balloon_collection.h" +#include "chrome/browser/notifications/notification.h" +#include "chrome/browser/notifications/notification_ui_manager.h" #include "chrome/test/automation/automation_messages.h" #include "net/base/cookie_store.h" #include "net/url_request/url_request_context.h" @@ -2096,6 +2100,13 @@ void TestingAutomationProvider::SendJSONRequest(int handle, handler_map["FillAutoFillProfile"] = &TestingAutomationProvider::FillAutoFillProfile; + handler_map["GetActiveNotifications"] = + &TestingAutomationProvider::GetActiveNotifications; + handler_map["CloseNotification"] = + &TestingAutomationProvider::CloseNotification; + handler_map["WaitForNotificationCount"] = + &TestingAutomationProvider::WaitForNotificationCount; + if (handler_map.find(std::string(command)) != handler_map.end()) { (this->*handler_map[command])(browser, dict_value, reply_message); } else { @@ -3971,6 +3982,83 @@ std::map<AutoFillFieldType, std::wstring> return credit_card_type_to_string; } +// Refer to GetActiveNotifications() in chrome/test/pyautolib/pyauto.py for +// sample json input/output. +void TestingAutomationProvider::GetActiveNotifications( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + NotificationUIManager* manager = g_browser_process->notification_ui_manager(); + const BalloonCollection::Balloons& balloons = + manager->balloon_collection()->GetActiveBalloons(); + scoped_ptr<DictionaryValue> return_value(new DictionaryValue); + ListValue* list = new ListValue; + return_value->Set("notifications", list); + BalloonCollection::Balloons::const_iterator iter; + for (iter = balloons.begin(); iter != balloons.end(); ++iter) { + const Notification& notification = (*iter)->notification(); + DictionaryValue* balloon = new DictionaryValue; + balloon->SetString("content_url", notification.content_url().spec()); + balloon->SetString("origin_url", notification.origin_url().spec()); + balloon->SetString("display_source", notification.display_source()); + list->Append(balloon); + } + AutomationJSONReply(this, reply_message).SendSuccess(return_value.get()); +} + +// Refer to CloseNotification() in chrome/test/pyautolib/pyauto.py for +// sample json input. +// Returns empty json message. +void TestingAutomationProvider::CloseNotification( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + int index; + if (!args->GetInteger("index", &index)) { + AutomationJSONReply(this, reply_message).SendError( + "'index' missing or invalid."); + return; + } + NotificationUIManager* manager = g_browser_process->notification_ui_manager(); + BalloonCollection* collection = manager->balloon_collection(); + const BalloonCollection::Balloons& balloons = collection->GetActiveBalloons(); + int balloon_count = static_cast<int>(balloons.size()); + if (index < 0 || index >= balloon_count) { + AutomationJSONReply(this, reply_message).SendError( + StringPrintf("No notification at index %d", index)); + return; + } + // This will delete itself when finished. + new OnNotificationBalloonCountObserver( + this, reply_message, collection, balloon_count - 1); + manager->Cancel(balloons[index]->notification()); +} + +// Refer to WaitForNotificationCount() in chrome/test/pyautolib/pyauto.py for +// sample json input. +// Returns empty json message. +void TestingAutomationProvider::WaitForNotificationCount( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + int count; + if (!args->GetInteger("count", &count)) { + AutomationJSONReply(this, reply_message).SendError( + "'count' missing or invalid."); + return; + } + NotificationUIManager* manager = g_browser_process->notification_ui_manager(); + BalloonCollection* collection = manager->balloon_collection(); + const BalloonCollection::Balloons& balloons = collection->GetActiveBalloons(); + if (static_cast<int>(balloons.size()) == count) { + AutomationJSONReply(this, reply_message).SendSuccess(NULL); + return; + } + // This will delete itself when finished. + new OnNotificationBalloonCountObserver( + this, reply_message, collection, count); +} + void TestingAutomationProvider::WaitForTabCountToBecome( int browser_handle, int target_tab_count, diff --git a/chrome/browser/automation/testing_automation_provider.h b/chrome/browser/automation/testing_automation_provider.h index 9a0dec8..dc76603 100644 --- a/chrome/browser/automation/testing_automation_provider.h +++ b/chrome/browser/automation/testing_automation_provider.h @@ -637,6 +637,24 @@ class TestingAutomationProvider : public AutomationProvider, static std::map<AutoFillFieldType, std::wstring> GetCreditCardFieldToStringMap(); + // Get a list of active HTML5 notifications. + // Uses the JSON interface for input/output. + void GetActiveNotifications(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + + // Close an active HTML5 notification. + // Uses the JSON interface for input/output. + void CloseNotification(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + + // Waits for the number of active HTML5 notifications to reach a given count. + // Uses the JSON interface for input/output. + void WaitForNotificationCount(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + void WaitForTabCountToBecome(int browser_handle, int target_tab_count, IPC::Message* reply_message); diff --git a/chrome/browser/notifications/balloon_collection.cc b/chrome/browser/notifications/balloon_collection.cc index ebbe566f..a125837 100644 --- a/chrome/browser/notifications/balloon_collection.cc +++ b/chrome/browser/notifications/balloon_collection.cc @@ -68,6 +68,10 @@ void BalloonCollectionImpl::Add(const Notification& notification, // There may be no listener in a unit test. if (space_change_listener_) space_change_listener_->OnBalloonSpaceChanged(); + + // This is used only for testing. + if (on_collection_changed_callback_.get()) + on_collection_changed_callback_->Run(); } bool BalloonCollectionImpl::Remove(const Notification& notification) { @@ -148,6 +152,10 @@ void BalloonCollectionImpl::OnBalloonClosed(Balloon* source) { // There may be no listener in a unit test. if (space_change_listener_) space_change_listener_->OnBalloonSpaceChanged(); + + // This is used only for testing. + if (on_collection_changed_callback_.get()) + on_collection_changed_callback_->Run(); } void BalloonCollectionImpl::PositionBalloonsInternal(bool reposition) { diff --git a/chrome/browser/notifications/balloon_collection.h b/chrome/browser/notifications/balloon_collection.h index 7a7b081..499b937 100644 --- a/chrome/browser/notifications/balloon_collection.h +++ b/chrome/browser/notifications/balloon_collection.h @@ -10,6 +10,9 @@ #include <deque> +#include "base/callback.h" +#include "base/scoped_ptr.h" + class Balloon; class Notification; class Profile; @@ -68,9 +71,17 @@ class BalloonCollection { space_change_listener_ = listener; } + void set_on_collection_changed_callback(Callback0::Type* callback) { + on_collection_changed_callback_.reset(callback); + } + protected: // Non-owned pointer to an object listening for space changes. BalloonSpaceChangeListener* space_change_listener_; + + // For use only with testing. This callback is invoked when a balloon + // is added or removed from the collection. + scoped_ptr<Callback0::Type> on_collection_changed_callback_; }; #endif // CHROME_BROWSER_NOTIFICATIONS_BALLOON_COLLECTION_H_ diff --git a/chrome/test/data/notifications/notification_tester.html b/chrome/test/data/notifications/notification_tester.html new file mode 100644 index 0000000..7a4aec3 --- /dev/null +++ b/chrome/test/data/notifications/notification_tester.html @@ -0,0 +1,98 @@ +<html> +<!--notification_tester.html +Script with javascript functions for creating and canceling notifications. +Also can be used to request permission for notifications. +--> +<script> + +// Array of all notifications this page has created. +var g_notifications = []; +// Whether the site has requested and been granted permission. +var g_permissionGranted = false; + +// Creates a notification with a iconUrl, title, text, and replaceId. +// Returns an id for the notification, which can be used to cancel it with +// |cancelNotification|. If two notifications are created with the same +// replaceId, the second one should replace the first. +function createNotification(iconUrl, title, text, replaceId) { + try { + var note = webkitNotifications.createNotification(iconUrl, + title, + text); + } catch (exception) { + sendResultToTest(-1); + return; + } + sendResultToTest(createNotificationHelper(note, replaceId)); +} + +// Creates an HTML notification with a given content url. +// Returns an id for the notification, which can be used to cancel it with +// |cancelNotification|. If two notifications are created with the same +// replaceId, the second one should replace the first. If the notification +// cannot be created, this returns -1. +function createHTMLNotification(contentUrl, replaceId) { + try { + var note = webkitNotifications.createHTMLNotification(contentUrl); + } catch (exception) { + sendResultToTest(-1); + return; + } + sendResultToTest(createNotificationHelper(note, replaceId)); +} + +// Cancels a notification with the given id. Returns 1 on success. +function cancelNotification(id) { + if (id < 0 || id > g_notifications.length) { + var errorMsg = "Attempted to cancel notification with invalid ID.\n" + + "ID: " + id + "\n# of notifications: " + g_notifications.length; + window.domAutomationController.send(errorMsg); + alert(errorMsg); + } + g_notifications[id].cancel(); + sendResultToTest(1); +} + +// Requests permission for this origin to create notifications. +function requestPermission() { + window.webkitNotifications.requestPermission(onPermissionGranted); + sendResultToTest(1); +} + +// Waits for the permission to create notifications to be granted. +function waitForPermissionGranted() { + if (g_permissionGranted) { + sendResultToTest(1); + } else { + setTimeout(waitForPermissionGranted, 50); + } +} + +// Callback for requesting notification privileges. +function onPermissionGranted() { + g_permissionGranted = true; +} + +// Helper function that shows the notification and adds it to +// |g_notifications|. +function createNotificationHelper(note, replaceId) { + note.show(); + // |push| returns the length of the array after the add. + return g_notifications.push(note) - 1; +} + +// Sends a result back to the main test logic. +function sendResultToTest(result) { + // Convert the result to a string. + var stringResult = "" + result; + if (typeof stringResult != "string") + stringResult = JSON.stringify(result); + window.domAutomationController.send(stringResult); +} + +</script> + +<body> +This page is used for testing HTML5 notifications. +</body> +</html> diff --git a/chrome/test/functional/PYAUTO_TESTS b/chrome/test/functional/PYAUTO_TESTS index d5fbea8..87f8641 100644 --- a/chrome/test/functional/PYAUTO_TESTS +++ b/chrome/test/functional/PYAUTO_TESTS @@ -39,6 +39,7 @@ 'imports', 'infobars', 'navigation', + 'notifications', 'omnibox', 'passwords', 'plugins', diff --git a/chrome/test/functional/notifications.py b/chrome/test/functional/notifications.py new file mode 100644 index 0000000..1cf414b --- /dev/null +++ b/chrome/test/functional/notifications.py @@ -0,0 +1,337 @@ +#!/usr/bin/python +# 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. + +import urllib + +import pyauto_functional +import pyauto + + +class NotificationsTest(pyauto.PyUITest): + """Test of HTML5 desktop notifications.""" + def __init__(self, methodName='runTest'): + super(NotificationsTest, self).__init__(methodName) + self.NO_SUCH_URL = 'http://no_such_url_exists/' + # Content settings for default notification permission. + self.ALLOW_ALL_SETTING = 1 + self.DENY_ALL_SETTING = 2 + self.ASK_SETTING = 3 + + # HTML page used for notification testing. + self.TEST_PAGE_URL = ( + self.GetFileURLForDataPath('notifications/notification_tester.html')) + + def Debug(self): + """Test method for experimentation. + + This method will not run automatically. + """ + while True: + raw_input('Interact with the browser and hit <enter> to dump notification' + 'state...') + print '*' * 20 + import pprint + pp = pprint.PrettyPrinter(indent=2) + pp.pprint(self.GetActiveNotifications()) + + def _SetDefaultPermissionSetting(self, setting): + """Sets the default setting for whether sites are allowed to create + notifications. + """ + self.SetPrefs(pyauto.kDesktopNotificationDefaultContentSetting, setting) + + def _GetDefaultPermissionSetting(self): + """Gets the default setting for whether sites are allowed to create + notifications. + """ + return self.GetPrefsInfo().Prefs( + pyauto.kDesktopNotificationDefaultContentSetting) + + def _GetDeniedOrigins(self): + """Gets the list of origins that are explicitly denied to create + notifications. + """ + return self.GetPrefsInfo().Prefs(pyauto.kDesktopNotificationDeniedOrigins) + + def _GetAllowedOrigins(self): + """Gets the list of origins that are explicitly allowed to create + notifications. + """ + return self.GetPrefsInfo().Prefs(pyauto.kDesktopNotificationAllowedOrigins) + + def _SetAllowedOrigins(self, origins): + """Sets the list of allowed origins to the given list. + + None of the items in the list should be explicitly denied. + """ + return self.SetPrefs(pyauto.kDesktopNotificationAllowedOrigins, origins) + + def _SetDeniedOrigins(self, origins): + """Sets the list of denied origins to the given list. + + None of the items in the list should be explicitly allowed. + """ + return self.SetPrefs(pyauto.kDesktopNotificationDeniedOrigins, origins) + + def _DenyOrigin(self, new_origin): + """Denies the given origin to create notifications. + + If it was explicitly allowed, that preference is dropped. + """ + self._DropOriginPreference(new_origin) + denied = self._GetDeniedOrigins() or [] + if new_origin not in denied: + self._SetDeniedOrigins(denied + [new_origin]) + + def _AllowOrigin(self, new_origin): + """Allows the given origin to create notifications. If it was explicitly + denied, that preference is dropped. + """ + self._DropOriginPreference(new_origin) + allowed = self._GetAllowedOrigins() or [] + if new_origin not in allowed: + self._SetAllowedOrigins(allowed + [new_origin]) + + def _DropOriginPreference(self, new_origin): + """Drops the preference as to whether this origin should be allowed to + create notifications. If it was explicitly allowed or explicitly denied, + that preference is removed. + """ + allowed = self._GetAllowedOrigins() + if allowed and new_origin in allowed: + allowed.remove(new_origin) + self._SetAllowedOrigins(allowed) + denied = self._GetDeniedOrigins() + if denied and new_origin in denied: + denied.remove(new_origin) + self._SetDeniedOrigins(denied) + + def _AllowAllOrigins(self): + """Allows any origin to create notifications.""" + self._SetDefaultPermissionSetting(self.ALLOW_ALL_SETTING) + self._SetDeniedOrigins([]) + + def _VerifyInfobar(self, origin, tab_index=0, windex=0): + """Helper to verify the notification infobar contents are correct. + + Defaults to first tab in first window. + + Args: + origin: origin of the notification, e.g., www.gmail.com + tab_index: index of the tab within the given window + windex: index of the window + """ + tab_info = self.GetBrowserInfo()['windows'][windex]['tabs'][tab_index] + self.assertEquals(1, len(tab_info['infobars'])) + infobar = tab_info['infobars'][0] + text = 'Allow %s to show desktop notifications?' % origin + self.assertEqual(text, infobar['text']) + self.assertEqual(2, len(infobar['buttons'])) + self.assertEqual('Allow', infobar['buttons'][0]) + self.assertEqual('Deny', infobar['buttons'][1]) + + def _CallJavascriptFunc(self, function, args=[], tab_index=0, windex=0): + """Helper function to execute a script that calls a given function. + + Defaults to first tab in first window. + + Args: + function: name of the function + args: list of all the arguments to pass into the called function. These + should be able to be converted to a string using the |str| function. + tab_index: index of the tab within the given window + windex: index of the window + """ + # Convert the given arguments for evaluation in a javascript statement. + converted_args = [] + for arg in args: + # If it is a string argument, we need to quote and escape it properly. + if type(arg) == type('string') or type(arg) == type(u'unicode'): + # We must convert all " in the string to \", so that we don't try + # to evaluate invalid javascript like ""arg"". + converted_arg = '"' + arg.replace('"', '\\"') + '"' + else: + # Convert it to a string so that we can use |join| later. + converted_arg = str(arg) + converted_args += [converted_arg] + js = '%s(%s)' % (function, ', '.join(converted_args)) + return self.ExecuteJavascript(js, windex, tab_index) + + def _CreateSimpleNotification(self, img_url, title, text, + replace_id='', tab_index=0, windex=0): + """Creates a simple notification. + + Returns the id of the notification, which can be used to cancel it later. + + This executes a script in the page which shows a notification. + This will only work if the page is navigated to |TEST_PAGE_URL|. + The page must also have permission to show notifications. + + Args: + img_url: url of a image to use; can be a data url + title: title of the notification + text: text in the notification + replace_id: id string to be used for this notification. If another + notification is shown with the same replace_id, the former + will be replaced. + tab_index: index of the tab within the given window + windex: index of the window + """ + return self._CallJavascriptFunc('createNotification', + [img_url, title, text, replace_id], + tab_index, + windex); + + def _CreateHTMLNotification(self, content_url, replace_id='', tab_index=0, + windex=0): + """Creates an HTML notification. + + Returns the id of the notification, which can be used to cancel it later. + + This executes a script in the page which shows a notification. + This will only work if the page is navigated to |TEST_PAGE_URL|. + The page must also have permission to show notifications. + + Args: + content_url: url of the page to show in the notification + replace_id: id string to be used for this notification. If another + notification is shown with the same replace_id, the former + will be replaced. + tab_index: index of the tab within the given window + windex: index of the window + """ + return self._CallJavascriptFunc('createHTMLNotification', + [content_url, replace_id], + tab_index, + windex) + + def _RequestPermission(self, tab_index=0, windex=0): + """Requests permission to create notifications. + + This will only work if the current page is navigated to |TEST_PAGE_URL|. + + Args: + tab_index: index of the tab within the given window + windex: index of the window + """ + self._CallJavascriptFunc('requestPermission', [], windex, tab_index) + + def _CancelNotification(self, notification_id, tab_index=0, windex=0): + """Cancels a notification with the given id. + + This canceling is done in the page that showed that notification and so + follows a different path than closing a notification via the UI. + + A notification can be canceled even if it has not been shown yet. + This will only work if the page is navigated to |TEST_PAGE_URL|. + + Args: + tab_index: index of the tab within the given window that created the + notification + windex: index of the window + """ + self._CallJavascriptFunc( + 'cancelNotification', [notification_id], tab_index, windex) + + def testCreateSimpleNotification(self): + """Creates a simple notification.""" + self._AllowAllOrigins() + self.NavigateToURL(self.TEST_PAGE_URL) + self._CreateSimpleNotification('no_such_file.png', 'My Title', 'My Body') + self.assertEquals(len(self.GetActiveNotifications()), 1) + notification = self.GetActiveNotifications()[0] + html_data = urllib.unquote(notification['content_url']) + self.assertTrue('no_such_file.png' in html_data) + self.assertTrue('My Title' in html_data) + self.assertTrue('My Body' in html_data) + + def testCreateHTMLNotification(self): + """Creates an HTML notification using a fake url.""" + self._AllowAllOrigins() + self.NavigateToURL(self.TEST_PAGE_URL) + self._CreateHTMLNotification(self.NO_SUCH_URL) + self.assertEquals(len(self.GetActiveNotifications()), 1) + notification = self.GetActiveNotifications()[0] + self.assertEquals(self.NO_SUCH_URL, notification['content_url']) + self.assertEquals('', notification['display_source']) + self.assertEquals('file:///', notification['origin_url']) + + def testCloseNotification(self): + """Creates a notification and closes it.""" + self._AllowAllOrigins() + self.NavigateToURL(self.TEST_PAGE_URL) + self._CreateHTMLNotification(self.NO_SUCH_URL) + self.CloseNotification(0) + self.assertEquals(len(self.GetActiveNotifications()), 0) + + def testCancelNotification(self): + """Creates a notification and cancels it in the origin page.""" + self._AllowAllOrigins() + self.NavigateToURL(self.TEST_PAGE_URL) + note_id = self._CreateHTMLNotification(self.NO_SUCH_URL) + self.assertNotEquals(-1, note_id) + self._CancelNotification(note_id) + self.assertEquals(len(self.GetActiveNotifications()), 0) + + def testPermissionInfobarAppears(self): + """Requests notification privileges and verifies the infobar appears.""" + self.NavigateToURL(self.TEST_PAGE_URL) + self._RequestPermission() + self.assertTrue(self.WaitForInfobarCount(1)) + self.assertEquals(len(self.GetActiveNotifications()), 0) + self._VerifyInfobar('') # file:/// origins are blank + + def testAllowOnPermissionInfobar(self): + """Tries to create a notification and clicks allow on the infobar.""" + self.NavigateToURL(self.TEST_PAGE_URL) + # This notification should not be shown because we don't have permission. + self._CreateHTMLNotification(self.NO_SUCH_URL) + self.assertEquals(len(self.GetActiveNotifications()), 0) + + self._RequestPermission() + self.assertTrue(self.WaitForInfobarCount(1)) + self.PerformActionOnInfobar('accept', infobar_index=0) + self._CreateHTMLNotification(self.NO_SUCH_URL) + self.WaitForNotificationCount(1) + + def testOriginPreferencesBasic(self): + """Tests that we can allow and deny origins.""" + altavista = 'www.altavista.com' + gmail = 'www.gmail.com' + yahoo = 'www.yahoo.com' + self._SetDeniedOrigins([altavista, gmail]) + self.assertEquals(altavista, self._GetDeniedOrigins()[0]) + self.assertEquals(gmail, self._GetDeniedOrigins()[1]) + self._DenyOrigin(yahoo) + self.assertEquals(yahoo, self._GetDeniedOrigins()[2]) + self.assertEquals(3, len(self._GetDeniedOrigins())) + self._DropOriginPreference(gmail) + self.assertEquals(2, len(self._GetDeniedOrigins())) + self.assertFalse(gmail in self._GetDeniedOrigins()) + + self._AllowOrigin(yahoo) + self.assertEquals(1, len(self._GetDeniedOrigins())) + self.assertFalse(yahoo in self._GetDeniedOrigins()) + self.assertTrue(yahoo in self._GetAllowedOrigins()) + + self._SetAllowedOrigins([altavista, gmail]) + self._SetDeniedOrigins([]) + self.assertEquals(altavista, self._GetAllowedOrigins()[0]) + self.assertEquals(gmail, self._GetAllowedOrigins()[1]) + self._AllowOrigin(yahoo) + self.assertEquals(yahoo, self._GetAllowedOrigins()[2]) + self.assertEquals(3, len(self._GetAllowedOrigins())) + self._DropOriginPreference(gmail) + self.assertEquals(2, len(self._GetAllowedOrigins())) + self.assertFalse(gmail in self._GetAllowedOrigins()) + + self._DenyOrigin(yahoo) + self.assertEquals(1, len(self._GetAllowedOrigins())) + self.assertTrue(yahoo in self._GetDeniedOrigins()) + self.assertFalse(yahoo in self._GetAllowedOrigins()) + + +if __name__ == '__main__': + pyauto_functional.Main() diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py index 02d342e..60b4728 100644 --- a/chrome/test/pyautolib/pyauto.py +++ b/chrome/test/pyautolib/pyauto.py @@ -188,6 +188,15 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): return 'file://' + quoted_path @staticmethod + def GetFileURLForDataPath(relative_path): + """Get file:// url for the given path relative to the chrome test data dir. + + Also quotes the url using urllib.quote(). + """ + return PyUITest.GetFileURLForPath( + os.path.join(PyUITest.DataDir(), relative_path)) + + @staticmethod def IsMac(): """Are we on Mac?""" return 'darwin' == sys.platform @@ -1497,6 +1506,60 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): } return self._GetResultFromJSONRequest(cmd_dict) + def GetActiveNotifications(self): + """Gets a list of the currently active/shown HTML5 notifications. + + Returns: + a list containing info about each active notification, with the + first item in the list being the notification on the bottom of the + notification stack. The 'content_url' key can refer to a URL or a data + URI. + + SAMPLE: + [ { u'content_url': u'data:text/html;charset=utf-8,%3C!DOCTYPE%l%3E%0Atm...' + u'display_source': 'www.corp.google.com', + u'origin_url': 'http://www.corp.google.com/'}, + { u'content_url': 'http://www.gmail.com/special_notification.html', + u'display_source': 'www.gmail.com', + u'origin_url': 'http://www.gmail.com/'}] + + Raises: + pyauto_errors.JSONInterfaceError if the automation call returns an error. + """ + cmd_dict = { + 'command': 'GetActiveNotifications', + } + return self._GetResultFromJSONRequest(cmd_dict)['notifications'] + + def CloseNotification(self, index): + """Closes the active HTML5 notification at the given index. + + Args: + index: the index of the notification to close. 0 refers to the + notification on the bottom of the notification stack. + + Raises: + pyauto_errors.JSONInterfaceError if the automation call returns an error. + """ + cmd_dict = { + 'command': 'CloseNotification', + 'index': index, + } + return self._GetResultFromJSONRequest(cmd_dict) + + def WaitForNotificationCount(self, count): + """Waits for the number of active HTML5 notifications to reach the given + count. + + Raises: + pyauto_errors.JSONInterfaceError if the automation call returns an error. + """ + cmd_dict = { + 'command': 'WaitForNotificationCount', + 'count': count, + } + self._GetResultFromJSONRequest(cmd_dict) + def FindInPage(self, search_string, forward=True, match_case=False, find_next=False, tab_index=0, windex=0): |