summaryrefslogtreecommitdiffstats
path: root/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/compare.js
diff options
context:
space:
mode:
Diffstat (limited to 'editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/compare.js')
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/compare.js489
1 files changed, 489 insertions, 0 deletions
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/compare.js b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/compare.js
new file mode 100644
index 0000000000..be059cfc86
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/compare.js
@@ -0,0 +1,489 @@
+/**
+ * @fileoverview
+ * Comparison functions used in the RTE test suite.
+ *
+ * Copyright 2010 Google Inc.
+ *
+ * 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.
+ *
+ * @version 0.1
+ * @author rolandsteiner@google.com
+ */
+
+/**
+ * constants used only in the compare functions.
+ */
+var RESULT_DIFF = 0; // actual result doesn't match expectation
+var RESULT_SEL = 1; // actual result matches expectation in HTML only
+var RESULT_EQUAL = 2; // actual result matches expectation in both HTML and selection
+
+/**
+ * Gets the test expectations as an array from the passed-in field.
+ *
+ * @param {Array|String} the test expectation(s) as string or array.
+ * @return {Array} test expectations as an array.
+ */
+function getExpectationArray(expected) {
+ if (expected === undefined) {
+ return [];
+ }
+ if (expected === null) {
+ return [null];
+ }
+ switch (typeof expected) {
+ case 'string':
+ case 'boolean':
+ case 'number':
+ return [expected];
+ }
+ // Assume it's already an array.
+ return expected;
+}
+
+/**
+ * Compare a test result to a single expectation string.
+ *
+ * FIXME: add support for optional elements/attributes.
+ *
+ * @param expected {String} the already canonicalized (with the exception of selection marks) expectation string
+ * @param actual {String} the already canonicalized (with the exception of selection marks) actual result
+ * @return {Integer} one of the RESULT_... return values
+ * @see variables.js for return values
+ */
+function compareHTMLToSingleExpectation(expected, actual) {
+ // If the test checks the selection, then the actual string must match the
+ // expectation exactly.
+ if (expected == actual) {
+ return RESULT_EQUAL;
+ }
+
+ // Remove selection markers and see if strings match then.
+ expected = expected.replace(/ [{}\|]>/g, '>'); // intra-tag
+ expected = expected.replace(/[\[\]\^{}\|]/g, ''); // outside tag
+ actual = actual.replace(/ [{}\|]>/g, '>'); // intra-tag
+ actual = actual.replace(/[\[\]\^{}\|]/g, ''); // outside tag
+
+ return (expected == actual) ? RESULT_SEL : RESULT_DIFF;
+}
+
+/**
+ * Compare the current HTMLtest result to the expectation string(s).
+ *
+ * @param actual {String/Boolean} actual value
+ * @param expected {String/Array} expectation(s)
+ * @param emitFlags {Object} flags to use for canonicalization
+ * @return {Integer} one of the RESULT_... return values
+ * @see variables.js for return values
+ */
+function compareHTMLToExpectation(actual, expected, emitFlags) {
+ // Find the most favorable result among the possible expectation strings.
+ var expectedArr = getExpectationArray(expected);
+ var count = expectedArr ? expectedArr.length : 0;
+ var best = RESULT_DIFF;
+
+ for (var idx = 0; idx < count && best < RESULT_EQUAL; ++idx) {
+ var expected = expectedArr[idx];
+ expected = canonicalizeSpaces(expected);
+ expected = canonicalizeElementsAndAttributes(expected, emitFlags);
+
+ var singleResult = compareHTMLToSingleExpectation(expected, actual);
+
+ best = Math.max(best, singleResult);
+ }
+ return best;
+}
+
+/**
+ * Compare the current HTMLtest result to expected and acceptable results
+ *
+ * @param expected {String/Array} expected result(s)
+ * @param accepted {String/Array} accepted result(s)
+ * @param actual {String} actual result
+ * @param emitFlags {Object} how to canonicalize the HTML strings
+ * @param result {Object} [out] object recieving the result of the comparison.
+ */
+function compareHTMLTestResultTo(expected, accepted, actual, emitFlags, result) {
+ actual = actual.replace(/[\x60\xb4]/g, '');
+ actual = canonicalizeElementsAndAttributes(actual, emitFlags);
+
+ var bestExpected = compareHTMLToExpectation(actual, expected, emitFlags);
+
+ if (bestExpected == RESULT_EQUAL) {
+ // Shortcut - it doesn't get any better
+ result.valresult = VALRESULT_EQUAL;
+ result.selresult = SELRESULT_EQUAL;
+ return;
+ }
+
+ var bestAccepted = compareHTMLToExpectation(actual, accepted, emitFlags);
+
+ switch (bestExpected) {
+ case RESULT_SEL:
+ switch (bestAccepted) {
+ case RESULT_EQUAL:
+ // The HTML was equal to the/an expected HTML result as well
+ // (just not the selection there), therefore the difference
+ // between expected and accepted can only lie in the selection.
+ result.valresult = VALRESULT_EQUAL;
+ result.selresult = SELRESULT_ACCEPT;
+ return;
+
+ case RESULT_SEL:
+ case RESULT_DIFF:
+ // The acceptable expectations did not yield a better result
+ // -> stay with the original (i.e., comparison to 'expected') result.
+ result.valresult = VALRESULT_EQUAL;
+ result.selresult = SELRESULT_DIFF;
+ return;
+ }
+ break;
+
+ case RESULT_DIFF:
+ switch (bestAccepted) {
+ case RESULT_EQUAL:
+ result.valresult = VALRESULT_ACCEPT;
+ result.selresult = SELRESULT_EQUAL;
+ return;
+
+ case RESULT_SEL:
+ result.valresult = VALRESULT_ACCEPT;
+ result.selresult = SELRESULT_DIFF;
+ return;
+
+ case RESULT_DIFF:
+ result.valresult = VALRESULT_DIFF;
+ result.selresult = SELRESULT_NA;
+ return;
+ }
+ break;
+ }
+
+ throw INTERNAL_ERR + HTML_COMPARISON;
+}
+
+/**
+ * Verify that the canaries are unviolated.
+ *
+ * @param container {Object} the test container descriptor as object reference
+ * @param result {Object} object reference that contains the result data
+ * @return {Boolean} whether the canaries' HTML is OK (selection flagged, but not fatal)
+ */
+function verifyCanaries(container, result) {
+ if (!container.canary) {
+ return true;
+ }
+
+ var str = canonicalizeElementsAndAttributes(result.bodyInnerHTML, emitFlagsForCanary);
+
+ if (str.length < 2 * container.canary.length) {
+ result.valresult = VALRESULT_CANARY;
+ result.selresult = SELRESULT_NA;
+ result.output = result.bodyOuterHTML;
+ return false;
+ }
+
+ var strBefore = str.substr(0, container.canary.length);
+ var strAfter = str.substr(str.length - container.canary.length);
+
+ // Verify that the canary stretch doesn't contain any selection markers
+ if (SELECTION_MARKERS.test(strBefore) || SELECTION_MARKERS.test(strAfter)) {
+ str = str.replace(SELECTION_MARKERS, '');
+ if (str.length < 2 * container.canary.length) {
+ result.valresult = VALRESULT_CANARY;
+ result.selresult = SELRESULT_NA;
+ result.output = result.bodyOuterHTML;
+ return false;
+ }
+
+ // Selection escaped contentEditable element, but HTML may still be ok.
+ result.selresult = SELRESULT_CANARY;
+ strBefore = str.substr(0, container.canary.length);
+ strAfter = str.substr(str.length - container.canary.length);
+ }
+
+ if (strBefore !== container.canary || strAfter !== container.canary) {
+ result.valresult = VALRESULT_CANARY;
+ result.selresult = SELRESULT_NA;
+ result.output = result.bodyOuterHTML;
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Compare the current HTMLtest result to the expectation string(s).
+ * Sets the global result variables.
+ *
+ * @param suite {Object} the test suite as object reference
+ * @param group {Object} group of tests within the suite the test belongs to
+ * @param test {Object} the test as object reference
+ * @param container {Object} the test container description
+ * @param result {Object} [in/out] the result description, incl. HTML strings
+ * @see variables.js for result values
+ */
+function compareHTMLTestResult(suite, group, test, container, result) {
+ if (!verifyCanaries(container, result)) {
+ return;
+ }
+
+ var emitFlags = {
+ emitAttrs: getTestParameter(suite, group, test, PARAM_CHECK_ATTRIBUTES),
+ emitStyle: getTestParameter(suite, group, test, PARAM_CHECK_STYLE),
+ emitClass: getTestParameter(suite, group, test, PARAM_CHECK_CLASS),
+ emitID: getTestParameter(suite, group, test, PARAM_CHECK_ID),
+ lowercase: true,
+ canonicalizeUnits: true
+ };
+
+ // 2a.) Compare opening tag -
+ // decide whether to compare vs. outer or inner HTML based on this.
+ var openingTagEnd = result.outerHTML.indexOf('>') + 1;
+ var openingTag = result.outerHTML.substr(0, openingTagEnd);
+
+ openingTag = canonicalizeElementsAndAttributes(openingTag, emitFlags);
+ var tagCmp = compareHTMLToExpectation(openingTag, container.tagOpen, emitFlags);
+
+ if (tagCmp == RESULT_EQUAL) {
+ result.output = result.innerHTML;
+ compareHTMLTestResultTo(
+ getTestParameter(suite, group, test, PARAM_EXPECTED),
+ getTestParameter(suite, group, test, PARAM_ACCEPT),
+ result.innerHTML,
+ emitFlags,
+ result)
+ } else {
+ result.output = result.outerHTML;
+ compareHTMLTestResultTo(
+ getContainerParameter(suite, group, test, container, PARAM_EXPECTED_OUTER),
+ getContainerParameter(suite, group, test, container, PARAM_ACCEPT_OUTER),
+ result.outerHTML,
+ emitFlags,
+ result)
+ }
+}
+
+/**
+ * Insert a selection position indicator.
+ *
+ * @param node {DOMNode} the node where to insert the selection indicator
+ * @param offs {Integer} the offset of the selection indicator
+ * @param textInd {String} the indicator to use if the node is a text node
+ * @param elemInd {String} the indicator to use if the node is an element node
+ */
+function insertSelectionIndicator(node, offs, textInd, elemInd) {
+ switch (node.nodeType) {
+ case DOM_NODE_TYPE_TEXT:
+ // Insert selection marker for text node into text content.
+ var text = node.data;
+ node.data = text.substring(0, offs) + textInd + text.substring(offs);
+ break;
+
+ case DOM_NODE_TYPE_ELEMENT:
+ var child = node.firstChild;
+ try {
+ // node has other children: insert marker as comment node
+ var comment = document.createComment(elemInd);
+ while (child && offs) {
+ --offs;
+ child = child.nextSibling;
+ }
+ if (child) {
+ node.insertBefore(comment, child);
+ } else {
+ node.appendChild(comment);
+ }
+ } catch (ex) {
+ // can't append child comment -> insert as special attribute(s)
+ switch (elemInd) {
+ case '|':
+ node.setAttribute(ATTRNAME_SEL_START, '1');
+ node.setAttribute(ATTRNAME_SEL_END, '1');
+ break;
+
+ case '{':
+ node.setAttribute(ATTRNAME_SEL_START, '1');
+ break;
+
+ case '}':
+ node.setAttribute(ATTRNAME_SEL_END, '1');
+ break;
+ }
+ }
+ break;
+ }
+}
+
+/**
+ * Adds quotes around all text nodes to show cases with non-normalized
+ * text nodes. Those are not a bug, but may still be usefil in helping to
+ * debug erroneous cases.
+ *
+ * @param node {DOMNode} root node from which to descend
+ */
+function encloseTextNodesWithQuotes(node) {
+ switch (node.nodeType) {
+ case DOM_NODE_TYPE_ELEMENT:
+ for (var i = 0; i < node.childNodes.length; ++i) {
+ encloseTextNodesWithQuotes(node.childNodes[i]);
+ }
+ break;
+
+ case DOM_NODE_TYPE_TEXT:
+ node.data = '\x60' + node.data + '\xb4';
+ break;
+ }
+}
+
+/**
+ * Retrieve the result of a test run and do some preliminary canonicalization.
+ *
+ * @param container {Object} the container where to retrieve the result from as object reference
+ * @param result {Object} object reference that contains the result data
+ * @return {String} a preliminarily canonicalized innerHTML with selection markers
+ */
+function prepareHTMLTestResult(container, result) {
+ // Start with empty strings in case any of the below throws.
+ result.innerHTML = '';
+ result.outerHTML = '';
+
+ // 1.) insert selection markers
+ var selRange = createFromWindow(container.win);
+ if (selRange) {
+ // save values, since range object gets auto-modified
+ var node1 = selRange.getAnchorNode();
+ var offs1 = selRange.getAnchorOffset();
+ var node2 = selRange.getFocusNode();
+ var offs2 = selRange.getFocusOffset();
+
+ // add markers
+ if (node1 && node1 == node2 && offs1 == offs2) {
+ // collapsed selection
+ insertSelectionIndicator(node1, offs1, '^', '|');
+ } else {
+ // Start point and end point are different
+ if (node1) {
+ insertSelectionIndicator(node1, offs1, '[', '{');
+ }
+
+ if (node2) {
+ if (node1 == node2 && offs1 < offs2) {
+ // Anchor indicator was inserted under the same node, so we need
+ // to shift the offset by 1
+ ++offs2;
+ }
+ insertSelectionIndicator(node2, offs2, ']', '}');
+ }
+ }
+ }
+
+ // 2.) insert markers for text node boundaries;
+ encloseTextNodesWithQuotes(container.editor);
+
+ // 3.) retrieve inner and outer HTML
+ result.innerHTML = initialCanonicalizationOf(container.editor.innerHTML);
+ result.bodyInnerHTML = initialCanonicalizationOf(container.body.innerHTML);
+ if (goog.userAgent.IE) {
+ result.outerHTML = initialCanonicalizationOf(container.editor.outerHTML);
+ result.bodyOuterHTML = initialCanonicalizationOf(container.body.outerHTML);
+ result.outerHTML = result.outerHTML.replace(/^\s+/, '');
+ result.outerHTML = result.outerHTML.replace(/\s+$/, '');
+ result.bodyOuterHTML = result.bodyOuterHTML.replace(/^\s+/, '');
+ result.bodyOuterHTML = result.bodyOuterHTML.replace(/\s+$/, '');
+ } else {
+ result.outerHTML = initialCanonicalizationOf(new XMLSerializer().serializeToString(container.editor));
+ result.bodyOuterHTML = initialCanonicalizationOf(new XMLSerializer().serializeToString(container.body));
+ }
+}
+
+/**
+ * Compare a text test result to the expectation string(s).
+ *
+ * @param suite {Object} the test suite as object reference
+ * @param group {Object} group of tests within the suite the test belongs to
+ * @param test {Object} the test as object reference
+ * @param actual {String/Boolean} actual value
+ * @param expected {String/Array} expectation(s)
+ * @return {Boolean} whether we found a match
+ */
+function compareTextTestResultWith(suite, group, test, actual, expected) {
+ var expectedArr = getExpectationArray(expected);
+ // Find the most favorable result among the possible expectation strings.
+ var count = expectedArr.length;
+
+ // If the value matches the expectation exactly, then we're fine.
+ for (var idx = 0; idx < count; ++idx) {
+ if (actual === expectedArr[idx])
+ return true;
+ }
+
+ // Otherwise see if we should canonicalize specific value types.
+ //
+ // We only need to look at font name, color and size units if the originating
+ // test was both a) queryCommandValue and b) querying a font name/color/size
+ // specific criterion.
+ //
+ // TODO(rolandsteiner): This is ugly! Refactor!
+ switch (getTestParameter(suite, group, test, PARAM_QUERYCOMMANDVALUE)) {
+ case 'backcolor':
+ case 'forecolor':
+ case 'hilitecolor':
+ for (var idx = 0; idx < count; ++idx) {
+ if (new Color(actual).compare(new Color(expectedArr[idx])))
+ return true;
+ }
+ return false;
+
+ case 'fontname':
+ for (var idx = 0; idx < count; ++idx) {
+ if (new FontName(actual).compare(new FontName(expectedArr[idx])))
+ return true;
+ }
+ return false;
+
+ case 'fontsize':
+ for (var idx = 0; idx < count; ++idx) {
+ if (new FontSize(actual).compare(new FontSize(expectedArr[idx])))
+ return true;
+ }
+ return false;
+ }
+
+ return false;
+}
+
+/**
+ * Compare the passed-in text test result to the expectation string(s).
+ * Sets the global result variables.
+ *
+ * @param suite {Object} the test suite as object reference
+ * @param group {Object} group of tests within the suite the test belongs to
+ * @param test {Object} the test as object reference
+ * @param actual {String/Boolean} actual value
+ * @return {Integer} a RESUTLHTML... result value
+ * @see variables.js for result values
+ */
+function compareTextTestResult(suite, group, test, result) {
+ var expected = getTestParameter(suite, group, test, PARAM_EXPECTED);
+ if (compareTextTestResultWith(suite, group, test, result.output, expected)) {
+ result.valresult = VALRESULT_EQUAL;
+ return;
+ }
+ var accepted = getTestParameter(suite, group, test, PARAM_ACCEPT);
+ if (accepted && compareTextTestResultWith(suite, group, test, result.output, accepted)) {
+ result.valresult = VALRESULT_ACCEPT;
+ return;
+ }
+ result.valresult = VALRESULT_DIFF;
+}
+