diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /editor/libeditor/tests/browserscope/lib/richtext | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'editor/libeditor/tests/browserscope/lib/richtext')
8 files changed, 1429 insertions, 0 deletions
diff --git a/editor/libeditor/tests/browserscope/lib/richtext/LICENSE b/editor/libeditor/tests/browserscope/lib/richtext/LICENSE new file mode 100644 index 0000000000..57bc88a15a --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + diff --git a/editor/libeditor/tests/browserscope/lib/richtext/README b/editor/libeditor/tests/browserscope/lib/richtext/README new file mode 100644 index 0000000000..a3bc3110f4 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/README @@ -0,0 +1,58 @@ +README FOR BROWSERSCOPE +----------------------- + +Hey there - thanks for downloading the code. This file has instructions +for getting setup so that you can run the codebase locally. + +This project is built on Google App Engine using the +Django web application framework and written in Python. + +To get started, you'll need to first download the App Engine SDK at: +http://code.google.com/appengine/downloads.html + +For local development, just startup the server: +./pathto/google_appengine/dev_appserver.py --port=8080 browserscope + +You should then be able to access the local application at: +http://localhost:8080/ + +Note: the first time you hit the homepage it may take a little +while - that's because it's trying to read out median times for all +of the tests from a nonexistent datastore and write to memcache. +Just be a lil patient. + +You can run the unit tests at: + http://localhost:8080/test + + +CONTRIBUTING +------------------ + +Most likely you are interested in adding new tests or creating +a new test category. If you are interested in adding tests to an existing +"category" you may want to get in touch with the maintainer for that +branch of the tree. We are really looking forward to receiving your +code in patch format. Currently the category maintainers are: +Network: Steve Souders <souders@gmail.com> +Reflow: Lindsey Simon <elsigh@gmail.com> +Security: Adam Barth <adam@adambarth.com> and Collin Jackson <collin@collinjackson.com> + + +To create a completely new test category: + * Copy one of the existing directories in categories/ + * Edit your test_set.py, handlers.py + * Add your files in templates/ and static/ + * Update urls.py and settings.CATEGORIES + * Follow the examples of other tests re: + * beaconing using/testdriver_base + * your GetScoreAndDisplayValue method + * your GetRowScoreAndDisplayValue method + +References: + * App Engine Docs - http://code.google.com/appengine/docs/python/overview.html + * App Engine Group - http://groups.google.com/group/google-appengine + * Python Docs - http://www.python.org/doc/ + * Django - http://www.djangoproject.com/ + + + diff --git a/editor/libeditor/tests/browserscope/lib/richtext/README.Mozilla b/editor/libeditor/tests/browserscope/lib/richtext/README.Mozilla new file mode 100644 index 0000000000..5d304943f7 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/README.Mozilla @@ -0,0 +1,17 @@ +The BrowserScope project provides a set of cross-browser HTML editor tests, +which we import in our test suite in order to run them as part of our +continuous integration system. + +We pull tests occasionally from their Subversion repository using the pull +script which can be found in this directory. We also record the revision ID +which we've used in the current_revision file inside this directory. + +Using the pull script is quite easy, just switch to this directory, and say: + +sh update_from_upstream + +There are tests which we're currently failing on, and there will probably be +more of those in the future. We should maintain a list of the failing tests +manually in currentStatus.js (which can also be found in this directory), to +make sure that the suite passes entirely, with failing tests marked as todo +items. diff --git a/editor/libeditor/tests/browserscope/lib/richtext/currentStatus.js b/editor/libeditor/tests/browserscope/lib/richtext/currentStatus.js new file mode 100644 index 0000000000..4b645d9db4 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/currentStatus.js @@ -0,0 +1,43 @@ +/** + * This file lists the tests in the BrowserScope suite which we are currently + * failing. We mark them as todo items to keep track of them. + */ + +var knownFailures = { + // Dummy result items. There is one for each category. + 'apply' : { + '0-undefined' : true + }, + 'unapply' : { + '0-undefined' : true + }, + 'change' : { + '0-undefined' : true + }, + 'query' : { + '0-undefined' : true + }, + 'a' : { + 'createbookmark-0' : true, + 'decreasefontsize-0' : true, + 'fontsize-1' : true, + 'subscript-1' : true, + 'superscript-1' : true, + }, + 'u': { + 'removeformat-1' : true, + 'removeformat-2' : true, + 'strikethrough-2' : true, + 'subscript-1' : true, + 'superscript-1' : true, + 'unbookmark-0' : true, + }, + 'q': { + 'fontsize-1' : true, + 'fontsize-2' : true, + }, +}; + +function isKnownFailure(type, test, param) { + return (type in knownFailures) && knownFailures[type][test + "-" + param]; +} diff --git a/editor/libeditor/tests/browserscope/lib/richtext/current_revision b/editor/libeditor/tests/browserscope/lib/richtext/current_revision new file mode 100644 index 0000000000..1e25699145 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/current_revision @@ -0,0 +1 @@ +775 diff --git a/editor/libeditor/tests/browserscope/lib/richtext/richtext/editable.html b/editor/libeditor/tests/browserscope/lib/richtext/richtext/editable.html new file mode 100644 index 0000000000..a294f0b56b --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/richtext/editable.html @@ -0,0 +1,11 @@ +<html>
+<head>
+ <script>
+ function load(){
+ window.document.designMode = "On";
+ }
+ </script>
+</head>
+<body contentEditable="true" onload="load()">
+</body>
+</html>
\ No newline at end of file diff --git a/editor/libeditor/tests/browserscope/lib/richtext/richtext/richtext.html b/editor/libeditor/tests/browserscope/lib/richtext/richtext/richtext.html new file mode 100644 index 0000000000..140f1a2045 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/richtext/richtext.html @@ -0,0 +1,1081 @@ +<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <title>Rich Text Tests</title>
+ <script src="js/range.js"></script>
+ <script>
+ /**
+ * Color class allows cross-browser comparison of values, which can
+ * be returned from queryCommandValue in several formats:
+ * 0xff00ff
+ * rgb(255, 0, 0)
+ * Number containing the hex value
+ */
+ function Color(value) {
+ this.compare = function(other) {
+ if (!this.valid || !other.valid) {
+ return false;
+ }
+ return this.red == other.red && this.green == other.green && this.blue == other.blue;
+ }
+ this.parse = function(value) {
+ var hexMatch = String(value).match(/#([0-9a-f]{6})/i);
+ if (hexMatch) {
+ this.red = parseInt(hexMatch[1].substring(0, 2), 16);
+ this.green = parseInt(hexMatch[1].substring(2, 4), 16);
+ this.blue = parseInt(hexMatch[1].substring(4, 6), 16);
+ return true;
+ }
+ var rgbMatch = String(value).match(/rgb\(([0-9]{1,3}),\s*([0-9]{1,3}),\s*([0-9]{1,3})\)/i);
+ if (rgbMatch) {
+ this.red = Number(rgbMatch[1]);
+ this.green = Number(rgbMatch[2]);
+ this.blue = Number(rgbMatch[3]);
+ return true;
+ }
+ if (Number(value)) {
+ this.red = value & 0xFF;
+ this.green = (value & 0xFF00) >> 8;
+ this.blue = (value & 0xFF0000) >> 16;
+ return true;
+ }
+ return false;
+ }
+ this.toString = function() {
+ return this.red + ',' + this.green + ',' + this.blue;
+ }
+ this.valid = this.parse(value);
+ }
+
+ /**
+ * Utility class for converting font sizes to the size
+ * attribute in a font tag. Currently only converts px because
+ * only the sizes and px ever come from queryCommandValue.
+ */
+ function Size(value) {
+ var pxMatch = String(value).match(/([0-9]+)px/);
+ if (pxMatch) {
+ var px = Number(pxMatch[1]);
+ if (px <= 10) {
+ this.size = 1;
+ } else if (px <= 13) {
+ this.size = 2;
+ } else if (px <= 16) {
+ this.size = 3;
+ } else if (px <= 18) {
+ this.size = 4;
+ } else if (px <= 24) {
+ this.size = 5;
+ } else if (px <= 32) {
+ this.size = 6;
+ } else if (px <= 47) {
+ this.size = 7;
+ } else {
+ this.size = NaN;
+ }
+ } else if (Number(value)) {
+ this.size = Number(value);
+ } else {
+ switch (value) {
+ case 'x-small':
+ this.size = 1;
+ break;
+ case 'small':
+ this.size = 2;
+ break;
+ case 'medium':
+ this.size = 3;
+ break;
+ case 'large':
+ this.size = 4;
+ break;
+ case 'x-large':
+ this.size = 5;
+ break;
+ case 'xx-large':
+ this.size = 6;
+ break;
+ case 'xxx-large':
+ case '-webkit-xxx-large':
+ this.size = 7;
+ break;
+ default:
+ this.size = null;
+ }
+ }
+ this.compare = function(other) {
+ return this.size == other.size;
+ }
+ this.toString = function() {
+ return String(this.size);
+ }
+ }
+
+ var IMAGE_URI = '/tests/editor/libeditor/tests/green.png';
+
+ var APPLY_TESTS = {
+ 'backcolor' : {
+ opt_arg: '#FF0000',
+ styleWithCSS: 'background-color'},
+ 'bold' : {
+ opt_arg: null,
+ styleWithCSS: 'font-weight'},
+ 'createbookmark' : {
+ opt_arg: 'bookmark_name'},
+ 'createlink' : {
+ opt_arg: 'http://www.openweb.org'},
+ 'decreasefontsize' : {
+ opt_arg: null},
+ 'fontname' : {
+ opt_arg: 'Arial',
+ styleWithCSS: 'font-family'},
+ 'fontsize' : {
+ opt_arg: 4,
+ styleWithCSS: 'font-size'},
+ 'forecolor' : {
+ opt_arg: '#FF0000',
+ styleWithCSS: 'color'},
+ 'formatblock' : {
+ opt_arg: 'h1',
+ wholeline: true},
+ 'hilitecolor' : {
+ opt_arg: '#FF0000',
+ styleWithCSS: 'background-color'},
+ 'indent' : {
+ opt_arg: null,
+ wholeline: true,
+ styleWithCSS: 'margin'},
+ 'inserthorizontalrule' : {
+ opt_arg: null,
+ collapse: true},
+ 'inserthtml': {
+ opt_arg: '<br>',
+ collapse: true},
+ 'insertimage': {
+ opt_arg: IMAGE_URI,
+ collapse: true},
+ 'insertorderedlist' : {
+ opt_arg: null,
+ wholeline: true},
+ 'insertunorderedlist' : {
+ opt_arg: null,
+ wholeline: true},
+ 'italic' : {
+ opt_arg: null,
+ styleWithCSS: 'font-style'},
+ 'justifycenter' : {
+ opt_arg: null,
+ wholeline: true,
+ styleWithCSS: 'text-align'},
+ 'justifyfull' : {
+ opt_arg: null,
+ wholeline: true,
+ styleWithCSS: 'text-align'},
+ 'justifyleft' : {
+ opt_arg: null,
+ wholeline: true,
+ styleWithCSS: 'text-align'},
+ 'justifyright' : {
+ opt_arg: null,
+ wholeline: true,
+ styleWithCSS: 'text-align'},
+ 'strikethrough' : {
+ opt_arg: null,
+ styleWithCSS: 'text-decoration'},
+ 'subscript' : {
+ opt_arg: null,
+ styleWithCSS: 'vertical-align'},
+ 'superscript' : {
+ opt_arg: null,
+ styleWithCSS: 'vertical-align'},
+ 'underline' : {
+ opt_arg: null,
+ styleWithCSS: 'text-decoration'}};
+
+ var UNAPPLY_TESTS = {
+ 'bold' : {
+ tags: [
+ ['<b>', '</b>'],
+ ['<STRONG>', '</STRONG>'],
+ ['<span style="font-weight: bold;">', '</span>']]},
+ 'italic' : {
+ tags: [
+ ['<i>', '</i>'],
+ ['<EM>', '</EM>'],
+ ['<span style="font-style: italic;">', '</span>']]},
+ 'outdent' : {
+ unapply: 'indent',
+ block: true,
+ tags: [
+ ['<blockquote>', '</blockquote>'],
+ ['<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px;">', '</blockquote>'],
+ ['<ul><li>', '</li></ul>'],
+ ['<ol><li>', '</li></ol>'],
+ ['<div style="margin-left: 40px;">', '</div>']]},
+ 'removeformat' : {
+ unapply: '*',
+ block: true,
+ tags: [
+ ['<b>', '</b>'],
+ ['<a href="http://www.foo.com">', '</a>'],
+ ['<table><tr><td>', '</td></tr></table>']]},
+ 'strikethrough' : {
+ tags: [
+ ['<strike>', '</strike>'],
+ ['<s>', '</s>'],
+ ['<del>', '</del>'],
+ ['<span style="text-decoration: line-through;">', '</span>']]},
+ 'subscript' : {
+ tags: [
+ ['<sub>', '</sub>'],
+ ['<span style="vertical-align: sub;">', '</span>']]},
+ 'superscript' : {
+ tags: [
+ ['<sup>', '</sup>'],
+ ['<span style="vertical-align: super;">', '</span>']]},
+ 'unbookmark' : {
+ unapply: 'createbookmark',
+ tags: [
+ ['<a name="bookmark">', '</a>']]},
+ 'underline' : {
+ tags: [
+ ['<u>', '</u>'],
+ ['<span style="text-decoration: underline;">', '</span>']]},
+ 'unlink' : {
+ unapply: 'createbookmark',
+ tags: [
+ ['<a href="http://www.foo.com">', '</a>']]}};
+
+ var QUERY_TESTS = {
+ 'backcolor' : {
+ type: 'value',
+ tests: [
+ {html: '<FONT style="BACKGROUND-COLOR: #ffccaa">foo bar baz</FONT>', expected: new Color('#ffccaa')},
+ {html: '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0);">foo bar baz</span>', expected: new Color('#ff0000')},
+ {html: '<span style="background-color: #ff0000">foo bar baz</span>', expected: new Color('#ff0000')}
+ ]
+ },
+ 'bold' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<b>foo bar baz</b>', expected: true},
+ {html: '<STRONG>foo bar baz</STRONG>', expected: true},
+ {html: '<span style="font-weight:bold">foo bar baz</span>', expected: true},
+ {html: '<b style="font-weight:normal">foo bar baz</b>', expected: false},
+ {html: '<b><span style="font-weight:normal;">foo bar baz</span>', expected: false}
+ ]
+ },
+ 'fontname' : {
+ type: 'value',
+ tests: [
+ {html: '<font face="Arial">foo bar baz</font>', expected: 'Arial'},
+ {html: '<span style="font-family:Arial">foo bar baz</span>', expected: 'Arial'},
+ {html: '<font face="Arial" style="font-family:Courier">foo bar baz</font>', expected: 'Courier'},
+ {html: '<font face="Courier"><font face="Arial">foo bar baz</font></font>', expected: 'Arial'},
+ {html: '<span style="font-family:Courier"><font face="Arial">foo bar baz</font></span>', expected: 'Arial'}
+ ]
+ },
+ 'fontsize' : {
+ type: 'value',
+ tests: [
+ {html: '<font size=4>foo bar baz</font>', expected: new Size(4)},
+ // IE adds +1 to font size from font-size style attributes.
+ // This is hard to correct for since it does NOT add +1 to size attribute from font tag.
+ {html: '<span class="Apple-style-span" style="font-size: large;">foo bar baz</span>', expected: new Size(4)},
+ {html: '<font size=1 style="font-size:x-large;">foo bar baz</font>', expected: new Size(5)}
+ ]
+ },
+ 'forecolor' : {
+ type: 'value',
+ tests: [
+ {html: '<font color="#ff0000">foo bar baz</font>', expected: new Color('#ff0000')},
+ {html: '<span style="color:#ff0000">foo bar baz</span>', expected: new Color('#ff0000')},
+ {html: '<font color="#0000ff" style="color:#ff0000">foo bar baz</span>', expected: new Color('#ff0000')}
+ ]
+ },
+ 'hilitecolor' : {
+ type: 'value',
+ tests: [
+ {html: '<FONT style="BACKGROUND-COLOR: #ffccaa">foo bar baz</FONT>', expected: new Color('#ffccaa')},
+ {html: '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0);">foo bar baz</span>', expected: new Color('#ff0000')},
+ {html: '<span style="background-color: #ff0000">foo bar baz</span>', expected: new Color('#ff0000')}
+ ]
+ },
+ 'insertorderedlist' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<ol><li>foo bar baz</li></ol>', expected: true},
+ {html: '<ul><li>foo bar baz</li></ul>', expected: false}
+ ]
+ },
+ 'insertunorderedlist' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<ol><li>foo bar baz</li></ol>', expected: false},
+ {html: '<ul><li>foo bar baz</li></ul>', expected: true}
+ ]
+ },
+ 'italic' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<i>foo bar baz</i>', expected: true},
+ {html: '<EM>foo bar baz</EM>', expected: true},
+ {html: '<span style="font-style:italic">foo bar baz</span>', expected: true},
+ {html: '<i><span style="font-style:normal">foo bar baz</span></i>', expected: false}
+ ]
+ },
+ 'justifycenter' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<div align="center">foo bar baz</div>', expected: true},
+ {html: '<p align="center">foo bar baz</p>', expected: true},
+ {html: '<div style="text-align: center;">foo bar baz</div>', expected: true}
+ ]
+ },
+ 'justifyfull' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<div align="justify">foo bar baz</div>', expected: true},
+ {html: '<p align="justify">foo bar baz</p>', expected: true},
+ {html: '<div style="text-align: justify;">foo bar baz</div>', expected: true}
+ ]
+ },
+ 'justifyleft' : {
+ type: 'state',
+ tests: [
+ {html: '<div align="left">foo bar baz</div>', expected: true},
+ {html: '<p align="left">foo bar baz</p>', expected: true},
+ {html: '<div style="text-align: left;">foo bar baz</div>', expected: true}
+ ]
+ },
+ 'justifyright' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<div align="right">foo bar baz</div>', expected: true},
+ {html: '<p align="right">foo bar baz</p>', expected: true},
+ {html: '<div style="text-align: right;">foo bar baz</div>', expected: true}
+ ]
+ },
+ 'strikethrough' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<strike>foo bar baz</strike>', expected: true},
+ {html: '<strike style="text-decoration: none">foo bar baz</strike>', expected: false},
+ {html: '<s>foo bar baz</s>', expected: true},
+ {html: '<del>foo bar baz</del>', expected: true},
+ {html: '<span style="text-decoration:line-through">foo bar baz</span>', expected: true}
+ ]
+ },
+ 'subscript' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<sub>foo bar baz</sub>', expected: true}
+ ]
+ },
+ 'superscript' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<sup>foo bar baz</sup>', expected: true}
+ ]
+ },
+ 'underline' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<u>foo bar baz</u>', expected: true},
+ {html: '<a href="http://www.foo.com">foo bar baz</a>', expected: true},
+ {html: '<span style="text-decoration:underline">foo bar baz</span>', expected: true},
+ {html: '<u style="text-decoration:none">foo bar baz</u>', expected: false},
+ {html: '<a style="text-decoration:none" href="http://www.foo.com">foo bar baz</a>', expected: false}
+ ]
+ }
+ };
+
+ var CHANGE_TESTS = {
+ 'backcolor' : {
+ type: 'value',
+ tests: [
+ {html: '<FONT style="BACKGROUND-COLOR: #ffccaa">foo bar baz</FONT>', opt_arg: '#884422'},
+ {html: '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0);">foo bar baz</span>', opt_arg: '#0000ff'},
+ {html: '<span style="background-color: #ff0000">foo bar baz</span>', opt_arg: '#0000ff'}
+ ]
+ },
+ 'fontname' : {
+ type: 'value',
+ tests: [
+ {html: '<font face="Arial">foo bar baz</font>', opt_arg: 'Courier'},
+ {html: '<span style="font-family:Arial">foo bar baz</span>', opt_arg: 'Courier'},
+ {html: '<font face="Arial" style="font-family:Verdana">foo bar baz</font>', opt_arg: 'Courier'},
+ {html: '<font face="Verdana"><font face="Arial">foo bar baz</font></font>', opt_arg: 'Courier'},
+ {html: '<span style="font-family:Verdana"><font face="Arial">foo bar baz</font></span>', opt_arg: 'Courier'}
+ ]
+ },
+ 'fontsize' : {
+ type: 'value',
+ tests: [
+ {html: '<font size=4>foo bar baz</font>', opt_arg: 1},
+ {html: '<span class="Apple-style-span" style="font-size: large;">foo bar baz</span>', opt_arg: 1},
+ {html: '<font size=1 style="font-size:x-small;">foo bar baz</font>', opt_arg: 5}
+ ]
+ },
+ 'forecolor' : {
+ type: 'value',
+ tests: [
+ {html: '<font color="#ff0000">foo bar baz</font>', opt_arg: '#00ff00'},
+ {html: '<span style="color:#ff0000">foo bar baz</span>', opt_arg: '#00ff00'},
+ {html: '<font color="#0000ff" style="color:#ff0000">foo bar baz</span>', opt_arg: '#00ff00'}
+ ]
+ },
+ 'hilitecolor' : {
+ type: 'value',
+ tests: [
+ {html: '<FONT style="BACKGROUND-COLOR: #ffccaa">foo bar baz</FONT>', opt_arg: '#884422'},
+ {html: '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0);">foo bar baz</span>', opt_arg: '#00ff00'},
+ {html: '<span style="background-color: #ff0000">foo bar baz</span>', opt_arg: '#00ff00'}
+ ]
+ }
+ };
+
+ /** The document of the editable iframe */
+ var editorDoc = null;
+ /** Dummy text to apply and unapply formatting to */
+ var TEST_CONTENT = 'foo bar baz';
+ /**
+ * Word in dummy text that should change. Formatting is sometimes applied
+ * to a single word instead of the entire text node because sometimes a
+ * style might get applied to the body node instead of wrapped around
+ * the text, and that's not what's being tested.
+ */
+ var TEST_WORD = 'bar';
+ /** Constant for indicating an action is unsupported (threw exception) */
+ var UNSUPPORTED = 'UNSUPPORTED';
+ /** <br> and <p> are acceptable HTML to be left over from block elements */
+ var BLOCK_REMOVE_TAGS = [/\s*<br>\s*/i, /\s*<p>\s*/i];
+ /** Array used to accumulate test results */
+ // Tack on the actual display tests with bogus data
+ // otherwise the beacon will fail.
+ var results = ['apply=0', 'unapply=0', 'change=0', 'query=0'];
+
+ /**
+ *
+ */
+ function resetIframe(newHtml) {
+ // These attributes can get set on the iframe by some errant execCommands
+ editorDoc.body.setAttribute('style', '');
+ editorDoc.body.setAttribute('bgcolor', '');
+ editorDoc.body.innerHTML = newHtml;
+ }
+
+ /**
+ * Finds the text node in the given node containing the given word.
+ * Returns null if not found.
+ */
+ function findTextNode(word, node) {
+ if (node.nodeType == 3) {
+ // Text node, check value.
+ if (node.data.includes(word)) {
+ return node;
+ }
+ } else if (node.nodeType == 1) {
+ // Element node, check children.
+ for (var i = 0; i < node.childNodes.length; i++) {
+ var result = findTextNode(word, node.childNodes[i]);
+ if (result) {
+ return result;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the selection to be collapsed at the start of the word,
+ * or the start of the editor if no word is passed in.
+ */
+ function selectStart(word) {
+ var textNode = findTextNode(word || '', editorDoc.body);
+ var startOffset = 0;
+ if (word) {
+ startOffset = textNode.data.indexOf(word);
+ }
+ var range = createCaret(textNode, startOffset);
+ range.select();
+ }
+
+ /**
+ * Selects the given word in the editor iframe.
+ */
+ function selectWord(word) {
+ var textNode = findTextNode(word, editorDoc.body);
+ if (!textNode) {
+ return;
+ }
+ var start = textNode.data.indexOf(word);
+ var range = createFromNodes(textNode, start, textNode, start + word.length);
+ range.select();
+ }
+
+ /**
+ * Gets the HTML before the text, so that we know how the browser
+ * applied a style
+ */
+ function getSurroundingTags(text) {
+ var html = editorDoc.body.innerHTML;
+ var tagStart = html.indexOf('<');
+ var index = editorDoc.body.innerHTML.indexOf(text);
+ if (tagStart == -1 || index == -1) {
+ return '';
+ }
+ return editorDoc.body.innerHTML.substring(tagStart, index);
+ }
+
+ /**
+ * Does the test for an apply execCommand.
+ */
+ function doApplyTest(command, styleWithCSS) {
+ try {
+ // Set styleWithCSS
+ try {
+ editorDoc.execCommand('styleWithCSS', false, styleWithCSS);
+ } catch (ex) {
+ // Ignore errors
+ }
+ resetIframe(TEST_CONTENT);
+ if (APPLY_TESTS[command].collapse) {
+ selectStart(TEST_WORD);
+ } else {
+ selectWord(TEST_WORD);
+ }
+ try {
+ editorDoc.execCommand(command, false, APPLY_TESTS[command].opt_arg);
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ return getSurroundingTags(APPLY_TESTS[command].wholeline? TEST_CONTENT : TEST_WORD);
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ }
+
+ /**
+ * Outputs the result of the apply command to a table.
+ * @return {boolean} success
+ */
+ function outputApplyResult(command, result, styleWithCSS) {
+ // The apply command "succeeded" if HTML was generated.
+ var success = (result != UNSUPPORTED) && result;
+ // Except for styleWithCSS commands, which only succeed if the
+ // expected style was applied.
+ if (styleWithCSS) {
+ success = result && result.toLowerCase().includes(APPLY_TESTS[command].styleWithCSS);
+ }
+ results.push('a-' + command + '-' + (styleWithCSS ? 1 : 0) + '=' + (success ? '1' : '0'));
+
+ // Each command is displayed as a table row with 3 columns
+ var tr = document.createElement('TR');
+ tr.className = success ? 'success' : 'fail';
+
+ // Column 1: command name
+ var td = document.createElement('TD');
+ td.innerHTML = command;
+ tr.appendChild(td);
+
+ // Column 2: styleWithCSS
+ var td = document.createElement('TD');
+ td.innerHTML = styleWithCSS ? 'true' : 'false';
+ tr.appendChild(td);
+
+ // Column 3: pass/fail
+ td = document.createElement('TD');
+ td.innerHTML = success ? 'PASS' : 'FAIL';
+ tr.appendChild(td);
+
+ // Column 4: generated HTML (for passing commands)
+ td = document.createElement('TD');
+ // Escape the HTML in the result for printing.
+ result = result.replace(/\</g, '<').replace(/\>/g, '>');
+ td.innerHTML = result;
+ tr.appendChild(td);
+ var table = document.getElementById('apply_output');
+ table.appendChild(tr);
+ return success;
+ }
+
+ /**
+ * Does the test for an unapply execCommand.
+ */
+ function doUnapplyTest(command, index) {
+ try {
+ var wordStart = TEST_CONTENT.indexOf(TEST_WORD);
+ resetIframe(
+ TEST_CONTENT.substring(0, wordStart) +
+ UNAPPLY_TESTS[command].tags[index][0] +
+ TEST_WORD +
+ UNAPPLY_TESTS[command].tags[index][1] +
+ TEST_CONTENT.substring(wordStart + TEST_WORD.length));
+ selectWord(TEST_WORD);
+ try {
+ editorDoc.execCommand(command, false, UNAPPLY_TESTS[command].opt_arg || null);
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ return getSurroundingTags(TEST_WORD);
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ }
+
+ /**
+ * Check if the given unapply execCommand succeeded. It succeeded if
+ * the following conditions are true:
+ * - The execCommand did not throw an exception
+ * - One of the following:
+ * - The html was removed after the execCommand
+ * - The html was block and the html was replaced with <p> or <br>
+ */
+ function unapplyCommandSucceeded(command, result) {
+ if (result != UNSUPPORTED) {
+ if (!result) {
+ return true;
+ } else if (UNAPPLY_TESTS[command].block) {
+ for (var i = 0; i < BLOCK_REMOVE_TAGS.length; i++) {
+ if (result.match(BLOCK_REMOVE_TAGS[i])) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Outputs the result of the unapply command to a table.
+ * @return {boolean} success
+ */
+ function outputUnapplyResult(command, result, index) {
+ // The apply command "succeeded" if HTML was removed.
+ var success = unapplyCommandSucceeded(command, result);
+ results.push('u-' + command + '-' + index + '=' + (success ? '1' : '0'));
+
+ // Each command is displayed as a table row with 5 columns
+ var tr = document.createElement('TR');
+ tr.className = success ? 'success' : 'fail';
+
+ // Column 1: command name
+ var td = document.createElement('TD');
+ td.innerHTML = command;
+ tr.appendChild(td);
+
+ // Column 2: command name being unapplied
+ var td = document.createElement('TD');
+ td.innerHTML = UNAPPLY_TESTS[command].unapply || command;
+ tr.appendChild(td);
+
+ // Column 3: pass/fail
+ td = document.createElement('TD');
+ td.innerHTML = success ? 'PASS' : 'FAIL';
+ tr.appendChild(td);
+
+ // Column 4: html being removed
+ td = document.createElement('TD');
+ // Escape the html for printing.
+ var htmlToRemove = UNAPPLY_TESTS[command].tags[index][0].replace(/\</g, '<').replace(/\>/g, '>');
+ td.innerHTML = htmlToRemove;
+ tr.appendChild(td);
+
+ // Column 5: resulting html
+ td = document.createElement('TD');
+ // Escape the HTML in the result for printing.
+ result = result.replace(/\</g, '<').replace(/\>/g, '>');
+ td.innerHTML = success ? ' ' : result;
+ tr.appendChild(td);
+ var table = document.getElementById('unapply_output');
+ table.appendChild(tr);
+ return success;
+ }
+
+ /**
+ * Does a queryCommandState or queryCommandValue test for an execCommand.
+ */
+ function doQueryTest(command, index) {
+ try {
+ resetIframe(QUERY_TESTS[command].tests[index].html);
+ selectWord(TEST_WORD);
+ // Dummy val that won't match any expected vals, including false.
+ var result = UNSUPPORTED;
+ if (QUERY_TESTS[command].type == 'state') {
+ try {
+ result = editorDoc.queryCommandState(command);
+ } catch (ex) {
+ result = UNSUPPORTED;
+ }
+ } else {
+ try {
+ // A return value of false indicates the command is not supported.
+ result = editorDoc.queryCommandValue(command) || UNSUPPORTED;
+ } catch (ex) {
+ result = UNSUPPORTED;
+ }
+ }
+ return result;
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ }
+
+ /**
+ * Check if the given queryCommandState or queryCommandValue succeeded.
+ */
+ function queryCommandSucceeded(command, index, result) {
+ var expected = QUERY_TESTS[command].tests[index].expected;
+ if (expected instanceof Color) {
+ return expected.compare(new Color(result));
+ } else if (expected instanceof Size) {
+ return expected.compare(new Size(result));
+ } else {
+ return (result == expected);
+ }
+ }
+
+ /**
+ * @return {boolean} success
+ */
+ function outputQueryResult(command, index, result) {
+ // Create table row for results.
+ var tr = document.createElement('TR');
+ var success = queryCommandSucceeded(command, index, result);
+ tr.className = success ? 'success' : 'fail';
+ results.push('q-' + command + '-' + index + '=' + (success ? '1' : '0'));
+
+ // Column 1: command name
+ var td = document.createElement('TD');
+ td.innerHTML = command;
+ tr.appendChild(td);
+
+ // Column 2: pass/fail
+ td = document.createElement('TD');
+ td.innerHTML = success ? 'PASS' : 'FAIL';
+ tr.appendChild(td);
+
+ // Column 3: test HTML
+ td = document.createElement('TD');
+ var testHtml = QUERY_TESTS[command].tests[index].html.replace(/</g, '<').replace(/>/g, '>');
+ td.innerHTML = testHtml.substring(0, testHtml.indexOf(TEST_CONTENT));
+ tr.appendChild(td);
+
+ // Column 4: Expected result
+ td = document.createElement('TD');
+ td.innerHTML = QUERY_TESTS[command].tests[index].expected;
+ tr.appendChild(td);
+
+ // Column 5: Actual result
+ td = document.createElement('TD');
+ td.innerHTML = result;
+ tr.appendChild(td);
+
+ // Append result to the state or value table, depending on what
+ // type of command this is.
+ var table = document.getElementById(
+ QUERY_TESTS[command].type == 'state' ? 'querystate_output' : 'queryvalue_output');
+ table.appendChild(tr);
+ return success;
+ }
+
+ function doChangeTest(command, index) {
+ try {
+ resetIframe(CHANGE_TESTS[command].tests[index].html);
+ selectWord(TEST_CONTENT);
+ try {
+ editorDoc.execCommand(command, false, CHANGE_TESTS[command].tests[index].opt_arg);
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ }
+
+ function checkChangeSuccess(command, index) {
+ var textNode = findTextNode(TEST_CONTENT, editorDoc.body);
+ if (!textNode) {
+ // The text has been removed from the document, or split up for no reason.
+ return false;
+ }
+ var expected = null, attributeName = null, styleName = null;
+ switch (command) {
+ case 'backcolor':
+ case 'hilitecolor':
+ expected = new Color(CHANGE_TESTS[command].tests[index].opt_arg);
+ styleName = 'backgroundColor';
+ break;
+ case 'fontname':
+ expected = CHANGE_TESTS[command].tests[index].opt_arg;
+ attributeName = 'face';
+ styleName = 'fontFamily';
+ break;
+ case 'fontsize':
+ expected = new Size(CHANGE_TESTS[command].tests[index].opt_arg);
+ attributeName = 'size';
+ styleName = 'fontSize';
+ break;
+ case 'forecolor':
+ expected = new Color(CHANGE_TESTS[command].tests[index].opt_arg);
+ attributeName = 'color';
+ styleName = 'color';
+ }
+ var foundExpected = false;
+
+ // Loop through all the parent nodes that format the text node,
+ // checking that there is exactly one font attribute or
+ // style, and that it's set correctly.
+ var currentNode = textNode.parentNode;
+ while(currentNode && currentNode.nodeName != 'BODY') {
+ // Check font attribute.
+ if (attributeName && currentNode.nodeName == 'FONT' && currentNode.getAttribute(attributeName)) {
+ var foundAttribute = false;
+ switch(command) {
+ case 'backcolor':
+ case 'forecolor':
+ case 'hilitecolor':
+ foundAttribute = new Color(currentNode.getAttribute(attributeName)).compare(expected);
+ break;
+ case 'fontsize':
+ foundAttribute = new Size(currentNode.getAttribute(attributeName)).compare(expected);
+ break;
+ case 'fontname':
+ foundAttribute = (currentNode.getAttribute(attributeName).toLowerCase() == expected.toLowerCase());
+ }
+ if (foundAttribute && foundExpected) {
+ // This is the correct attribute, but the style has been applied
+ // twice. This makes it hard for other browsers to remove the
+ // style.
+ return false;
+ } else if (!foundAttribute) {
+ // This node has an incorrect font attribute.
+ return false;
+ }
+ // The expected font attribute was found.
+ foundExpected = true;
+ }
+ // Check node style.
+ if (currentNode.style[styleName]) {
+ var foundStyle = false;
+ switch(command) {
+ case 'backcolor':
+ case 'forecolor':
+ case 'hilitecolor':
+ foundStyle = new Color(currentNode.style[styleName]).compare(expected);
+ break;
+ case 'fontsize':
+ foundStyle = new Size(currentNode.style[styleName]).compare(expected);
+ break;
+ case 'fontname':
+ foundStyle = (currentNode.style[styleName].toLowerCase() == expected.toLowerCase());
+ }
+ if (foundStyle && foundExpected) {
+ // This is the correct style, but the style has been
+ // applied twice. This makes it hard for other browsers to
+ // remove the style.
+ return false;
+ } else if (!foundStyle) {
+ // This node has an incorrect font style.
+ return false;
+ }
+ foundExpected = true;
+ }
+ currentNode = currentNode.parentNode;
+ }
+ return foundExpected;
+ }
+
+ /**
+ * @return {boolean} success
+ */
+ function outputChangeResult(command, index) {
+ // Each command is displayed as a table row with 4 columns
+ var tr = document.createElement('TR');
+ var success = checkChangeSuccess(command, index);
+ tr.className = success ? 'success' : 'fail';
+ results.push('c-' + command + '-' + index + '=' + (success ? '1' : '0'));
+
+ // Column 1: command name
+ var td = document.createElement('TD');
+ td.innerHTML = command;
+ tr.appendChild(td);
+
+ // Column 2: status
+ td = document.createElement('TD');
+ td.innerHTML = (success == null) ? '?' : (success == true ? 'PASS' : 'FAIL');
+ tr.appendChild(td);
+
+ // Column 3: opt_arg
+ td = document.createElement('TD');
+ td.innerHTML = CHANGE_TESTS[command].tests[index].opt_arg;
+ tr.appendChild(td);
+
+ // Column 4: original html
+ td = document.createElement('TD');
+ td.innerHTML = CHANGE_TESTS[command].tests[index].html.replace(/\</g, '<').replace(/\>/g, '>');;
+ tr.appendChild(td);
+
+ // Column 5: resulting html
+ td = document.createElement('TD');
+ td.innerHTML = editorDoc.body.innerHTML.replace(/\</g, '<').replace(/\>/g, '>');;
+ tr.appendChild(td);
+
+ var table = document.getElementById('change_output');
+ table.appendChild(tr);
+ return success;
+ }
+
+ function runTests() {
+ // Wrap initialization code in a try/catch so we can fail gracefully
+ // on older browsers.
+ try {
+ editorDoc = document.getElementById('editor').contentWindow.document;
+ // Default styleWithCSS to false, since it's not supported by IE.
+ try {
+ editorDoc.execCommand('styleWithCSS', false, false);
+ } catch (ex) {
+ // Not supported by IE.
+ }
+ } catch (ex) {}
+
+ // Apply tests
+ var apply_score = 0;
+ var apply_count = 0;
+ var unapply_score= 0;
+ var unapply_count = 0;
+ var change_score = 0;
+ var change_count = 0;
+ var query_score = 0;
+ var query_count = 0;
+ for (var command in APPLY_TESTS) {
+ try {
+ var result = doApplyTest(command, false);
+ var success = outputApplyResult(command, result, false);
+ apply_score += success ? 1 : 0;
+ } catch (ex) {
+ // An exception is counted as a failed test, don't increment success.
+ }
+ apply_count++;
+ if (APPLY_TESTS[command].styleWithCSS) {
+ try {
+ var result = doApplyTest(command, true);
+ var success = outputApplyResult(command, result, true);
+ apply_score += success ? 1 : 0;
+ } catch (ex) {
+ // An exception is counted as a failed test, don't increment success.
+ }
+ apply_count++;
+ }
+ }
+
+ // Unapply tests
+ for (var command in UNAPPLY_TESTS) {
+ for (var i = 0; i < UNAPPLY_TESTS[command].tags.length; i++) {
+ try {
+ var result = doUnapplyTest(command, i);
+ var success = outputUnapplyResult(command, result, i);
+ unapply_score += success ? 1 : 0;
+ } catch (ex) {
+ // An exception is counted as a failed test, don't increment success.
+ }
+ unapply_count++;
+ }
+ }
+
+ // Query tests
+ for (var command in QUERY_TESTS) {
+ for (var i = 0; i < QUERY_TESTS[command].tests.length; i++) {
+ try {
+ var result = doQueryTest(command, i);
+ var success = outputQueryResult(command, i, result);
+ query_score += success ? 1 : 0;
+ } catch (ex) {
+ // An exception is counted as a failed test, don't increment success.
+ }
+ query_count++;
+ }
+ }
+
+ // Change tests
+ for (var command in CHANGE_TESTS) {
+ for (var i = 0; i < CHANGE_TESTS[command].tests.length; i++) {
+ try {
+ doChangeTest(command, i);
+ var success = outputChangeResult(command, i);
+ change_score += success ? 1 : 0;
+ } catch (ex) {
+ // An exception is counted as a failed test, don't increment success.
+ }
+ change_count++;
+ }
+ }
+
+ // Beacon all test results.
+ // and construct a shorter version for the results page.
+ try {
+ document.getElementById('apply-score').innerHTML =
+ apply_score + '/' + apply_count;
+ document.getElementById('unapply-score').innerHTML =
+ unapply_score + '/' + unapply_count;
+ document.getElementById('query-score').innerHTML =
+ query_score + '/' + query_count;
+ document.getElementById('change-score').innerHTML =
+ change_score + '/' + change_count;
+ } catch (ex) {}
+ var continueParams = [
+ 'apply=' + apply_score,
+ 'unapply=' + unapply_score,
+ 'query=' + query_score,
+ 'change=' + change_score
+ ];
+ parent.sendScore(results, continueParams);
+ }
+ </script>
+ <style>
+ .success {
+ background-color: #93c47d;
+ }
+ .fail {
+ background-color: #ea9999;
+ }
+ .score {
+ color: #666;
+ }
+ </style>
+</head>
+<body onload="runTests()">
+ <h1>Apply Formatting <span id="apply-score" class="score"></span></h1>
+ <table id="apply"><tbody id="apply_output"><tr><th>Command</th><th>styleWithCSS</th><th>Status</th><th>Output</th></tr></tbody></table>
+ <h1>Unapply Formatting <span id="unapply-score" class="score"></span></h1>
+ <table id="unapply">
+ <thead><tr><th>Command</th><th>Command unapplied</th><th>Status</th><th>HTML Attempted to Unapply</th><th>Resulting HTML</th></tr></thead>
+ <tbody id="unapply_output"></tbody></table>
+ <h1>Query Formatting State <span id="query-score" class="score"></span></h1>
+ <table id="querystate">
+ <thead><tr><th>Command</th><th>Status</th><th>HTML</th><th>Expected</th><th>Actual</th></tr></thead>
+ <tbody id="querystate_output"></tbody></table>
+ <h1>Query Formatting Value </h1>
+ <table id="queryvalue">
+ <thead><tr><th>Command</th><th>Status</th><th>HTML</th><th>Expected</th><th>Actual</th></tr></thead>
+ <tbody id="queryvalue_output"></tbody></table>
+ <h1>Change Formatting <span id="change-score" class="score"></span></h1>
+ <table id="change">
+ <thead><tr><th>Command</th><th>Status</th><th>Argument</th><th>Original HTML</th><th>Resulting HTML</th></tr></thead>
+ <tbody id="change_output"></tbody></table>
+ <iframe name="editor" id="editor" src="editable.html"></iframe>
+</body>
+</html>
diff --git a/editor/libeditor/tests/browserscope/lib/richtext/update_from_upstream b/editor/libeditor/tests/browserscope/lib/richtext/update_from_upstream new file mode 100644 index 0000000000..2071454a85 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/update_from_upstream @@ -0,0 +1,16 @@ +#!/bin/sh + +set -x + +if test -d richtext; then + rm -drf richtext; +fi + +svn checkout http://browserscope.googlecode.com/svn/trunk/categories/richtext/static richtext | tail -1 | sed 's/[^0-9]//g' > current_revision + +find richtext -type d -name .svn -exec rm -drf \{\} \; 2> /dev/null + +hg add current_revision richtext + +hg stat . + |