blob: 763e3da53274391fb4ae5236ce498c4c56e7f327 [file] [log] [blame]
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Class that allows for simple text editing tests.
*
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.testing.editor.TestHelper');
goog.require('goog.Disposable');
goog.require('goog.dom');
goog.require('goog.dom.Range');
goog.require('goog.editor.BrowserFeature');
goog.require('goog.editor.node');
goog.require('goog.editor.plugins.AbstractBubblePlugin');
goog.require('goog.testing.dom');
/**
* Create a new test controller.
* @param {Element} root The root editable element.
* @constructor
* @extends {goog.Disposable}
* @final
*/
goog.testing.editor.TestHelper = function(root) {
if (!root) {
throw Error('Null root');
}
goog.Disposable.call(this);
/**
* Convenience variable for root DOM element.
* @type {!Element}
* @private
*/
this.root_ = root;
/**
* The starting HTML of the editable element.
* @type {string}
* @private
*/
this.savedHtml_ = '';
};
goog.inherits(goog.testing.editor.TestHelper, goog.Disposable);
/**
* Selects a new root element.
* @param {Element} root The root editable element.
*/
goog.testing.editor.TestHelper.prototype.setRoot = function(root) {
if (!root) {
throw Error('Null root');
}
this.root_ = root;
};
/**
* Make the root element editable. Alse saves its HTML to be restored
* in tearDown.
*/
goog.testing.editor.TestHelper.prototype.setUpEditableElement = function() {
this.savedHtml_ = this.root_.innerHTML;
if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
this.root_.contentEditable = true;
} else {
this.root_.ownerDocument.designMode = 'on';
}
this.root_.setAttribute('g_editable', 'true');
};
/**
* Reset the element previously initialized, restoring its HTML and making it
* non editable.
* @suppress {accessControls} Private state of
* {@link goog.editor.plugins.AbstractBubblePlugin} is accessed for test
* purposes.
*/
goog.testing.editor.TestHelper.prototype.tearDownEditableElement = function() {
if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
this.root_.contentEditable = false;
} else {
this.root_.ownerDocument.designMode = 'off';
}
goog.dom.removeChildren(this.root_);
this.root_.innerHTML = this.savedHtml_;
this.root_.removeAttribute('g_editable');
if (goog.editor.plugins && goog.editor.plugins.AbstractBubblePlugin) {
// Remove old bubbles.
for (var key in goog.editor.plugins.AbstractBubblePlugin.bubbleMap_) {
goog.editor.plugins.AbstractBubblePlugin.bubbleMap_[key].dispose();
}
// Ensure we get a new bubble for each test.
goog.editor.plugins.AbstractBubblePlugin.bubbleMap_ = {};
}
};
/**
* Assert that the html in 'root' is substantially similar to htmlPattern.
* This method tests for the same set of styles, and for the same order of
* nodes. Breaking whitespace nodes are ignored. Elements can be annotated
* with classnames corresponding to keys in goog.userAgent and will be
* expected to show up in that user agent and expected not to show up in
* others.
* @param {string} htmlPattern The pattern to match.
*/
goog.testing.editor.TestHelper.prototype.assertHtmlMatches = function(
htmlPattern) {
goog.testing.dom.assertHtmlContentsMatch(htmlPattern, this.root_);
};
/**
* Finds the first text node descendant of root with the given content.
* @param {string|RegExp} textOrRegexp The text to find, or a regular
* expression to find a match of.
* @return {Node} The first text node that matches, or null if none is found.
*/
goog.testing.editor.TestHelper.prototype.findTextNode = function(textOrRegexp) {
return goog.testing.dom.findTextNode(textOrRegexp, this.root_);
};
/**
* Select from the given from offset in the given from node to the given
* to offset in the optionally given to node. If nodes are passed in, uses them,
* otherwise uses findTextNode to find the nodes to select. Selects a caret
* if opt_to and opt_toOffset are not given.
* @param {Node|string} from Node or text of the node to start the selection at.
* @param {number} fromOffset Offset within the above node to start the
* selection at.
* @param {Node|string=} opt_to Node or text of the node to end the selection
* at.
* @param {number=} opt_toOffset Offset within the above node to end the
* selection at.
*/
goog.testing.editor.TestHelper.prototype.select = function(from, fromOffset,
opt_to, opt_toOffset) {
var end;
var start = end = goog.isString(from) ? this.findTextNode(from) : from;
var endOffset;
var startOffset = endOffset = fromOffset;
if (opt_to && goog.isNumber(opt_toOffset)) {
end = goog.isString(opt_to) ? this.findTextNode(opt_to) : opt_to;
endOffset = opt_toOffset;
}
goog.dom.Range.createFromNodes(start, startOffset, end, endOffset).select();
};
/** @override */
goog.testing.editor.TestHelper.prototype.disposeInternal = function() {
if (goog.editor.node.isEditableContainer(this.root_)) {
this.tearDownEditableElement();
}
delete this.root_;
goog.testing.editor.TestHelper.base(this, 'disposeInternal');
};