Commit 9d996c06 authored by kevers@chromium.org's avatar kevers@chromium.org

Add automated test for the edit search engine dialog.

BUG=chromium:97846
TEST=browser_tests -gtest_filter=EditSearchEngineDialogUITest*


Review URL: http://codereview.chromium.org/8676008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113022 0039d316-1c4b-4281-b951-d872f2087c98
parent 5c2d03f6
......@@ -5,6 +5,83 @@
cr.define('editSearchEngineDialog', function() {
'use strict';
/**
* Accessor for an entry field in the search engine dialog.
* @param {string} baseName Name of the field, which serves as a base name
* for the text input field and icon.
* @constructor
*/
function SearchEngineDialogEntryField(baseName) {
this.name_ = baseName;
this.text_ = $(baseName + '-text');
this.icon_ = $(baseName + '-icon');
this.text_.oninput = validate;
return this;
}
SearchEngineDialogEntryField.prototype = {
/*
* Getter for the name of the field.
* @type {string} Descriptive name of the field.
*/
get name() {
return this.name_;
},
/*
* Getter for the content of the input field.
* @type {string} Text content in the input field.
*/
get value() {
return this.text_.value;
},
/**
* Setter for the content of the input field. The validity of the input is
* not automatically revalidated.
* @type {string} New content for the input field.
*/
set value(text) {
this.text_.value = text;
// Validity is in an indeterminate state until validate is called. Clear
// the class name associated with the icon.
this.icon_.className = '';
},
/**
* Getter for the validity of an input field.
* @type {boolean} True if the text input is valid, otherwise false.
*/
get valid() {
return this.icon_.className == 'valid';
},
/**
* Setter for the input field validily.
* @type {boolean} True if the input field is valid, false for invalid.
*/
set valid(state) {
this.icon_.className = state ? 'valid' : 'invalid';
},
/**
* Creates a text representation of the class containing the name,
* text field contents and validity.
* @return {string} Text representation.
*/
toString: function() {
return this.name_ + ': \'' + this.text_.value + '\' (' +
this.icon_.className + ')';
}
};
/**
* Accessors for entry fields in the search engine dialog. Initialized after
* content is loaded.
* @type {Object.<string, SearchEngineDialogEntryField>}
*/
var inputFields = {};
/**
* Disables the controls while the dialog is busy.
*/
......@@ -30,42 +107,37 @@ cr.define('editSearchEngineDialog', function() {
* for corresponding text fields.
*/
function setDetails(details) {
$('description-text').value = details.description;
$('keyword-text').value = details.keyword;
$('url-text').value = details.url;
inputFields.description.value = details.description;
inputFields.keyword.value = details.keyword;
inputFields.url.value = details.url;
validate();
}
/**
* Updates the validity icon element by changing its style.
* @param {Object} element The element to change.
* @param {boolean} valid True if the data is valid.
*/
function setValidImage(element, valid) {
element.className = valid ? 'valid' : 'invalid';
}
/**
* Sends all strings to Chrome for validation. Chrome is expected to respond
* by calling setValidation.
*/
function validate() {
chrome.send('requestValidation', [$('description-text').value,
$('keyword-text').value, $('url-text').value]);
chrome.send('requestValidation', [inputFields.description.value,
inputFields.keyword.value,
inputFields.url.value]);
}
/**
* Sets dialog state given the results of the validation of input by Chrome.
* @param {{description: boolean, details: boolean, url: boolean, ok:boolean}}
* details A dictionary of booleans indicating the validation results of
* various parts of the UI. |description|, |details| and |url| indicate
* the validity of the respective text fields, and |ok| indicates whether
* @param {{description: boolean,
keyword: boolean,
url: boolean,
ok: boolean}} details
* A dictionary of booleans indicating the validation results of various
* parts of the UI. |description|, |keyword| and |url| indicate the
* validity of the respective text fields, and |ok| indicates whether
* the OK/Save button can be pressed.
*/
function setValidation(details) {
setValidImage($('description-icon'), details.description);
setValidImage($('keyword-icon'), details.keyword);
setValidImage($('url-icon'), details.url);
inputFields.description.valid = details.description;
inputFields.keyword.valid = details.keyword;
inputFields.url.valid = details.url;
$('save').disabled = !details.ok;
}
......@@ -78,7 +150,7 @@ cr.define('editSearchEngineDialog', function() {
var childNodes = parent.childNodes;
for (var i = childNodes.length - 1; i >= 0; i--)
parent.appendChild(childNodes[i]);
};
}
var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
......@@ -88,34 +160,52 @@ cr.define('editSearchEngineDialog', function() {
function initialize() {
i18nTemplate.process(document, templateData);
inputFields.description = new SearchEngineDialogEntryField('description');
inputFields.keyword = new SearchEngineDialogEntryField('keyword');
inputFields.url = new SearchEngineDialogEntryField('url');
document.title = chrome.dialogArguments == 'add' ? templateData.titleNew :
templateData.titleEdit;
$('cancel').onclick = function() {
closeWithResult();
}
};
$('save').onclick = function() {
closeWithResult({description: $('description-text').value,
keyword: $('keyword-text').value,
url: $('url-text').value});
}
$('description-text').oninput = validate;
$('keyword-text').oninput = validate;
$('url-text').oninput = validate;
closeWithResult({description: inputFields.description.value,
keyword: inputFields.keyword.value,
url: inputFields.url.value});
};
setValidation({description: false, keyword: false, url: false});
if (cr.isViews)
forEach(document.querySelectorAll('.button-strip'), reverseChildren);
chrome.send('requestDetails')
// TODO(kevers): Should be a cleaner way to implement without requiring
// multiple callback to C++. The call to |requestDetails| fetches the
// content to insert into the input fields without inidicating if the
// inputs are valid. A separate callback is then required to determine if
// the inputs are OK. In fact, it should be possible to pass in the details
// when the dialog is created rather than using a callback.
chrome.send('requestDetails');
}
/**
* Retrieves the save button element.
* @return {Element}
*/
function getSave() {
return $('save');
}
document.addEventListener('DOMContentLoaded', initialize);
return {
inputFields: inputFields,
getSave: getSave,
setDetails: setDetails,
setValidation: setValidation,
validate: validate
};
});
......
......@@ -19,9 +19,9 @@
#include "chrome/browser/ui/search_engines/edit_search_engine_controller.h"
#include "chrome/browser/ui/webui/theme_source.h"
#include "chrome/common/url_constants.h"
#include "grit/ui_resources.h"
#include "grit/theme_resources.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "grit/ui_resources.h"
#include "ui/base/l10n/l10n_util.h"
namespace {
......@@ -155,11 +155,11 @@ void EditSearchEngineDialogHandlerWebUI::RequestValidation(
bool isDescriptionValid = controller_->IsTitleValid(description_str);
bool isKeywordValid = controller_->IsKeywordValid(keyword_str);
bool isUrlValid = controller_->IsURLValid(url_str);
validation.SetBoolean("description", isDescriptionValid );
validation.SetBoolean("keyword", isKeywordValid );
validation.SetBoolean("url", isUrlValid );
validation.SetBoolean("description", isDescriptionValid);
validation.SetBoolean("keyword", isKeywordValid);
validation.SetBoolean("url", isUrlValid);
validation.SetBoolean("ok", isDescriptionValid && isKeywordValid &&
isUrlValid );
isUrlValid);
web_ui_->CallJavascriptFunction("editSearchEngineDialog.setValidation",
validation);
}
......
......@@ -2693,6 +2693,9 @@
'test/data/webui/chrome_send_browsertest.cc',
'test/data/webui/chrome_send_browsertest.h',
'test/data/webui/chrome_send_browsertest.js',
'test/data/webui/edit_search_engine_dialog_browsertest.cc',
'test/data/webui/edit_search_engine_dialog_browsertest.h',
'test/data/webui/edit_search_engine_dialog_browsertest.js',
'test/data/webui/hung_renderer_dialog_test.js',
'test/data/webui/hung_renderer_dialog_ui_test-inl.h',
'test/data/webui/mock4js_browsertest.js',
......
// Copyright (c) 2011 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.
#include "chrome/test/data/webui/edit_search_engine_dialog_browsertest.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/webui/chrome_web_ui.h"
#include "chrome/browser/ui/webui/edit_search_engine_dialog_webui.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/test_html_dialog_observer.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/browser/webui/web_ui.h"
namespace {
// Creates a Template URL for testing purposes.
TemplateURL* CreateTestTemplateURL(int seed) {
TemplateURL* turl = new TemplateURL();
turl->SetURL(base::StringPrintf("http://www.test%d.com/", seed), 0, 0);
turl->set_keyword(ASCIIToUTF16(base::StringPrintf("keyword%d", seed)));
turl->set_short_name(ASCIIToUTF16(base::StringPrintf("short%d", seed)));
turl->set_safe_for_autoreplace(true);
GURL favicon_url("http://favicon.url");
turl->SetFaviconURL(favicon_url);
turl->set_date_created(base::Time::FromTimeT(100));
turl->set_last_modified(base::Time::FromTimeT(100));
turl->SetPrepopulateId(999999);
turl->set_sync_guid(base::StringPrintf("0000-0000-0000-%04d", seed));
return turl;
}
} // namespace
EditSearchEngineDialogUITest::EditSearchEngineDialogUITest() {}
EditSearchEngineDialogUITest::~EditSearchEngineDialogUITest() {}
void EditSearchEngineDialogUITest::ShowSearchEngineDialog() {
// Force the flag so that we will use the WebUI version of the Dialog.
ChromeWebUI::OverrideMoreWebUI(true);
// The TestHtmlDialogObserver will catch our dialog when it gets created.
TestHtmlDialogObserver dialog_observer(this);
// Show the Edit Search Engine Dialog.
EditSearchEngineDialogWebUI::ShowEditSearchEngineDialog(
CreateTestTemplateURL(0),
browser()->profile());
// Now we can get the WebUI object from the observer, and make some details
// about our test available to the JavaScript.
WebUI* webui = dialog_observer.GetWebUI();
webui->tab_contents()->render_view_host()->SetWebUIProperty(
"expectedUrl", chrome::kChromeUIEditSearchEngineDialogURL);
// Tell the test which WebUI instance we are dealing with and complete
// initialization of this test.
SetWebUIInstance(webui);
WebUIBrowserTest::SetUpOnMainThread();
}
// Copyright (c) 2011 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_DATA_WEBUI_EDIT_SEARCH_ENGINE_DIALOG_BROWSERTEST_H_
#define CHROME_TEST_DATA_WEBUI_EDIT_SEARCH_ENGINE_DIALOG_BROWSERTEST_H_
#pragma once
#include "chrome/browser/ui/webui/web_ui_browsertest.h"
// Test framework for the WebUI based edit search engine dialog.
class EditSearchEngineDialogUITest : public WebUIBrowserTest {
protected:
EditSearchEngineDialogUITest();
virtual ~EditSearchEngineDialogUITest();
// Displays the search engine dialog for testing.
void ShowSearchEngineDialog();
private:
DISALLOW_COPY_AND_ASSIGN(EditSearchEngineDialogUITest);
};
#endif // CHROME_TEST_DATA_WEBUI_EDIT_SEARCH_ENGINE_DIALOG_BROWSERTEST_H_
// Copyright (c) 2011 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.
/**
* Test fixture for search engine dialog tests.
* @extends {testing.Test}
*/
function EditSearchEngineDialogUITest() {};
EditSearchEngineDialogUITest.prototype = {
__proto__: testing.Test.prototype,
/**
* Define the C++ fixture class and include it.
* @type {?string}
* @override
*/
typedefCppFixture: 'EditSearchEngineDialogUITest',
/**
* Show the search engine dialog.
*/
testGenPreamble: function() {
GEN('ShowSearchEngineDialog();');
},
};
/**
* Asynchronous Test fixture for search engine dialog tests.
* @extends {EditSearchEngineDialogUITest}
*/
function EditSearchEngineDialogUITestAsync() {};
EditSearchEngineDialogUITestAsync.prototype = {
__proto__: EditSearchEngineDialogUITest.prototype,
/** @inheritDoc */
isAsync: true,
/**
* Argument to pass through to |setValidation| once the test is ready to
* process it. The argument consists of a mapping between field names and
* contents.
* @type{Object.<string, string>}
*/
pendingDetails: null,
/**
* Overrides |setValidation| to address a potential race condition due to
* asynchronous initialization of the edit search engine dialog. The override
* catches the data being passed to the original |setValidation| method and
* defers the validation call until the test is ready to process it.
*/
preLoad: function() {
var self = this;
window.addEventListener('DOMContentLoaded', function() {
self.originalSetValidation = editSearchEngineDialog.setValidation;
editSearchEngineDialog.setValidation = function(details) {
self.pendingDetails = details;
};
});
},
};
// Include the bulk of c++ code.
GEN('#include ' +
'"chrome/test/data/webui/edit_search_engine_dialog_browsertest.h"');
/**
* Confirms that the data is loaded into the text fields.
*/
TEST_F('EditSearchEngineDialogUITest', 'testData', function() {
var inputFields = editSearchEngineDialog.inputFields;
expectEquals(chrome.expectedUrl, window.location.href);
expectEquals('short0', inputFields.description.value);
expectEquals('keyword0', inputFields.keyword.value);
expectEquals('http://www.test0.com/', inputFields.url.value);
});
/**
* Confirms that the field validation is working.
*/
TEST_F('EditSearchEngineDialogUITestAsync',
'testInputValidation',
function() {
var inputFields = editSearchEngineDialog.inputFields;
var invalidData = {
description: '',
keyword: '',
url: '%'
};
var fieldNames = Object.keys(invalidData);
var index = 0;
var self = this;
var validityChecker = function(details) {
self.originalSetValidation(details);
// Ensure that all inputs are valid with the initial data, and that save
// is enabled. Each subsequent call will have an additional field
// containing invalid data.
for (var i = 0; i < fieldNames.length; i++) {
var field = fieldNames[i];
expectEquals(i >= index,
inputFields[field].valid,
'field = ' + field +
', index = ' + index);
}
var saveButtonState = editSearchEngineDialog.getSave().disabled;
if (index == 0)
expectFalse(saveButtonState);
else
expectTrue(saveButtonState);
// Once the validity of the input fields and save button are confirmed, we
// invalidate a single field in the input and trigger a new call to check
// the validity. We start with the first input field and increment the
// index with each call in order to advance to the next field. Although
// each test is asynchronous, we are guaranteed to get the calls in the
// correct order since we wait for the previous validation to complete
// before moving to the next field.
if (index < fieldNames.length) {
if (index == fieldNames.length - 1) {
editSearchEngineDialog.setValidation = this.continueTest(
WhenTestDone.ALWAYS,
validityChecker);
}
var fieldName = fieldNames[index++];
inputFields[fieldName].value = invalidData[fieldName];
editSearchEngineDialog.validate();
}
};
// Override |setValidation| to check the status of the icons and save button.
editSearchEngineDialog.setValidation = this.continueTest(
WhenTestDone.EXPECT,
validityChecker);
// Check if the first call to |setValidation| tripped before being redirected
// to |validityChecker|.
if (this.pendingDetails)
editSearchEngineDialog.setValidation(this.pendingDetails);
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment