summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/automation/automation_provider_observers.cc24
-rw-r--r--chrome/browser/automation/automation_provider_observers.h22
-rw-r--r--chrome/browser/automation/testing_automation_provider.cc88
-rw-r--r--chrome/browser/automation/testing_automation_provider.h18
-rw-r--r--chrome/browser/notifications/balloon_collection.cc8
-rw-r--r--chrome/browser/notifications/balloon_collection.h11
-rw-r--r--chrome/test/data/notifications/notification_tester.html98
-rw-r--r--chrome/test/functional/PYAUTO_TESTS1
-rw-r--r--chrome/test/functional/notifications.py337
-rw-r--r--chrome/test/pyautolib/pyauto.py63
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):