diff options
-rw-r--r-- | chrome/browser/automation/automation_provider_observers.cc | 7 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider_observers.h | 5 | ||||
-rw-r--r-- | chrome/browser/automation/testing_automation_provider.cc | 144 | ||||
-rw-r--r-- | chrome/browser/automation/testing_automation_provider.h | 26 | ||||
-rw-r--r-- | chrome/test/functional/search_engines.py | 78 | ||||
-rw-r--r-- | chrome/test/pyautolib/pyauto.py | 101 |
6 files changed, 296 insertions, 65 deletions
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc index af0cac0..d836bee 100644 --- a/chrome/browser/automation/automation_provider_observers.cc +++ b/chrome/browser/automation/automation_provider_observers.cc @@ -1192,13 +1192,8 @@ void AutomationProviderDownloadModelChangedObserver::ModelChanged() { void AutomationProviderSearchEngineObserver::OnTemplateURLModelChanged() { TemplateURLModel* url_model = provider_->profile()->GetTemplateURLModel(); - scoped_ptr<DictionaryValue> return_value(new DictionaryValue); - return_value->Set("search_engines", - provider_->ExtractSearchEngineInfo(url_model)); - url_model->RemoveObserver(this); - AutomationJSONReply(provider_, reply_message_).SendSuccess( - return_value.get()); + AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL); delete this; } diff --git a/chrome/browser/automation/automation_provider_observers.h b/chrome/browser/automation/automation_provider_observers.h index bc2fdb7..8706d85 100644 --- a/chrome/browser/automation/automation_provider_observers.h +++ b/chrome/browser/automation/automation_provider_observers.h @@ -10,7 +10,6 @@ #include <map> #include <set> -#include "chrome/browser/automation/testing_automation_provider.h" #include "chrome/browser/bookmarks/bookmark_model_observer.h" #include "chrome/browser/browsing_data_remover.h" #include "chrome/browser/download/download_item.h" @@ -740,7 +739,7 @@ class AutomationProviderSearchEngineObserver : public TemplateURLModelObserver { public: AutomationProviderSearchEngineObserver( - TestingAutomationProvider* provider, + AutomationProvider* provider, IPC::Message* reply_message) : provider_(provider), reply_message_(reply_message) {} @@ -748,7 +747,7 @@ class AutomationProviderSearchEngineObserver void OnTemplateURLModelChanged(); private: - TestingAutomationProvider* provider_; + AutomationProvider* provider_; IPC::Message* reply_message_; DISALLOW_COPY_AND_ASSIGN(AutomationProviderSearchEngineObserver); diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc index 14bc906..7e6ed0c 100644 --- a/chrome/browser/automation/testing_automation_provider.cc +++ b/chrome/browser/automation/testing_automation_provider.cc @@ -46,6 +46,7 @@ #include "chrome/browser/profile_manager.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/search_engines/keyword_editor_controller.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/tab_contents/infobar_delegate.h" @@ -430,25 +431,6 @@ void TestingAutomationProvider::OnChannelError() { AutomationProvider::OnChannelError(); } -ListValue* TestingAutomationProvider::ExtractSearchEngineInfo( - TemplateURLModel* url_model) { - ListValue* search_engines = new ListValue; - std::vector<const TemplateURL*> template_urls = url_model->GetTemplateURLs(); - for (std::vector<const TemplateURL*>::const_iterator it = - template_urls.begin(); - it != template_urls.end(); ++it) { - DictionaryValue* search_engine = new DictionaryValue; - search_engine->SetString("short_name", WideToUTF8((*it)->short_name())); - search_engine->SetString("description", WideToUTF8((*it)->description())); - search_engine->SetString("keyword", WideToUTF8((*it)->keyword())); - search_engine->SetBoolean("in_default_list", (*it)->ShowInDefaultList()); - search_engine->SetBoolean("is_default", - (*it) == url_model->GetDefaultSearchProvider()); - search_engines->Append(search_engine); - } - return search_engines; -} - void TestingAutomationProvider::CloseBrowser(int browser_handle, IPC::Message* reply_message) { if (browser_tracker_->ContainsHandle(browser_handle)) { @@ -2048,8 +2030,14 @@ void TestingAutomationProvider::SendJSONRequest(int handle, handler_map["OmniboxMovePopupSelection"] = &TestingAutomationProvider::OmniboxMovePopupSelection; + handler_map["LoadSearchEngineInfo"] = + &TestingAutomationProvider::LoadSearchEngineInfo; handler_map["GetSearchEngineInfo"] = &TestingAutomationProvider::GetSearchEngineInfo; + handler_map["AddOrEditSearchEngine"] = + &TestingAutomationProvider::AddOrEditSearchEngine; + handler_map["PerformActionOnSearchEngine"] = + &TestingAutomationProvider::PerformActionOnSearchEngine; handler_map["GetPrefsInfo"] = &TestingAutomationProvider::GetPrefsInfo; handler_map["SetPrefs"] = &TestingAutomationProvider::SetPrefs; @@ -2675,19 +2663,127 @@ void TestingAutomationProvider::PerformActionOnDownload( } } -void TestingAutomationProvider::GetSearchEngineInfo( +// Sample JSON input { "command": "LoadSearchEngineInfo" } +void TestingAutomationProvider::LoadSearchEngineInfo( Browser* browser, DictionaryValue* args, IPC::Message* reply_message) { TemplateURLModel* url_model(profile_->GetTemplateURLModel()); if (url_model->loaded()) { - scoped_ptr<DictionaryValue> return_value(new DictionaryValue); - return_value->Set("search_engines", ExtractSearchEngineInfo(url_model)); - AutomationJSONReply(this, reply_message).SendSuccess(return_value.get()); + AutomationJSONReply(this, reply_message).SendSuccess(NULL); + return; + } + url_model->AddObserver(new AutomationProviderSearchEngineObserver( + this, reply_message)); + url_model->Load(); +} + +// Sample JSON input { "command": "GetSearchEngineInfo" } +// Refer to pyauto.py for sample output. +void TestingAutomationProvider::GetSearchEngineInfo( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + TemplateURLModel* url_model(profile_->GetTemplateURLModel()); + scoped_ptr<DictionaryValue> return_value(new DictionaryValue); + ListValue* search_engines = new ListValue; + std::vector<const TemplateURL*> template_urls = url_model->GetTemplateURLs(); + for (std::vector<const TemplateURL*>::const_iterator it = + template_urls.begin(); it != template_urls.end(); ++it) { + DictionaryValue* search_engine = new DictionaryValue; + search_engine->SetString("short_name", WideToUTF8((*it)->short_name())); + search_engine->SetString("description", WideToUTF8((*it)->description())); + search_engine->SetString("keyword", WideToUTF8((*it)->keyword())); + search_engine->SetBoolean("in_default_list", (*it)->ShowInDefaultList()); + search_engine->SetBoolean("is_default", + (*it) == url_model->GetDefaultSearchProvider()); + search_engine->SetBoolean("is_valid", (*it)->url()->IsValid()); + search_engine->SetBoolean("supports_replacement", + (*it)->url()->SupportsReplacement()); + search_engine->SetString("url", (*it)->url()->url()); + search_engine->SetString("host", (*it)->url()->GetHost()); + search_engine->SetString("path", (*it)->url()->GetPath()); + search_engine->SetString("display_url", + WideToUTF8((*it)->url()->DisplayURL())); + search_engines->Append(search_engine); + } + return_value->Set("search_engines", search_engines); + AutomationJSONReply(this, reply_message).SendSuccess(return_value.get()); +} + +// Refer to pyauto.py for sample JSON input. +void TestingAutomationProvider::AddOrEditSearchEngine( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + TemplateURLModel* url_model(profile_->GetTemplateURLModel()); + const TemplateURL* template_url; + string16 new_title; + string16 new_keyword; + std::string new_url; + std::string keyword; + if (!args->GetString("new_title", &new_title) || + !args->GetString("new_keyword", &new_keyword) || + !args->GetString("new_url", &new_url)) { + AutomationJSONReply(this, reply_message).SendError( + "One or more inputs invalid"); + return; + } + std::string new_ref_url = TemplateURLRef::DisplayURLToURLRef( + UTF8ToWide(new_url)); + scoped_ptr<KeywordEditorController> controller( + new KeywordEditorController(profile_)); + if (args->GetString("keyword", &keyword)) { + template_url = url_model->GetTemplateURLForKeyword(UTF8ToWide(keyword)); + if (template_url == NULL) { + AutomationJSONReply(this, reply_message).SendError( + StringPrintf("No match for keyword: %s", keyword.c_str())); + return; + } + url_model->AddObserver(new AutomationProviderSearchEngineObserver( + this, reply_message)); + controller->ModifyTemplateURL(template_url, new_title, new_keyword, + new_ref_url); } else { url_model->AddObserver(new AutomationProviderSearchEngineObserver( + this, reply_message)); + controller->AddTemplateURL(new_title, new_keyword, new_ref_url); + } +} + +// Sample json input: { "command": "PerformActionOnSearchEngine", +// "keyword": keyword, "action": action } +void TestingAutomationProvider::PerformActionOnSearchEngine( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + TemplateURLModel* url_model(profile_->GetTemplateURLModel()); + std::string keyword; + std::string action; + if (!args->GetString("keyword", &keyword) || + !args->GetString("action", &action)) { + AutomationJSONReply(this, reply_message).SendError( + "One or more inputs invalid"); + return; + } + const TemplateURL* template_url( + url_model->GetTemplateURLForKeyword(UTF8ToWide(keyword))); + if (template_url == NULL) { + AutomationJSONReply(this, reply_message).SendError( + StringPrintf("No match for keyword: %s", keyword.c_str())); + return; + } + if (action == "delete") { + url_model->AddObserver(new AutomationProviderSearchEngineObserver( this, reply_message)); - url_model->Load(); + url_model->Remove(template_url); + } else if (action == "default") { + url_model->AddObserver(new AutomationProviderSearchEngineObserver( + this, reply_message)); + url_model->SetDefaultSearchProvider(template_url); + } else { + AutomationJSONReply(this, reply_message).SendError( + StringPrintf("Invalid action: %s", action.c_str())); } } diff --git a/chrome/browser/automation/testing_automation_provider.h b/chrome/browser/automation/testing_automation_provider.h index 9449558..715a89f 100644 --- a/chrome/browser/automation/testing_automation_provider.h +++ b/chrome/browser/automation/testing_automation_provider.h @@ -32,11 +32,6 @@ class TestingAutomationProvider : public AutomationProvider, virtual void OnMessageReceived(const IPC::Message& msg); virtual void OnChannelError(); - // Helper to extract search engine info. - // Caller owns returned DictionaryValue. - // This will only yield data if url model has loaded. - ListValue* ExtractSearchEngineInfo(TemplateURLModel* url_model); - private: class PopupMenuWaiter; @@ -426,12 +421,33 @@ class TestingAutomationProvider : public AutomationProvider, DictionaryValue* args, IPC::Message* reply_message); + // Invoke loading of template url model. + // Uses the JSON interface for input/output. + void LoadSearchEngineInfo(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + // Get search engines list. + // Assumes that the profile's template url model is loaded. // Uses the JSON interface for input/output. void GetSearchEngineInfo(Browser* browser, DictionaryValue* args, IPC::Message* reply_message); + // Add or edit search engine. + // Assumes that the profile's template url model is loaded. + // Uses the JSON interface for input/output. + void AddOrEditSearchEngine(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + + // Perform a given action on an existing search engine. + // Assumes that the profile's template url model is loaded. + // Uses the JSON interface for input/output. + void PerformActionOnSearchEngine(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + // Get info about preferences. // Uses the JSON interface for input/output. void GetPrefsInfo(Browser* browser, diff --git a/chrome/test/functional/search_engines.py b/chrome/test/functional/search_engines.py index ed90bb2..5d1082d 100644 --- a/chrome/test/functional/search_engines.py +++ b/chrome/test/functional/search_engines.py @@ -13,6 +13,21 @@ import pyauto class SearchEnginesTest(pyauto.PyUITest): """TestCase for Search Engines.""" + def _GetSearchEngineWithKeyword(self, keyword): + """Get search engine info and return an element that matches keyword. + + Args: + keyword: Search engine keyword field. + + Returns: + A search engine info dict or None. + """ + match_list = ([x for x in self.GetSearchEngineInfo() + if x['keyword'] == keyword]) + if match_list: + return match_list[0] + return None + def Debug(self): """Test method for experimentation. @@ -47,20 +62,59 @@ class SearchEnginesTest(pyauto.PyUITest): self.assertFalse(youtube['in_default_list']) self.assertFalse(youtube['is_default']) - def _GetSearchEngineWithKeyword(self, keyword): - """Get search engine info and return an element that matches keyword. + def testAddSearchEngine(self): + """Test searching using keyword of user-added search engine.""" + self.AddSearchEngine(title='foo', + keyword='foo.com', + url='http://foo/?q=%s') + self.SetOmniboxText('foo.com foobar') + self.OmniboxAcceptInput() + self.assertEqual('http://foo/?q=foobar', self.GetActiveTabURL().spec()) - Args: - keyword: Search engine keyword field. + def testEditSearchEngine(self): + """Test editing a search engine's properties.""" + self.AddSearchEngine(title='foo', + keyword='foo.com', + url='http://foo/?q=%s') + self.EditSearchEngine(keyword='foo.com', + new_title='bar', + new_keyword='bar.com', + new_url='http://foo/?bar=true&q=%s') + self.assertTrue(self._GetSearchEngineWithKeyword('bar.com')) + self.assertFalse(self._GetSearchEngineWithKeyword('foo.com')) + self.SetOmniboxText('bar.com foobar') + self.OmniboxAcceptInput() + self.assertEqual('http://foo/?bar=true&q=foobar', + self.GetActiveTabURL().spec()) - Returns: - A search engine info dict or None. - """ - match_list = ([x for x in self.GetSearchEngineInfo() - if x['keyword'] == 'youtube.com']) - if match_list: - return match_list[0] - return None + def testDeleteSearchEngine(self): + """Test adding then deleting a search engine.""" + self.AddSearchEngine(title='foo', + keyword='foo.com', + url='http://foo/?q=%s') + foo = self._GetSearchEngineWithKeyword('foo.com') + self.assertTrue(foo) + self.DeleteSearchEngine('foo.com') + foo = self._GetSearchEngineWithKeyword('foo.com') + self.assertFalse(foo) + + def testMakeSearchEngineDefault(self): + """Test adding then making a search engine default.""" + self.AddSearchEngine( + title='foo', + keyword='foo.com', + url='http://foo/?q=%s') + foo = self._GetSearchEngineWithKeyword('foo.com') + self.assertTrue(foo) + self.assertFalse(foo['is_default']) + self.MakeSearchEngineDefault('foo.com') + foo = self._GetSearchEngineWithKeyword('foo.com') + self.assertTrue(foo) + self.assertTrue(foo['is_default']) + self.SetOmniboxText('foobar') + self.OmniboxAcceptInput() + self.assertEqual('http://foo/?q=foobar', + self.GetActiveTabURL().spec()) if __name__ == '__main__': diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py index f51c11d..9c52c34 100644 --- a/chrome/test/pyautolib/pyauto.py +++ b/chrome/test/pyautolib/pyauto.py @@ -398,6 +398,9 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): windex: the index of the browser window to work on. Default: 0 (first window) """ + # Ensure that keyword data is loaded from the profile. + # This would normally be triggered by the user inputting this text. + self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}) cmd_dict = { 'command': 'SetOmniboxText', 'text': text, @@ -454,25 +457,93 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): An ordered list of dictionaries describing info about each search engine. Example: - [ { u'description': u'', - u'in_default_list': True, - u'is_default': True, - u'keyword': u'google.com', - u'short_name': u'Google'}, - { u'description': u'', - u'in_default_list': True, - u'is_default': False, - u'keyword': u'yahoo.com', - u'short_name': u'Yahoo!'}, - { u'description': u'', - u'in_default_list': True, - u'is_default': False, - u'keyword': u'bing.com', - u'short_name': u'Bing'}] + [ { u'description': u'', + u'display_url': u'{google:baseURL}search?q=%s', + u'host': u'www.google.com', + u'in_default_list': True, + u'is_default': True, + u'is_valid': True, + u'keyword': u'google.com', + u'path': u'/search', + u'short_name': u'Google', + u'supports_replacement': True, + u'url': u'{google:baseURL}search?q={searchTerms}'}, + { u'description': u'', + u'display_url': u'http://search.yahoo.com/search?p=%s', + u'host': u'search.yahoo.com', + u'in_default_list': True, + u'is_default': False, + u'is_valid': True, + u'keyword': u'yahoo.com', + u'path': u'/search', + u'short_name': u'Yahoo!', + u'supports_replacement': True, + u'url': u'http://search.yahoo.com/search?p={searchTerms}'}, """ + # Ensure that the search engine profile is loaded into data model. + self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}) cmd_dict = {'command': 'GetSearchEngineInfo'} return self._GetResultFromJSONRequest(cmd_dict)['search_engines'] + def AddSearchEngine(self, title, keyword, url): + """Add a search engine, as done through the search engines UI. + + Args: + title: name for search engine. + keyword: keyword, used to initiate a custom search from omnibox. + url: url template for this search engine's query. + '%s' is replaced by search query string when used to search. + """ + # Ensure that the search engine profile is loaded into data model. + self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}) + cmd_dict = {'command': 'AddOrEditSearchEngine', + 'new_title': title, + 'new_keyword': keyword, + 'new_url': url} + self._GetResultFromJSONRequest(cmd_dict) + + def EditSearchEngine(self, keyword, new_title, new_keyword, new_url): + """Edit info for existing search engine. + + Args: + keyword: existing search engine keyword. + new_title: new name for this search engine. + new_keyword: new keyword for this search engine. + new_url: new url for this search engine. + """ + # Ensure that the search engine profile is loaded into data model. + self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}) + cmd_dict = {'command': 'AddOrEditSearchEngine', + 'keyword': keyword, + 'new_title': new_title, + 'new_keyword': new_keyword, + 'new_url': new_url} + self._GetResultFromJSONRequest(cmd_dict) + + def DeleteSearchEngine(self, keyword): + """Delete search engine with given keyword. + + Args: + keyword: the keyword string of the search engine to delete. + """ + # Ensure that the search engine profile is loaded into data model. + self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}) + cmd_dict = {'command': 'PerformActionOnSearchEngine', 'keyword': keyword, + 'action': 'delete'} + self._GetResultFromJSONRequest(cmd_dict) + + def MakeSearchEngineDefault(self, keyword): + """Make search engine with given keyword the default search. + + Args: + keyword: the keyword string of the search engine to make default. + """ + # Ensure that the search engine profile is loaded into data model. + self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}) + cmd_dict = {'command': 'PerformActionOnSearchEngine', 'keyword': keyword, + 'action': 'default'} + self._GetResultFromJSONRequest(cmd_dict) + def GetPrefsInfo(self): """Return info about preferences. |