import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,6 @@
[browser_bug527935.js]
skip-if = toolkit == 'android'
support-files = bug527935.html
[browser_bug629172.js]
skip-if = toolkit == 'android'
support-files = bug629172.html

View file

@ -0,0 +1,63 @@
add_task(function*() {
yield new Promise(resolve => waitForFocus(resolve, window));
const kPageURL = "http://example.org/browser/editor/libeditor/tests/bug527935.html";
yield BrowserTestUtils.withNewTab({
gBrowser,
url: kPageURL
}, function*(aBrowser) {
var popupShown = false;
function listener() {
popupShown = true;
}
SpecialPowers.addAutoCompletePopupEventListener(window, "popupshowing", listener);
yield ContentTask.spawn(aBrowser, {}, function*() {
var window = content.window.wrappedJSObject;
var document = window.document;
var formTarget = document.getElementById("formTarget");
var initValue = document.getElementById("initValue");
window.loadPromise = new Promise(resolve => {
formTarget.onload = resolve;
});
initValue.focus();
initValue.value = "foo";
});
EventUtils.synthesizeKey("VK_RETURN", {});
yield ContentTask.spawn(aBrowser, {}, function*() {
var window = content.window.wrappedJSObject;
var document = window.document;
yield window.loadPromise;
var newInput = document.createElement("input");
newInput.setAttribute("name", "test");
document.body.appendChild(newInput);
var event = document.createEvent("KeyboardEvent");
event.initKeyEvent("keypress", true, true, null, false, false,
false, false, 0, "f".charCodeAt(0));
newInput.value = "";
newInput.focus();
newInput.dispatchEvent(event);
});
yield new Promise(resolve => hitEventLoop(resolve, 100));
ok(!popupShown, "Popup must not be opened");
SpecialPowers.removeAutoCompletePopupEventListener(window, "popupshowing", listener);
});
});
function hitEventLoop(func, times) {
if (times > 0) {
setTimeout(hitEventLoop, 0, func, times - 1);
} else {
setTimeout(func, 0);
}
}

View file

@ -0,0 +1,106 @@
add_task(function*() {
yield new Promise(resolve => waitForFocus(resolve, window));
const kPageURL = "http://example.org/browser/editor/libeditor/tests/bug629172.html";
yield BrowserTestUtils.withNewTab({
gBrowser,
url: kPageURL
}, function*(aBrowser) {
yield ContentTask.spawn(aBrowser, {}, function*() {
var window = content.window.wrappedJSObject;
var document = window.document;
// Note: Using the with keyword, we would have been able to write this as:
//
// with (window) {
// Screenshots = {};
// // so on
// }
//
// However, browser-chrome tests are somehow forced to run in strict mode,
// which doesn't permit the usage of the with keyword, turning the following
// into the ugliest test you've ever seen. :(
var LTRRef = document.getElementById("ltr-ref");
var RTLRef = document.getElementById("rtl-ref");
window.Screenshots = {};
// generate the reference screenshots
LTRRef.style.display = "";
document.body.clientWidth;
window.Screenshots.ltr = window.snapshotWindow(window);
LTRRef.parentNode.removeChild(LTRRef);
RTLRef.style.display = "";
document.body.clientWidth;
window.Screenshots.rtl = window.snapshotWindow(window);
RTLRef.parentNode.removeChild(RTLRef);
window.Screenshots.get = function(dir, flip) {
if (flip) {
return this[dir == "rtl" ? "ltr" : "rtl"];
} else {
return this[dir];
}
};
});
function simulateCtrlShiftX(aBrowser) {
// In e10s, the keypress event will be dispatched to the content process,
// but in non-e10s it is handled by the browser UI code and hence won't
// reach the web page. As a result, we need to observe the event in
// the content process only in e10s mode.
var waitForKeypressContent = BrowserTestUtils.waitForContentEvent(aBrowser, "keypress");
EventUtils.synthesizeKey("x", {accelKey: true, shiftKey: true});
if (gMultiProcessBrowser) {
return waitForKeypressContent;
}
return Promise.resolve();
}
function* testDirection(initialDir, aBrowser) {
yield ContentTask.spawn(aBrowser, {initialDir}, function({initialDir}) {
var window = content.window.wrappedJSObject;
var document = window.document;
var t = window.t = document.createElement("textarea");
t.setAttribute("dir", initialDir);
t.value = "test.";
window.inputEventCount = 0;
t.oninput = function() { window.inputEventCount++; };
document.getElementById("content").appendChild(t);
document.body.clientWidth;
var s1 = window.snapshotWindow(window);
ok(window.compareSnapshots(s1, window.Screenshots.get(initialDir, false), true)[0],
"Textarea should appear correctly before switching the direction (" + initialDir + ")");
t.focus();
is(window.inputEventCount, 0, "input event count must be 0 before");
});
yield simulateCtrlShiftX(aBrowser);
yield ContentTask.spawn(aBrowser, {initialDir}, function({initialDir}) {
var window = content.window.wrappedJSObject;
is(window.t.getAttribute("dir"), initialDir == "ltr" ? "rtl" : "ltr", "The dir attribute must be correctly updated");
is(window.inputEventCount, 1, "input event count must be 1 after");
window.t.blur();
var s2 = window.snapshotWindow(window);
ok(window.compareSnapshots(s2, window.Screenshots.get(initialDir, true), true)[0],
"Textarea should appear correctly after switching the direction (" + initialDir + ")");
window.t.focus();
is(window.inputEventCount, 1, "input event count must be 1 before");
});
yield simulateCtrlShiftX(aBrowser);
yield ContentTask.spawn(aBrowser, {initialDir}, function({initialDir}) {
var window = content.window.wrappedJSObject;
is(window.inputEventCount, 2, "input event count must be 2 after");
is(window.t.getAttribute("dir"), initialDir == "ltr" ? "ltr" : "rtl", "The dir attribute must be correctly updated");
window.t.blur();
var s3 = window.snapshotWindow(window);
ok(window.compareSnapshots(s3, window.Screenshots.get(initialDir, false), true)[0],
"Textarea should appear correctly after switching back the direction (" + initialDir + ")");
window.t.parentNode.removeChild(window.t);
});
}
yield testDirection("ltr", aBrowser);
yield testDirection("rtl", aBrowser);
});
});

View file

@ -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.

View file

@ -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/

View file

@ -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.

View file

@ -0,0 +1,46 @@
/**
* 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,
'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,
},
'c': {
'fontsize-1' : true,
'fontsize-2' : true,
},
};
function isKnownFailure(type, test, param) {
return (type in knownFailures) && ((test + "-" + param) in knownFailures[type]);
}

View file

@ -0,0 +1 @@
775

View file

@ -0,0 +1,11 @@
<html>
<head>
<script>
function load(){
window.document.designMode = "On";
}
</script>
</head>
<body contentEditable="true" onload="load()">
</body>
</html>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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 .

View file

@ -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.

View file

@ -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/

View file

@ -0,0 +1,23 @@
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.
The current status of the test suite needs to be updated whenever an editor
bug gets fixed, which makes us pass one of the tests. When that happens,
you should set the UPDATE_TEST_RESULTS constant to true in test_richtext2.html,
run the test suite, paste the result JSON string in a JSON beautifier (such
as http://jsbeautifier.org/), and use the result to update currentStatus.js.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
805

View file

@ -0,0 +1,25 @@
#!/usr/bin/python2.5
#
# 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.
"""Common constants"""
__author__ = 'rolandsteiner@google.com (Roland Steiner)'
CATEGORY = 'richtext2'
TEST_ID_PREFIX = 'RTE2'
CLASSES = ['Finalized', 'RFC', 'Proposed']

View file

@ -0,0 +1,107 @@
#!/usr/bin/python2.5
#
# 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.
"""Handlers for New Rich Text Tests"""
__author__ = 'rolandsteiner@google.com (Roland Steiner)'
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.api import memcache
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
import django
from django import http
from django import shortcuts
from django.template import add_to_builtins
add_to_builtins('base.custom_filters')
# Shared stuff
from categories import all_test_sets
from base import decorators
from base import util
# common to the RichText2 suite
from categories.richtext2 import common
# tests
from categories.richtext2.tests.apply import APPLY_TESTS
from categories.richtext2.tests.applyCSS import APPLY_TESTS_CSS
from categories.richtext2.tests.change import CHANGE_TESTS
from categories.richtext2.tests.changeCSS import CHANGE_TESTS_CSS
from categories.richtext2.tests.delete import DELETE_TESTS
from categories.richtext2.tests.forwarddelete import FORWARDDELETE_TESTS
from categories.richtext2.tests.insert import INSERT_TESTS
from categories.richtext2.tests.selection import SELECTION_TESTS
from categories.richtext2.tests.unapply import UNAPPLY_TESTS
from categories.richtext2.tests.unapplyCSS import UNAPPLY_TESTS_CSS
from categories.richtext2.tests.querySupported import QUERYSUPPORTED_TESTS
from categories.richtext2.tests.queryEnabled import QUERYENABLED_TESTS
from categories.richtext2.tests.queryIndeterm import QUERYINDETERM_TESTS
from categories.richtext2.tests.queryState import QUERYSTATE_TESTS, QUERYSTATE_TESTS_CSS
from categories.richtext2.tests.queryValue import QUERYVALUE_TESTS, QUERYVALUE_TESTS_CSS
def About(request):
"""About page."""
overview = """These tests cover browers' implementations of
<a href="http://blog.whatwg.org/the-road-to-html-5-contenteditable">contenteditable</a>
for basic rich text formatting commands. Most browser implementations do very
well at editing the HTML which is generated by their own execCommands. But a
big problem happens when developers try to make cross-browser web
applications using contenteditable - most browsers are not able to correctly
change formatting generated by other browsers. On top of that, most browsers
allow users to to paste arbitrary HTML from other webpages into a
contenteditable region, which is even harder for browsers to properly
format. These tests check how well the execCommand, queryCommandState,
and queryCommandValue functions work with different types of HTML."""
return util.About(request, common.CATEGORY, category_title='Rich Text',
overview=overview, show_hidden=False)
def RunRichText2Tests(request):
params = {
'classes': common.CLASSES,
'commonIDPrefix': common.TEST_ID_PREFIX,
'strict': False,
'suites': [
SELECTION_TESTS,
APPLY_TESTS,
APPLY_TESTS_CSS,
CHANGE_TESTS,
CHANGE_TESTS_CSS,
UNAPPLY_TESTS,
UNAPPLY_TESTS_CSS,
DELETE_TESTS,
FORWARDDELETE_TESTS,
INSERT_TESTS,
QUERYSUPPORTED_TESTS,
QUERYENABLED_TESTS,
QUERYINDETERM_TESTS,
QUERYSTATE_TESTS,
QUERYSTATE_TESTS_CSS,
QUERYVALUE_TESTS,
QUERYVALUE_TESTS_CSS
]
}
return shortcuts.render_to_response('%s/templates/richtext2.html' % common.CATEGORY, params)

View file

@ -0,0 +1,116 @@
.framed {
vertical-align: top;
margin: 8px;
border: 1px solid black;
}
.legend {
padding: 12px;
background-color: #f8f8ff;
}
.legendHdr {
font-size: large;
text-decoration: underline;
}
table.legend {
display: inline-table;
}
.suite-thead {
text-align: left;
}
.lo {
background-color: #dddddd;
}
.hi {
background-color: #eeeeee;
}
.lo .grey {
background-color: #dddddd;
}
.lo .na {
background-color: #dddddd;
}
.lo .pass {
background-color: #d4ffc0;
}
.lo .canary {
background-color: #ffcccc;
}
.lo .fail {
background-color: #ffcccc;
}
.lo .accept {
background-color: #ffffc0;
}
.lo .exception {
background-color: #f0d0f4;
}
.lo .unsupported {
background-color: #f0d0f4;
}
.hi .grey {
background-color: #eeeeee;
}
.hi .na {
background-color: #eeeeee;
}
.hi .pass {
background-color: #e0ffdc;
}
.hi .canary {
background-color: #ffd8d8;
}
.hi .fail {
background-color: #ffd8d8;
}
.hi .accept {
background-color: #ffffd8;
}
.hi .exception {
background-color: #f4dcf8;
}
.hi .unsupported {
background-color: #f4dcf8;
}
.sel {
color: blue;
}
.txt {
padding: 1px;
margin: 1px;
border: 1px solid #b0b0b0;
}
.idLabel {
font-size: small;
}
.fade {
color: grey;
}
.accexp {
color: #606070;
}
.comment {
color: grey;
}
.score {
color: #666666;
}
.fatalerror {
color: red;
font-size: large;
font-weight: bold;
}

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="editable.css" type="text/css">
</head>
<body contentEditable="true">
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="editable.css" type="text/css">
<script>
function setDesignMode() {
window.document.designMode = "On";
}
</script>
</head>
<body onload="setDesignMode()">
</body>
</html>

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="editable.css" type="text/css">
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,66 @@
.b, myb {
font-weight: bold;
}
.i, myi {
font-style: italic;
}
.s, mys {
text-decoration: line-through;
}
.u, myu {
text-decoration: underline;
}
.sub, mysub {
vertical-align: sub;
}
.sup, mysup {
vertical-align: super;
}
.jc, myjc {
text-align: center;
}
.jf, myjf {
text-align: justify;
}
.jl, myjl {
text-align: left;
}
.jr, myjr {
text-align: right;
}
.red, myred {
color: red;
}
.bcred, mybcred {
background-color: red;
}
.large, mylarge {
font-size: large;
}
.fs18px, myfs18px {
font-size: 18px;
}
.courier, mycourier {
font-family: courier;
}
gen::before {
content: "[BEFORE]";
}
gen::after {
content: "[AFTER]";
}

View file

@ -0,0 +1,436 @@
/**
* @fileoverview
* Canonicalization 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
*/
/**
* Canonicalize HTML entities to their actual character
*
* @param str {String} the HTML string to be canonicalized
* @return {String} the canonicalized string
*/
function canonicalizeEntities(str) {
// TODO(rolandsteiner): this function is very much not optimized, but that shouldn't
// theoretically matter too much - look into it at some point.
var match;
while (match = str.match(/&#x([0-9A-F]+);/i)) {
str = str.replace('&#x' + match[1] + ';', String.fromCharCode(parseInt(match[1], 16)));
}
while (match = str.match(/&#([0-9]+);/)) {
str = str.replace('&#' + match[1] + ';', String.fromCharCode(Number(match[1])));
}
return str;
}
/**
* Canonicalize the contents of the HTML 'style' attribute.
* I.e. sorts the CSS attributes alphabetically and canonicalizes the values
* CSS attributes where necessary.
*
* If this would return an empty string, return null instead to suppress the
* whole 'style' attribute.
*
* Avoid tests that contain {, } or : within CSS values!
*
* Note that this function relies on the spaces of the input string already
* having been normalized by canonicalizeSpaces!
*
* FIXME: does not canonicalize the contents of compound attributes
* (e.g., 'border').
*
* @param str {String} contents of the 'style' attribute
* @param emitFlags {Object} flags used for this output
* @return {String/null} canonicalized string, null instead of the empty string
*/
function canonicalizeStyle(str, emitFlags) {
// Remove any enclosing curly brackets
str = str.replace(/ ?[\{\}] ?/g, '');
var attributes = str.split(';');
var count = attributes.length;
var resultArr = [];
for (var a = 0; a < count; ++a) {
// Retrieve "name: value" pair
// Note: may expectedly fail if the last pair was terminated with ';'
var avPair = attributes[a].match(/ ?([^ :]+) ?: ?(.+)/);
if (!avPair)
continue;
var name = avPair[1];
var value = avPair[2].replace(/ $/, ''); // Remove any trailing space.
switch (name) {
case 'color':
case 'background-color':
case 'border-color':
if (emitFlags.canonicalizeUnits) {
resultArr.push(name + ': #' + new Color(value).toHexString());
} else {
resultArr.push(name + ': ' + value);
}
break;
case 'font-family':
if (emitFlags.canonicalizeUnits) {
resultArr.push(name + ': ' + new FontName(value).toString());
} else {
resultArr.push(name + ': ' + value);
}
break;
case 'font-size':
if (emitFlags.canonicalizeUnits) {
resultArr.push(name + ': ' + new FontSize(value).toString());
} else {
resultArr.push(name + ': ' + value);
}
break;
default:
resultArr.push(name + ': ' + value);
}
}
// Sort by name, assuming no duplicate CSS attribute names.
resultArr.sort();
return resultArr.join('; ') || null;
}
/**
* Canonicalize a single attribute value.
*
* Note that this function relies on the spaces of the input string already
* having been normalized by canonicalizeSpaces!
*
* @param elemName {String} the name of the element
* @param attrName {String} the name of the attribute
* @param attrValue {String} the value of the attribute
* @param emitFlags {Object} flags used for this output
* @return {String/null} the canonicalized value, or null if the attribute should be skipped.
*/
function canonicalizeSingleAttribute(elemName, attrName, attrValue, emitFlags) {
// We emit attributes as name="value", so change any contained apostrophes
// to quote marks.
attrValue = attrValue.replace(/\x22/, '\x27');
switch (attrName) {
case 'class':
return emitFlags.emitClass ? attrValue : null;
case 'id':
if (!emitFlags.emitID) {
return null;
}
if (attrValue && attrValue.substr(0, 7) == 'editor-') {
return null;
}
return attrValue;
// Remove empty style attributes, canonicalize the contents otherwise,
// provided the test cares for styles.
case 'style':
return (emitFlags.emitStyle && attrValue)
? canonicalizeStyle(attrValue, emitFlags)
: null;
// Never output onload handlers as they are set by the test environment.
case 'onload':
return null;
// Canonicalize colors.
case 'bgcolor':
case 'color':
if (!attrValue) {
return null;
}
return emitFlags.canonicalizeUnits ? new Color(attrValue).toString() : attrValue;
// Canonicalize font names.
case 'face':
return emitFlags.canonicalizeUnits ? new FontName(attrValue).toString() : attrValue;
// Canonicalize font sizes (leave other 'size' attributes as-is).
case 'size':
if (!attrValue) {
return null;
}
switch (elemName) {
case 'basefont':
case 'font':
return emitFlags.canonicalizeUnits ? new FontSize(attrValue).toString() : attrValue;
}
return attrValue;
// Remove spans with value 1. Retain spans with other values, even if
// empty or with a value 0, since those indicate a flawed implementation.
case 'colspan':
case 'rowspan':
case 'span':
return (attrValue == '1' || attrValue === '') ? null : attrValue;
// Boolean attributes: presence equals true. If present, the value must be
// the empty string or the attribute's canonical name.
// (http://www.whatwg.org/specs/web-apps/current-work/#boolean-attributes)
// Below we only normalize empty string to the canonical name for
// comparison purposes. All other values are not touched and will therefore
// in all likelihood result in a failed test (even if they may be accepted
// by the UA).
case 'async':
case 'autofocus':
case 'checked':
case 'compact':
case 'declare':
case 'defer':
case 'disabled':
case 'formnovalidate':
case 'frameborder':
case 'ismap':
case 'loop':
case 'multiple':
case 'nohref':
case 'nosize':
case 'noshade':
case 'novalidate':
case 'nowrap':
case 'open':
case 'readonly':
case 'required':
case 'reversed':
case 'seamless':
case 'selected':
return attrValue ? attrValue : attrName;
default:
return attrValue;
}
}
/**
* Canonicalize the contents of an element tag.
*
* I.e. sorts the attributes alphabetically and canonicalizes their
* values where necessary. Also removes attributes we're not interested in.
*
* Note that this function relies on the spaces of the input string already
* having been normalized by canonicalizeSpaces!
*
* @param str {String} the contens of the element tag, excluding < and >.
* @param emitFlags {Object} flags used for this output
* @return {String} the canonicalized contents.
*/
function canonicalizeElementTag(str, emitFlags) {
// FIXME: lowercase only if emitFlags.lowercase is set
str = str.toLowerCase();
var pos = str.search(' ');
// element name only
if (pos == -1) {
return str;
}
var elemName = str.substr(0, pos);
str = str.substr(pos + 1);
// Even if emitFlags.emitAttrs is not set, we must iterate over the
// attributes to catch the special selection attribute and/or selection
// markers. :(
// Iterate over attributes, add them to an array, canonicalize their
// contents, and finally output the (remaining) attributes in sorted order.
// Note: We can't do a simple split on space here, because the value of,
// e.g., 'style' attributes may also contain spaces.
var attrs = [];
var selStartInTag = false;
var selEndInTag = false;
while (str) {
var attrName;
var attrValue = '';
pos = str.search(/[ =]/);
if (pos >= 0) {
attrName = str.substr(0, pos);
if (str.charAt(pos) == ' ') {
++pos;
}
if (str.charAt(pos) == '=') {
++pos;
if (str.charAt(pos) == ' ') {
++pos;
}
str = str.substr(pos);
switch (str.charAt(0)) {
case '"':
case "'":
pos = str.indexOf(str.charAt(0), 1);
pos = (pos < 0) ? str.length : pos;
attrValue = str.substring(1, pos);
++pos;
break;
default:
pos = str.indexOf(' ', 0);
pos = (pos < 0) ? str.length : pos;
attrValue = (pos == -1) ? str : str.substr(0, pos);
break;
}
attrValue = attrValue.replace(/^ /, '');
attrValue = attrValue.replace(/ $/, '');
}
} else {
attrName = str;
}
str = (pos == -1 || pos >= str.length) ? '' : str.substr(pos + 1);
// Remove special selection attributes.
switch (attrName) {
case ATTRNAME_SEL_START:
selStartInTag = true;
continue;
case ATTRNAME_SEL_END:
selEndInTag = true;
continue;
}
switch (attrName) {
case '':
case 'onload':
case 'xmlns':
break;
default:
if (!emitFlags.emitAttrs) {
break;
}
// >>> fall through >>>
case 'contenteditable':
attrValue = canonicalizeEntities(attrValue);
attrValue = canonicalizeSingleAttribute(elemName, attrName, attrValue, emitFlags);
if (attrValue !== null) {
attrs.push(attrName + '="' + attrValue + '"');
}
}
}
var result = elemName;
// Sort alphabetically (on full string rather than just attribute value for
// simplicity. Also, attribute names will differ when encountering the '=').
if (attrs.length > 0) {
attrs.sort();
result += ' ' + attrs.join(' ');
}
// Add intra-tag selection marker(s) or attribute(s), if any, at the end.
if (selStartInTag && selEndInTag) {
result += ' |';
} else if (selStartInTag) {
result += ' {';
} else if (selEndInTag) {
result += ' }';
}
return result;
}
/**
* Canonicalize elements and attributes to facilitate comparison to the
* expectation string: sort attributes, canonicalize values and remove chaff.
*
* Note that this function relies on the spaces of the input string already
* having been normalized by canonicalizeSpaces!
*
* @param str {String} the HTML string to be canonicalized
* @param emitFlags {Object} flags used for this output
* @return {String} the canonicalized string
*/
function canonicalizeElementsAndAttributes(str, emitFlags) {
var tagStart = str.indexOf('<');
var tagEnd = 0;
var result = '';
while (tagStart >= 0) {
++tagStart;
if (str.charAt(tagStart) == '/') {
++tagStart;
}
result = result + canonicalizeEntities(str.substring(tagEnd, tagStart));
tagEnd = str.indexOf('>', tagStart);
if (tagEnd < 0) {
tagEnd = str.length - 1;
}
if (str.charAt(tagEnd - 1) == '/') {
--tagEnd;
}
var elemStr = str.substring(tagStart, tagEnd);
elemStr = canonicalizeElementTag(elemStr, emitFlags);
result = result + elemStr;
tagStart = str.indexOf('<', tagEnd);
}
return result + canonicalizeEntities(str.substring(tagEnd));
}
/**
* Canonicalize an innerHTML string to uniform single whitespaces.
*
* FIXME: running this prevents testing for pre-formatted content
* and the CSS 'white-space' attribute.
*
* @param str {String} the HTML string to be canonicalized
* @return {String} the canonicalized string
*/
function canonicalizeSpaces(str) {
// Collapse sequential whitespace.
str = str.replace(/\s+/g, ' ');
// Remove spaces immediately inside angle brackets <, >, </ and />.
// While doing this also canonicalize <.../> to <...>.
str = str.replace(/\< ?/g, '<');
str = str.replace(/\<\/ ?/g, '</');
str = str.replace(/ ?\/?\>/g, '>');
return str;
}
/**
* Canonicalize an innerHTML string to uniform single whitespaces.
* Also remove comments to retain only embedded selection markers, and
* remove </br> and </hr> if present.
*
* FIXME: running this prevents testing for pre-formatted content
* and the CSS 'white-space' attribute.
*
* @param str {String} the HTML string to be canonicalized
* @return {String} the canonicalized string
*/
function initialCanonicalizationOf(str) {
str = canonicalizeSpaces(str);
str = str.replace(/ ?<!-- ?/g, '');
str = str.replace(/ ?--> ?/g, '');
str = str.replace(/<\/[bh]r>/g, '');
return str;
}

View file

@ -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;
}

View file

@ -0,0 +1,456 @@
/**
* @fileoverview
* Functions used to format the test result output.
*
* 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
*/
/**
* Writes a fatal error to the output (replaces alert box)
*
* @param text {String} text to output
*/
function writeFatalError(text) {
var errorsStart = document.getElementById('errors');
var divider = document.getElementById('divider');
if (!errorsStart) {
errorsStart = document.createElement('hr');
errorsStart.id = 'errors';
divider.parentNode.insertBefore(errorsStart, divider);
}
var error = document.createElement('div');
error.className = 'fatalerror';
error.innerHTML = 'FATAL ERROR: ' + escapeOutput(text);
errorsStart.parentNode.insertBefore(error, divider);
}
/**
* Generates a unique ID for a given single test out of the suite ID and
* test ID.
*
* @param suiteID {string} ID string of the suite
* @param testID {string} ID string of the individual tests
* @return {string} globally unique ID
*/
function generateOutputID(suiteID, testID) {
return commonIDPrefix + '-' + suiteID + '_' + testID;
}
/**
* Function to highlight the selection markers
*
* @param str {String} a HTML string containing selection markers
* @return {String} the HTML string with highlighting tags around the markers
*/
function highlightSelectionMarkers(str) {
str = str.replace(/\[/g, '<span class="sel">[</span>');
str = str.replace(/\]/g, '<span class="sel">]</span>');
str = str.replace(/\^/g, '<span class="sel">^</span>');
str = str.replace(/{/g, '<span class="sel">{</span>');
str = str.replace(/}/g, '<span class="sel">}</span>');
str = str.replace(/\|/g, '<b class="sel">|</b>');
return str;
}
/**
* Function to highlight the selection markers
*
* @param str {String} a HTML string containing selection markers
* @return {String} the HTML string with highlighting tags around the markers
*/
function highlightSelectionMarkersAndTextNodes(str) {
str = highlightSelectionMarkers(str);
str = str.replace(/\x60/g, '<span class="txt">');
str = str.replace(/\xb4/g, '</span>');
return str;
}
/**
* Function to format output according to type
*
* @param value {String/Boolean} string or value to format
* @return {String} HTML-formatted string
*/
function formatValueOrString(value) {
if (value === undefined)
return '<i>undefined</i>';
if (value === null)
return '<i>null</i>';
switch (typeof value) {
case 'boolean':
return '<i>' + value.toString() + '</i>';
case 'number':
return value.toString();
case 'string':
return "'" + escapeOutput(value) + "'";
default:
return '<i>(' + escapeOutput(value.toString()) + ')</i>';
}
}
/**
* Function to highlight text nodes
*
* @param suite {Object} the suite the test belongs to
* @param group {Object} the group within the suite the test belongs to
* @param test {Object} the test description as object reference
* @param actual {String} a HTML string containing text nodes with markers
* @return {String} string with highlighting tags around the text node parts
*/
function formatActualResult(suite, group, test, actual) {
if (typeof actual != 'string')
return formatValueOrString(actual);
actual = escapeOutput(actual);
// Fade attributes (or just style) if not actually tested for
if (!getTestParameter(suite, group, test, PARAM_CHECK_ATTRIBUTES)) {
actual = actual.replace(/([^ =]+)=\x22([^\x22]*)\x22/g, '<span class="fade">$1="$2"</span>');
} else {
// NOTE: convert 'class="..."' first, before adding other <span class="fade">...</span> !!!
if (!getTestParameter(suite, group, test, PARAM_CHECK_CLASS)) {
actual = actual.replace(/class=\x22([^\x22]*)\x22/g, '<span class="fade">class="$1"</span>');
}
if (!getTestParameter(suite, group, test, PARAM_CHECK_STYLE)) {
actual = actual.replace(/style=\x22([^\x22]*)\x22/g, '<span class="fade">style="$1"</span>');
}
if (!getTestParameter(suite, group, test, PARAM_CHECK_ID)) {
actual = actual.replace(/id=\x22([^\x22]*)\x22/g, '<span class="fade">id="$1"</span>');
} else {
// fade out contenteditable host element's 'editor-<xyz>' ID.
actual = actual.replace(/id=\x22editor-([^\x22]*)\x22/g, '<span class="fade">id="editor-$1"</span>');
}
// grey out 'xmlns'
actual = actual.replace(/xmlns=\x22([^\x22]*)\x22/g, '<span class="fade">xmlns="$1"</span>');
// remove 'onload'
actual = actual.replace(/onload=\x22[^\x22]*\x22 ?/g, '');
}
// Highlight selection markers and text nodes.
actual = highlightSelectionMarkersAndTextNodes(actual);
return actual;
}
/**
* Escape text content for use with .innerHTML.
*
* @param str {String} HTML text to displayed
* @return {String} the escaped HTML
*/
function escapeOutput(str) {
return str ? str.replace(/\</g, '&lt;').replace(/\>/g, '&gt;') : '';
}
/**
* Fills in a single output table cell
*
* @param id {String} ID of the table cell
* @param val {String} inner HTML to set
* @param ttl {String, optional} value of the 'title' attribute
* @param cls {String, optional} class name for the cell
*/
function setTD(id, val, ttl, cls) {
var td = document.getElementById(id);
if (td) {
td.innerHTML = val;
if (ttl) {
td.title = ttl;
}
if (cls) {
td.className = cls;
}
}
}
/**
* Outputs the results of a single test suite
*
* @param suite {Object} test suite as object reference
* @param clsID {String} test class ID ('Proposed', 'RFC', 'Final')
* @param group {Object} the group of tests within the suite the test belongs to
* @param testIdx {Object} the test as object reference
*/
function outputTestResults(suite, clsID, group, test) {
var suiteID = suite.id;
var cls = suite[clsID];
var trID = generateOutputID(suiteID, test.id);
var testResult = results[suiteID][clsID][test.id];
var testValOut = VALOUTPUT[testResult.valresult];
var testSelOut = SELOUTPUT[testResult.selresult];
var suiteChecksSelOnly = !suiteChecksHTMLOrText(suite);
var testUsesHTML = !!getTestParameter(suite, group, test, PARAM_EXECCOMMAND) ||
!!getTestParameter(suite, group, test, PARAM_FUNCTION);
// Set background color for test ID
var td = document.getElementById(trID + IDOUT_TESTID);
if (td) {
td.className = (suiteChecksSelOnly && testResult.selresult != SELRESULT_NA) ? testSelOut.css : testValOut.css;
}
// Fill in "Command" and "Value" cells
var cmd;
var cmdOutput = '&nbsp;';
var valOutput = '&nbsp;';
if (cmd = getTestParameter(suite, group, test, PARAM_EXECCOMMAND)) {
cmdOutput = escapeOutput(cmd);
var val = getTestParameter(suite, group, test, PARAM_VALUE);
if (val !== undefined) {
valOutput = formatValueOrString(val);
}
} else if (cmd = getTestParameter(suite, group, test, PARAM_FUNCTION)) {
cmdOutput = '<i>' + escapeOutput(cmd) + '</i>';
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSUPPORTED)) {
cmdOutput = '<i>queryCommandSupported</i>';
valOutput = escapeOutput(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDENABLED)) {
cmdOutput = '<i>queryCommandEnabled</i>';
valOutput = escapeOutput(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDINDETERM)) {
cmdOutput = '<i>queryCommandIndeterm</i>';
valOutput = escapeOutput(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSTATE)) {
cmdOutput = '<i>queryCommandState</i>';
valOutput = escapeOutput(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDVALUE)) {
cmdOutput = '<i>queryCommandValue</i>';
valOutput = escapeOutput(cmd);
} else {
cmdOutput = '<i>(none)</i>';
}
setTD(trID + IDOUT_COMMAND, cmdOutput);
setTD(trID + IDOUT_VALUE, valOutput);
// Fill in "Attribute checked?" and "Style checked?" cells
if (testUsesHTML) {
var checkAttrs = getTestParameter(suite, group, test, PARAM_CHECK_ATTRIBUTES);
var checkStyle = getTestParameter(suite, group, test, PARAM_CHECK_STYLE);
setTD(trID + IDOUT_CHECKATTRS,
checkAttrs ? OUTSTR_YES : OUTSTR_NO,
checkAttrs ? 'attributes must match' : 'attributes are ignored');
if (checkAttrs && checkStyle) {
setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_YES, 'style attribute contents must match');
} else if (checkAttrs) {
setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_NO, 'style attribute contents is ignored');
} else {
setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_NO, 'all attributes (incl. style) are ignored');
}
} else {
setTD(trID + IDOUT_CHECKATTRS, OUTSTR_NA, 'attributes not applicable');
setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_NA, 'style not applicable');
}
// Fill in test pad specification cell (initial HTML + selection markers)
setTD(trID + IDOUT_PAD, highlightSelectionMarkers(escapeOutput(getTestParameter(suite, group, test, PARAM_PAD))));
// Fill in expected result(s) cell
var expectedOutput = '';
var expectedArr = getExpectationArray(getTestParameter(suite, group, test, PARAM_EXPECTED));
for (var idx = 0; idx < expectedArr.length; ++idx) {
if (expectedOutput) {
expectedOutput += '\xA0\xA0\xA0<i>or</i><br>';
}
expectedOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(expectedArr[idx]))
: formatValueOrString(expectedArr[idx]);
}
var acceptedArr = getExpectationArray(getTestParameter(suite, group, test, PARAM_ACCEPT));
for (var idx = 0; idx < acceptedArr.length; ++idx) {
expectedOutput += '<span class="accexp">\xA0\xA0\xA0<i>or</i></span><br><span class="accexp">';
expectedOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(acceptedArr[idx]))
: formatValueOrString(acceptedArr[idx]);
expectedOutput += '</span>';
}
// TODO(rolandsteiner): THIS IS UGLY, relying on 'div' container being index 2,
// AND not allowing other containers to have 'outer' results - change!!!
var outerOutput = '';
expectedArr = getExpectationArray(getContainerParameter(suite, group, test, containers[2], PARAM_EXPECTED_OUTER));
for (var idx = 0; idx < expectedArr.length; ++idx) {
if (outerOutput) {
outerOutput += '\xA0\xA0\xA0<i>or</i><br>';
}
outerOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(expectedArr[idx]))
: formatValueOrString(expectedArr[idx]);
}
acceptedArr = getExpectationArray(getContainerParameter(suite, group, test, containers[2], PARAM_ACCEPT_OUTER));
for (var idx = 0; idx < acceptedArr.length; ++idx) {
if (outerOutput) {
outerOutput += '<span class="accexp">\xA0\xA0\xA0<i>or</i></span><br>';
}
outerOutput += '<span class="accexp">';
outerOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(acceptedArr[idx]))
: formatValueOrString(acceptedArr[idx]);
outerOutput += '</span>';
}
if (outerOutput) {
expectedOutput += '<hr>' + outerOutput;
}
setTD(trID + IDOUT_EXPECTED, expectedOutput);
// Iterate over the individual container results
for (var cntIdx = 0; cntIdx < containers.length; ++cntIdx) {
var cntID = containers[cntIdx].id;
var cntTD = document.getElementById(trID + IDOUT_CONTAINER + cntID);
var cntResult = testResult[cntID];
var cntValOut = VALOUTPUT[cntResult.valresult];
var cntSelOut = SELOUTPUT[cntResult.selresult];
var cssVal = cntValOut.css;
var cssSel = (!suiteChecksSelOnly || cntResult.selresult != SELRESULT_NA) ? cntSelOut.css : cssVal;
var cssCnt = cssVal;
// Fill in result status cell ("PASS", "ACC.", "FAIL", "EXC.", etc.)
setTD(trID + IDOUT_STATUSVAL + cntID, cntValOut.output, cntValOut.title, cssVal);
// Fill in selection status cell ("PASS", "ACC.", "FAIL", "N/A")
setTD(trID + IDOUT_STATUSSEL + cntID, cntSelOut.output, cntSelOut.title, cssSel);
// Fill in actual result
switch (cntResult.valresult) {
case VALRESULT_SETUP_EXCEPTION:
setTD(trID + IDOUT_ACTUAL + cntID,
SETUP_EXCEPTION + '(mouseover)',
escapeOutput(cntResult.output),
cssVal);
break;
case VALRESULT_EXECUTION_EXCEPTION:
setTD(trID + IDOUT_ACTUAL + cntID,
EXECUTION_EXCEPTION + '(mouseover)',
escapeOutput(cntResult.output.toString()),
cssVal);
break;
case VALRESULT_VERIFICATION_EXCEPTION:
setTD(trID + IDOUT_ACTUAL + cntID,
VERIFICATION_EXCEPTION + '(mouseover)',
escapeOutput(cntResult.output.toString()),
cssVal);
break;
case VALRESULT_UNSUPPORTED:
setTD(trID + IDOUT_ACTUAL + cntID,
escapeOutput(cntResult.output),
'',
cssVal);
break;
case VALRESULT_CANARY:
setTD(trID + IDOUT_ACTUAL + cntID,
highlightSelectionMarkersAndTextNodes(escapeOutput(cntResult.output)),
'',
cssVal);
break;
case VALRESULT_DIFF:
case VALRESULT_ACCEPT:
case VALRESULT_EQUAL:
if (!testUsesHTML) {
setTD(trID + IDOUT_ACTUAL + cntID,
formatValueOrString(cntResult.output),
'',
cssVal);
} else if (cntResult.selresult == SELRESULT_CANARY) {
cssCnt = suiteChecksSelOnly ? cssSel : cssVal;
setTD(trID + IDOUT_ACTUAL + cntID,
highlightSelectionMarkersAndTextNodes(escapeOutput(cntResult.output)),
'',
cssCnt);
} else {
cssCnt = suiteChecksSelOnly ? cssSel : cssVal;
setTD(trID + IDOUT_ACTUAL + cntID,
formatActualResult(suite, group, test, cntResult.output),
'',
cssCnt);
}
break;
default:
cssCnt = 'exception';
setTD(trID + IDOUT_ACTUAL + cntID,
INTERNAL_ERR + 'UNKNOWN RESULT VALUE',
'',
cssCnt);
}
if (cntTD) {
cntTD.className = cssCnt;
}
}
}
/**
* Outputs the results of a single test suite
*
* @param {Object} suite as object reference
*/
function outputTestSuiteResults(suite) {
var suiteID = suite.id;
var span;
span = document.getElementById(suiteID + '-score');
if (span) {
span.innerHTML = results[suiteID].valscore + '/' + results[suiteID].count;
}
span = document.getElementById(suiteID + '-selscore');
if (span) {
span.innerHTML = results[suiteID].selscore + '/' + results[suiteID].count;
}
span = document.getElementById(suiteID + '-time');
if (span) {
span.innerHTML = results[suiteID].time;
}
span = document.getElementById(suiteID + '-progress');
if (span) {
span.style.color = 'green';
}
for (var clsIdx = 0; clsIdx < testClassCount; ++clsIdx) {
var clsID = testClassIDs[clsIdx];
var cls = suite[clsID];
if (!cls)
continue;
span = document.getElementById(suiteID + '-' + clsID + '-score');
if (span) {
span.innerHTML = results[suiteID][clsID].valscore + '/' + results[suiteID][clsID].count;
}
span = document.getElementById(suiteID + '-' + clsID + '-selscore');
if (span) {
span.innerHTML = results[suiteID][clsID].selscore + '/' + results[suiteID][clsID].count;
}
var groupCount = cls.length;
for (var groupIdx = 0; groupIdx < groupCount; ++groupIdx) {
var group = cls[groupIdx];
var testCount = group.tests.length;
for (var testIdx = 0; testIdx < testCount; ++testIdx) {
var test = group.tests[testIdx];
outputTestResults(suite, clsID, group, test);
}
}
}
}

View file

@ -0,0 +1,269 @@
/**
* @fileoverview
* Functions used to handle test and expectation strings.
*
* 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
*/
/**
* Normalize text selection indicators and convert inter-element selection
* indicators to comments.
*
* Note that this function relies on the spaces of the input string already
* having been normalized by canonicalizeSpaces!
*
* @param pad {String} HTML string that includes selection marker characters
* @return {String} the HTML string with the selection markers converted
*/
function convertSelectionIndicators(pad) {
// Sanity check: Markers { } | only directly before or after an element,
// or just before a closing > (i.e., not within a text node).
// Note that intra-tag selection markers have already been converted to the
// special selection attribute(s) above.
if (/[^>][{}\|][^<>]/.test(pad) ||
/^[{}\|][^<]/.test(pad) ||
/[^>][{}\|]$/.test(pad) ||
/^[{}\|]*$/.test(pad)) {
throw SETUP_BAD_SELECTION_SPEC;
}
// Convert intra-tag selection markers to special attributes.
pad = pad.replace(/\{\>/g, ATTRNAME_SEL_START + '="1">');
pad = pad.replace(/\}\>/g, ATTRNAME_SEL_END + '="1">');
pad = pad.replace(/\|\>/g, ATTRNAME_SEL_START + '="1" ' +
ATTRNAME_SEL_END + '="1">');
// Convert remaining {, }, | to comments with '[' and ']' data.
pad = pad.replace('{', '<!--[-->');
pad = pad.replace('}', '<!--]-->');
pad = pad.replace('|', '<!--[--><!--]-->');
// Convert caret indicator ^ to empty selection indicator []
// (this simplifies further processing).
pad = pad.replace(/\^/, '[]');
return pad;
}
/**
* Derives one point of the selection from the indicators with the HTML tree:
* '[' or ']' within a text or comment node, or the special selection
* attributes within an element node.
*
* @param root {DOMNode} root node of the recursive search
* @param marker {String} which marker to look for: '[' or ']'
* @return {Object} a pair object: {node: {DOMNode}/null, offset: {Integer}}
*/
function deriveSelectionPoint(root, marker) {
switch (root.nodeType) {
case DOM_NODE_TYPE_ELEMENT:
if (root.attributes) {
// Note: getAttribute() is necessary for this to work on all browsers!
if (marker == '[' && root.getAttribute(ATTRNAME_SEL_START)) {
root.removeAttribute(ATTRNAME_SEL_START);
return {node: root, offs: 0};
}
if (marker == ']' && root.getAttribute(ATTRNAME_SEL_END)) {
root.removeAttribute(ATTRNAME_SEL_END);
return {node: root, offs: 0};
}
}
for (var i = 0; i < root.childNodes.length; ++i) {
var pair = deriveSelectionPoint(root.childNodes[i], marker);
if (pair.node) {
return pair;
}
}
break;
case DOM_NODE_TYPE_TEXT:
var pos = root.data.indexOf(marker);
if (pos != -1) {
// Remove selection marker from text.
var nodeText = root.data;
root.data = nodeText.substr(0, pos) + nodeText.substr(pos + 1);
return {node: root, offs: pos };
}
break;
case DOM_NODE_TYPE_COMMENT:
var pos = root.data.indexOf(marker);
if (pos != -1) {
// Remove comment node from parent.
var helper = root.previousSibling;
for (pos = 0; helper; ++pos ) {
helper = helper.previousSibling;
}
helper = root;
root = root.parentNode;
root.removeChild(helper);
return {node: root, offs: pos };
}
break;
}
return {node: null, offs: 0 };
}
/**
* Initialize the test HTML with the starting state specified in the test.
*
* The selection is specified "inline", using special characters:
* ^ a collapsed text caret selection (same as [])
* [ the selection start within a text node
* ] the selection end within a text node
* | collapsed selection between elements (same as {})
* { selection starting with the following element
* } selection ending with the preceding element
* {, } and | can also be used within an element tag, just before the closing
* angle bracket > to specify a selection [element, 0] where the element
* doesn't otherwise have any children. Ex.: <hr {>foobarbaz<hr }>
*
* Explicit and implicit specification can also be mixed between the 2 points.
*
* A pad string must only contain at most ONE of the above that is suitable for
* that start or end point, respectively, and must contain either both or none.
*
* @param suite {Object} suite that test originates in as object reference
* @param group {Object} group of tests within the suite the test belongs to
* @param test {Object} test to be run as object reference
* @param container {Object} container descriptor as object reference
*/
function initContainer(suite, group, test, container) {
var pad = getTestParameter(suite, group, test, PARAM_PAD);
pad = canonicalizeSpaces(pad);
pad = convertSelectionIndicators(pad);
if (container.editorID) {
container.body.innerHTML = container.canary + container.tagOpen + pad + container.tagClose + container.canary;
container.editor = container.doc.getElementById(container.editorID);
} else {
container.body.innerHTML = pad;
container.editor = container.body;
}
win = container.win;
doc = container.doc;
body = container.body;
editor = container.editor;
sel = null;
if (!editor) {
throw SETUP_CONTAINER;
}
if (getTestParameter(suite, group, test, PARAM_STYLE_WITH_CSS)) {
try {
container.doc.execCommand('styleWithCSS', false, true);
} catch (ex) {
// ignore exception if unsupported
}
}
var selAnchor = deriveSelectionPoint(editor, '[');
var selFocus = deriveSelectionPoint(editor, ']');
// sanity check
if (!selAnchor || !selFocus) {
throw SETUP_SELECTION;
}
if (!selAnchor.node || !selFocus.node) {
if (selAnchor.node || selFocus.node) {
// Broken test: only one selection point was specified
throw SETUP_BAD_SELECTION_SPEC;
}
sel = null;
return;
}
if (selAnchor.node === selFocus.node) {
if (selAnchor.offs > selFocus.offs) {
// Both selection points are within the same node, the selection was
// specified inline (thus the end indicator element or character was
// removed), and the end point is before the start (reversed selection).
// Start offset that was derived is now off by 1 and needs adjustment.
--selAnchor.offs;
}
if (selAnchor.offs === selFocus.offs) {
createCaret(selAnchor.node, selAnchor.offs).select();
try {
sel = win.getSelection();
} catch (ex) {
sel = undefined;
}
return;
}
}
createFromNodes(selAnchor.node, selAnchor.offs, selFocus.node, selFocus.offs).select();
try {
sel = win.getSelection();
} catch (ex) {
sel = undefined;
}
}
/**
* Reset the editor element after a test is run.
*
* @param container {Object} container descriptor as object reference
*/
function resetContainer(container) {
// Remove errant styles and attributes that may have been set on the <body>.
container.body.removeAttribute('style');
container.body.removeAttribute('color');
container.body.removeAttribute('bgcolor');
try {
container.doc.execCommand('styleWithCSS', false, false);
} catch (ex) {
// Ignore exception if unsupported.
}
}
/**
* Initialize the editor document.
*/
function initEditorDocs() {
for (var c = 0; c < containers.length; ++c) {
var container = containers[c];
container.iframe = document.getElementById('iframe-' + container.id);
container.win = container.iframe.contentWindow;
container.doc = container.win.document;
container.body = container.doc.body;
// container.editor is set per test (changes on embedded editor elements).
// Some browsers require a selection to go with their 'styleWithCSS'.
try {
container.win.getSelection().selectAllChildren(editor);
} catch (ex) {
// ignore exception if unsupported
}
// Default styleWithCSS to false.
try {
container.doc.execCommand('styleWithCSS', false, false);
} catch (ex) {
// ignore exception if unsupported
}
}
}

View file

@ -0,0 +1,5 @@
goog.require('goog.dom.Range');
window.createFromWindow = goog.dom.Range.createFromWindow;
window.createFromNodes = goog.dom.Range.createFromNodes;
window.createCaret = goog.dom.Range.createCaret;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,383 @@
/**
* @fileoverview
* Main functions used in running 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
*/
/**
* Info function: returns true if the suite (mainly) tests the result HTML/Text.
*
* @param suite {String} the test suite
* @return {boolean} Whether the suite main focus is the output HTML/Text
*/
function suiteChecksHTMLOrText(suite) {
return suite.id[0] != 'S';
}
/**
* Info function: returns true if the suite checks the result selection.
*
* @param suite {String} the test suite
* @return {boolean} Whether the suite checks the selection
*/
function suiteChecksSelection(suite) {
return suite.id[0] != 'Q';
}
/**
* Helper function returning the effective value of a test parameter.
*
* @param suite {Object} the test suite
* @param group {Object} group of tests within the suite the test belongs to
* @param test {Object} the test
* @param param {String} the test parameter to be checked
* @return {Any} the effective value of the parameter (can be undefined)
*/
function getTestParameter(suite, group, test, param) {
var val = test[param];
if (val === undefined) {
val = group[param];
}
if (val === undefined) {
val = suite[param];
}
return val;
}
/**
* Helper function returning the effective value of a container/test parameter.
*
* @param suite {Object} the test suite
* @param group {Object} group of tests within the suite the test belongs to
* @param test {Object} the test
* @param container {Object} the container descriptor object
* @param param {String} the test parameter to be checked
* @return {Any} the effective value of the parameter (can be undefined)
*/
function getContainerParameter(suite, group, test, container, param) {
var val = undefined;
if (test[container.id]) {
val = test[container.id][param];
}
if (val === undefined) {
val = test[param];
}
if (val === undefined) {
val = group[param];
}
if (val === undefined) {
val = suite[param];
}
return val;
}
/**
* Initializes the global variables before any tests are run.
*/
function initVariables() {
results = {
count: 0,
valscore: 0,
selscore: 0
};
}
/**
* Runs a single test - outputs and sets the result variables.
*
* @param suite {Object} suite that test originates in as object reference
* @param group {Object} group of tests within the suite the test belongs to
* @param test {Object} test to be run as object reference
* @param container {Object} container descriptor as object reference
* @see variables.js for RESULT... values
*/
function runSingleTest(suite, group, test, container) {
var result = {
valscore: 0,
selscore: 0,
valresult: VALRESULT_NOT_RUN,
selresult: SELRESULT_NOT_RUN,
output: ''
};
// 1.) Populate the editor element with the initial test setup HTML.
try {
initContainer(suite, group, test, container);
} catch(ex) {
result.valresult = VALRESULT_SETUP_EXCEPTION;
result.selresult = SELRESULT_NA;
result.output = SETUP_EXCEPTION + ex.toString();
return result;
}
// 2.) Run the test command, general function or query function.
var isHTMLTest = false;
try {
var cmd = undefined;
if (cmd = getTestParameter(suite, group, test, PARAM_EXECCOMMAND)) {
isHTMLTest = true;
// Note: "getTestParameter(suite, group, test, PARAM_VALUE) || null"
// doesn't work, since value might be the empty string, e.g., for 'insertText'!
var value = getTestParameter(suite, group, test, PARAM_VALUE);
if (value === undefined) {
value = null;
}
container.doc.execCommand(cmd, false, value);
} else if (cmd = getTestParameter(suite, group, test, PARAM_FUNCTION)) {
isHTMLTest = true;
eval(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSUPPORTED)) {
result.output = container.doc.queryCommandSupported(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDENABLED)) {
result.output = container.doc.queryCommandEnabled(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDINDETERM)) {
result.output = container.doc.queryCommandIndeterm(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSTATE)) {
result.output = container.doc.queryCommandState(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDVALUE)) {
result.output = container.doc.queryCommandValue(cmd);
if (result.output === false) {
// A return value of boolean 'false' for queryCommandValue means 'not supported'.
result.valresult = VALRESULT_UNSUPPORTED;
result.selresult = SELRESULT_NA;
result.output = UNSUPPORTED;
return result;
}
} else {
result.valresult = VALRESULT_SETUP_EXCEPTION;
result.selresult = SELRESULT_NA;
result.output = SETUP_EXCEPTION + SETUP_NOCOMMAND;
return result;
}
} catch (ex) {
result.valresult = VALRESULT_EXECUTION_EXCEPTION;
result.selresult = SELRESULT_NA;
result.output = EXECUTION_EXCEPTION + ex.toString();
return result;
}
// 4.) Verify test result
try {
if (isHTMLTest) {
// First, retrieve HTML from container
prepareHTMLTestResult(container, result);
// Compare result to expectations
compareHTMLTestResult(suite, group, test, container, result);
result.valscore = (result.valresult === VALRESULT_EQUAL) ? 1 : 0;
result.selscore = (result.selresult === SELRESULT_EQUAL) ? 1 : 0;
} else {
compareTextTestResult(suite, group, test, result);
result.selresult = SELRESULT_NA;
result.valscore = (result.valresult === VALRESULT_EQUAL) ? 1 : 0;
}
} catch (ex) {
result.valresult = VALRESULT_VERIFICATION_EXCEPTION;
result.selresult = SELRESULT_NA;
result.output = VERIFICATION_EXCEPTION + ex.toString();
return result;
}
return result;
}
/**
* Initializes the results dictionary for a given test suite.
* (for all classes -> tests -> containers)
*
* @param {Object} suite as object reference
*/
function initTestSuiteResults(suite) {
var suiteID = suite.id;
// Initialize results entries for this suite
results[suiteID] = {
count: 0,
valscore: 0,
selscore: 0,
time: 0
};
var totalTestCount = 0;
for (var clsIdx = 0; clsIdx < testClassCount; ++clsIdx) {
var clsID = testClassIDs[clsIdx];
var cls = suite[clsID];
if (!cls)
continue;
results[suiteID][clsID] = {
count: 0,
valscore: 0,
selscore: 0
};
var clsTestCount = 0;
var groupCount = cls.length;
for (var groupIdx = 0; groupIdx < groupCount; ++groupIdx) {
var group = cls[groupIdx];
var testCount = group.tests.length;
clsTestCount += testCount;
totalTestCount += testCount;
for (var testIdx = 0; testIdx < testCount; ++testIdx) {
var test = group.tests[testIdx];
results[suiteID][clsID ][test.id] = {
valscore: 0,
selscore: 0,
valresult: VALRESULT_NOT_RUN,
selresult: SELRESULT_NOT_RUN
};
for (var cntIdx = 0; cntIdx < containers.length; ++cntIdx) {
var cntID = containers[cntIdx].id;
results[suiteID][clsID][test.id][cntID] = {
valscore: 0,
selscore: 0,
valresult: VALRESULT_NOT_RUN,
selresult: SELRESULT_NOT_RUN,
output: ''
}
}
}
}
results[suiteID][clsID].count = clsTestCount;
}
results[suiteID].count = totalTestCount;
}
/**
* Runs a single test suite (such as DELETE tests or INSERT tests).
*
* @param suite {Object} suite as object reference
*/
function runTestSuite(suite) {
var suiteID = suite.id;
var suiteStartTime = new Date().getTime();
initTestSuiteResults(suite);
for (var clsIdx = 0; clsIdx < testClassCount; ++clsIdx) {
var clsID = testClassIDs[clsIdx];
var cls = suite[clsID];
if (!cls)
continue;
var groupCount = cls.length;
for (var groupIdx = 0; groupIdx < groupCount; ++groupIdx) {
var group = cls[groupIdx];
var testCount = group.tests.length;
for (var testIdx = 0; testIdx < testCount; ++testIdx) {
var test = group.tests[testIdx];
var valscore = 1;
var selscore = 1;
var valresult = VALRESULT_EQUAL;
var selresult = SELRESULT_EQUAL;
for (var cntIdx = 0; cntIdx < containers.length; ++cntIdx) {
var container = containers[cntIdx];
var cntID = container.id;
var result = runSingleTest(suite, group, test, container);
results[suiteID][clsID][test.id][cntID] = result;
valscore = Math.min(valscore, result.valscore);
selscore = Math.min(selscore, result.selscore);
valresult = Math.min(valresult, result.valresult);
selresult = Math.min(selresult, result.selresult);
resetContainer(container);
}
results[suiteID][clsID][test.id].valscore = valscore;
results[suiteID][clsID][test.id].selscore = selscore;
results[suiteID][clsID][test.id].valresult = valresult;
results[suiteID][clsID][test.id].selresult = selresult;
results[suiteID][clsID].valscore += valscore;
results[suiteID][clsID].selscore += selscore;
results[suiteID].valscore += valscore;
results[suiteID].selscore += selscore;
results.valscore += valscore;
results.selscore += selscore;
}
}
}
results[suiteID].time = new Date().getTime() - suiteStartTime;
}
/**
* Runs a single test suite (such as DELETE tests or INSERT tests)
* and updates the output HTML.
*
* @param {Object} suite as object reference
*/
function runAndOutputTestSuite(suite) {
runTestSuite(suite);
outputTestSuiteResults(suite);
}
/**
* Fills the beacon with the test results.
*/
function fillResults() {
// Result totals of the individual categories
categoryTotals = [
'selection=' + results['S'].selscore,
'apply=' + results['A'].valscore,
'applyCSS=' + results['AC'].valscore,
'change=' + results['C'].valscore,
'changeCSS=' + results['CC'].valscore,
'unapply=' + results['U'].valscore,
'unapplyCSS=' + results['UC'].valscore,
'delete=' + results['D'].valscore,
'forwarddelete=' + results['FD'].valscore,
'insert=' + results['I'].valscore,
'selectionResult=' + (results['A'].selscore +
results['AC'].selscore +
results['C'].selscore +
results['CC'].selscore +
results['U'].selscore +
results['UC'].selscore +
results['D'].selscore +
results['FD'].selscore +
results['I'].selscore),
'querySupported=' + results['Q'].valscore,
'queryEnabled=' + results['QE'].valscore,
'queryIndeterm=' + results['QI'].valscore,
'queryState=' + results['QS'].valscore,
'queryStateCSS=' + results['QSC'].valscore,
'queryValue=' + results['QV'].valscore,
'queryValueCSS=' + results['QVC'].valscore
];
// Beacon copies category results
beacon = categoryTotals.slice(0);
}

View file

@ -0,0 +1,416 @@
/**
* @fileoverview
* Common constants and variables 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
*/
// All colors defined in CSS3.
var colorChart = {
'aliceblue': {red: 0xF0, green: 0xF8, blue: 0xFF},
'antiquewhite': {red: 0xFA, green: 0xEB, blue: 0xD7},
'aqua': {red: 0x00, green: 0xFF, blue: 0xFF},
'aquamarine': {red: 0x7F, green: 0xFF, blue: 0xD4},
'azure': {red: 0xF0, green: 0xFF, blue: 0xFF},
'beige': {red: 0xF5, green: 0xF5, blue: 0xDC},
'bisque': {red: 0xFF, green: 0xE4, blue: 0xC4},
'black': {red: 0x00, green: 0x00, blue: 0x00},
'blanchedalmond': {red: 0xFF, green: 0xEB, blue: 0xCD},
'blue': {red: 0x00, green: 0x00, blue: 0xFF},
'blueviolet': {red: 0x8A, green: 0x2B, blue: 0xE2},
'brown': {red: 0xA5, green: 0x2A, blue: 0x2A},
'burlywood': {red: 0xDE, green: 0xB8, blue: 0x87},
'cadetblue': {red: 0x5F, green: 0x9E, blue: 0xA0},
'chartreuse': {red: 0x7F, green: 0xFF, blue: 0x00},
'chocolate': {red: 0xD2, green: 0x69, blue: 0x1E},
'coral': {red: 0xFF, green: 0x7F, blue: 0x50},
'cornflowerblue': {red: 0x64, green: 0x95, blue: 0xED},
'cornsilk': {red: 0xFF, green: 0xF8, blue: 0xDC},
'crimson': {red: 0xDC, green: 0x14, blue: 0x3C},
'cyan': {red: 0x00, green: 0xFF, blue: 0xFF},
'darkblue': {red: 0x00, green: 0x00, blue: 0x8B},
'darkcyan': {red: 0x00, green: 0x8B, blue: 0x8B},
'darkgoldenrod': {red: 0xB8, green: 0x86, blue: 0x0B},
'darkgray': {red: 0xA9, green: 0xA9, blue: 0xA9},
'darkgreen': {red: 0x00, green: 0x64, blue: 0x00},
'darkgrey': {red: 0xA9, green: 0xA9, blue: 0xA9},
'darkkhaki': {red: 0xBD, green: 0xB7, blue: 0x6B},
'darkmagenta': {red: 0x8B, green: 0x00, blue: 0x8B},
'darkolivegreen': {red: 0x55, green: 0x6B, blue: 0x2F},
'darkorange': {red: 0xFF, green: 0x8C, blue: 0x00},
'darkorchid': {red: 0x99, green: 0x32, blue: 0xCC},
'darkred': {red: 0x8B, green: 0x00, blue: 0x00},
'darksalmon': {red: 0xE9, green: 0x96, blue: 0x7A},
'darkseagreen': {red: 0x8F, green: 0xBC, blue: 0x8F},
'darkslateblue': {red: 0x48, green: 0x3D, blue: 0x8B},
'darkslategray': {red: 0x2F, green: 0x4F, blue: 0x4F},
'darkslategrey': {red: 0x2F, green: 0x4F, blue: 0x4F},
'darkturquoise': {red: 0x00, green: 0xCE, blue: 0xD1},
'darkviolet': {red: 0x94, green: 0x00, blue: 0xD3},
'deeppink': {red: 0xFF, green: 0x14, blue: 0x93},
'deepskyblue': {red: 0x00, green: 0xBF, blue: 0xFF},
'dimgray': {red: 0x69, green: 0x69, blue: 0x69},
'dimgrey': {red: 0x69, green: 0x69, blue: 0x69},
'dodgerblue': {red: 0x1E, green: 0x90, blue: 0xFF},
'firebrick': {red: 0xB2, green: 0x22, blue: 0x22},
'floralwhite': {red: 0xFF, green: 0xFA, blue: 0xF0},
'forestgreen': {red: 0x22, green: 0x8B, blue: 0x22},
'fuchsia': {red: 0xFF, green: 0x00, blue: 0xFF},
'gainsboro': {red: 0xDC, green: 0xDC, blue: 0xDC},
'ghostwhite': {red: 0xF8, green: 0xF8, blue: 0xFF},
'gold': {red: 0xFF, green: 0xD7, blue: 0x00},
'goldenrod': {red: 0xDA, green: 0xA5, blue: 0x20},
'gray': {red: 0x80, green: 0x80, blue: 0x80},
'green': {red: 0x00, green: 0x80, blue: 0x00},
'greenyellow': {red: 0xAD, green: 0xFF, blue: 0x2F},
'grey': {red: 0x80, green: 0x80, blue: 0x80},
'honeydew': {red: 0xF0, green: 0xFF, blue: 0xF0},
'hotpink': {red: 0xFF, green: 0x69, blue: 0xB4},
'indianred': {red: 0xCD, green: 0x5C, blue: 0x5C},
'indigo': {red: 0x4B, green: 0x00, blue: 0x82},
'ivory': {red: 0xFF, green: 0xFF, blue: 0xF0},
'khaki': {red: 0xF0, green: 0xE6, blue: 0x8C},
'lavender': {red: 0xE6, green: 0xE6, blue: 0xFA},
'lavenderblush': {red: 0xFF, green: 0xF0, blue: 0xF5},
'lawngreen': {red: 0x7C, green: 0xFC, blue: 0x00},
'lemonchiffon': {red: 0xFF, green: 0xFA, blue: 0xCD},
'lightblue': {red: 0xAD, green: 0xD8, blue: 0xE6},
'lightcoral': {red: 0xF0, green: 0x80, blue: 0x80},
'lightcyan': {red: 0xE0, green: 0xFF, blue: 0xFF},
'lightgoldenrodyellow': {red: 0xFA, green: 0xFA, blue: 0xD2},
'lightgray': {red: 0xD3, green: 0xD3, blue: 0xD3},
'lightgreen': {red: 0x90, green: 0xEE, blue: 0x90},
'lightgrey': {red: 0xD3, green: 0xD3, blue: 0xD3},
'lightpink': {red: 0xFF, green: 0xB6, blue: 0xC1},
'lightsalmon': {red: 0xFF, green: 0xA0, blue: 0x7A},
'lightseagreen': {red: 0x20, green: 0xB2, blue: 0xAA},
'lightskyblue': {red: 0x87, green: 0xCE, blue: 0xFA},
'lightslategray': {red: 0x77, green: 0x88, blue: 0x99},
'lightslategrey': {red: 0x77, green: 0x88, blue: 0x99},
'lightsteelblue': {red: 0xB0, green: 0xC4, blue: 0xDE},
'lightyellow': {red: 0xFF, green: 0xFF, blue: 0xE0},
'lime': {red: 0x00, green: 0xFF, blue: 0x00},
'limegreen': {red: 0x32, green: 0xCD, blue: 0x32},
'linen': {red: 0xFA, green: 0xF0, blue: 0xE6},
'magenta': {red: 0xFF, green: 0x00, blue: 0xFF},
'maroon': {red: 0x80, green: 0x00, blue: 0x00},
'mediumaquamarine': {red: 0x66, green: 0xCD, blue: 0xAA},
'mediumblue': {red: 0x00, green: 0x00, blue: 0xCD},
'mediumorchid': {red: 0xBA, green: 0x55, blue: 0xD3},
'mediumpurple': {red: 0x93, green: 0x70, blue: 0xDB},
'mediumseagreen': {red: 0x3C, green: 0xB3, blue: 0x71},
'mediumslateblue': {red: 0x7B, green: 0x68, blue: 0xEE},
'mediumspringgreen': {red: 0x00, green: 0xFA, blue: 0x9A},
'mediumturquoise': {red: 0x48, green: 0xD1, blue: 0xCC},
'mediumvioletred': {red: 0xC7, green: 0x15, blue: 0x85},
'midnightblue': {red: 0x19, green: 0x19, blue: 0x70},
'mintcream': {red: 0xF5, green: 0xFF, blue: 0xFA},
'mistyrose': {red: 0xFF, green: 0xE4, blue: 0xE1},
'moccasin': {red: 0xFF, green: 0xE4, blue: 0xB5},
'navajowhite': {red: 0xFF, green: 0xDE, blue: 0xAD},
'navy': {red: 0x00, green: 0x00, blue: 0x80},
'oldlace': {red: 0xFD, green: 0xF5, blue: 0xE6},
'olive': {red: 0x80, green: 0x80, blue: 0x00},
'olivedrab': {red: 0x6B, green: 0x8E, blue: 0x23},
'orange': {red: 0xFF, green: 0xA5, blue: 0x00},
'orangered': {red: 0xFF, green: 0x45, blue: 0x00},
'orchid': {red: 0xDA, green: 0x70, blue: 0xD6},
'palegoldenrod': {red: 0xEE, green: 0xE8, blue: 0xAA},
'palegreen': {red: 0x98, green: 0xFB, blue: 0x98},
'paleturquoise': {red: 0xAF, green: 0xEE, blue: 0xEE},
'palevioletred': {red: 0xDB, green: 0x70, blue: 0x93},
'papayawhip': {red: 0xFF, green: 0xEF, blue: 0xD5},
'peachpuff': {red: 0xFF, green: 0xDA, blue: 0xB9},
'peru': {red: 0xCD, green: 0x85, blue: 0x3F},
'pink': {red: 0xFF, green: 0xC0, blue: 0xCB},
'plum': {red: 0xDD, green: 0xA0, blue: 0xDD},
'powderblue': {red: 0xB0, green: 0xE0, blue: 0xE6},
'purple': {red: 0x80, green: 0x00, blue: 0x80},
'red': {red: 0xFF, green: 0x00, blue: 0x00},
'rosybrown': {red: 0xBC, green: 0x8F, blue: 0x8F},
'royalblue': {red: 0x41, green: 0x69, blue: 0xE1},
'saddlebrown': {red: 0x8B, green: 0x45, blue: 0x13},
'salmon': {red: 0xFA, green: 0x80, blue: 0x72},
'sandybrown': {red: 0xF4, green: 0xA4, blue: 0x60},
'seagreen': {red: 0x2E, green: 0x8B, blue: 0x57},
'seashell': {red: 0xFF, green: 0xF5, blue: 0xEE},
'sienna': {red: 0xA0, green: 0x52, blue: 0x2D},
'silver': {red: 0xC0, green: 0xC0, blue: 0xC0},
'skyblue': {red: 0x87, green: 0xCE, blue: 0xEB},
'slateblue': {red: 0x6A, green: 0x5A, blue: 0xCD},
'slategray': {red: 0x70, green: 0x80, blue: 0x90},
'slategrey': {red: 0x70, green: 0x80, blue: 0x90},
'snow': {red: 0xFF, green: 0xFA, blue: 0xFA},
'springgreen': {red: 0x00, green: 0xFF, blue: 0x7F},
'steelblue': {red: 0x46, green: 0x82, blue: 0xB4},
'tan': {red: 0xD2, green: 0xB4, blue: 0x8C},
'teal': {red: 0x00, green: 0x80, blue: 0x80},
'thistle': {red: 0xD8, green: 0xBF, blue: 0xD8},
'tomato': {red: 0xFF, green: 0x63, blue: 0x47},
'turquoise': {red: 0x40, green: 0xE0, blue: 0xD0},
'violet': {red: 0xEE, green: 0x82, blue: 0xEE},
'wheat': {red: 0xF5, green: 0xDE, blue: 0xB3},
'white': {red: 0xFF, green: 0xFF, blue: 0xFF},
'whitesmoke': {red: 0xF5, green: 0xF5, blue: 0xF5},
'yellow': {red: 0xFF, green: 0xFF, blue: 0x00},
'yellowgreen': {red: 0x9A, green: 0xCD, blue: 0x32},
'transparent': {red: 0x00, green: 0x00, blue: 0x00, alpha: 0.0}
};
/**
* Color class allows cross-browser comparison of values, which can
* be returned from queryCommandValue in several formats:
* #ff00ff
* #f0f
* rgb(255, 0, 0)
* rgb(100%, 0%, 28%) // disabled for the time being (see below)
* rgba(127, 0, 64, 0.25)
* rgba(50%, 0%, 10%, 0.65) // disabled for the time being (see below)
* palegoldenrod
* transparent
*
* @constructor
* @param value {String} original value
*/
function Color(value) {
this.compare = function(other) {
if (!this.valid || !other.valid) {
return false;
}
if (this.alpha != other.alpha) {
return false;
}
if (this.alpha == 0.0) {
// both are fully transparent -> ignore the specific color information
return true;
}
// TODO(rolandsteiner): handle hsl/hsla values
return this.red == other.red && this.green == other.green && this.blue == other.blue;
}
this.parse = function(value) {
if (!value)
return false;
value = String(value).toLowerCase();
var match;
// '#' + 6 hex digits, e.g., #ff3300
match = value.match(/#([0-9a-f]{6})/i);
if (match) {
this.red = parseInt(match[1].substring(0, 2), 16);
this.green = parseInt(match[1].substring(2, 4), 16);
this.blue = parseInt(match[1].substring(4, 6), 16);
this.alpha = 1.0;
return true;
}
// '#' + 3 hex digits, e.g., #f30
match = value.match(/#([0-9a-f]{3})/i);
if (match) {
this.red = parseInt(match[1].substring(0, 1), 16) * 16;
this.green = parseInt(match[1].substring(1, 2), 16) * 16;
this.blue = parseInt(match[1].substring(2, 3), 16) * 16;
this.alpha = 1.0;
return true;
}
// a color name, e.g., springgreen
match = colorChart[value];
if (match) {
this.red = match.red;
this.green = match.green;
this.blue = match.blue;
this.alpha = (match.alpha === undefined) ? 1.0 : match.alpha;
return true;
}
// rgb(r, g, b), e.g., rgb(128, 12, 217)
match = value.match(/rgb\(([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/i);
if (match) {
this.red = Number(match[1]);
this.green = Number(match[2]);
this.blue = Number(match[3]);
this.alpha = 1.0;
return true;
}
// rgb(r%, g%, b%), e.g., rgb(100%, 0%, 50%)
// Commented out for the time being, since it seems likely that the resulting
// decimal values will create false negatives when compared with non-% values.
//
// => store as separate percent values and do exact matching when compared with % values
// and fuzzy matching when compared with non-% values?
//
// match = value.match(/rgb\(([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%\s*\)/i);
// if (match) {
// this.red = Number(match[1]) * 255 / 100;
// this.green = Number(match[2]) * 255 / 100;
// this.blue = Number(match[3]) * 255 / 100;
// this.alpha = 1.0;
// return true;
// }
// rgba(r, g, b, a), e.g., rgb(128, 12, 217, 0.2)
match = value.match(/rgba\(([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/i);
if (match) {
this.red = Number(match[1]);
this.green = Number(match[2]);
this.blue = Number(match[3]);
this.alpha = Number(match[4]);
return true;
}
// rgba(r%, g%, b%, a), e.g., rgb(100%, 0%, 50%, 0.3)
// Commented out for the time being (cf. rgb() matching above)
// match = value.match(/rgba\(([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/i);
// if (match) {
// this.red = Number(match[1]) * 255 / 100;
// this.green = Number(match[2]) * 255 / 100;
// this.blue = Number(match[3]) * 255 / 100;
// this.alpha = Number(match[4]);
// return true;
// }
// TODO(rolandsteiner): handle "hsl(h, s, l)" and "hsla(h, s, l, a)" notation
return false;
}
this.toString = function() {
return this.valid ? this.red + ',' + this.green + ',' + this.blue : '(invalid)';
}
this.toHexString = function() {
if (!this.valid)
return '(invalid)';
return ((this.red < 16) ? '0' : '') + this.red.toString(16) +
((this.green < 16) ? '0' : '') + this.green.toString(16) +
((this.blue < 16) ? '0' : '') + this.blue.toString(16);
}
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.
*
* @constructor
* @param value {String} original value
*/
function FontSize(value) {
this.parse = function(str) {
if (!str)
this.valid = false;
var match;
if (match = String(str).match(/([0-9]+)px/)) {
var px = Number(match[1]);
if (px <= 0 || px > 47)
return false;
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 {
this.size = '7';
}
return true;
}
if (match = String(str).match(/([+-][0-9]+)/)) {
this.size = match[1];
return this.size >= 1 && this.size <= 7;
}
if (Number(str)) {
this.size = String(Number(str));
return this.size >= 1 && this.size <= 7;
}
switch (str) {
case 'x-small':
this.size = '1';
return true;
case 'small':
this.size = '2';
return true;
case 'medium':
this.size = '3';
return true;
case 'large':
this.size = '4';
return true;
case 'x-large':
this.size = '5';
return true;
case 'xx-large':
this.size = '6';
return true;
case 'xxx-large':
this.size = '7';
return true;
case '-webkit-xxx-large':
this.size = '7';
return true;
case 'larger':
this.size = '+1';
return true;
case 'smaller':
this.size = '-1';
return true;
}
return false;
}
this.compare = function(other) {
return this.valid && other.valid && this.size === other.size;
}
this.toString = function() {
return this.valid ? this.size : '(invalid)';
}
this.valid = this.parse(value);
}
/**
* Utility class for converting & canonicalizing font names.
*
* @constructor
* @param value {String} original value
*/
function FontName(value) {
this.parse = function(str) {
if (!str)
return false;
str = String(str).toLowerCase();
switch (str) {
case 'arial new':
this.fontname = 'arial';
return true;
case 'courier new':
this.fontname = 'courier';
return true;
case 'times new':
case 'times roman':
case 'times new roman':
this.fontname = 'times';
return true;
}
this.fontname = value;
return true;
}
this.compare = function(other) {
return this.valid && other.valid && this.fontname === other.fontname;
}
this.toString = function() {
return this.valid ? this.fontname : '(invalid)';
}
this.valid = this.parse(value);
}

View file

@ -0,0 +1,227 @@
/**
* @fileoverview
* Common constants and variables 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
*/
// Constant for indicating a test setup is unsupported or incorrect
// (threw exception).
var INTERNAL_ERR = 'INTERNAL ERROR: ';
var SETUP_EXCEPTION = 'SETUP EXCEPTION: ';
var EXECUTION_EXCEPTION = 'EXECUTION EXCEPTION: ';
var VERIFICATION_EXCEPTION = 'VERIFICATION EXCEPTION: ';
var SETUP_CONTAINER = 'WHEN INITIALIZING TEST CONTAINER';
var SETUP_BAD_SELECTION_SPEC = 'BAD SELECTION SPECIFICATION IN TEST OR EXPECTATION STRING';
var SETUP_HTML = 'WHEN SETTING TEST HTML';
var SETUP_SELECTION = 'WHEN SETTING SELECTION';
var SETUP_NOCOMMAND = 'NO COMMAND, GENERAL FUNCTION OR QUERY FUNCTION GIVEN';
var HTML_COMPARISON = 'WHEN COMPARING OUTPUT HTML';
// Exceptiona to be thrown on unsupported selection operations
var SELMODIFY_UNSUPPORTED = 'UNSUPPORTED selection.modify()';
var SELALLCHILDREN_UNSUPPORTED = 'UNSUPPORTED selection.selectAllChildren()';
// Output string for unsupported functions
// (returning bool 'false' as opposed to throwing an exception)
var UNSUPPORTED = '<i>false</i> (UNSUPPORTED)';
// HTML comparison result contants.
var VALRESULT_NOT_RUN = 0; // test hasn't been run yet
var VALRESULT_SETUP_EXCEPTION = 1;
var VALRESULT_EXECUTION_EXCEPTION = 2;
var VALRESULT_VERIFICATION_EXCEPTION = 3;
var VALRESULT_UNSUPPORTED = 4;
var VALRESULT_CANARY = 5; // HTML changes bled into the canary.
var VALRESULT_DIFF = 6;
var VALRESULT_ACCEPT = 7; // HTML technically correct, but not ideal.
var VALRESULT_EQUAL = 8;
var VALOUTPUT = [ // IMPORTANT: this array MUST be coordinated with the values above!!
{css: 'grey', output: '???', title: 'The test has not been run yet.'}, // VALRESULT_NOT_RUN
{css: 'exception', output: 'EXC.', title: 'Exception was thrown during setup.'}, // VALRESULT_SETUP_EXCEPTION
{css: 'exception', output: 'EXC.', title: 'Exception was thrown during execution.'}, // VALRESULT_EXECUTION_EXCEPTION
{css: 'exception', output: 'EXC.', title: 'Exception was thrown during result verification.'}, // VALRESULT_VERIFICATION_EXCEPTION
{css: 'unsupported', output: 'UNS.', title: 'Unsupported command or value'}, // VALRESULT_UNSUPPORTED
{css: 'canary', output: 'CANARY', title: 'The command affected the contentEditable root element, or outside HTML.'}, // VALRESULT_CANARY
{css: 'fail', output: 'FAIL', title: 'The result differs from the expectation(s).'}, // VALRESULT_DIFF
{css: 'accept', output: 'ACC.', title: 'The result is technically correct, but sub-optimal.'}, // VALRESULT_ACCEPT
{css: 'pass', output: 'PASS', title: 'The test result matches the expectation.'} // VALRESULT_EQUAL
]
// Selection comparison result contants.
var SELRESULT_NOT_RUN = 0; // test hasn't been run yet
var SELRESULT_CANARY = 1; // selection escapes the contentEditable element
var SELRESULT_DIFF = 2;
var SELRESULT_NA = 3;
var SELRESULT_ACCEPT = 4; // Selection is acceptable, but not ideal.
var SELRESULT_EQUAL = 5;
var SELOUTPUT = [ // IMPORTANT: this array MUST be coordinated with the values above!!
{css: 'grey', output: 'grey', title: 'The test has not been run yet.'}, // SELRESULT_NOT_RUN
{css: 'canary', output: 'CANARY', title: 'The selection escaped the contentEditable boundary!'}, // SELRESULT_CANARY
{css: 'fail', output: 'FAIL', title: 'The selection differs from the expectation(s).'}, // SELRESULT_DIFF
{css: 'na', output: 'N/A', title: 'The correctness of the selection could not be verified.'}, // SELRESULT_NA
{css: 'accept', output: 'ACC.', title: 'The selection is technically correct, but sub-optimal.'}, // SELRESULT_ACCEPT
{css: 'pass', output: 'PASS', title: 'The selection matches the expectation.'} // SELRESULT_EQUAL
];
// RegExp for selection markers
var SELECTION_MARKERS = /[\[\]\{\}\|\^]/;
// Special attributes used to mark selections within elements that otherwise
// have no children. Important: attribute name MUST be lower case!
var ATTRNAME_SEL_START = 'bsselstart';
var ATTRNAME_SEL_END = 'bsselend';
// DOM node type constants.
var DOM_NODE_TYPE_ELEMENT = 1;
var DOM_NODE_TYPE_TEXT = 3;
var DOM_NODE_TYPE_COMMENT = 8;
// Test parameter names
var PARAM_DESCRIPTION = 'desc';
var PARAM_PAD = 'pad';
var PARAM_EXECCOMMAND = 'command';
var PARAM_FUNCTION = 'function';
var PARAM_QUERYCOMMANDSUPPORTED = 'qcsupported';
var PARAM_QUERYCOMMANDENABLED = 'qcenabled';
var PARAM_QUERYCOMMANDINDETERM = 'qcindeterm';
var PARAM_QUERYCOMMANDSTATE = 'qcstate';
var PARAM_QUERYCOMMANDVALUE = 'qcvalue';
var PARAM_VALUE = 'value';
var PARAM_EXPECTED = 'expected';
var PARAM_EXPECTED_OUTER = 'expOuter';
var PARAM_ACCEPT = 'accept';
var PARAM_ACCEPT_OUTER = 'accOuter';
var PARAM_CHECK_ATTRIBUTES = 'checkAttrs';
var PARAM_CHECK_STYLE = 'checkStyle';
var PARAM_CHECK_CLASS = 'checkClass';
var PARAM_CHECK_ID = 'checkID';
var PARAM_STYLE_WITH_CSS = 'styleWithCSS';
// ID suffixes for the output columns
var IDOUT_TR = '_:TR:'; // per container
var IDOUT_TESTID = '_:tid'; // per test
var IDOUT_COMMAND = '_:cmd'; // per test
var IDOUT_VALUE = '_:val'; // per test
var IDOUT_CHECKATTRS = '_:att'; // per test
var IDOUT_CHECKSTYLE = '_:sty'; // per test
var IDOUT_CONTAINER = '_:cnt:'; // per container
var IDOUT_STATUSVAL = '_:sta:'; // per container
var IDOUT_STATUSSEL = '_:sel:'; // per container
var IDOUT_PAD = '_:pad'; // per test
var IDOUT_EXPECTED = '_:exp'; // per test
var IDOUT_ACTUAL = '_:act:'; // per container
// Output strings to use for yes/no/NA
var OUTSTR_YES = '&#x25CF;';
var OUTSTR_NO = '&#x25CB;';
var OUTSTR_NA = '-';
// Tags at the start of HTML strings where they were taken from
var HTMLTAG_BODY = 'B:';
var HTMLTAG_OUTER = 'O:';
var HTMLTAG_INNER = 'I:';
// What to use for the canary
var CANARY = 'CAN<br>ARY';
// Containers for tests, and their associated DOM elements:
// iframe, win, doc, body, elem
var containers = [
{ id: 'dM',
iframe: null,
win: null,
doc: null,
body: null,
editor: null,
tagOpen: '<body>',
tagClose: '</body>',
editorID: null,
canary: '',
},
{ id: 'body',
iframe: null,
win: null,
doc: null,
body: null,
editor: null,
tagOpen: '<body contenteditable="true">',
tagClose: '</body>',
editorID: null,
canary: ''
},
{ id: 'div',
iframe: null,
win: null,
doc: null,
body: null,
editor: null,
tagOpen: '<div contenteditable="true" id="editor-div">',
tagClose: '</div>',
editorID: 'editor-div',
canary: CANARY
}
];
// Helper variables to use in test functions
var win = null; // window object to use for test functions
var doc = null; // document object to use for test functions
var body = null; // The <body> element of the current document
var editor = null; // The contentEditable element (i.e., the <body> or <div>)
var sel = null; // The current selection after the pad is set up
// Canonicalization emit flags for various purposes
var emitFlagsForCanary = {
emitAttrs: true,
emitStyle: true,
emitClass: true,
emitID: true,
lowercase: true,
canonicalizeUnits: true
};
var emitFlagsForOutput = {
emitAttrs: true,
emitStyle: true,
emitClass: true,
emitID: true,
lowercase: false,
canonicalizeUnits: false
};
// Shades of output colors
var colorShades = ['Lo', 'Hi'];
// Classes of tests
var testClassIDs = ['Finalized', 'RFC', 'Proposed'];
var testClassCount = testClassIDs.length;
// Dictionary storing the detailed test results.
var results = {
count: 0,
score: 0
};
// Results - populated by the fillResults() function.
var beacon = [];
// "compatibility" between Python and JS for test quines
var True = true;
var False = false;

View file

@ -0,0 +1,138 @@
<!-- Legend -->
<TABLE CLASS="legend framed">
<THEAD>
<TR><TH COLSPAN=3 CLASS="legendHdr">Result Description</TH></TR>
<TR><TH>Status</TH><TH ALIGN="LEFT">Meaning</TH><TH ALIGN="LEFT">Explanation</TH><TH>Scoring</TH></TR>
</THEAD>
<TBODY>
<TR CLASS="lo"><TD CLASS="pass" ALIGN="CENTER">&nbsp;PASS&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Passed</TD><TD CLASS="legend" ROWSPAN=2>The result matches the expectation.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="pass">PASS (+1)</TD></TR>
<TR CLASS="hi"><TD CLASS="pass" ALIGN="CENTER">&nbsp;PASS&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="accept" ALIGN="CENTER">&nbsp;ACC.&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Acceptable</TD><TD CLASS="legend" ROWSPAN=2>The result is technically correct, but not ideal (too verbose, deprecated usage, etc.) - for informative purposes only.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="accept" ALIGN="CENTER">&nbsp;ACC.&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="fail" ALIGN="CENTER">&nbsp;FAIL&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Failure</TD><TD CLASS="legend" ROWSPAN=2>The result does not match any given expectation.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="fail" ALIGN="CENTER">&nbsp;FAIL&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="canary" ALIGN="CENTER">&nbsp;CANARY&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Canary</TD><TD CLASS="legend" ROWSPAN=2>The result changes HTML other than children of the contentEditable element.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="canary" ALIGN="CENTER">&nbsp;CANARY&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="unsupported" ALIGN="CENTER">&nbsp;UNS.&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Unsupported</TD><TD CLASS="legend" ROWSPAN=2>The specific function or value is unsupported (returned boolean 'false').</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="unsupported" ALIGN="CENTER">&nbsp;UNS.&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="exception" ALIGN="CENTER">&nbsp;EXC.&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Exception</TD><TD CLASS="legend" ROWSPAN=2>An unexpected exception was thrown during the execution of the test.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="exception" ALIGN="CENTER">&nbsp;EXC.&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="na" ALIGN="CENTER">&nbsp;N/A&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Not Applicable</TD><TD CLASS="legend" ROWSPAN=2>The selection could not be tested, because the tested function failed to return a known result.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="na" ALIGN="CENTER">&nbsp;N/A&nbsp;</TD></TR>
</TBODY>
</TABLE>
<TABLE CLASS="legend framed">
<THEAD>
<TR><TH COLSPAN=2 CLASS="legendHdr">Selection and Result Display</TH></TR>
<TR><TH>Character</TH><TH ALIGN="LEFT">Explanation</TH></TR>
</THEAD>
<TBODY>
<TR><TD CLASS="sel" ALIGN="CENTER">[</TD><TD>Start of selection - selection point is within a text node.</TD></TR>
<TR><TD CLASS="sel" ALIGN="CENTER">]</TD><TD>End of selection - selection point is within a text node.</TD></TR>
<TR><TD CLASS="sel" ALIGN="CENTER">^</TD><TD>Collapsed selection - selection point is within a text node.</TD></TR>
<TR><TD COLSPAN=2>&nbsp;</TD></TR>
<TR><TD CLASS="sel" ALIGN="CENTER">{</TD><TD>Start of selection - selection point is within an element node.</TD></TR>
<TR><TD CLASS="sel" ALIGN="CENTER">}</TD><TD>End of selection - selection point is within an element node.</TD></TR>
<TR><TD CLASS="sel" ALIGN="CENTER">|</TD><TD>Collapsed selection - selection point is within an element node.</TD></TR>
<TR><TD COLSPAN=2>&nbsp;</TD></TR>
<TR><TD ALIGN="CENTER"><SPAN CLASS="fade">foo</SPAN></TD><TD>Greyed text indicates parts of the output that are ignored for the purposes of checking the result.</TD></TR>
<TR><TD ALIGN="CENTER"><SPAN CLASS="txt">foo</SPAN></TD><TD>Grey border indicates extent of text nodes in the result.</TD></TR>
</TBODY>
</TABLE>
<!-- progress meter -->
<HR ID="divider">
<H1>Running Test Suites: {% for s in suites %}<A HREF="#{{ s.id }}" ID="{{ s.id }}-progress" STYLE="color: #eeeeee">{{ s.id }}</A> {% endfor %}<SPAN ID="done">&nbsp;</SPAN></H1>
<HR>
<!-- main output -->
{% for s in suites %}
<H1 ID="{{ s.id }}"><A NAME="{{ s.id }}" HREF="#{{ s.id }}">{{ s.id }}</A> - {{ s.caption }}:
<SPAN ID="{{ s.id }}-{% ifequal s.id.0 'S' %}sel{% endifequal %}score">?/?</SPAN>
{% ifnotequal s.id.0 "Q" %}{% ifnotequal s.id.0 "S" %}
(Selection: <SPAN ID="{{ s.id }}-selscore">?/?</SPAN>)
{% endifnotequal %}{% endifnotequal %}
(time: <SPAN ID="{{ s.id }}-time">?</SPAN>&nbsp;ms)
</H1>
{% if s.comment %}
<DIV CLASS="comment">{{ s.comment|safe }}</DIV>
{% endif %}
{% for cls in classes %}{% for pk, pv in s.items %}{% ifequal pk cls %}
<H2 ID="{{ s.id }}-{{ cls }}"><A NAME="{{ s.id }}-{{ cls }}" HREF="#{{ s.id }}-{{ cls }}">{{ cls }} Tests</A>:
<SPAN ID="{{ s.id }}-{{ cls }}-{% ifequal s.id.0 'S' %}sel{% endifequal %}score">?/?</SPAN>
{% ifnotequal s.id.0 "Q" %}{% ifnotequal s.id.0 "S" %}
(Selection: <SPAN ID="{{ s.id }}-{{ cls }}-selscore">?/?</SPAN>)
{% endifnotequal %}{% endifnotequal %}
</H2>
<TABLE WIDTH=100%>
<THEAD>
<TR>
<TH TITLE="Unique ID of the test" ALIGN="LEFT">ID</TH>
<TH TITLE="Command or function used in the test" ALIGN="LEFT">Command</TH>
<TH TITLE="Value field for commands" ALIGN="LEFT">Value</TH>
{% ifnotequal s.id.0 "S" %}{% ifnotequal s.id.0 "Q" %}{% comment %} Don't output attribute and style columns for selection and "queryCommand..." tests. {% endcomment %}
<TH TITLE="check Atributes?">A</TH>
<TH TITLE="check Style">S</TH>
{% endifnotequal %}{% endifnotequal %}
<TH TITLE="Testing HTML Element">Env.</TH>
{% ifnotequal s.id.0 "S" %}{% comment %} Don't output HTML status column for selection tests. {% endcomment %}
<TH TITLE="State of the test">Status</TH>
{% endifnotequal %}
{% ifnotequal s.id.0 "Q" %}{% comment %} Don't output selection result column for "queryCommand..." tests. {% endcomment %}
<TH TITLE="State of the test regarding the selection">Selection</TH>
{% endifnotequal %}
<TH TITLE="Initial HTML and selection" ALIGN="LEFT">Initial</TH>
<TH TITLE="Expected HTML and selection" ALIGN="LEFT">Expected</TH>
<TH TITLE="Actual result HTML and selection" ALIGN="LEFT">Actual (lower case, canonicalized, selection marks)</TH>
<TH TITLE="Short description of the test" ALIGN="LEFT">Description</TH>
</TR>
</THEAD>
<TBODY>
{% for g in pv %}{% for t in g.tests %}
<TR ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:TR:dM" CLASS="{% cycle 'lo' 'lo' 'lo' 'hi' 'hi' 'hi' as shade %}">
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:tid"><A CLASS="idLabel" NAME="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}" HREF="#{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}">{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}</A></TD>
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cmd">&nbsp;</TD>
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:val">&nbsp;</TD>
{% ifnotequal s.id.0 "S" %}{% ifnotequal s.id.0 "Q" %}{% comment %} Don't output attribute and style columns for selection and "queryCommand..." tests. {% endcomment %}
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:att" ALIGN="CENTER">&nbsp;</TD>
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sty" ALIGN="CENTER">&nbsp;</TD>
{% endifnotequal %}{% endifnotequal %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cnt:dM" TITLE="designMode=&quot;on&quot;" ALIGN="CENTER">dM</TD>
{% ifnotequal s.id.0 "S" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sta:dM" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
{% ifnotequal s.id.0 "Q" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sel:dM" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:pad">&nbsp;</TD>
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:exp">&nbsp;</TD>
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:act:dM"><I>Processing...</I></TD>
<TD ROWSPAN=3>{{ t.desc|default:"&nbsp;" }}</TD>
</TR>
<TR ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:TR:body" CLASS="{% cycle shade %}">
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cnt:body" TITLE="&lt;body contentEditable=&quot;true&quot;&gt;" ALIGN="CENTER">body</TD>
{% ifnotequal s.id.0 "S" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sta:body" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
{% ifnotequal s.id.0 "Q" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sel:body" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:act:body"><I>Processing...</I></TD>
</TR>
<TR ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:TR:div" CLASS="{% cycle shade %}">
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cnt:div" TITLE="&lt;div contentEditable=&quot;true&quot;&gt;" ALIGN="CENTER">div</TD>
{% ifnotequal s.id.0 "S" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sta:div" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
{% ifnotequal s.id.0 "Q" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sel:div" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:act:div"><I>Processing...</I></TD>
</TR>
{% endfor %}{% endfor %}
</TBODY>
</TABLE>
{% endifequal %}{% endfor %}{% endfor %}
{% endfor %}

View file

@ -0,0 +1,107 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>New Rich Text Tests</title>
<link rel="stylesheet" href="static/common.css" type="text/css">
<link rel="stylesheet" href="static/editable.css" type="text/css">
<!-- utility scripts -->
<script src="static/js/variables.js"></script>
<script src="static/js/canonicalize.js"></script>
<script src="static/js/compare.js"></script>
<script src="static/js/output.js"></script>
<script src="static/js/pad.js"></script>
<script src="static/js/range.js"></script>
<script src="static/js/units.js"></script>
<script src="static/js/run.js"></script>
<!-- new tests -->
<script type="text/javascript">
{% autoescape off %}
var commonIDPrefix = '{{ commonIDPrefix }}';
{% for s in suites %}
var {{ s.id }}_TESTS = {{ s }};
{% endfor %}
/**
* Stuff to do after all tests are run:
* - write a nice "DONE!" at the end of the progress meter
* - beacon the results
* - remove the testing <iframe>s
*/
function finish() {
var span = document.getElementById('done');
if (span)
span.innerHTML = ' ... DONE!';
fillResults();
parent.sendScore(beacon, categoryTotals);
cleanUp();
}
/**
* Run every individual suite, with a a brief timeout in between
* to allow for screen updates.
*/
{% for s in suites %}
{% if not forloop.first %}
setTimeout("runSuite{{ s.id }}()", 100);
}
{% endif %}
function runSuite{{ s.id }}() {
runAndOutputTestSuite({{ s.id }}_TESTS);
{% endfor %}
finish();
}
/**
* Runs all tests in all suites.
*/
function doRunTests() {
initVariables();
initEditorDocs();
// Start with the first test suite
runSuite{{ suites.0.id }}();
}
/**
* Runs after allowing for some time to have everything loaded
* (aka. horrible IE9 kludge)
*/
function runTests() {
setTimeout("doRunTests()", 1500);
}
/**
* Removes the <iframe>s after all tests are finished
*/
function cleanUp() {
var e = document.getElementById('iframe-dM');
e.parentNode.removeChild(e);
e = document.getElementById('iframe-body');
e.parentNode.removeChild(e);
e = document.getElementById('iframe-div');
e.parentNode.removeChild(e);
}
{% endautoescape %}
</script>
</head>
<body onload="runTests()">
{% include "richtext2/templates/output.html" %}
<hr>
<iframe name="iframe-dM" id="iframe-dM" src="static/editable-dM.html"></iframe>
<iframe name="iframe-body" id="iframe-body" src="static/editable-body.html"></iframe>
<iframe name="iframe-div" id="iframe-div" src="static/editable-div.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,17 @@
__all__ = [
'apply',
'applyCSS',
'change',
'changeCSS',
'delete',
'forwarddelete',
'insert',
'queryEnabled',
'queryIndeterm',
'queryState',
'querySupported',
'queryValue',
'selection',
'unapply',
'unapplyCSS'
]

View file

@ -0,0 +1,364 @@
APPLY_TESTS = {
'id': 'A',
'caption': 'Apply Formatting Tests',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': False,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': '[HTML5] bold',
'command': 'bold',
'tests': [
{ 'id': 'B_TEXT-1_SI',
'rte1-id': 'a-bold-0',
'desc': 'Bold selection',
'pad': 'foo[bar]baz',
'expected': [ 'foo<b>[bar]</b>baz',
'foo<strong>[bar]</strong>baz' ] },
{ 'id': 'B_TEXT-1_SIR',
'desc': 'Bold reversed selection',
'pad': 'foo]bar[baz',
'expected': [ 'foo<b>[bar]</b>baz',
'foo<strong>[bar]</strong>baz' ] },
{ 'id': 'B_I-1_SL',
'desc': 'Bold selection, partially including italic',
'pad': 'foo[bar<i>baz]qoz</i>quz',
'expected': [ 'foo<b>[bar</b><i><b>baz]</b>qoz</i>quz',
'foo<b>[bar<i>baz]</i></b><i>qoz</i>quz',
'foo<strong>[bar</strong><i><strong>baz]</strong>qoz</i>quz',
'foo<strong>[bar<i>baz]</i></strong><i>qoz</i>quz' ] }
]
},
{ 'desc': '[HTML5] italic',
'command': 'italic',
'tests': [
{ 'id': 'I_TEXT-1_SI',
'rte1-id': 'a-italic-0',
'desc': 'Italicize selection',
'pad': 'foo[bar]baz',
'expected': [ 'foo<i>[bar]</i>baz',
'foo<em>[bar]</em>baz' ] }
]
},
{ 'desc': '[HTML5] underline',
'command': 'underline',
'tests': [
{ 'id': 'U_TEXT-1_SI',
'rte1-id': 'a-underline-0',
'desc': 'Underline selection',
'pad': 'foo[bar]baz',
'expected': 'foo<u>[bar]</u>baz' }
]
},
{ 'desc': '[HTML5] strikethrough',
'command': 'strikethrough',
'tests': [
{ 'id': 'S_TEXT-1_SI',
'rte1-id': 'a-strikethrough-0',
'desc': 'Strike-through selection',
'pad': 'foo[bar]baz',
'expected': [ 'foo<s>[bar]</s>baz',
'foo<strike>[bar]</strike>baz',
'foo<del>[bar]</del>baz' ] }
]
},
{ 'desc': '[HTML5] subscript',
'command': 'subscript',
'tests': [
{ 'id': 'SUB_TEXT-1_SI',
'rte1-id': 'a-subscript-0',
'desc': 'Change selection to subscript',
'pad': 'foo[bar]baz',
'expected': 'foo<sub>[bar]</sub>baz' }
]
},
{ 'desc': '[HTML5] superscript',
'command': 'superscript',
'tests': [
{ 'id': 'SUP_TEXT-1_SI',
'rte1-id': 'a-superscript-0',
'desc': 'Change selection to superscript',
'pad': 'foo[bar]baz',
'expected': 'foo<sup>[bar]</sup>baz' }
]
},
{ 'desc': '[HTML5] createlink',
'command': 'createlink',
'tests': [
{ 'id': 'CL:url_TEXT-1_SI',
'rte1-id': 'a-createlink-0',
'desc': 'create a link around the selection',
'value': '#foo',
'pad': 'foo[bar]baz',
'expected': 'foo<a href="#foo">[bar]</a>baz' }
]
},
{ 'desc': '[HTML5] formatBlock',
'command': 'formatblock',
'tests': [
{ 'id': 'FB:H1_TEXT-1_SI',
'rte1-id': 'a-formatblock-0',
'desc': 'format the selection into a block: use <h1>',
'value': 'h1',
'pad': 'foo[bar]baz',
'expected': '<h1>foo[bar]baz</h1>' },
{ 'id': 'FB:P_TEXT-1_SI',
'desc': 'format the selection into a block: use <p>',
'value': 'p',
'pad': 'foo[bar]baz',
'expected': '<p>foo[bar]baz</p>' },
{ 'id': 'FB:PRE_TEXT-1_SI',
'desc': 'format the selection into a block: use <pre>',
'value': 'pre',
'pad': 'foo[bar]baz',
'expected': '<pre>foo[bar]baz</pre>' },
{ 'id': 'FB:ADDRESS_TEXT-1_SI',
'desc': 'format the selection into a block: use <address>',
'value': 'address',
'pad': 'foo[bar]baz',
'expected': '<address>foo[bar]baz</address>' },
{ 'id': 'FB:BQ_TEXT-1_SI',
'desc': 'format the selection into a block: use <blockquote>',
'value': 'blockquote',
'pad': 'foo[bar]baz',
'expected': '<blockquote>foo[bar]baz</blockquote>' },
{ 'id': 'FB:BQ_BR.BR-1_SM',
'desc': 'format a multi-line selection into a block: use <blockquote>',
'command': 'formatblock',
'value': 'blockquote',
'pad': 'fo[o<br>bar<br>b]az',
'expected': '<blockquote>fo[o<br>bar<br>b]az</blockquote>' }
]
},
{ 'desc': '[MIDAS] backcolor',
'command': 'backcolor',
'tests': [
{ 'id': 'BC:blue_TEXT-1_SI',
'rte1-id': 'a-backcolor-0',
'desc': 'Change background color (note: no non-CSS variant available)',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
'foo<font style="background-color: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] forecolor',
'command': 'forecolor',
'tests': [
{ 'id': 'FC:blue_TEXT-1_SI',
'rte1-id': 'a-forecolor-0',
'desc': 'Change the text color',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': 'foo<font color="blue">[bar]</font>baz' }
]
},
{ 'desc': '[MIDAS] hilitecolor',
'command': 'hilitecolor',
'tests': [
{ 'id': 'HC:blue_TEXT-1_SI',
'rte1-id': 'a-hilitecolor-0',
'desc': 'Change the hilite color',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
'foo<font style="background-color: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] fontname',
'command': 'fontname',
'tests': [
{ 'id': 'FN:a_TEXT-1_SI',
'rte1-id': 'a-fontname-0',
'desc': 'Change the font name',
'value': 'arial',
'pad': 'foo[bar]baz',
'expected': 'foo<font face="arial">[bar]</font>baz' }
]
},
{ 'desc': '[MIDAS] fontsize',
'command': 'fontsize',
'tests': [
{ 'id': 'FS:2_TEXT-1_SI',
'rte1-id': 'a-fontsize-0',
'desc': 'Change the font size to "2"',
'value': '2',
'pad': 'foo[bar]baz',
'expected': 'foo<font size="2">[bar]</font>baz' },
{ 'id': 'FS:18px_TEXT-1_SI',
'desc': 'Change the font size to "18px"',
'value': '18px',
'pad': 'foo[bar]baz',
'expected': 'foo<font size="18px">[bar]</font>baz' },
{ 'id': 'FS:large_TEXT-1_SI',
'desc': 'Change the font size to "large"',
'value': 'large',
'pad': 'foo[bar]baz',
'expected': 'foo<font size="large">[bar]</font>baz' }
]
},
{ 'desc': '[MIDAS] increasefontsize',
'command': 'increasefontsize',
'tests': [
{ 'id': 'INCFS:2_TEXT-1_SI',
'desc': 'Decrease the font size (to small)',
'pad': 'foo[bar]baz',
'expected': [ 'foo<font size="4">[bar]</font>baz',
'foo<font size="+1">[bar]</font>baz',
'foo<big>[bar]</big>baz' ] }
]
},
{ 'desc': '[MIDAS] decreasefontsize',
'command': 'decreasefontsize',
'tests': [
{ 'id': 'DECFS:2_TEXT-1_SI',
'rte1-id': 'a-decreasefontsize-0',
'desc': 'Decrease the font size (to small)',
'pad': 'foo[bar]baz',
'expected': [ 'foo<font size="2">[bar]</font>baz',
'foo<font size="-1">[bar]</font>baz',
'foo<small>[bar]</small>baz' ] }
]
},
{ 'desc': '[MIDAS] indent (note: accept the de-facto standard indent of 40px)',
'command': 'indent',
'tests': [
{ 'id': 'IND_TEXT-1_SI',
'rte1-id': 'a-indent-0',
'desc': 'Indent the text (accept the de-facto standard of 40px indent)',
'pad': 'foo[bar]baz',
'checkAttrs': False,
'expected': [ '<blockquote>foo[bar]baz</blockquote>',
'<div style="margin-left: 40px">foo[bar]baz</div>' ],
'div': {
'accOuter': '<div contenteditable="true" style="margin-left: 40px">foo[bar]baz</div>' } }
]
},
{ 'desc': '[MIDAS] outdent (-> unapply tests)',
'command': 'outdent',
'tests': [
]
},
{ 'desc': '[MIDAS] justifycenter',
'command': 'justifycenter',
'tests': [
{ 'id': 'JC_TEXT-1_SC',
'rte1-id': 'a-justifycenter-0',
'desc': 'justify the text centrally',
'pad': 'foo^bar',
'expected': [ '<center>foo^bar</center>',
'<p align="center">foo^bar</p>',
'<p align="middle">foo^bar</p>',
'<div align="center">foo^bar</div>',
'<div align="middle">foo^bar</div>' ],
'div': {
'accOuter': [ '<div align="center" contenteditable="true">foo^bar</div>',
'<div align="middle" contenteditable="true">foo^bar</div>' ] } }
]
},
{ 'desc': '[MIDAS] justifyfull',
'command': 'justifyfull',
'tests': [
{ 'id': 'JF_TEXT-1_SC',
'rte1-id': 'a-justifyfull-0',
'desc': 'justify the text fully',
'pad': 'foo^bar',
'expected': [ '<p align="justify">foo^bar</p>',
'<div align="justify">foo^bar</div>' ],
'div': {
'accOuter': '<div align="justify" contenteditable="true">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] justifyleft',
'command': 'justifyleft',
'tests': [
{ 'id': 'JL_TEXT-1_SC',
'rte1-id': 'a-justifyleft-0',
'desc': 'justify the text left',
'pad': 'foo^bar',
'expected': [ '<p align="left">foo^bar</p>',
'<div align="left">foo^bar</div>' ],
'div': {
'accOuter': '<div align="left" contenteditable="true">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] justifyright',
'command': 'justifyright',
'tests': [
{ 'id': 'JR_TEXT-1_SC',
'rte1-id': 'a-justifyright-0',
'desc': 'justify the text right',
'pad': 'foo^bar',
'expected': [ '<p align="right">foo^bar</p>',
'<div align="right">foo^bar</div>' ],
'div': {
'accOuter': '<div align="right" contenteditable="true">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] heading',
'command': 'heading',
'tests': [
{ 'id': 'H:H1_TEXT-1_SC',
'desc': 'create a heading from the paragraph that contains the selection',
'value': 'h1',
'pad': 'foo[bar]baz',
'expected': '<h1>foo[bar]baz</h1>' }
]
},
{ 'desc': '[Other] createbookmark',
'command': 'createbookmark',
'tests': [
{ 'id': 'CB:name_TEXT-1_SI',
'rte1-id': 'a-createbookmark-0',
'desc': 'create a bookmark (named link) around selection',
'value': 'created',
'pad': 'foo[bar]baz',
'expected': 'foo<a name="created">[bar]</a>baz' }
]
}
]
}

View file

@ -0,0 +1,244 @@
APPLY_TESTS_CSS = {
'id': 'AC',
'caption': 'Apply Formatting Tests, using styleWithCSS',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': True,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': '[HTML5] bold',
'command': 'bold',
'tests': [
{ 'id': 'B_TEXT-1_SI',
'rte1-id': 'a-bold-1',
'desc': 'Bold selection',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="font-weight: bold">[bar]</span>baz' }
]
},
{ 'desc': '[HTML5] italic',
'command': 'italic',
'tests': [
{ 'id': 'I_TEXT-1_SI',
'rte1-id': 'a-italic-1',
'desc': 'Italicize selection',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="font-style: italic">[bar]</span>baz' }
]
},
{ 'desc': '[HTML5] underline',
'command': 'underline',
'tests': [
{ 'id': 'U_TEXT-1_SI',
'rte1-id': 'a-underline-1',
'desc': 'Underline selection',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="text-decoration: underline">[bar]</span>baz' }
]
},
{ 'desc': '[HTML5] strikethrough',
'command': 'strikethrough',
'tests': [
{ 'id': 'S_TEXT-1_SI',
'rte1-id': 'a-strikethrough-1',
'desc': 'Strike-through selection',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="text-decoration: line-through">[bar]</span>baz' }
]
},
{ 'desc': '[HTML5] subscript',
'command': 'subscript',
'tests': [
{ 'id': 'SUB_TEXT-1_SI',
'rte1-id': 'a-subscript-1',
'desc': 'Change selection to subscript',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="vertical-align: sub">[bar]</span>baz' }
]
},
{ 'desc': '[HTML5] superscript',
'command': 'superscript',
'tests': [
{ 'id': 'SUP_TEXT-1_SI',
'rte1-id': 'a-superscript-1',
'desc': 'Change selection to superscript',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="vertical-align: super">[bar]</span>baz' }
]
},
{ 'desc': '[MIDAS] backcolor',
'command': 'backcolor',
'tests': [
{ 'id': 'BC:blue_TEXT-1_SI',
'rte1-id': 'a-backcolor-1',
'desc': 'Change background color',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
'foo<font style="background-color: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] forecolor',
'command': 'forecolor',
'tests': [
{ 'id': 'FC:blue_TEXT-1_SI',
'rte1-id': 'a-forecolor-1',
'desc': 'Change the text color',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="color: blue">[bar]</span>baz',
'foo<font style="color: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] hilitecolor',
'command': 'hilitecolor',
'tests': [
{ 'id': 'HC:blue_TEXT-1_SI',
'rte1-id': 'a-hilitecolor-1',
'desc': 'Change the hilite color',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
'foo<font style="background-color: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] fontname',
'command': 'fontname',
'tests': [
{ 'id': 'FN:a_TEXT-1_SI',
'rte1-id': 'a-fontname-1',
'desc': 'Change the font name',
'value': 'arial',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="font-family: arial">[bar]</span>baz',
'foo<font style="font-family: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] fontsize',
'command': 'fontsize',
'tests': [
{ 'id': 'FS:2_TEXT-1_SI',
'rte1-id': 'a-fontsize-1',
'desc': 'Change the font size to "2"',
'value': '2',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="font-size: small">[bar]</span>baz',
'foo<font style="font-size: small">[bar]</font>baz' ] },
{ 'id': 'FS:18px_TEXT-1_SI',
'desc': 'Change the font size to "18px"',
'value': '18px',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="font-size: 18px">[bar]</span>baz',
'foo<font style="font-size: 18px">[bar]</font>baz' ] },
{ 'id': 'FS:large_TEXT-1_SI',
'desc': 'Change the font size to "large"',
'value': 'large',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="font-size: large">[bar]</span>baz',
'foo<font style="font-size: large">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] indent',
'command': 'indent',
'tests': [
{ 'id': 'IND_TEXT-1_SI',
'rte1-id': 'a-indent-1',
'desc': 'Indent the text (assume "standard" 40px)',
'pad': 'foo[bar]baz',
'expected': [ '<div style="margin-left: 40px">foo[bar]baz</div>',
'<div style="margin: 0 0 0 40px">foo[bar]baz</div>',
'<blockquote style="margin-left: 40px">foo[bar]baz</blockquote>',
'<blockquote style="margin: 0 0 0 40px">foo[bar]baz</blockquote>' ],
'div': {
'accOuter': [ '<div contenteditable="true" style="margin-left: 40px">foo[bar]baz</div>',
'<div contenteditable="true" style="margin: 0 0 0 40px">foo[bar]baz</div>' ] } }
]
},
{ 'desc': '[MIDAS] outdent (-> unapply tests)',
'command': 'outdent',
'tests': [
]
},
{ 'desc': '[MIDAS] justifycenter',
'command': 'justifycenter',
'tests': [
{ 'id': 'JC_TEXT-1_SC',
'rte1-id': 'a-justifycenter-1',
'desc': 'justify the text centrally',
'pad': 'foo^bar',
'expected': [ '<p style="text-align: center">foo^bar</p>',
'<div style="text-align: center">foo^bar</div>' ],
'div': {
'accOuter': '<div contenteditable="true" style="text-align: center">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] justifyfull',
'command': 'justifyfull',
'tests': [
{ 'id': 'JF_TEXT-1_SC',
'rte1-id': 'a-justifyfull-1',
'desc': 'justify the text fully',
'pad': 'foo^bar',
'expected': [ '<p style="text-align: justify">foo^bar</p>',
'<div style="text-align: justify">foo^bar</div>' ],
'div': {
'accOuter': '<div contenteditable="true" style="text-align: justify">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] justifyleft',
'command': 'justifyleft',
'tests': [
{ 'id': 'JL_TEXT-1_SC',
'rte1-id': 'a-justifyleft-1',
'desc': 'justify the text left',
'pad': 'foo^bar',
'expected': [ '<p style="text-align: left">foo^bar</p>',
'<div style="text-align: left">foo^bar</div>' ],
'div': {
'accOuter': '<div contenteditable="true" style="text-align: left">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] justifyright',
'command': 'justifyright',
'tests': [
{ 'id': 'JR_TEXT-1_SC',
'rte1-id': 'a-justifyright-1',
'desc': 'justify the text right',
'pad': 'foo^bar',
'expected': [ '<p style="text-align: right">foo^bar</p>',
'<div style="text-align: right">foo^bar</div>' ],
'div': {
'accOuter': '<div contenteditable="true" style="text-align: right">foo^bar</div>' } }
]
}
]
}

View file

@ -0,0 +1,273 @@
CHANGE_TESTS = {
'id': 'C',
'caption': 'Change Existing Format to Different Format Tests',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': False,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': '[HTML5] italic',
'command': 'italic',
'tests': [
{ 'id': 'I_I-1_SL',
'desc': 'Italicize partially italicized text',
'pad': 'foo[bar<i>baz]</i>qoz',
'expected': 'foo<i>[barbaz]</i>qoz' },
{ 'id': 'I_B-I-1_SO',
'desc': 'Italicize partially italicized text in bold context',
'pad': '<b>foo[bar<i>baz</i>}</b>',
'expected': '<b>foo<i>[barbaz]</i></b>' }
]
},
{ 'desc': '[HTML5] underline',
'command': 'underline',
'tests': [
{ 'id': 'U_U-1_SO',
'desc': 'Underline partially underlined text',
'pad': 'foo[bar<u>baz</u>qoz]quz',
'expected': 'foo<u>[barbazqoz]</u>quz' },
{ 'id': 'U_U-1_SL',
'desc': 'Underline partially underlined text',
'pad': 'foo[bar<u>baz]qoz</u>quz',
'expected': 'foo<u>[barbaz]qoz</u>quz' },
{ 'id': 'U_S-U-1_SO',
'desc': 'Underline partially underlined text in striked context',
'pad': '<s>foo[bar<u>baz</u>}</s>',
'expected': '<s>foo<u>[barbaz]</u></s>' }
]
},
{ 'desc': '[MIDAS] backcolor',
'command': 'backcolor',
'tests': [
{ 'id': 'BC:842_FONTs:bc:fca-1_SW',
'rte1-id': 'c-backcolor-0',
'desc': 'Change background color to new color',
'value': '#884422',
'pad': '<font style="background-color: #ffccaa">[foobarbaz]</font>',
'expected': [ '<font style="background-color: #884422">[foobarbaz]</font>',
'<span style="background-color: #884422">[foobarbaz]</span>' ] },
{ 'id': 'BC:00f_SPANs:bc:f00-1_SW',
'rte1-id': 'c-backcolor-2',
'desc': 'Change background color to new color',
'value': '#0000ff',
'pad': '<span style="background-color: #ff0000">[foobarbaz]</span>',
'expected': [ '<font style="background-color: #0000ff">[foobarbaz]</font>',
'<span style="background-color: #0000ff">[foobarbaz]</span>' ] },
{ 'id': 'BC:ace_FONT.ass.s:bc:rgb-1_SW',
'rte1-id': 'c-backcolor-1',
'desc': 'Change background color in styled span to new color',
'value': '#aaccee',
'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0)">[foobarbaz]</span>',
'expected': [ '<font style="background-color: #aaccee">[foobarbaz]</font>',
'<span style="background-color: #aaccee">[foobarbaz]</span>' ] }
]
},
{ 'desc': '[MIDAS] forecolor',
'command': 'forecolor',
'tests': [
{ 'id': 'FC:g_FONTc:b-1_SW',
'rte1-id': 'c-forecolor-0',
'desc': 'Change the text color (without CSS)',
'value': 'green',
'pad': '<font color="blue">[foobarbaz]</font>',
'expected': '<font color="green">[foobarbaz]</font>' },
{ 'id': 'FC:g_SPANs:c:g-1_SW',
'rte1-id': 'c-forecolor-1',
'desc': 'Change the text color from a styled span (without CSS)',
'value': 'green',
'pad': '<span style="color: blue">[foobarbaz]</span>',
'expected': '<font color="green">[foobarbaz]</font>' },
{ 'id': 'FC:g_FONTc:b.s:c:r-1_SW',
'rte1-id': 'c-forecolor-2',
'desc': 'Change the text color from conflicting color and style (without CSS)',
'value': 'green',
'pad': '<font color="blue" style="color: red">[foobarbaz]</font>',
'expected': '<font color="green">[foobarbaz]</font>' },
{ 'id': 'FC:g_FONTc:b.sz:6-1_SI',
'desc': 'Change the font color in content with a different font size and font color',
'value': 'green',
'pad': '<font color="blue" size="6">foo[bar]baz</font>',
'expected': [ '<font color="blue" size="6">foo<font color="green">[bar]</font>baz</font>',
'<font size="6"><font color="blue">foo<font color="green">[bar]</font><font color="blue">baz</font></font>' ] }
]
},
{ 'desc': '[MIDAS] hilitecolor',
'command': 'hilitecolor',
'tests': [
{ 'id': 'HC:g_FONTs:c:b-1_SW',
'rte1-id': 'c-hilitecolor-0',
'desc': 'Change the hilite color (without CSS)',
'value': 'green',
'pad': '<font style="background-color: blue">[foobarbaz]</font>',
'expected': [ '<font style="background-color: green">[foobarbaz]</font>',
'<span style="background-color: green">[foobarbaz]</span>' ] },
{ 'id': 'HC:g_SPANs:c:g-1_SW',
'rte1-id': 'c-hilitecolor-2',
'desc': 'Change the hilite color from a styled span (without CSS)',
'value': 'green',
'pad': '<span style="background-color: blue">[foobarbaz]</span>',
'expected': '<span style="background-color: green">[foobarbaz]</span>' },
{ 'id': 'HC:g_SPAN.ass.s:c:rgb-1_SW',
'rte1-id': 'c-hilitecolor-1',
'desc': 'Change the hilite color from a styled span (without CSS)',
'value': 'green',
'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0);">[foobarbaz]</span>',
'expected': '<span style="background-color: green">[foobarbaz]</span>' }
]
},
{ 'desc': '[MIDAS] fontname',
'command': 'fontname',
'tests': [
{ 'id': 'FN:c_FONTf:a-1_SW',
'rte1-id': 'c-fontname-0',
'desc': 'Change existing font name to new font name (without CSS)',
'value': 'courier',
'pad': '<font face="arial">[foobarbaz]</font>',
'expected': '<font face="courier">[foobarbaz]</font>' },
{ 'id': 'FN:c_SPANs:ff:a-1_SW',
'rte1-id': 'c-fontname-1',
'desc': 'Change existing font name from style to new font name (without CSS)',
'value': 'courier',
'pad': '<span style="font-family: arial">[foobarbaz]</span>',
'expected': '<font face="courier">[foobarbaz]</font>' },
{ 'id': 'FN:c_FONTf:a.s:ff:v-1_SW',
'rte1-id': 'c-fontname-2',
'desc': 'Change existing font name with conflicting face and style to new font name (without CSS)',
'value': 'courier',
'pad': '<font face="arial" style="font-family: verdana">[foobarbaz]</font>',
'expected': '<font face="courier">[foobarbaz]</font>' },
{ 'id': 'FN:c_FONTf:a-1_SI',
'desc': 'Change existing font name to new font name, text partially selected',
'value': 'courier',
'pad': '<font face="arial">foo[bar]baz</font>',
'expected': '<font face="arial">foo</font><font face="courier">[bar]</font><font face="arial">baz</font>',
'accept': '<font face="arial">foo<font face="courier">[bar]</font>baz</font>' },
{ 'id': 'FN:c_FONTf:a-2_SL',
'desc': 'Change existing font name to new font name, using CSS styling',
'value': 'courier',
'pad': 'foo[bar<font face="arial">baz]qoz</font>',
'expected': 'foo<font face="courier">[barbaz]</font><font face="arial">qoz</font>' },
{ 'id': 'FN:c_FONTf:v-FONTf:a-1_SW',
'rte1-id': 'c-fontname-3',
'desc': 'Change existing font name in nested <font> tags to new font name (without CSS)',
'value': 'courier',
'pad': '<font face="verdana"><font face="arial">[foobarbaz]</font></font>',
'expected': '<font face="courier">[foobarbaz]</font>',
'accept': '<font face="verdana"><font face="courier">[foobarbaz]</font></font>' },
{ 'id': 'FN:c_SPANs:ff:v-FONTf:a-1_SW',
'rte1-id': 'c-fontname-4',
'desc': 'Change existing font name in nested mixed tags to new font name (without CSS)',
'value': 'courier',
'pad': '<span style="font-family: verdana"><font face="arial">[foobarbaz]</font></span>',
'expected': '<font face="courier">[foobarbaz]</font>',
'accept': '<span style="font-family: verdana"><font face="courier">[foobarbaz]</font></span>' }
]
},
{ 'desc': '[MIDAS] fontsize',
'command': 'fontsize',
'tests': [
{ 'id': 'FS:1_FONTsz:4-1_SW',
'rte1-id': 'c-fontsize-0',
'desc': 'Change existing font size to new size (without CSS)',
'value': '1',
'pad': '<font size="4">[foobarbaz]</font>',
'expected': '<font size="1">[foobarbaz]</font>' },
{ 'id': 'FS:1_SPAN.ass.s:fs:large-1_SW',
'rte1-id': 'c-fontsize-1',
'desc': 'Change existing font size from styled span to new size (without CSS)',
'value': '1',
'pad': '<span class="Apple-style-span" style="font-size: large">[foobarbaz]</span>',
'expected': '<font size="1">[foobarbaz]</font>' },
{ 'id': 'FS:5_FONTsz:1.s:fs:xs-1_SW',
'rte1-id': 'c-fontsize-2',
'desc': 'Change existing font size from tag with conflicting size and style to new size (without CSS)',
'value': '5',
'pad': '<font size="1" style="font-size:x-small">[foobarbaz]</font>',
'expected': '<font size="5">[foobarbaz]</font>' },
{ 'id': 'FS:2_FONTc:b.sz:6-1_SI',
'desc': 'Change the font size in content with a different font size and font color',
'value': '2',
'pad': '<font color="blue" size="6">foo[bar]baz</font>',
'expected': [ '<font color="blue" size="6">foo<font size="2">[bar]</font>baz</font>',
'<font color="blue"><font size="6">foo</font><font size="2">[bar]</font><font size="6">baz</font></font>' ] },
{ 'id': 'FS:larger_FONTsz:4',
'desc': 'Change selection to use next larger font',
'value': 'larger',
'pad': '<font size="4">foo[bar]baz</font>',
'expected': '<font size="4">foo<font size="larger">[bar]</font>baz</font>',
'accept': '<font size="4">foo</font><font size="5">[bar]</font><font size="4">baz</font>' },
{ 'id': 'FS:smaller_FONTsz:4',
'desc': 'Change selection to use next smaller font',
'value': 'smaller',
'pad': '<font size="4">foo[bar]baz</font>',
'expected': '<font size="4">foo<font size="smaller">[bar]</font>baz</font>',
'accept': '<font size="4">foo</font><font size="3">[bar]</font><font size="4">baz</font>' }
]
},
{ 'desc': '[MIDAS] formatblock',
'command': 'formatblock',
'tests': [
{ 'id': 'FB:h1_ADDRESS-1_SW',
'desc': 'change block from <address> to <h1>',
'value': 'h1',
'pad': '<address>foo [bar] baz</address>',
'expected': '<h1>foo [bar] baz</h1>' },
{ 'id': 'FB:h1_ADDRESS-FONTsz:4-1_SO',
'desc': 'change block from <address> with partially formatted content to <h1>',
'value': 'h1',
'pad': '<address>foo [<font size="4">bar</font>] baz</address>',
'expected': '<h1>foo [bar] baz</h1>' },
{ 'id': 'FB:h1_ADDRESS-FONTsz:4-1_SW',
'desc': 'change block from <address> with partially formatted content to <h1>',
'value': 'h1',
'pad': '<address>foo <font size="4">[bar]</font> baz</address>',
'expected': '<h1>foo [bar] baz</h1>' },
{ 'id': 'FB:h1_ADDRESS-FONT.ass.sz:4-1_SW',
'desc': 'change block from <address> with partially formatted content to <h1>',
'value': 'h1',
'pad': '<address>foo <font class="Apple-style-span" size="4">[bar]</font> baz</address>',
'expected': '<h1>foo [bar] baz</h1>' }
]
}
]
}

View file

@ -0,0 +1,210 @@
CHANGE_TESTS_CSS = {
'id': 'CC',
'caption': 'Change Existing Format to Different Format Tests, using styleWithCSS',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': True,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': '[HTML5] italic',
'command': 'italic',
'tests': [
{ 'id': 'I_I-1_SL',
'desc': 'Italicize partially italicized text',
'pad': 'foo[bar<i>baz]</i>qoz',
'expected': 'foo<span style="font-style: italic">[barbaz]</span>qoz' },
{ 'id': 'I_B-1_SL',
'desc': 'Italicize partially bolded text',
'pad': 'foo[bar<b>baz]</b>qoz',
'expected': 'foo<span style="font-style: italic">[bar<b>baz]</b></span>qoz',
'accept': 'foo<span style="font-style: italic">[bar<b>baz</b>}</span>qoz' },
{ 'id': 'I_B-1_SW',
'desc': 'Italicize bold text, ideally combining both',
'pad': 'foobar<b>[baz]</b>qoz',
'expected': 'foobar<span style="font-style: italic; font-weight: bold">[baz]</span>qoz',
'accept': 'foobar<b><span style="font-style: italic">[baz]</span></b>qoz' }
]
},
{ 'desc': '[MIDAS] backcolor',
'command': 'backcolor',
'tests': [
{ 'id': 'BC:gray_SPANs:bc:b-1_SW',
'desc': 'Change background color from blue to gray',
'value': 'gray',
'pad': '<span style="background-color: blue">[foobarbaz]</span>',
'expected': '<span style="background-color: gray">[foobarbaz]</span>' },
{ 'id': 'BC:gray_SPANs:bc:b-1_SO',
'desc': 'Change background color from blue to gray',
'value': 'gray',
'pad': '{<span style="background-color: blue">foobarbaz</span>}',
'expected': [ '{<span style="background-color: gray">foobarbaz</span>}',
'<span style="background-color: gray">[foobarbaz]</span>' ] },
{ 'id': 'BC:gray_SPANs:bc:b-1_SI',
'desc': 'Change background color from blue to gray',
'value': 'gray',
'pad': '<span style="background-color: blue">foo[bar]baz</span>',
'expected': '<span style="background-color: blue">foo</span><span style="background-color: gray">[bar]</span><span style="background-color: blue">baz</span>',
'accept': '<span style="background-color: blue">foo<span style="background-color: gray">[bar]</span>baz</span>' },
{ 'id': 'BC:gray_P-SPANs:bc:b-1_SW',
'desc': 'Change background color within a paragraph from blue to gray',
'value': 'gray',
'pad': '<p><span style="background-color: blue">[foobarbaz]</span></p>',
'expected': [ '<p><span style="background-color: gray">[foobarbaz]</span></p>',
'<p style="background-color: gray">[foobarbaz]</p>' ] },
{ 'id': 'BC:gray_P-SPANs:bc:b-2_SW',
'desc': 'Change background color within a paragraph from blue to gray',
'value': 'gray',
'pad': '<p>foo<span style="background-color: blue">[bar]</span>baz</p>',
'expected': '<p>foo<span style="background-color: gray">[bar]</span>baz</p>' },
{ 'id': 'BC:gray_P-SPANs:bc:b-3_SO',
'desc': 'Change background color within a paragraph from blue to gray (selection encloses more than previous span)',
'value': 'gray',
'pad': '<p>[foo<span style="background-color: blue">barbaz</span>qoz]quz</p>',
'expected': '<p><span style="background-color: gray">[foobarbazqoz]</span>quz</p>' },
{ 'id': 'BC:gray_P-SPANs:bc:b-3_SL',
'desc': 'Change background color within a paragraph from blue to gray (previous span partially selected)',
'value': 'gray',
'pad': '<p>[foo<span style="background-color: blue">bar]baz</span>qozquz</p>',
'expected': '<p><span style="background-color: gray">[foobar]</span><span style="background-color: blue">baz</span>qozquz</p>' },
{ 'id': 'BC:gray_SPANs:bc:b-2_SL',
'desc': 'Change background color from blue to gray on partially covered span, selection extends left',
'value': 'gray',
'pad': 'foo [bar <span style="background-color: blue">baz] qoz</span> quz sic',
'expected': 'foo <span style="background-color: gray">[bar baz]</span><span style="background-color: blue"> qoz</span> quz sic' },
{ 'id': 'BC:gray_SPANs:bc:b-2_SR',
'desc': 'Change background color from blue to gray on partially covered span, selection extends right',
'value': 'gray',
'pad': 'foo bar <span style="background-color: blue">baz [qoz</span> quz] sic',
'expected': 'foo bar <span style="background-color: blue">baz </span><span style="background-color: gray">[qoz quz]</span> sic' }
]
},
{ 'desc': '[MIDAS] fontname',
'command': 'fontname',
'tests': [
{ 'id': 'FN:c_SPANs:ff:a-1_SW',
'desc': 'Change existing font name to new font name, using CSS styling',
'value': 'courier',
'pad': '<span style="font-family: arial">[foobarbaz]</span>',
'expected': '<span style="font-family: courier">[foobarbaz]</span>' },
{ 'id': 'FN:c_FONTf:a-1_SW',
'desc': 'Change existing font name to new font name, using CSS styling',
'value': 'courier',
'pad': '<font face="arial">[foobarbaz]</font>',
'expected': [ '<font style="font-family: courier">[foobarbaz]</font>',
'<span style="font-family: courier">[foobarbaz]</span>' ] },
{ 'id': 'FN:c_FONTf:a-1_SI',
'desc': 'Change existing font name to new font name, using CSS styling',
'value': 'courier',
'pad': '<font face="arial">foo[bar]baz</font>',
'expected': '<font face="arial">foo</font><span style="font-family: courier">[bar]</span><font face="arial">baz</font>' },
{ 'id': 'FN:a_FONTf:a-1_SI',
'desc': 'Change existing font name to same font name, using CSS styling (should be noop)',
'value': 'arial',
'pad': '<font face="arial">foo[bar]baz</font>',
'expected': '<font face="arial">foo[bar]baz</font>' },
{ 'id': 'FN:a_FONTf:a-1_SW',
'desc': 'Change existing font name to same font name, using CSS styling (should be noop or perhaps change tag)',
'value': 'arial',
'pad': '<font face="arial">[foobarbaz]</font>',
'expected': [ '<font face="arial">[foobarbaz]</font>',
'<span style="font-family: arial">[foobarbaz]</span>' ] },
{ 'id': 'FN:a_FONTf:a-1_SO',
'desc': 'Change existing font name to same font name, using CSS styling (should be noop or perhaps change tag)',
'value': 'arial',
'pad': '{<font face="arial">foobarbaz</font>}',
'expected': [ '{<font face="arial">foobarbaz</font>}',
'<font face="arial">[foobarbaz]</font>',
'{<span style="font-family: arial">foobarbaz</span>}',
'<span style="font-family: arial">[foobarbaz]</span>' ] },
{ 'id': 'FN:a_SPANs:ff:a-1_SI',
'desc': 'Change existing font name to same font name, using CSS styling (should be noop)',
'value': 'arial',
'pad': '<span style="font-family: arial">[foobarbaz]</span>',
'expected': '<span style="font-family: arial">[foobarbaz]</span>' },
{ 'id': 'FN:c_FONTf:a-2_SL',
'desc': 'Change existing font name to new font name, using CSS styling',
'value': 'courier',
'pad': 'foo[bar<font face="arial">baz]qoz</font>',
'expected': 'foo<span style="font-family: courier">[barbaz]</span><font face="arial">qoz</font>' }
]
},
{ 'desc': '[MIDAS] fontsize',
'command': 'fontsize',
'tests': [
{ 'id': 'FS:1_SPANs:fs:l-1_SW',
'desc': 'Change existing font size to new size, using CSS styling',
'value': '1',
'pad': '<span style="font-size: large">[foobarbaz]</span>',
'expected': '<span style="font-size: x-small">[foobarbaz]</span>' },
{ 'id': 'FS:large_SPANs:fs:l-1_SW',
'desc': 'Change existing font size to same size (should be noop)',
'value': 'large',
'pad': '<span style="font-size: large">[foobarbaz]</span>',
'expected': '<span style="font-size: large">[foobarbaz]</span>' },
{ 'id': 'FS:18px_SPANs:fs:l-1_SW',
'desc': 'Change existing font size to equivalent px size (should be noop, or change unit)',
'value': '18px',
'pad': '<span style="font-size: large">[foobarbaz]</span>',
'expected': [ '<span style="font-size: 18px">[foobarbaz]</span>',
'<span style="font-size: large">[foobarbaz]</span>' ] },
{ 'id': 'FS:4_SPANs:fs:l-1_SW',
'desc': 'Change existing font size to equivalent numeric size (should be noop)',
'value': '4',
'pad': '<span style="font-size: large">[foobarbaz]</span>',
'expected': '<span style="font-size: large">[foobarbaz]</span>' },
{ 'id': 'FS:4_SPANs:fs:18px-1_SW',
'desc': 'Change existing font size to equivalent numeric size (should be noop)',
'value': '4',
'pad': '<span style="font-size: 18px">[foobarbaz]</span>',
'expected': '<span style="font-size: 18px">[foobarbaz]</span>' },
{ 'id': 'FS:larger_SPANs:fs:l-1_SI',
'desc': 'Change selection to use next larger font',
'value': 'larger',
'pad': '<span style="font-size: large">foo[bar]baz</span>',
'expected': [ '<span style="font-size: large">foo<span style="font-size: x-large">[bar]</span>baz</span>',
'<span style="font-size: large">foo</span><span style="font-size: x-large">[bar]</span><span style="font-size: large">baz</span>' ],
'accept': '<span style="font-size: large">foo<font size="larger">[bar]</font>baz</span>' },
{ 'id': 'FS:smaller_SPANs:fs:l-1_SI',
'desc': 'Change selection to use next smaller font',
'value': 'smaller',
'pad': '<span style="font-size: large">foo[bar]baz</span>',
'expected': [ '<span style="font-size: large">foo<span style="font-size: medium">[bar]</span>baz</span>',
'<span style="font-size: large">foo</span><span style="font-size: medium">[bar]</span><span style="font-size: large">baz</span>' ],
'accept': '<span style="font-size: large">foo<font size="smaller">[bar]</font>baz</span>' }
]
}
]
}

View file

@ -0,0 +1,330 @@
DELETE_TESTS = {
'id': 'D',
'caption': 'Delete Tests',
'command': 'delete',
'checkAttrs': True,
'checkStyle': False,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': 'delete single characters',
'tests': [
{ 'id': 'CHAR-1_SC',
'desc': 'Delete 1 character',
'pad': 'foo^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-2_SC',
'desc': 'Delete 1 pre-composed character o with diaeresis',
'pad': 'fo&#xF6;^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-3_SC',
'desc': 'Delete 1 character with combining diaeresis above',
'pad': 'foo&#x0308;^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-4_SC',
'desc': 'Delete 1 character with combining diaeresis below',
'pad': 'foo&#x0324;^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-5_SC',
'desc': 'Delete 1 character with combining diaeresis above and below',
'pad': 'foo&#x0308;&#x0324;^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-5_SI-1',
'desc': 'Delete 1 character with combining diaeresis above and below, selection on diaeresis above',
'pad': 'foo[&#x0308;]&#x0324;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-5_SI-2',
'desc': 'Delete 1 character with combining diaeresis above and below, selection on diaeresis below',
'pad': 'foo&#x0308;[&#x0324;]barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-5_SR',
'desc': 'Delete 1 character with combining diaeresis above and below, selection oblique on diaeresis and following text',
'pad': 'foo&#x0308;[&#x0324;bar]baz',
'expected': 'fo^baz' },
{ 'id': 'CHAR-6_SC',
'desc': 'Delete 1 character with enclosing square',
'pad': 'foo&#x20DE;^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-7_SC',
'desc': 'Delete 1 character with combining long solidus overlay',
'pad': 'foo&#x0338;^barbaz',
'expected': 'fo^barbaz' }
]
},
{ 'desc': 'delete text selection',
'tests': [
{ 'id': 'TEXT-1_SI',
'desc': 'Delete text selection',
'pad': 'foo[bar]baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SS',
'desc': 'Delete at start of span',
'pad': 'foo<b>^bar</b>baz',
'expected': 'fo^<b>bar</b>baz' },
{ 'id': 'B-1_SA',
'desc': 'Delete from position after span',
'pad': 'foo<b>bar</b>^baz',
'expected': 'foo<b>ba^</b>baz' },
{ 'id': 'B-1_SW',
'desc': 'Delete selection that wraps the whole span content',
'pad': 'foo<b>[bar]</b>baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SO',
'desc': 'Delete selection that wraps the whole span',
'pad': 'foo[<b>bar</b>]baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SL',
'desc': 'Delete oblique selection that starts before span',
'pad': 'foo[bar<b>baz]quoz</b>quuz',
'expected': 'foo^<b>quoz</b>quuz' },
{ 'id': 'B-1_SR',
'desc': 'Delete oblique selection that ends after span',
'pad': 'foo<b>bar[baz</b>quoz]quuz',
'expected': 'foo<b>bar^</b>quuz' },
{ 'id': 'B.I-1_SM',
'desc': 'Delete oblique selection that starts and ends in different spans',
'pad': 'foo<b>bar[baz</b><i>qoz]quuz</i>quuuz',
'expected': 'foo<b>bar^</b><i>quuz</i>quuuz' },
{ 'id': 'GEN-1_SS',
'desc': 'Delete at start of span with generated content',
'pad': 'foo<gen>^bar</gen>baz',
'expected': 'fo^<gen>bar</gen>baz' },
{ 'id': 'GEN-1_SA',
'desc': 'Delete from position after span with generated content',
'pad': 'foo<gen>bar</gen>^baz',
'expected': 'foo<gen>ba^</gen>baz' }
]
},
{ 'desc': 'delete paragraphs',
'tests': [
{ 'id': 'P2-1_SS2',
'desc': 'Delete from collapsed selection at start of paragraph - should merge with previous',
'pad': '<p>foobar</p><p>^bazqoz</p>',
'expected': '<p>foobar^bazqoz</p>' },
{ 'id': 'P2-1_SI2',
'desc': 'Delete non-collapsed selection at start of paragraph - should not merge with previous',
'pad': '<p>foobar</p><p>[baz]qoz</p>',
'expected': '<p>foobar</p><p>^qoz</p>' },
{ 'id': 'P2-1_SM',
'desc': 'Delete non-collapsed selection spanning 2 paragraphs - should merge them',
'pad': '<p>foo[bar</p><p>baz]qoz</p>',
'expected': '<p>foo^qoz</p>' }
]
},
{ 'desc': 'delete lists and list items',
'tests': [
{ 'id': 'OL-LI2-1_SO1',
'desc': 'Delete fully wrapped list item',
'pad': 'foo<ol>{<li>bar</li>}<li>baz</li></ol>qoz',
'expected': ['foo<ol>|<li>baz</li></ol>qoz',
'foo<ol><li>^baz</li></ol>qoz'] },
{ 'id': 'OL-LI2-1_SM',
'desc': 'Delete oblique range between list items within same list',
'pad': 'foo<ol><li>ba[r</li><li>b]az</li></ol>qoz',
'expected': 'foo<ol><li>ba^az</li></ol>qoz' },
{ 'id': 'OL-LI-1_SW',
'desc': 'Delete contents of last list item (list should remain)',
'pad': 'foo<ol><li>[foo]</li></ol>qoz',
'expected': ['foo<ol><li>|</li></ol>qoz',
'foo<ol><li>^</li></ol>qoz'] },
{ 'id': 'OL-LI-1_SO',
'desc': 'Delete last list item of list (should remove entire list)',
'pad': 'foo<ol>{<li>foo</li>}</ol>qoz',
'expected': 'foo^qoz' }
]
},
{ 'desc': 'delete with strange selections',
'tests': [
{ 'id': 'HR.BR-1_SM',
'desc': 'Delete selection that starts and ends within nodes that don\'t have children',
'pad': 'foo<hr {>bar<br }>baz',
'expected': 'foo<hr>|<br>baz' }
]
},
{ 'desc': 'delete after table',
'tests': [
{ 'id': 'TABLE-1_SA',
'desc': 'Delete from position immediately after table (should have no effect)',
'pad': 'foo<table><tbody><tr><td>bar</td></tr></tbody></table>^baz',
'expected': 'foo<table><tbody><tr><td>bar</td></tr></tbody></table>^baz' }
]
},
{ 'desc': 'delete within table cells',
'tests': [
{ 'id': 'TD-1_SS',
'desc': 'Delete from start of first cell (should have no effect)',
'pad': 'foo<table><tbody><tr><td>^bar</td></tr></tbody></table>baz',
'expected': 'foo<table><tbody><tr><td>^bar</td></tr></tbody></table>baz' },
{ 'id': 'TD2-1_SS2',
'desc': 'Delete from start of inner cell (should have no effect)',
'pad': 'foo<table><tbody><tr><td>bar</td><td>^baz</td></tr></tbody></table>quoz',
'expected': 'foo<table><tbody><tr><td>bar</td><td>^baz</td></tr></tbody></table>quoz' },
{ 'id': 'TD2-1_SM',
'desc': 'Delete with selection spanning 2 cells',
'pad': 'foo<table><tbody><tr><td>ba[r</td><td>b]az</td></tr></tbody></table>quoz',
'expected': 'foo<table><tbody><tr><td>ba^</td><td>az</td></tr></tbody></table>quoz' }
]
},
{ 'desc': 'delete table rows',
'tests': [
{ 'id': 'TR3-1_SO1',
'desc': 'Delete first table row',
'pad': '<table><tbody>{<tr><td>A</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>^B</td></tr><tr><td>C</td></tr></tbody></table>'] },
{ 'id': 'TR3-1_SO2',
'desc': 'Delete middle table row',
'pad': '<table><tbody><tr><td>A</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody><tr><td>A</td></tr>|<tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>A</td></tr><tr><td>^C</td></tr></tbody></table>'] },
{ 'id': 'TR3-1_SO3',
'desc': 'Delete last table row',
'pad': '<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td></tr><tr><td>B^</td></tr></tbody></table>'] },
{ 'id': 'TR2rs:2-1_SO1',
'desc': 'Delete first table row where a cell has rowspan 2',
'pad': '<table><tbody>{<tr><td>A</td><td rowspan=2>R</td></tr>}<tr><td>B</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>B</td><td>R</td></tr></tbody></table>',
'<table><tbody><tr><td>^B</td><td>R</td></tr></tbody></table>'] },
{ 'id': 'TR2rs:2-1_SO2',
'desc': 'Delete second table row where a cell has rowspan 2',
'pad': '<table><tbody><tr><td>A</td><td rowspan=2>R</td></tr>{<tr><td>B</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td><td>R</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td><td>R^</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO1',
'desc': 'Delete first table row where a cell has rowspan 3',
'pad': '<table><tbody>{<tr><td>A</td><td rowspan=3>R</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>^A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO2',
'desc': 'Delete middle table row where a cell has rowspan 3',
'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr>|<tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr><tr><td>^C</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO3',
'desc': 'Delete last table row where a cell has rowspan 3',
'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B^</td></tr></tbody></table>'] }
]
},
{ 'desc': 'delete with non-editable nested content',
'tests': [
{ 'id': 'DIV:ce:false-1_SO',
'desc': 'Delete nested non-editable <div>',
'pad': 'foo[bar<div contenteditable="false">NESTED</div>baz]qoz',
'expected': 'foo^qoz' },
{ 'id': 'DIV:ce:false-1_SB',
'desc': 'Delete from immediately after a nested non-editable <div> (should be no-op)',
'pad': 'foobar<div contenteditable="false">NESTED</div>^bazqoz',
'expected': 'foobar<div contenteditable="false">NESTED</div>^bazqoz' },
{ 'id': 'DIV:ce:false-1_SL',
'desc': 'Delete nested non-editable <div> with oblique selection',
'pad': 'foo[bar<div contenteditable="false">NES]TED</div>bazqoz',
'expected': [ 'foo^<div contenteditable="false">NESTED</div>bazqoz',
'foo<div contenteditable="false">[NES]TED</div>bazqoz' ] },
{ 'id': 'DIV:ce:false-1_SR',
'desc': 'Delete nested non-editable <div> with oblique selection',
'pad': 'foobar<div contenteditable="false">NES[TED</div>baz]qoz',
'expected': [ 'foobar<div contenteditable="false">NESTED</div>^qoz',
'foobar<div contenteditable="false">NES[TED]</div>qoz' ] },
{ 'id': 'DIV:ce:false-1_SI',
'desc': 'Delete inside nested non-editable <div> (should be no-op)',
'pad': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz',
'expected': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz' }
]
},
{ 'desc': 'Delete with display:inline-block',
'checkStyle': True,
'tests': [
{ 'id': 'SPAN:d:ib-1_SC',
'desc': 'Delete inside an inline-block <span>',
'pad': 'foo<span style="display: inline-block">bar^baz</span>qoz',
'expected': 'foo<span style="display: inline-block">ba^baz</span>qoz' },
{ 'id': 'SPAN:d:ib-1_SA',
'desc': 'Delete from immediately after an inline-block <span>',
'pad': 'foo<span style="display: inline-block">barbaz</span>^qoz',
'expected': 'foo<span style="display: inline-block">barba^</span>qoz' },
{ 'id': 'SPAN:d:ib-2_SL',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo[DEL<span style="display: inline-block">ETE]bar</span>baz',
'expected': 'foo^<span style="display: inline-block">bar</span>baz' },
{ 'id': 'SPAN:d:ib-3_SR',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">bar[DEL</span>ETE]baz',
'expected': 'foo<span style="display: inline-block">bar^</span>baz' },
{ 'id': 'SPAN:d:ib-4i_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">bar[DELETE]baz</span>qoz',
'expected': 'foo<span style="display: inline-block">bar^baz</span>qoz' },
{ 'id': 'SPAN:d:ib-4l_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">[DELETE]barbaz</span>qoz',
'expected': 'foo<span style="display: inline-block">^barbaz</span>qoz' },
{ 'id': 'SPAN:d:ib-4r_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">barbaz[DELETE]</span>qoz',
'expected': 'foo<span style="display: inline-block">barbaz^</span>qoz' }
]
}
]
}

View file

@ -0,0 +1,315 @@
FORWARDDELETE_TESTS = {
'id': 'FD',
'caption': 'Forward-Delete Tests',
'command': 'forwardDelete',
'checkAttrs': True,
'checkStyle': False,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': 'forward-delete single characters',
'tests': [
{ 'id': 'CHAR-1_SC',
'desc': 'Delete 1 character',
'pad': 'foo^barbaz',
'expected': 'foo^arbaz' },
{ 'id': 'CHAR-2_SC',
'desc': 'Delete 1 pre-composed character o with diaeresis',
'pad': 'fo^&#xF6;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-3_SC',
'desc': 'Delete 1 character with combining diaeresis above',
'pad': 'fo^o&#x0308;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-4_SC',
'desc': 'Delete 1 character with combining diaeresis below',
'pad': 'fo^o&#x0324;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-5_SC',
'desc': 'Delete 1 character with combining diaeresis above and below',
'pad': 'fo^o&#x0308;&#x0324;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-6_SC',
'desc': 'Delete 1 character with enclosing square',
'pad': 'fo^o&#x20DE;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-7_SC',
'desc': 'Delete 1 character with combining long solidus overlay',
'pad': 'fo^o&#x0338;barbaz',
'expected': 'fo^barbaz' }
]
},
{ 'desc': 'forward-delete text selections',
'tests': [
{ 'id': 'TEXT-1_SI',
'desc': 'Delete text selection',
'pad': 'foo[bar]baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SE',
'desc': 'Forward-delete at end of span',
'pad': 'foo<b>bar^</b>baz',
'expected': 'foo<b>bar^</b>az' },
{ 'id': 'B-1_SB',
'desc': 'Forward-delete from position before span',
'pad': 'foo^<b>bar</b>baz',
'expected': 'foo^<b>ar</b>baz' },
{ 'id': 'B-1_SW',
'desc': 'Delete selection that wraps the whole span content',
'pad': 'foo<b>[bar]</b>baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SO',
'desc': 'Delete selection that wraps the whole span',
'pad': 'foo[<b>bar</b>]baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SL',
'desc': 'Delete oblique selection that starts before span',
'pad': 'foo[bar<b>baz]quoz</b>quuz',
'expected': 'foo^<b>quoz</b>quuz' },
{ 'id': 'B-1_SR',
'desc': 'Delete oblique selection that ends after span',
'pad': 'foo<b>bar[baz</b>quoz]quuz',
'expected': 'foo<b>bar^</b>quuz' },
{ 'id': 'B.I-1_SM',
'desc': 'Delete oblique selection that starts and ends in different spans',
'pad': 'foo<b>bar[baz</b><i>qoz]quuz</i>quuuz',
'expected': 'foo<b>bar^</b><i>quuz</i>quuuz' },
{ 'id': 'GEN-1_SE',
'desc': 'Delete at end of span with generated content',
'pad': 'foo<gen>bar^</gen>baz',
'expected': 'foo<gen>bar^</gen>az' },
{ 'id': 'GEN-1_SB',
'desc': 'Delete from position before span with generated content',
'pad': 'foo^<gen>bar</gen>baz',
'expected': 'foo^<gen>ar</gen>baz' }
]
},
{ 'desc': 'forward-delete paragraphs',
'tests': [
{ 'id': 'P2-1_SE1',
'desc': 'Delete from collapsed selection at end of paragraph - should merge with next',
'pad': '<p>foobar^</p><p>bazqoz</p>',
'expected': '<p>foobar^bazqoz</p>' },
{ 'id': 'P2-1_SI1',
'desc': 'Delete non-collapsed selection at end of paragraph - should not merge with next',
'pad': '<p>foo[bar]</p><p>bazqoz</p>',
'expected': '<p>foo^</p><p>bazqoz</p>' },
{ 'id': 'P2-1_SM',
'desc': 'Delete non-collapsed selection spanning 2 paragraphs - should merge them',
'pad': '<p>foo[bar</p><p>baz]qoz</p>',
'expected': '<p>foo^qoz</p>' }
]
},
{ 'desc': 'forward-delete lists and list items',
'tests': [
{ 'id': 'OL-LI2-1_SO1',
'desc': 'Delete fully wrapped list item',
'pad': 'foo<ol>{<li>bar</li>}<li>baz</li></ol>qoz',
'expected': ['foo<ol>|<li>baz</li></ol>qoz',
'foo<ol><li>^baz</li></ol>qoz'] },
{ 'id': 'OL-LI2-1_SM',
'desc': 'Delete oblique range between list items within same list',
'pad': 'foo<ol><li>ba[r</li><li>b]az</li></ol>qoz',
'expected': 'foo<ol><li>ba^az</li></ol>qoz' },
{ 'id': 'OL-LI-1_SW',
'desc': 'Delete contents of last list item (list should remain)',
'pad': 'foo<ol><li>[foo]</li></ol>qoz',
'expected': ['foo<ol><li>|</li></ol>qoz',
'foo<ol><li>^</li></ol>qoz'] },
{ 'id': 'OL-LI-1_SO',
'desc': 'Delete last list item of list (should remove entire list)',
'pad': 'foo<ol>{<li>foo</li>}</ol>qoz',
'expected': 'foo^qoz' }
]
},
{ 'desc': 'forward-delete with strange selections',
'tests': [
{ 'id': 'HR.BR-1_SM',
'desc': 'Delete selection that starts and ends within nodes that don\'t have children',
'pad': 'foo<hr {>bar<br }>baz',
'expected': 'foo<hr>|<br>baz' }
]
},
{ 'desc': 'forward-delete from immediately before a table',
'tests': [
{ 'id': 'TABLE-1_SB',
'desc': 'Delete from position immediately before table (should have no effect)',
'pad': 'foo^<table><tbody><tr><td>bar</td></tr></tbody></table>baz',
'expected': 'foo^<table><tbody><tr><td>bar</td></tr></tbody></table>baz' }
]
},
{ 'desc': 'forward-delete within table cells',
'tests': [
{ 'id': 'TD-1_SE',
'desc': 'Delete from end of last cell (should have no effect)',
'pad': 'foo<table><tbody><tr><td>bar^</td></tr></tbody></table>baz',
'expected': 'foo<table><tbody><tr><td>bar^</td></tr></tbody></table>baz' },
{ 'id': 'TD2-1_SE1',
'desc': 'Delete from end of inner cell (should have no effect)',
'pad': 'foo<table><tbody><tr><td>bar^</td><td>baz</td></tr></tbody></table>quoz',
'expected': 'foo<table><tbody><tr><td>bar^</td><td>baz</td></tr></tbody></table>quoz' },
{ 'id': 'TD2-1_SM',
'desc': 'Delete with selection spanning 2 cells',
'pad': 'foo<table><tbody><tr><td>ba[r</td><td>b]az</td></tr></tbody></table>quoz',
'expected': 'foo<table><tbody><tr><td>ba^</td><td>az</td></tr></tbody></table>quoz' }
]
},
{ 'desc': 'forward-delete table rows',
'tests': [
{ 'id': 'TR3-1_SO1',
'desc': 'Delete first table row',
'pad': '<table><tbody>{<tr><td>A</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>^B</td></tr><tr><td>C</td></tr></tbody></table>'] },
{ 'id': 'TR3-1_SO2',
'desc': 'Delete middle table row',
'pad': '<table><tbody><tr><td>A</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody><tr><td>A</td></tr>|<tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>A</td></tr><tr><td>^C</td></tr></tbody></table>'] },
{ 'id': 'TR3-1_SO3',
'desc': 'Delete last table row',
'pad': '<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td></tr><tr><td>B^</td></tr></tbody></table>'] },
{ 'id': 'TR2rs:2-1_SO1',
'desc': 'Delete first table row where a cell has rowspan 2',
'pad': '<table><tbody>{<tr><td>A</td><td rowspan=2>R</td></tr>}<tr><td>B</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>B</td><td>R</td></tr></tbody></table>',
'<table><tbody><tr><td>^B</td><td>R</td></tr></tbody></table>'] },
{ 'id': 'TR2rs:2-1_SO2',
'desc': 'Delete second table row where a cell has rowspan 2',
'pad': '<table><tbody><tr><td>A</td><td rowspan=2>R</td></tr>{<tr><td>B</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td><td>R</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td><td>R^</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO1',
'desc': 'Delete first table row where a cell has rowspan 3',
'pad': '<table><tbody>{<tr><td>A</td><td rowspan=3>R</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>^A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO2',
'desc': 'Delete middle table row where a cell has rowspan 3',
'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr>|<tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr><tr><td>^C</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO3',
'desc': 'Delete last table row where a cell has rowspan 3',
'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B^</td></tr></tbody></table>'] }
]
},
{ 'desc': 'delete with non-editable nested content',
'tests': [
{ 'id': 'DIV:ce:false-1_SO',
'desc': 'Delete nested non-editable <div>',
'pad': 'foo[bar<div contenteditable="false">NESTED</div>baz]qoz',
'expected': 'foo^qoz' },
{ 'id': 'DIV:ce:false-1_SB',
'desc': 'Delete from immediately before a nested non-editable <div> (should be no-op)',
'pad': 'foobar^<div contenteditable="false">NESTED</div>bazqoz',
'expected': 'foobar^<div contenteditable="false">NESTED</div>bazqoz' },
{ 'id': 'DIV:ce:false-1_SL',
'desc': 'Delete nested non-editable <div> with oblique selection',
'pad': 'foo[bar<div contenteditable="false">NES]TED</div>bazqoz',
'expected': [ 'foo^<div contenteditable="false">NESTED</div>bazqoz',
'foo<div contenteditable="false">[NES]TED</div>bazqoz' ] },
{ 'id': 'DIV:ce:false-1_SR',
'desc': 'Delete nested non-editable <div> with oblique selection',
'pad': 'foobar<div contenteditable="false">NES[TED</div>baz]qoz',
'expected': [ 'foobar<div contenteditable="false">NESTED</div>^qoz',
'foobar<div contenteditable="false">NES[TED]</div>qoz' ] },
{ 'id': 'DIV:ce:false-1_SI',
'desc': 'Delete inside nested non-editable <div> (should be no-op)',
'pad': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz',
'expected': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz' }
]
},
{ 'desc': 'Delete with display:inline-block',
'checkStyle': True,
'tests': [
{ 'id': 'SPAN:d:ib-1_SC',
'desc': 'Delete inside an inline-block <span>',
'pad': 'foo<span style="display: inline-block">bar^baz</span>qoz',
'expected': 'foo<span style="display: inline-block">bar^az</span>qoz' },
{ 'id': 'SPAN:d:ib-1_SA',
'desc': 'Delete from immediately before an inline-block <span>',
'pad': 'foo^<span style="display: inline-block">barbaz</span>qoz',
'expected': 'foo^<span style="display: inline-block">arbaz</span>qoz' },
{ 'id': 'SPAN:d:ib-2_SL',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo[DEL<span style="display: inline-block">ETE]bar</span>baz',
'expected': 'foo^<span style="display: inline-block">bar</span>baz' },
{ 'id': 'SPAN:d:ib-3_SR',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">bar[DEL</span>ETE]baz',
'expected': 'foo<span style="display: inline-block">bar^</span>baz' },
{ 'id': 'SPAN:d:ib-4i_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">bar[DELETE]baz</span>qoz',
'expected': 'foo<span style="display: inline-block">bar^baz</span>qoz' },
{ 'id': 'SPAN:d:ib-4l_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">[DELETE]barbaz</span>qoz',
'expected': 'foo<span style="display: inline-block">^barbaz</span>qoz' },
{ 'id': 'SPAN:d:ib-4r_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">barbaz[DELETE]</span>qoz',
'expected': 'foo<span style="display: inline-block">barbaz^</span>qoz' }
]
}
]
}

View file

@ -0,0 +1,285 @@
INSERT_TESTS = {
'id': 'I',
'caption': 'Insert Tests',
'checkAttrs': False,
'checkStyle': False,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': 'insert <hr>',
'command': 'inserthorizontalrule',
'tests': [
{ 'id': 'IHR_TEXT-1_SC',
'rte1-id': 'a-inserthorizontalrule-0',
'desc': 'Insert <hr> into text',
'pad': 'foo^bar',
'expected': 'foo<hr>^bar',
'accept': 'foo<hr>|bar' },
{ 'id': 'IHR_TEXT-1_SI',
'desc': 'Insert <hr>, replacing selected text',
'pad': 'foo[bar]baz',
'expected': 'foo<hr>^baz',
'accept': 'foo<hr>|baz' },
{ 'id': 'IHR_DIV-B-1_SX',
'desc': 'Insert <hr> between elements',
'pad': '<div><b>foo</b>|<b>bar</b></div>',
'expected': '<div><b>foo</b><hr>|<b>bar</b></div>' },
{ 'id': 'IHR_DIV-B-2_SO',
'desc': 'Insert <hr>, replacing a fully wrapped element',
'pad': '<div><b>foo</b>{<b>bar</b>}<b>baz</b></div>',
'expected': '<div><b>foo</b><hr>|<b>baz</b></div>' },
{ 'id': 'IHR_B-1_SC',
'desc': 'Insert <hr> into a span, splitting it',
'pad': '<b>foo^bar</b>',
'expected': '<b>foo</b><hr><b>^bar</b>' },
{ 'id': 'IHR_B-1_SS',
'desc': 'Insert <hr> into a span at the start (should not create an empty span)',
'pad': '<b>^foobar</b>',
'expected': '<hr><b>^foobar</b>' },
{ 'id': 'IHR_B-1_SE',
'desc': 'Insert <hr> into a span at the end',
'pad': '<b>foobar^</b>',
'expected': [ '<b>foobar</b><hr>|',
'<b>foobar</b><hr><b>^</b>' ] },
{ 'id': 'IHR_B-2_SL',
'desc': 'Insert <hr> with oblique selection starting outside of span',
'pad': 'foo[bar<b>baz]qoz</b>',
'expected': 'foo<hr>|<b>qoz</b>' },
{ 'id': 'IHR_B-2_SLR',
'desc': 'Insert <hr> with oblique reversed selection starting outside of span',
'pad': 'foo]bar<b>baz[qoz</b>',
'expected': [ 'foo<hr>|<b>qoz</b>',
'foo<hr><b>^qoz</b>' ] },
{ 'id': 'IHR_B-3_SR',
'desc': 'Insert <hr> with oblique selection ending outside of span',
'pad': '<b>foo[bar</b>baz]quoz',
'expected': [ '<b>foo</b><hr>|quoz',
'<b>foo</b><hr><b>^</b>quoz' ] },
{ 'id': 'IHR_B-3_SRR',
'desc': 'Insert <hr> with oblique reversed selection starting outside of span',
'pad': '<b>foo]bar</b>baz[quoz',
'expected': '<b>foo</b><hr>|quoz' },
{ 'id': 'IHR_B-I-1_SM',
'desc': 'Insert <hr> with oblique selection between different spans',
'pad': '<b>foo[bar</b><i>baz]quoz</i>',
'expected': [ '<b>foo</b><hr>|<i>quoz</i>',
'<b>foo</b><hr><b>^</b><i>quoz</i>' ] },
{ 'id': 'IHR_B-I-1_SMR',
'desc': 'Insert <hr> with reversed oblique selection between different spans',
'pad': '<b>foo]bar</b><i>baz[quoz</i>',
'expected': '<b>foo</b><hr><i>^quoz</i>' },
{ 'id': 'IHR_P-1_SC',
'desc': 'Insert <hr> into a paragraph, splitting it',
'pad': '<p>foo^bar</p>',
'expected': [ '<p>foo</p><hr>|<p>bar</p>',
'<p>foo</p><hr><p>^bar</p>' ] },
{ 'id': 'IHR_P-1_SS',
'desc': 'Insert <hr> into a paragraph at the start (should not create an empty span)',
'pad': '<p>^foobar</p>',
'expected': [ '<hr>|<p>foobar</p>',
'<hr><p>^foobar</p>' ] },
{ 'id': 'IHR_P-1_SE',
'desc': 'Insert <hr> into a paragraph at the end (should not create an empty span)',
'pad': '<p>foobar^</p>',
'expected': '<p>foobar</p><hr>|' }
]
},
{ 'desc': 'insert <p>',
'command': 'insertparagraph',
'tests': [
{ 'id': 'IP_P-1_SC',
'desc': 'Split paragraph',
'pad': '<p>foo^bar</p>',
'expected': '<p>foo</p><p>^bar</p>' },
{ 'id': 'IP_UL-LI-1_SC',
'desc': 'Split list item',
'pad': '<ul><li>foo^bar</li></ul>',
'expected': '<ul><li>foo</li><li>^bar</li></ul>' }
]
},
{ 'desc': 'insert text',
'command': 'inserttext',
'tests': [
{ 'id': 'ITEXT:text_TEXT-1_SC',
'desc': 'Insert text',
'value': 'text',
'pad': 'foo^bar',
'expected': 'footext^bar' },
{ 'id': 'ITEXT:text_TEXT-1_SI',
'desc': 'Insert text, replacing selected text',
'value': 'text',
'pad': 'foo[bar]baz',
'expected': 'footext^baz' }
]
},
{ 'desc': 'insert <br>',
'command': 'insertlinebreak',
'tests': [
{ 'id': 'IBR_TEXT-1_SC',
'desc': 'Insert <br> into text',
'pad': 'foo^bar',
'expected': [ 'foo<br>|bar',
'foo<br>^bar' ] },
{ 'id': 'IBR_TEXT-1_SI',
'desc': 'Insert <br>, replacing selected text',
'pad': 'foo[bar]baz',
'expected': [ 'foo<br>|baz',
'foo<br>^baz' ] },
{ 'id': 'IBR_LI-1_SC',
'desc': 'Insert <br> within list item',
'pad': '<ul><li>foo^bar</li></ul>',
'expected': '<ul><li>foo<br>^bar</li></ul>' }
]
},
{ 'desc': 'insert <img>',
'command': 'insertimage',
'tests': [
{ 'id': 'IIMG:url_TEXT-1_SC',
'rte1-id': 'a-insertimage-0',
'desc': 'Insert image with URL "bar.png"',
'value': 'bar.png',
'checkAttrs': True,
'pad': 'foo^bar',
'expected': [ 'foo<img src="bar.png">|bar',
'foo<img src="bar.png">^bar' ] },
{ 'id': 'IIMG:url_IMG-1_SO',
'desc': 'Change existing image to new URL, selection on <img>',
'value': 'quz.png',
'checkAttrs': True,
'pad': '<span>foo{<img src="bar.png">}bar</span>',
'expected': [ '<span>foo<img src="quz.png"/>|bar</span>',
'<span>foo<img src="quz.png"/>^bar</span>' ] },
{ 'id': 'IIMG:url_SPAN-IMG-1_SO',
'desc': 'Change existing image to new URL, selection in text surrounding <img>',
'value': 'quz.png',
'checkAttrs': True,
'pad': 'foo[<img src="bar.png">]bar',
'expected': [ 'foo<img src="quz.png"/>|bar',
'foo<img src="quz.png"/>^bar' ] },
{ 'id': 'IIMG:._SPAN-IMG-1_SO',
'desc': 'Remove existing image or URL, selection on <img>',
'value': '',
'checkAttrs': True,
'pad': '<span>foo{<img src="bar.png">}bar</span>',
'expected': [ '<span>foo^bar</span>',
'<span>foo<img>|bar</span>',
'<span>foo<img>^bar</span>',
'<span>foo<img src="">|bar</span>',
'<span>foo<img src="">^bar</span>' ] },
{ 'id': 'IIMG:._IMG-1_SO',
'desc': 'Remove existing image or URL, selection in text surrounding <img>',
'value': '',
'checkAttrs': True,
'pad': 'foo[<img src="bar.png">]bar',
'expected': [ 'foo^bar',
'foo<img>|bar',
'foo<img>^bar',
'foo<img src="">|bar',
'foo<img src="">^bar' ] }
]
},
{ 'desc': 'insert <ol>',
'command': 'insertorderedlist',
'tests': [
{ 'id': 'IOL_TEXT-1_SC',
'rte1-id': 'a-insertorderedlist-0',
'desc': 'Insert ordered list on collapsed selection',
'pad': 'foo^bar',
'expected': '<ol><li>foo^bar</li></ol>' },
{ 'id': 'IOL_TEXT-1_SI',
'desc': 'Insert ordered list on selected text',
'pad': 'foo[bar]baz',
'expected': '<ol><li>foo[bar]baz</li></ol>' }
]
},
{ 'desc': 'insert <ul>',
'command': 'insertunorderedlist',
'tests': [
{ 'id': 'IUL_TEXT-1_SC',
'desc': 'Insert unordered list on collapsed selection',
'pad': 'foo^bar',
'expected': '<ul><li>foo^bar</li></ul>' },
{ 'id': 'IUL_TEXT-1_SI',
'rte1-id': 'a-insertunorderedlist-0',
'desc': 'Insert unordered list on selected text',
'pad': 'foo[bar]baz',
'expected': '<ul><li>foo[bar]baz</li></ul>' }
]
},
{ 'desc': 'insert arbitrary HTML',
'command': 'inserthtml',
'tests': [
{ 'id': 'IHTML:BR_TEXT-1_SC',
'rte1-id': 'a-inserthtml-0',
'desc': 'InsertHTML: <br>',
'value': '<br>',
'pad': 'foo^barbaz',
'expected': 'foo<br>^barbaz' },
{ 'id': 'IHTML:text_TEXT-1_SI',
'desc': 'InsertHTML: "NEW"',
'value': 'NEW',
'pad': 'foo[bar]baz',
'expected': 'fooNEW^baz' },
{ 'id': 'IHTML:S_TEXT-1_SI',
'desc': 'InsertHTML: "<span>NEW<span>"',
'value': '<span>NEW</span>',
'pad': 'foo[bar]baz',
'expected': 'foo<span>NEW</span>^baz' },
{ 'id': 'IHTML:H1.H2_TEXT-1_SI',
'desc': 'InsertHTML: "<h1>NEW</h1><h2>HTML</h2>"',
'value': '<h1>NEW</h1><h2>HTML</h2>',
'pad': 'foo[bar]baz',
'expected': 'foo<h1>NEW</h1><h2>HTML</h2>^baz' },
{ 'id': 'IHTML:P-B_TEXT-1_SI',
'desc': 'InsertHTML: "<p>NEW<b>HTML</b>!</p>"',
'value': '<p>NEW<b>HTML</b>!</p>',
'pad': 'foo[bar]baz',
'expected': 'foo<p>NEW<b>HTML</b>!</p>^baz' }
]
}
]
}

View file

@ -0,0 +1,215 @@
QUERYENABLED_TESTS = {
'id': 'QE',
'caption': 'queryCommandEnabled Tests',
'pad': 'foo[bar]baz',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': False,
'expected': True,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': 'HTML5 commands',
'tests': [
{ 'id': 'SELECTALL_TEXT-1',
'desc': 'check whether the "selectall" command is enabled',
'qcenabled': 'selectall' },
{ 'id': 'UNSELECT_TEXT-1',
'desc': 'check whether the "unselect" command is enabled',
'qcenabled': 'unselect' },
{ 'id': 'UNDO_TEXT-1',
'desc': 'check whether the "undo" command is enabled',
'qcenabled': 'undo' },
{ 'id': 'REDO_TEXT-1',
'desc': 'check whether the "redo" command is enabled',
'qcenabled': 'redo' },
{ 'id': 'BOLD_TEXT-1',
'desc': 'check whether the "bold" command is enabled',
'qcenabled': 'bold' },
{ 'id': 'ITALIC_TEXT-1',
'desc': 'check whether the "italic" command is enabled',
'qcenabled': 'italic' },
{ 'id': 'UNDERLINE_TEXT-1',
'desc': 'check whether the "underline" command is enabled',
'qcenabled': 'underline' },
{ 'id': 'STRIKETHROUGH_TEXT-1',
'desc': 'check whether the "strikethrough" command is enabled',
'qcenabled': 'strikethrough' },
{ 'id': 'SUBSCRIPT_TEXT-1',
'desc': 'check whether the "subscript" command is enabled',
'qcenabled': 'subscript' },
{ 'id': 'SUPERSCRIPT_TEXT-1',
'desc': 'check whether the "superscript" command is enabled',
'qcenabled': 'superscript' },
{ 'id': 'FORMATBLOCK_TEXT-1',
'desc': 'check whether the "formatblock" command is enabled',
'qcenabled': 'formatblock' },
{ 'id': 'CREATELINK_TEXT-1',
'desc': 'check whether the "createlink" command is enabled',
'qcenabled': 'createlink' },
{ 'id': 'UNLINK_TEXT-1',
'desc': 'check whether the "unlink" command is enabled',
'qcenabled': 'unlink' },
{ 'id': 'INSERTHTML_TEXT-1',
'desc': 'check whether the "inserthtml" command is enabled',
'qcenabled': 'inserthtml' },
{ 'id': 'INSERTHORIZONTALRULE_TEXT-1',
'desc': 'check whether the "inserthorizontalrule" command is enabled',
'qcenabled': 'inserthorizontalrule' },
{ 'id': 'INSERTIMAGE_TEXT-1',
'desc': 'check whether the "insertimage" command is enabled',
'qcenabled': 'insertimage' },
{ 'id': 'INSERTLINEBREAK_TEXT-1',
'desc': 'check whether the "insertlinebreak" command is enabled',
'qcenabled': 'insertlinebreak' },
{ 'id': 'INSERTPARAGRAPH_TEXT-1',
'desc': 'check whether the "insertparagraph" command is enabled',
'qcenabled': 'insertparagraph' },
{ 'id': 'INSERTORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertorderedlist" command is enabled',
'qcenabled': 'insertorderedlist' },
{ 'id': 'INSERTUNORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertunorderedlist" command is enabled',
'qcenabled': 'insertunorderedlist' },
{ 'id': 'INSERTTEXT_TEXT-1',
'desc': 'check whether the "inserttext" command is enabled',
'qcenabled': 'inserttext' },
{ 'id': 'DELETE_TEXT-1',
'desc': 'check whether the "delete" command is enabled',
'qcenabled': 'delete' },
{ 'id': 'FORWARDDELETE_TEXT-1',
'desc': 'check whether the "forwarddelete" command is enabled',
'qcenabled': 'forwarddelete' }
]
},
{ 'desc': 'MIDAS commands',
'tests': [
{ 'id': 'STYLEWITHCSS_TEXT-1',
'desc': 'check whether the "styleWithCSS" command is enabled',
'qcenabled': 'styleWithCSS' },
{ 'id': 'CONTENTREADONLY_TEXT-1',
'desc': 'check whether the "contentreadonly" command is enabled',
'qcenabled': 'contentreadonly' },
{ 'id': 'BACKCOLOR_TEXT-1',
'desc': 'check whether the "backcolor" command is enabled',
'qcenabled': 'backcolor' },
{ 'id': 'FORECOLOR_TEXT-1',
'desc': 'check whether the "forecolor" command is enabled',
'qcenabled': 'forecolor' },
{ 'id': 'HILITECOLOR_TEXT-1',
'desc': 'check whether the "hilitecolor" command is enabled',
'qcenabled': 'hilitecolor' },
{ 'id': 'FONTNAME_TEXT-1',
'desc': 'check whether the "fontname" command is enabled',
'qcenabled': 'fontname' },
{ 'id': 'FONTSIZE_TEXT-1',
'desc': 'check whether the "fontsize" command is enabled',
'qcenabled': 'fontsize' },
{ 'id': 'INCREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "increasefontsize" command is enabled',
'qcenabled': 'increasefontsize' },
{ 'id': 'DECREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "decreasefontsize" command is enabled',
'qcenabled': 'decreasefontsize' },
{ 'id': 'HEADING_TEXT-1',
'desc': 'check whether the "heading" command is enabled',
'qcenabled': 'heading' },
{ 'id': 'INDENT_TEXT-1',
'desc': 'check whether the "indent" command is enabled',
'qcenabled': 'indent' },
{ 'id': 'OUTDENT_TEXT-1',
'desc': 'check whether the "outdent" command is enabled',
'qcenabled': 'outdent' },
{ 'id': 'CREATEBOOKMARK_TEXT-1',
'desc': 'check whether the "createbookmark" command is enabled',
'qcenabled': 'createbookmark' },
{ 'id': 'UNBOOKMARK_TEXT-1',
'desc': 'check whether the "unbookmark" command is enabled',
'qcenabled': 'unbookmark' },
{ 'id': 'JUSTIFYCENTER_TEXT-1',
'desc': 'check whether the "justifycenter" command is enabled',
'qcenabled': 'justifycenter' },
{ 'id': 'JUSTIFYFULL_TEXT-1',
'desc': 'check whether the "justifyfull" command is enabled',
'qcenabled': 'justifyfull' },
{ 'id': 'JUSTIFYLEFT_TEXT-1',
'desc': 'check whether the "justifyleft" command is enabled',
'qcenabled': 'justifyleft' },
{ 'id': 'JUSTIFYRIGHT_TEXT-1',
'desc': 'check whether the "justifyright" command is enabled',
'qcenabled': 'justifyright' },
{ 'id': 'REMOVEFORMAT_TEXT-1',
'desc': 'check whether the "removeformat" command is enabled',
'qcenabled': 'removeformat' },
{ 'id': 'COPY_TEXT-1',
'desc': 'check whether the "copy" command is enabled',
'qcenabled': 'copy' },
{ 'id': 'CUT_TEXT-1',
'desc': 'check whether the "cut" command is enabled',
'qcenabled': 'cut' },
{ 'id': 'PASTE_TEXT-1',
'desc': 'check whether the "paste" command is enabled',
'qcenabled': 'paste' }
]
},
{ 'desc': 'Other tests',
'tests': [
{ 'id': 'garbage-1_TEXT-1',
'desc': 'check correct return value with garbage input',
'qcenabled': '#!#@7',
'expected': False }
]
}
]
}

View file

@ -0,0 +1,214 @@
QUERYINDETERM_TESTS = {
'id': 'QI',
'caption': 'queryCommandIndeterm Tests',
'pad': 'foo[bar]baz',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': False,
'expected': False,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': 'HTML5 commands',
'tests': [
{ 'id': 'SELECTALL_TEXT-1',
'desc': 'check whether the "selectall" command is indeterminate',
'qcindeterm': 'selectall' },
{ 'id': 'UNSELECT_TEXT-1',
'desc': 'check whether the "unselect" command is indeterminate',
'qcindeterm': 'unselect' },
{ 'id': 'UNDO_TEXT-1',
'desc': 'check whether the "undo" command is indeterminate',
'qcindeterm': 'undo' },
{ 'id': 'REDO_TEXT-1',
'desc': 'check whether the "redo" command is indeterminate',
'qcindeterm': 'redo' },
{ 'id': 'BOLD_TEXT-1',
'desc': 'check whether the "bold" command is indeterminate',
'qcindeterm': 'bold' },
{ 'id': 'ITALIC_TEXT-1',
'desc': 'check whether the "italic" command is indeterminate',
'qcindeterm': 'italic' },
{ 'id': 'UNDERLINE_TEXT-1',
'desc': 'check whether the "underline" command is indeterminate',
'qcindeterm': 'underline' },
{ 'id': 'STRIKETHROUGH_TEXT-1',
'desc': 'check whether the "strikethrough" command is indeterminate',
'qcindeterm': 'strikethrough' },
{ 'id': 'SUBSCRIPT_TEXT-1',
'desc': 'check whether the "subscript" command is indeterminate',
'qcindeterm': 'subscript' },
{ 'id': 'SUPERSCRIPT_TEXT-1',
'desc': 'check whether the "superscript" command is indeterminate',
'qcindeterm': 'superscript' },
{ 'id': 'FORMATBLOCK_TEXT-1',
'desc': 'check whether the "formatblock" command is indeterminate',
'qcindeterm': 'formatblock' },
{ 'id': 'CREATELINK_TEXT-1',
'desc': 'check whether the "createlink" command is indeterminate',
'qcindeterm': 'createlink' },
{ 'id': 'UNLINK_TEXT-1',
'desc': 'check whether the "unlink" command is indeterminate',
'qcindeterm': 'unlink' },
{ 'id': 'INSERTHTML_TEXT-1',
'desc': 'check whether the "inserthtml" command is indeterminate',
'qcindeterm': 'inserthtml' },
{ 'id': 'INSERTHORIZONTALRULE_TEXT-1',
'desc': 'check whether the "inserthorizontalrule" command is indeterminate',
'qcindeterm': 'inserthorizontalrule' },
{ 'id': 'INSERTIMAGE_TEXT-1',
'desc': 'check whether the "insertimage" command is indeterminate',
'qcindeterm': 'insertimage' },
{ 'id': 'INSERTLINEBREAK_TEXT-1',
'desc': 'check whether the "insertlinebreak" command is indeterminate',
'qcindeterm': 'insertlinebreak' },
{ 'id': 'INSERTPARAGRAPH_TEXT-1',
'desc': 'check whether the "insertparagraph" command is indeterminate',
'qcindeterm': 'insertparagraph' },
{ 'id': 'INSERTORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertorderedlist" command is indeterminate',
'qcindeterm': 'insertorderedlist' },
{ 'id': 'INSERTUNORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertunorderedlist" command is indeterminate',
'qcindeterm': 'insertunorderedlist' },
{ 'id': 'INSERTTEXT_TEXT-1',
'desc': 'check whether the "inserttext" command is indeterminate',
'qcindeterm': 'inserttext' },
{ 'id': 'DELETE_TEXT-1',
'desc': 'check whether the "delete" command is indeterminate',
'qcindeterm': 'delete' },
{ 'id': 'FORWARDDELETE_TEXT-1',
'desc': 'check whether the "forwarddelete" command is indeterminate',
'qcindeterm': 'forwarddelete' }
]
},
{ 'desc': 'MIDAS commands',
'tests': [
{ 'id': 'STYLEWITHCSS_TEXT-1',
'desc': 'check whether the "styleWithCSS" command is indeterminate',
'qcindeterm': 'styleWithCSS' },
{ 'id': 'CONTENTREADONLY_TEXT-1',
'desc': 'check whether the "contentreadonly" command is indeterminate',
'qcindeterm': 'contentreadonly' },
{ 'id': 'BACKCOLOR_TEXT-1',
'desc': 'check whether the "backcolor" command is indeterminate',
'qcindeterm': 'backcolor' },
{ 'id': 'FORECOLOR_TEXT-1',
'desc': 'check whether the "forecolor" command is indeterminate',
'qcindeterm': 'forecolor' },
{ 'id': 'HILITECOLOR_TEXT-1',
'desc': 'check whether the "hilitecolor" command is indeterminate',
'qcindeterm': 'hilitecolor' },
{ 'id': 'FONTNAME_TEXT-1',
'desc': 'check whether the "fontname" command is indeterminate',
'qcindeterm': 'fontname' },
{ 'id': 'FONTSIZE_TEXT-1',
'desc': 'check whether the "fontsize" command is indeterminate',
'qcindeterm': 'fontsize' },
{ 'id': 'INCREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "increasefontsize" command is indeterminate',
'qcindeterm': 'increasefontsize' },
{ 'id': 'DECREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "decreasefontsize" command is indeterminate',
'qcindeterm': 'decreasefontsize' },
{ 'id': 'HEADING_TEXT-1',
'desc': 'check whether the "heading" command is indeterminate',
'qcindeterm': 'heading' },
{ 'id': 'INDENT_TEXT-1',
'desc': 'check whether the "indent" command is indeterminate',
'qcindeterm': 'indent' },
{ 'id': 'OUTDENT_TEXT-1',
'desc': 'check whether the "outdent" command is indeterminate',
'qcindeterm': 'outdent' },
{ 'id': 'CREATEBOOKMARK_TEXT-1',
'desc': 'check whether the "createbookmark" command is indeterminate',
'qcindeterm': 'createbookmark' },
{ 'id': 'UNBOOKMARK_TEXT-1',
'desc': 'check whether the "unbookmark" command is indeterminate',
'qcindeterm': 'unbookmark' },
{ 'id': 'JUSTIFYCENTER_TEXT-1',
'desc': 'check whether the "justifycenter" command is indeterminate',
'qcindeterm': 'justifycenter' },
{ 'id': 'JUSTIFYFULL_TEXT-1',
'desc': 'check whether the "justifyfull" command is indeterminate',
'qcindeterm': 'justifyfull' },
{ 'id': 'JUSTIFYLEFT_TEXT-1',
'desc': 'check whether the "justifyleft" command is indeterminate',
'qcindeterm': 'justifyleft' },
{ 'id': 'JUSTIFYRIGHT_TEXT-1',
'desc': 'check whether the "justifyright" command is indeterminate',
'qcindeterm': 'justifyright' },
{ 'id': 'REMOVEFORMAT_TEXT-1',
'desc': 'check whether the "removeformat" command is indeterminate',
'qcindeterm': 'removeformat' },
{ 'id': 'COPY_TEXT-1',
'desc': 'check whether the "copy" command is indeterminate',
'qcindeterm': 'copy' },
{ 'id': 'CUT_TEXT-1',
'desc': 'check whether the "cut" command is indeterminate',
'qcindeterm': 'cut' },
{ 'id': 'PASTE_TEXT-1',
'desc': 'check whether the "paste" command is indeterminate',
'qcindeterm': 'paste' }
]
},
{ 'desc': 'Other tests',
'tests': [
{ 'id': 'garbage-1_TEXT-1',
'desc': 'check correct return value with garbage input',
'qcindeterm': '#!#@7' }
]
}
]
}

View file

@ -0,0 +1,575 @@
QUERYSTATE_TESTS = {
'id': 'QS',
'caption': 'queryCommandState Tests',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': False,
'Proposed': [
{ 'desc': '',
'qcstate': '',
'tests': [
]
},
{ 'desc': 'query bold state',
'qcstate': 'bold',
'tests': [
{ 'id': 'B_TEXT_SI',
'rte1-id': 'q-bold-0',
'desc': 'query the "bold" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'B_B-1_SI',
'rte1-id': 'q-bold-1',
'desc': 'query the "bold" state',
'pad': '<b>foo[bar]baz</b>',
'expected': True },
{ 'id': 'B_STRONG-1_SI',
'rte1-id': 'q-bold-2',
'desc': 'query the "bold" state',
'pad': '<strong>foo[bar]baz</strong>',
'expected': True },
{ 'id': 'B_SPANs:fw:b-1_SI',
'rte1-id': 'q-bold-3',
'desc': 'query the "bold" state',
'pad': '<span style="font-weight: bold">foo[bar]baz</span>',
'expected': True },
{ 'id': 'B_SPANs:fw:n-1_SI',
'desc': 'query the "bold" state',
'pad': '<span style="font-weight: normal">foo[bar]baz</span>',
'expected': False },
{ 'id': 'B_Bs:fw:n-1_SI',
'rte1-id': 'q-bold-4',
'desc': 'query the "bold" state',
'pad': '<span style="font-weight: normal">foo[bar]baz</span>',
'expected': False },
{ 'id': 'B_B-SPANs:fw:n-1_SI',
'rte1-id': 'q-bold-5',
'desc': 'query the "bold" state',
'pad': '<b><span style="font-weight: normal">foo[bar]baz</span></b>',
'expected': False },
{ 'id': 'B_SPAN.b-1-SI',
'desc': 'query the "bold" state',
'pad': '<span class="b">foo[bar]baz</span>',
'expected': True },
{ 'id': 'B_MYB-1-SI',
'desc': 'query the "bold" state',
'pad': '<myb>foo[bar]baz</myb>',
'expected': True },
{ 'id': 'B_B-I-1_SC',
'desc': 'query the "bold" state, bold tag not immediate parent',
'pad': '<b>foo<i>ba^r</i>baz</b>',
'expected': True },
{ 'id': 'B_B-I-1_SL',
'desc': 'query the "bold" state, selection partially in child element',
'pad': '<b>fo[o<i>b]ar</i>baz</b>',
'expected': True },
{ 'id': 'B_B-I-1_SR',
'desc': 'query the "bold" state, selection partially in child element',
'pad': '<b>foo<i>ba[r</i>b]az</b>',
'expected': True },
{ 'id': 'B_STRONG-I-1_SC',
'desc': 'query the "bold" state, bold tag not immediate parent',
'pad': '<strong>foo<i>ba^r</i>baz</strong>',
'expected': True },
{ 'id': 'B_B-I-U-1_SC',
'desc': 'query the "bold" state, bold tag not immediate parent',
'pad': '<b>foo<i>bar<u>b^az</u></i></strong>',
'expected': True },
{ 'id': 'B_B-I-U-1_SM',
'desc': 'query the "bold" state, bold tag not immediate parent',
'pad': '<b>foo<i>ba[r<u>b]az</u></i></strong>',
'expected': True },
{ 'id': 'B_TEXT-B-1_SO-1',
'desc': 'query the "bold" state, selection wrapping the bold tag',
'pad': 'foo[<b>bar</b>]baz',
'expected': True },
{ 'id': 'B_TEXT-B-1_SO-2',
'desc': 'query the "bold" state, selection wrapping the bold tag',
'pad': 'foo{<b>bar</b>}baz',
'expected': True },
{ 'id': 'B_TEXT-B-1_SL',
'desc': 'query the "bold" state, selection containing non-bold text',
'pad': 'fo[o<b>ba]r</b>baz',
'expected': False },
{ 'id': 'B_TEXT-B-1_SR',
'desc': 'query the "bold" state, selection containing non-bold text',
'pad': 'foo<b>b[ar</b>b]az',
'expected': False },
{ 'id': 'B_TEXT-B-1_SO-3',
'desc': 'query the "bold" state, selection containing non-bold text',
'pad': 'fo[o<b>bar</b>b]az',
'expected': False },
{ 'id': 'B_B.TEXT.B-1_SM',
'desc': 'query the "bold" state, selection including non-bold text',
'pad': '<b>fo[o</b>bar<b>b]az</b>',
'expected': False },
{ 'id': 'B_B.B.B-1_SM',
'desc': 'query the "bold" state, selection mixed, but all bold',
'pad': '<b>fo[o</b><b>bar</b><b>b]az</b>',
'expected': True },
{ 'id': 'B_B.STRONG.B-1_SM',
'desc': 'query the "bold" state, selection mixed, but all bold',
'pad': '<b>fo[o</b><strong>bar</strong><b>b]az</b>',
'expected': True },
{ 'id': 'B_SPAN.b.MYB.SPANs:fw:b-1_SM',
'desc': 'query the "bold" state, selection mixed, but all bold',
'pad': '<span class="b">fo[o</span><myb>bar</myb><span style="font-weight: bold">b]az</span>',
'expected': True }
]
},
{ 'desc': 'query italic state',
'qcstate': 'italic',
'tests': [
{ 'id': 'I_TEXT_SI',
'rte1-id': 'q-italic-0',
'desc': 'query the "italic" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'I_I-1_SI',
'rte1-id': 'q-italic-1',
'desc': 'query the "italic" state',
'pad': '<i>foo[bar]baz</i>',
'expected': True },
{ 'id': 'I_EM-1_SI',
'rte1-id': 'q-italic-2',
'desc': 'query the "italic" state',
'pad': '<em>foo[bar]baz</em>',
'expected': True },
{ 'id': 'I_SPANs:fs:i-1_SI',
'rte1-id': 'q-italic-3',
'desc': 'query the "italic" state',
'pad': '<span style="font-style: italic">foo[bar]baz</span>',
'expected': True },
{ 'id': 'I_SPANs:fs:n-1_SI',
'desc': 'query the "italic" state',
'pad': '<span style="font-style: normal">foo[bar]baz</span>',
'expected': False },
{ 'id': 'I_I-SPANs:fs:n-1_SI',
'rte1-id': 'q-italic-4',
'desc': 'query the "italic" state',
'pad': '<i><span style="font-style: normal">foo[bar]baz</span></i>',
'expected': False },
{ 'id': 'I_SPAN.i-1-SI',
'desc': 'query the "italic" state',
'pad': '<span class="i">foo[bar]baz</span>',
'expected': True },
{ 'id': 'I_MYI-1-SI',
'desc': 'query the "italic" state',
'pad': '<myi>foo[bar]baz</myi>',
'expected': True }
]
},
{ 'desc': 'query underline state',
'qcstate': 'underline',
'tests': [
{ 'id': 'U_TEXT_SI',
'rte1-id': 'q-underline-0',
'desc': 'query the "underline" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'U_U-1_SI',
'rte1-id': 'q-underline-1',
'desc': 'query the "underline" state',
'pad': '<u>foo[bar]baz</u>',
'expected': True },
{ 'id': 'U_Us:td:n-1_SI',
'rte1-id': 'q-underline-4',
'desc': 'query the "underline" state',
'pad': '<u style="text-decoration: none">foo[bar]baz</u>',
'expected': False },
{ 'id': 'U_Ah:url-1_SI',
'rte1-id': 'q-underline-2',
'desc': 'query the "underline" state',
'pad': '<a href="http://www.goo.gl">foo[bar]baz</a>',
'expected': True },
{ 'id': 'U_Ah:url.s:td:n-1_SI',
'rte1-id': 'q-underline-5',
'desc': 'query the "underline" state',
'pad': '<a href="http://www.goo.gl" style="text-decoration: none">foo[bar]baz</a>',
'expected': False },
{ 'id': 'U_SPANs:td:u-1_SI',
'rte1-id': 'q-underline-3',
'desc': 'query the "underline" state',
'pad': '<span style="text-decoration: underline">foo[bar]baz</span>',
'expected': True },
{ 'id': 'U_SPAN.u-1-SI',
'desc': 'query the "underline" state',
'pad': '<span class="u">foo[bar]baz</span>',
'expected': True },
{ 'id': 'U_MYU-1-SI',
'desc': 'query the "underline" state',
'pad': '<myu>foo[bar]baz</myu>',
'expected': True }
]
},
{ 'desc': 'query strike-through state',
'qcstate': 'strikethrough',
'tests': [
{ 'id': 'S_TEXT_SI',
'rte1-id': 'q-strikethrough-0',
'desc': 'query the "strikethrough" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'S_S-1_SI',
'rte1-id': 'q-strikethrough-3',
'desc': 'query the "strikethrough" state',
'pad': '<s>foo[bar]baz</s>',
'expected': True },
{ 'id': 'S_STRIKE-1_SI',
'rte1-id': 'q-strikethrough-1',
'desc': 'query the "strikethrough" state',
'pad': '<strike>foo[bar]baz</strike>',
'expected': True },
{ 'id': 'S_STRIKEs:td:n-1_SI',
'rte1-id': 'q-strikethrough-2',
'desc': 'query the "strikethrough" state',
'pad': '<strike style="text-decoration: none">foo[bar]baz</strike>',
'expected': False },
{ 'id': 'S_DEL-1_SI',
'rte1-id': 'q-strikethrough-4',
'desc': 'query the "strikethrough" state',
'pad': '<del>foo[bar]baz</del>',
'expected': True },
{ 'id': 'S_SPANs:td:lt-1_SI',
'rte1-id': 'q-strikethrough-5',
'desc': 'query the "strikethrough" state',
'pad': '<span style="text-decoration: line-through">foo[bar]baz</span>',
'expected': True },
{ 'id': 'S_SPAN.s-1-SI',
'desc': 'query the "strikethrough" state',
'pad': '<span class="s">foo[bar]baz</span>',
'expected': True },
{ 'id': 'S_MYS-1-SI',
'desc': 'query the "strikethrough" state',
'pad': '<mys>foo[bar]baz</mys>',
'expected': True },
{ 'id': 'S_S.STRIKE.DEL-1_SM',
'desc': 'query the "strikethrough" state, selection mixed, but all struck',
'pad': '<s>fo[o</s><strike>bar</strike><del>b]az</del>',
'expected': True }
]
},
{ 'desc': 'query subscript state',
'qcstate': 'subscript',
'tests': [
{ 'id': 'SUB_TEXT_SI',
'rte1-id': 'q-subscript-0',
'desc': 'query the "subscript" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'SUB_SUB-1_SI',
'rte1-id': 'q-subscript-1',
'desc': 'query the "subscript" state',
'pad': '<sub>foo[bar]baz</sub>',
'expected': True },
{ 'id': 'SUB_SPAN.sub-1-SI',
'desc': 'query the "subscript" state',
'pad': '<span class="sub">foo[bar]baz</span>',
'expected': True },
{ 'id': 'SUB_MYSUB-1-SI',
'desc': 'query the "subscript" state',
'pad': '<mysub>foo[bar]baz</mysub>',
'expected': True }
]
},
{ 'desc': 'query superscript state',
'qcstate': 'superscript',
'tests': [
{ 'id': 'SUP_TEXT_SI',
'rte1-id': 'q-superscript-0',
'desc': 'query the "superscript" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'SUP_SUP-1_SI',
'rte1-id': 'q-superscript-1',
'desc': 'query the "superscript" state',
'pad': '<sup>foo[bar]baz</sup>',
'expected': True },
{ 'id': 'IOL_TEXT_SI',
'desc': 'query the "insertorderedlist" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'SUP_SPAN.sup-1-SI',
'desc': 'query the "superscript" state',
'pad': '<span class="sup">foo[bar]baz</span>',
'expected': True },
{ 'id': 'SUP_MYSUP-1-SI',
'desc': 'query the "superscript" state',
'pad': '<mysup>foo[bar]baz</mysup>',
'expected': True }
]
},
{ 'desc': 'query whether the selection is in an ordered list',
'qcstate': 'insertorderedlist',
'tests': [
{ 'id': 'IOL_TEXT-1_SI',
'rte1-id': 'q-insertorderedlist-0',
'desc': 'query the "insertorderedlist" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'IOL_OL-LI-1_SI',
'rte1-id': 'q-insertorderedlist-1',
'desc': 'query the "insertorderedlist" state',
'pad': '<ol><li>foo[bar]baz</li></ol>',
'expected': True },
{ 'id': 'IOL_UL_LI-1_SI',
'rte1-id': 'q-insertorderedlist-2',
'desc': 'query the "insertorderedlist" state',
'pad': '<ul><li>foo[bar]baz</li></ul>',
'expected': False }
]
},
{ 'desc': 'query whether the selection is in an unordered list',
'qcstate': 'insertunorderedlist',
'tests': [
{ 'id': 'IUL_TEXT_SI',
'rte1-id': 'q-insertunorderedlist-0',
'desc': 'query the "insertunorderedlist" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'IUL_OL-LI-1_SI',
'rte1-id': 'q-insertunorderedlist-1',
'desc': 'query the "insertunorderedlist" state',
'pad': '<ol><li>foo[bar]baz</li></ol>',
'expected': False },
{ 'id': 'IUL_UL-LI-1_SI',
'rte1-id': 'q-insertunorderedlist-2',
'desc': 'query the "insertunorderedlist" state',
'pad': '<ul><li>foo[bar]baz</li></ul>',
'expected': True }
]
},
{ 'desc': 'query whether the paragraph is centered',
'qcstate': 'justifycenter',
'tests': [
{ 'id': 'JC_TEXT_SI',
'rte1-id': 'q-justifycenter-0',
'desc': 'query the "justifycenter" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'JC_DIVa:c-1_SI',
'rte1-id': 'q-justifycenter-1',
'desc': 'query the "justifycenter" state',
'pad': '<div align="center">foo[bar]baz</div>',
'expected': True },
{ 'id': 'JC_Pa:c-1_SI',
'rte1-id': 'q-justifycenter-2',
'desc': 'query the "justifycenter" state',
'pad': '<p align="center">foo[bar]baz</p>',
'expected': True },
{ 'id': 'JC_SPANs:ta:c-1_SI',
'rte1-id': 'q-justifycenter-3',
'desc': 'query the "justifycenter" state',
'pad': '<div style="text-align: center">foo[bar]baz</div>',
'expected': True },
{ 'id': 'JC_SPAN.jc-1-SI',
'desc': 'query the "justifycenter" state',
'pad': '<span class="jc">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JC_MYJC-1-SI',
'desc': 'query the "justifycenter" state',
'pad': '<myjc>foo[bar]baz</myjc>',
'expected': True }
]
},
{ 'desc': 'query whether the paragraph is justified',
'qcstate': 'justifyfull',
'tests': [
{ 'id': 'JF_TEXT_SI',
'rte1-id': 'q-justifyfull-0',
'desc': 'query the "justifyfull" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'JF_DIVa:j-1_SI',
'rte1-id': 'q-justifyfull-1',
'desc': 'query the "justifyfull" state',
'pad': '<div align="justify">foo[bar]baz</div>',
'expected': True },
{ 'id': 'JF_Pa:j-1_SI',
'rte1-id': 'q-justifyfull-2',
'desc': 'query the "justifyfull" state',
'pad': '<p align="justify">foo[bar]baz</p>',
'expected': True },
{ 'id': 'JF_SPANs:ta:j-1_SI',
'rte1-id': 'q-justifyfull-3',
'desc': 'query the "justifyfull" state',
'pad': '<span style="text-align: justify">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JF_SPAN.jf-1-SI',
'desc': 'query the "justifyfull" state',
'pad': '<span class="jf">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JF_MYJF-1-SI',
'desc': 'query the "justifyfull" state',
'pad': '<myjf>foo[bar]baz</myjf>',
'expected': True }
]
},
{ 'desc': 'query whether the paragraph is aligned left',
'qcstate': 'justifyleft',
'tests': [
{ 'id': 'JL_TEXT_SI',
'desc': 'query the "justifyleft" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'JL_DIVa:l-1_SI',
'rte1-id': 'q-justifyleft-0',
'desc': 'query the "justifyleft" state',
'pad': '<div align="left">foo[bar]baz</div>',
'expected': True },
{ 'id': 'JL_Pa:l-1_SI',
'rte1-id': 'q-justifyleft-1',
'desc': 'query the "justifyleft" state',
'pad': '<p align="left">foo[bar]baz</p>',
'expected': True },
{ 'id': 'JL_SPANs:ta:l-1_SI',
'rte1-id': 'q-justifyleft-2',
'desc': 'query the "justifyleft" state',
'pad': '<span style="text-align: left">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JL_SPAN.jl-1-SI',
'desc': 'query the "justifyleft" state',
'pad': '<span class="jl">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JL_MYJL-1-SI',
'desc': 'query the "justifyleft" state',
'pad': '<myjl>foo[bar]baz</myjl>',
'expected': True }
]
},
{ 'desc': 'query whether the paragraph is aligned right',
'qcstate': 'justifyright',
'tests': [
{ 'id': 'JR_TEXT_SI',
'rte1-id': 'q-justifyright-0',
'desc': 'query the "justifyright" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'JR_DIVa:r-1_SI',
'rte1-id': 'q-justifyright-1',
'desc': 'query the "justifyright" state',
'pad': '<div align="right">foo[bar]baz</div>',
'expected': True },
{ 'id': 'JR_Pa:r-1_SI',
'rte1-id': 'q-justifyright-2',
'desc': 'query the "justifyright" state',
'pad': '<p align="right">foo[bar]baz</p>',
'expected': True },
{ 'id': 'JR_SPANs:ta:r-1_SI',
'rte1-id': 'q-justifyright-3',
'desc': 'query the "justifyright" state',
'pad': '<span style="text-align: right">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JR_SPAN.jr-1-SI',
'desc': 'query the "justifyright" state',
'pad': '<span class="jr">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JR_MYJR-1-SI',
'desc': 'query the "justifyright" state',
'pad': '<myjr>foo[bar]baz</myjr>',
'expected': True }
]
}
]
}
QUERYSTATE_TESTS_CSS = {
'id': 'QSC',
'caption': 'queryCommandState Tests, using styleWithCSS',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': True,
'Proposed': QUERYSTATE_TESTS['Proposed']
}

View file

@ -0,0 +1,226 @@
QUERYSUPPORTED_TESTS = {
'id': 'Q',
'caption': 'queryCommandSupported Tests',
'pad': 'foo[bar]baz',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': False,
'expected': True,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': 'HTML5 commands',
'tests': [
{ 'id': 'SELECTALL_TEXT-1',
'desc': 'check whether the "selectall" command is supported',
'qcsupported': 'selectall' },
{ 'id': 'UNSELECT_TEXT-1',
'desc': 'check whether the "unselect" command is supported',
'qcsupported': 'unselect' },
{ 'id': 'UNDO_TEXT-1',
'desc': 'check whether the "undo" command is supported',
'qcsupported': 'undo' },
{ 'id': 'REDO_TEXT-1',
'desc': 'check whether the "redo" command is supported',
'qcsupported': 'redo' },
{ 'id': 'BOLD_TEXT-1',
'desc': 'check whether the "bold" command is supported',
'qcsupported': 'bold' },
{ 'id': 'BOLD_B',
'desc': 'check whether the "bold" command is supported',
'qcsupported': 'bold',
'pad': '<b>foo[bar]baz</b>' },
{ 'id': 'ITALIC_TEXT-1',
'desc': 'check whether the "italic" command is supported',
'qcsupported': 'italic' },
{ 'id': 'ITALIC_I',
'desc': 'check whether the "italic" command is supported',
'qcsupported': 'italic',
'pad': '<i>foo[bar]baz</i>' },
{ 'id': 'UNDERLINE_TEXT-1',
'desc': 'check whether the "underline" command is supported',
'qcsupported': 'underline' },
{ 'id': 'STRIKETHROUGH_TEXT-1',
'desc': 'check whether the "strikethrough" command is supported',
'qcsupported': 'strikethrough' },
{ 'id': 'SUBSCRIPT_TEXT-1',
'desc': 'check whether the "subscript" command is supported',
'qcsupported': 'subscript' },
{ 'id': 'SUPERSCRIPT_TEXT-1',
'desc': 'check whether the "superscript" command is supported',
'qcsupported': 'superscript' },
{ 'id': 'FORMATBLOCK_TEXT-1',
'desc': 'check whether the "formatblock" command is supported',
'qcsupported': 'formatblock' },
{ 'id': 'CREATELINK_TEXT-1',
'desc': 'check whether the "createlink" command is supported',
'qcsupported': 'createlink' },
{ 'id': 'UNLINK_TEXT-1',
'desc': 'check whether the "unlink" command is supported',
'qcsupported': 'unlink' },
{ 'id': 'INSERTHTML_TEXT-1',
'desc': 'check whether the "inserthtml" command is supported',
'qcsupported': 'inserthtml' },
{ 'id': 'INSERTHORIZONTALRULE_TEXT-1',
'desc': 'check whether the "inserthorizontalrule" command is supported',
'qcsupported': 'inserthorizontalrule' },
{ 'id': 'INSERTIMAGE_TEXT-1',
'desc': 'check whether the "insertimage" command is supported',
'qcsupported': 'insertimage' },
{ 'id': 'INSERTLINEBREAK_TEXT-1',
'desc': 'check whether the "insertlinebreak" command is supported',
'qcsupported': 'insertlinebreak' },
{ 'id': 'INSERTPARAGRAPH_TEXT-1',
'desc': 'check whether the "insertparagraph" command is supported',
'qcsupported': 'insertparagraph' },
{ 'id': 'INSERTORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertorderedlist" command is supported',
'qcsupported': 'insertorderedlist' },
{ 'id': 'INSERTUNORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertunorderedlist" command is supported',
'qcsupported': 'insertunorderedlist' },
{ 'id': 'INSERTTEXT_TEXT-1',
'desc': 'check whether the "inserttext" command is supported',
'qcsupported': 'inserttext' },
{ 'id': 'DELETE_TEXT-1',
'desc': 'check whether the "delete" command is supported',
'qcsupported': 'delete' },
{ 'id': 'FORWARDDELETE_TEXT-1',
'desc': 'check whether the "forwarddelete" command is supported',
'qcsupported': 'forwarddelete' }
]
},
{ 'desc': 'MIDAS commands',
'tests': [
{ 'id': 'STYLEWITHCSS_TEXT-1',
'desc': 'check whether the "styleWithCSS" command is supported',
'qcsupported': 'styleWithCSS' },
{ 'id': 'CONTENTREADONLY_TEXT-1',
'desc': 'check whether the "contentreadonly" command is supported',
'qcsupported': 'contentreadonly' },
{ 'id': 'BACKCOLOR_TEXT-1',
'desc': 'check whether the "backcolor" command is supported',
'qcsupported': 'backcolor' },
{ 'id': 'FORECOLOR_TEXT-1',
'desc': 'check whether the "forecolor" command is supported',
'qcsupported': 'forecolor' },
{ 'id': 'HILITECOLOR_TEXT-1',
'desc': 'check whether the "hilitecolor" command is supported',
'qcsupported': 'hilitecolor' },
{ 'id': 'FONTNAME_TEXT-1',
'desc': 'check whether the "fontname" command is supported',
'qcsupported': 'fontname' },
{ 'id': 'FONTSIZE_TEXT-1',
'desc': 'check whether the "fontsize" command is supported',
'qcsupported': 'fontsize' },
{ 'id': 'INCREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "increasefontsize" command is supported',
'qcsupported': 'increasefontsize' },
{ 'id': 'DECREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "decreasefontsize" command is supported',
'qcsupported': 'decreasefontsize' },
{ 'id': 'HEADING_TEXT-1',
'desc': 'check whether the "heading" command is supported',
'qcsupported': 'heading' },
{ 'id': 'INDENT_TEXT-1',
'desc': 'check whether the "indent" command is supported',
'qcsupported': 'indent' },
{ 'id': 'OUTDENT_TEXT-1',
'desc': 'check whether the "outdent" command is supported',
'qcsupported': 'outdent' },
{ 'id': 'CREATEBOOKMARK_TEXT-1',
'desc': 'check whether the "createbookmark" command is supported',
'qcsupported': 'createbookmark' },
{ 'id': 'UNBOOKMARK_TEXT-1',
'desc': 'check whether the "unbookmark" command is supported',
'qcsupported': 'unbookmark' },
{ 'id': 'JUSTIFYCENTER_TEXT-1',
'desc': 'check whether the "justifycenter" command is supported',
'qcsupported': 'justifycenter' },
{ 'id': 'JUSTIFYFULL_TEXT-1',
'desc': 'check whether the "justifyfull" command is supported',
'qcsupported': 'justifyfull' },
{ 'id': 'JUSTIFYLEFT_TEXT-1',
'desc': 'check whether the "justifyleft" command is supported',
'qcsupported': 'justifyleft' },
{ 'id': 'JUSTIFYRIGHT_TEXT-1',
'desc': 'check whether the "justifyright" command is supported',
'qcsupported': 'justifyright' },
{ 'id': 'REMOVEFORMAT_TEXT-1',
'desc': 'check whether the "removeformat" command is supported',
'qcsupported': 'removeformat' },
{ 'id': 'COPY_TEXT-1',
'desc': 'check whether the "copy" command is supported',
'qcsupported': 'copy' },
{ 'id': 'CUT_TEXT-1',
'desc': 'check whether the "cut" command is supported',
'qcsupported': 'cut' },
{ 'id': 'PASTE_TEXT-1',
'desc': 'check whether the "paste" command is supported',
'qcsupported': 'paste' }
]
},
{ 'desc': 'Other tests',
'tests': [
{ 'id': 'garbage-1_TEXT-1',
'desc': 'check correct return value with garbage input',
'qcsupported': '#!#@7',
'expected': False }
]
}
]
}

View file

@ -0,0 +1,429 @@
QUERYVALUE_TESTS = {
'id': 'QV',
'caption': 'queryCommandValue Tests',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': False,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': '[HTML5] query bold value',
'qcvalue': 'bold',
'tests': [
{ 'id': 'B_TEXT_SI',
'desc': 'query the "bold" value',
'pad': 'foo[bar]baz',
'expected': 'false' },
{ 'id': 'B_B-1_SI',
'desc': 'query the "bold" value',
'pad': '<b>foo[bar]baz</b>',
'expected': 'true' },
{ 'id': 'B_STRONG-1_SI',
'desc': 'query the "bold" value',
'pad': '<strong>foo[bar]baz</strong>',
'expected': 'true' },
{ 'id': 'B_SPANs:fw:b-1_SI',
'desc': 'query the "bold" value',
'pad': '<span style="font-weight: bold">foo[bar]baz</span>',
'expected': 'true' },
{ 'id': 'B_SPANs:fw:n-1_SI',
'desc': 'query the "bold" value',
'pad': '<span style="font-weight: normal">foo[bar]baz</span>',
'expected': 'false' },
{ 'id': 'B_Bs:fw:n-1_SI',
'desc': 'query the "bold" value',
'pad': '<b><span style="font-weight: normal">foo[bar]baz</span></b>',
'expected': 'false' },
{ 'id': 'B_SPAN.b-1_SI',
'desc': 'query the "bold" value',
'pad': '<span class="b">foo[bar]baz</span>',
'expected': 'true' },
{ 'id': 'B_MYB-1-SI',
'desc': 'query the "bold" value',
'pad': '<myb>foo[bar]baz</myb>',
'expected': 'true' }
]
},
{ 'desc': '[HTML5] query italic value',
'qcvalue': 'italic',
'tests': [
{ 'id': 'I_TEXT_SI',
'desc': 'query the "bold" value',
'pad': 'foo[bar]baz',
'expected': 'false' },
{ 'id': 'I_I-1_SI',
'desc': 'query the "bold" value',
'pad': '<i>foo[bar]baz</i>',
'expected': 'true' },
{ 'id': 'I_EM-1_SI',
'desc': 'query the "bold" value',
'pad': '<em>foo[bar]baz</em>',
'expected': 'true' },
{ 'id': 'I_SPANs:fs:i-1_SI',
'desc': 'query the "bold" value',
'pad': '<span style="font-style: italic">foo[bar]baz</span>',
'expected': 'true' },
{ 'id': 'I_SPANs:fs:n-1_SI',
'desc': 'query the "bold" value',
'pad': '<span style="font-style: normal">foo[bar]baz</span>',
'expected': 'false' },
{ 'id': 'I_I-SPANs:fs:n-1_SI',
'desc': 'query the "bold" value',
'pad': '<i><span style="font-style: normal">foo[bar]baz</span></i>',
'expected': 'false' },
{ 'id': 'I_SPAN.i-1_SI',
'desc': 'query the "italic" value',
'pad': '<span class="i">foo[bar]baz</span>',
'expected': 'true' },
{ 'id': 'I_MYI-1-SI',
'desc': 'query the "italic" value',
'pad': '<myi>foo[bar]baz</myi>',
'expected': 'true' }
]
},
{ 'desc': '[HTML5] query block formatting value',
'qcvalue': 'formatblock',
'tests': [
{ 'id': 'FB_TEXT-1_SC',
'desc': 'query the "formatBlock" value',
'pad': 'foobar^baz',
'expected': '',
'accept': 'normal' },
{ 'id': 'FB_H1-1_SC',
'desc': 'query the "formatBlock" value',
'pad': '<h1>foobar^baz</h1>',
'expected': 'h1' },
{ 'id': 'FB_PRE-1_SC',
'desc': 'query the "formatBlock" value',
'pad': '<pre>foobar^baz</pre>',
'expected': 'pre' },
{ 'id': 'FB_BQ-1_SC',
'desc': 'query the "formatBlock" value',
'pad': '<blockquote>foobar^baz</blockquote>',
'expected': 'blockquote' },
{ 'id': 'FB_ADDRESS-1_SC',
'desc': 'query the "formatBlock" value',
'pad': '<address>foobar^baz</address>',
'expected': 'address' },
{ 'id': 'FB_H1-H2-1_SC',
'desc': 'query the "formatBlock" value',
'pad': '<h1>foo<h2>ba^r</h2>baz</h1>',
'expected': 'h2' },
{ 'id': 'FB_H1-H2-1_SL',
'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
'pad': '<h1>fo[o<h2>ba]r</h2>baz</h1>',
'expected': 'h1' },
{ 'id': 'FB_H1-H2-1_SR',
'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
'pad': '<h1>foo<h2>b[ar</h2>ba]z</h1>',
'expected': 'h1' },
{ 'id': 'FB_TEXT-ADDRESS-1_SL',
'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
'pad': 'fo[o<ADDRESS>ba]r</ADDRESS>baz',
'expected': '',
'accept': 'normal' },
{ 'id': 'FB_TEXT-ADDRESS-1_SR',
'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
'pad': 'foo<ADDRESS>b[ar</ADDRESS>ba]z',
'expected': '',
'accept': 'normal' },
{ 'id': 'FB_H1-H2.TEXT.H2-1_SM',
'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
'pad': '<h1><h2>fo[o</h2>bar<h2>b]az</h2></h1>',
'expected': 'h1' }
]
},
{ 'desc': '[MIDAS] query heading type',
'qcvalue': 'heading',
'tests': [
{ 'id': 'H_H1-1_SC',
'desc': 'query the "heading" type',
'pad': '<h1>foobar^baz</h1>',
'expected': 'h1',
'accept': '<h1>' },
{ 'id': 'H_H3-1_SC',
'desc': 'query the "heading" type',
'pad': '<h3>foobar^baz</h3>',
'expected': 'h3',
'accept': '<h3>' },
{ 'id': 'H_H1-H2-H3-H4-1_SC',
'desc': 'query the "heading" type within nested heading tags',
'pad': '<h1><h2><h3><h4>foobar^baz</h4></h3></h2></h1>',
'expected': 'h4',
'accept': '<h4>' },
{ 'id': 'H_P-1_SC',
'desc': 'query the "heading" type outside of a heading',
'pad': '<p>foobar^baz</p>',
'expected': '' }
]
},
{ 'desc': '[MIDAS] query font name',
'qcvalue': 'fontname',
'tests': [
{ 'id': 'FN_FONTf:a-1_SI',
'rte1-id': 'q-fontname-0',
'desc': 'query the "fontname" value',
'pad': '<font face="arial">foo[bar]baz</font>',
'expected': 'arial' },
{ 'id': 'FN_SPANs:ff:a-1_SI',
'rte1-id': 'q-fontname-1',
'desc': 'query the "fontname" value',
'pad': '<span style="font-family: arial">foo[bar]baz</span>',
'expected': 'arial' },
{ 'id': 'FN_FONTf:a.s:ff:c-1_SI',
'rte1-id': 'q-fontname-2',
'desc': 'query the "fontname" value',
'pad': '<font face="arial" style="font-family: courier">foo[bar]baz</font>',
'expected': 'courier' },
{ 'id': 'FN_FONTf:a-FONTf:c-1_SI',
'rte1-id': 'q-fontname-3',
'desc': 'query the "fontname" value',
'pad': '<font face="arial"><font face="courier">foo[bar]baz</font></font>',
'expected': 'courier' },
{ 'id': 'FN_SPANs:ff:c-FONTf:a-1_SI',
'rte1-id': 'q-fontname-4',
'desc': 'query the "fontname" value',
'pad': '<span style="font-family: courier"><font face="arial">foo[bar]baz</font></span>',
'expected': 'arial' },
{ 'id': 'FN_SPAN.fs18px-1_SI',
'desc': 'query the "fontname" value',
'pad': '<span class="courier">foo[bar]baz</span>',
'expected': 'courier' },
{ 'id': 'FN_MYCOURIER-1-SI',
'desc': 'query the "fontname" value',
'pad': '<mycourier>foo[bar]baz</mycourier>',
'expected': 'courier' }
]
},
{ 'desc': '[MIDAS] query font size',
'qcvalue': 'fontsize',
'tests': [
{ 'id': 'FS_FONTsz:4-1_SI',
'rte1-id': 'q-fontsize-0',
'desc': 'query the "fontsize" value',
'pad': '<font size=4>foo[bar]baz</font>',
'expected': '18px' },
{ 'id': 'FS_FONTs:fs:l-1_SI',
'desc': 'query the "fontsize" value',
'pad': '<font style="font-size: large">foo[bar]baz</font>',
'expected': '18px' },
{ 'id': 'FS_FONT.ass.s:fs:l-1_SI',
'rte1-id': 'q-fontsize-1',
'desc': 'query the "fontsize" value',
'pad': '<font class="Apple-style-span" style="font-size: large">foo[bar]baz</font>',
'expected': '18px' },
{ 'id': 'FS_FONTsz:1.s:fs:xl-1_SI',
'rte1-id': 'q-fontsize-2',
'desc': 'query the "fontsize" value',
'pad': '<font size=1 style="font-size: x-large">foo[bar]baz</font>',
'expected': '24px' },
{ 'id': 'FS_SPAN.large-1_SI',
'desc': 'query the "fontsize" value',
'pad': '<span class="large">foo[bar]baz</span>',
'expected': 'large' },
{ 'id': 'FS_SPAN.fs18px-1_SI',
'desc': 'query the "fontsize" value',
'pad': '<span class="fs18px">foo[bar]baz</span>',
'expected': '18px' },
{ 'id': 'FA_MYLARGE-1-SI',
'desc': 'query the "fontsize" value',
'pad': '<mylarge>foo[bar]baz</mylarge>',
'expected': 'large' },
{ 'id': 'FA_MYFS18PX-1-SI',
'desc': 'query the "fontsize" value',
'pad': '<myfs18px>foo[bar]baz</myfs18px>',
'expected': '18px' }
]
},
{ 'desc': '[MIDAS] query background color',
'qcvalue': 'backcolor',
'tests': [
{ 'id': 'BC_FONTs:bc:fca-1_SI',
'rte1-id': 'q-backcolor-0',
'desc': 'query the "backcolor" value',
'pad': '<font style="background-color: #ffccaa">foo[bar]baz</font>',
'expected': '#ffccaa' },
{ 'id': 'BC_SPANs:bc:abc-1_SI',
'rte1-id': 'q-backcolor-2',
'desc': 'query the "backcolor" value',
'pad': '<span style="background-color: #aabbcc">foo[bar]baz</span>',
'expected': '#aabbcc' },
{ 'id': 'BC_FONTs:bc:084-SPAN-1_SI',
'desc': 'query the "backcolor" value, where the color was set on an ancestor',
'pad': '<font style="background-color: #008844"><span>foo[bar]baz</span></font>',
'expected': '#008844' },
{ 'id': 'BC_SPANs:bc:cde-SPAN-1_SI',
'desc': 'query the "backcolor" value, where the color was set on an ancestor',
'pad': '<span style="background-color: #ccddee"><span>foo[bar]baz</span></span>',
'expected': '#ccddee' },
{ 'id': 'BC_SPAN.ass.s:bc:rgb-1_SI',
'rte1-id': 'q-backcolor-1',
'desc': 'query the "backcolor" value',
'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0)">foo[bar]baz</span>',
'expected': '#ff0000' },
{ 'id': 'BC_SPAN.bcred-1_SI',
'desc': 'query the "backcolor" value',
'pad': '<span class="bcred">foo[bar]baz</span>',
'expected': 'red' },
{ 'id': 'BC_MYBCRED-1-SI',
'desc': 'query the "backcolor" value',
'pad': '<mybcred>foo[bar]baz</mybcred>',
'expected': 'red' }
]
},
{ 'desc': '[MIDAS] query text color',
'qcvalue': 'forecolor',
'tests': [
{ 'id': 'FC_FONTc:f00-1_SI',
'rte1-id': 'q-forecolor-0',
'desc': 'query the "forecolor" value',
'pad': '<font color="#ff0000">foo[bar]baz</font>',
'expected': '#ff0000' },
{ 'id': 'FC_SPANs:c:0f0-1_SI',
'rte1-id': 'q-forecolor-1',
'desc': 'query the "forecolor" value',
'pad': '<span style="color: #00ff00">foo[bar]baz</span>',
'expected': '#00ff00' },
{ 'id': 'FC_FONTc:333.s:c:999-1_SI',
'rte1-id': 'q-forecolor-2',
'desc': 'query the "forecolor" value',
'pad': '<font color="#333333" style="color: #999999">foo[bar]baz</font>',
'expected': '#999999' },
{ 'id': 'FC_FONTc:641-SPAN-1_SI',
'desc': 'query the "forecolor" value, where the color was set on an ancestor',
'pad': '<font color="#664411"><span>foo[bar]baz</span></font>',
'expected': '#664411' },
{ 'id': 'FC_SPANs:c:d95-SPAN-1_SI',
'desc': 'query the "forecolor" value, where the color was set on an ancestor',
'pad': '<span style="color: #dd9955"><span>foo[bar]baz</span></span>',
'expected': '#dd9955' },
{ 'id': 'FC_SPAN.red-1_SI',
'desc': 'query the "forecolor" value',
'pad': '<span class="red">foo[bar]baz</span>',
'expected': 'red' },
{ 'id': 'FC_MYRED-1-SI',
'desc': 'query the "forecolor" value',
'pad': '<myred>foo[bar]baz</myred>',
'expected': 'red' }
]
},
{ 'desc': '[MIDAS] query hilight color (same as background color)',
'qcvalue': 'hilitecolor',
'tests': [
{ 'id': 'HC_FONTs:bc:fc0-1_SI',
'rte1-id': 'q-hilitecolor-0',
'desc': 'query the "hilitecolor" value',
'pad': '<font style="background-color: #ffcc00">foo[bar]baz</font>',
'expected': '#ffcc00' },
{ 'id': 'HC_SPANs:bc:a0c-1_SI',
'rte1-id': 'q-hilitecolor-2',
'desc': 'query the "hilitecolor" value',
'pad': '<span style="background-color: #aa00cc">foo[bar]baz</span>',
'expected': '#aa00cc' },
{ 'id': 'HC_SPAN.ass.s:bc:rgb-1_SI',
'rte1-id': 'q-hilitecolor-1',
'desc': 'query the "hilitecolor" value',
'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0)">foo[bar]baz</span>',
'expected': '#ff0000' },
{ 'id': 'HC_FONTs:bc:83e-SPAN-1_SI',
'desc': 'query the "hilitecolor" value, where the color was set on an ancestor',
'pad': '<font style="background-color: #8833ee"><span>foo[bar]baz</span></font>',
'expected': '#8833ee' },
{ 'id': 'HC_SPANs:bc:b12-SPAN-1_SI',
'desc': 'query the "hilitecolor" value, where the color was set on an ancestor',
'pad': '<span style="background-color: #bb1122"><span>foo[bar]baz</span></span>',
'expected': '#bb1122' },
{ 'id': 'HC_SPAN.bcred-1_SI',
'desc': 'query the "hilitecolor" value',
'pad': '<span class="bcred">foo[bar]baz</span>',
'expected': 'red' },
{ 'id': 'HC_MYBCRED-1-SI',
'desc': 'query the "hilitecolor" value',
'pad': '<mybcred>foo[bar]baz</mybcred>',
'expected': 'red' }
]
}
]
}
QUERYVALUE_TESTS_CSS = {
'id': 'QVC',
'caption': 'queryCommandValue Tests, using styleWithCSS',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': True,
'Proposed': QUERYVALUE_TESTS['Proposed']
}

View file

@ -0,0 +1,772 @@
SELECTION_TESTS = {
'id': 'S',
'caption': 'Selection Tests',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': False,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': 'selectall',
'command': 'selectall',
'tests': [
{ 'id': 'SELALL_TEXT-1_SI',
'desc': 'select all, text only',
'pad': 'foo [bar] baz',
'expected': [ '[foo bar baz]',
'{foo bar baz}' ] },
{ 'id': 'SELALL_I-1_SI',
'desc': 'select all, with outer tags',
'pad': '<i>foo [bar] baz</i>',
'expected': '{<i>foo bar baz</i>}' }
]
},
{ 'desc': 'unselect',
'command': 'unselect',
'tests': [
{ 'id': 'UNSEL_TEXT-1_SI',
'desc': 'unselect',
'pad': 'foo [bar] baz',
'expected': 'foo bar baz' }
]
},
{ 'desc': 'sel.modify (generic)',
'tests': [
{ 'id': 'SM:m.f.c_TEXT-1_SC-1',
'desc': 'move caret 1 character forward',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo b^ar baz',
'expected': 'foo ba^r baz' },
{ 'id': 'SM:m.b.c_TEXT-1_SC-1',
'desc': 'move caret 1 character backward',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo b^ar baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:m.f.c_TEXT-1_SI-1',
'desc': 'move caret forward (sollapse selection)',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo [bar] baz',
'expected': 'foo bar^ baz' },
{ 'id': 'SM:m.b.c_TEXT-1_SI-1',
'desc': 'move caret backward (collapse selection)',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo [bar] baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:m.f.w_TEXT-1_SC-1',
'desc': 'move caret 1 word forward',
'function': 'sel.modify("move", "forward", "word");',
'pad': 'foo b^ar baz',
'expected': 'foo bar^ baz' },
{ 'id': 'SM:m.f.w_TEXT-1_SC-2',
'desc': 'move caret 1 word forward',
'function': 'sel.modify("move", "forward", "word");',
'pad': 'foo^ bar baz',
'expected': 'foo bar^ baz' },
{ 'id': 'SM:m.f.w_TEXT-1_SI-1',
'desc': 'move caret 1 word forward from non-collapsed selection',
'function': 'sel.modify("move", "forward", "word");',
'pad': 'foo [bar] baz',
'expected': 'foo bar baz^' },
{ 'id': 'SM:m.b.w_TEXT-1_SC-1',
'desc': 'move caret 1 word backward',
'function': 'sel.modify("move", "backward", "word");',
'pad': 'foo b^ar baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:m.b.w_TEXT-1_SC-3',
'desc': 'move caret 1 word backward',
'function': 'sel.modify("move", "backward", "word");',
'pad': 'foo bar ^baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:m.b.w_TEXT-1_SI-1',
'desc': 'move caret 1 word backward from non-collapsed selection',
'function': 'sel.modify("move", "backward", "word");',
'pad': 'foo [bar] baz',
'expected': '^foo bar baz' }
]
},
{ 'desc': 'sel.modify: move forward over combining diacritics, etc.',
'tests': [
{ 'id': 'SM:m.f.c_CHAR-2_SC-1',
'desc': 'move 1 character forward over combined o with diaeresis',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^&#xF6;barbaz',
'expected': 'fo&#xF6;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-3_SC-1',
'desc': 'move 1 character forward over character with combining diaeresis above',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^o&#x0308;barbaz',
'expected': 'foo&#x0308;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-4_SC-1',
'desc': 'move 1 character forward over character with combining diaeresis below',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^o&#x0324;barbaz',
'expected': 'foo&#x0324;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-5_SC-1',
'desc': 'move 1 character forward over character with combining diaeresis above and below',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^o&#x0308;&#x0324;barbaz',
'expected': 'foo&#x0308;&#x0324;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-5_SI-1',
'desc': 'move 1 character forward over character with combining diaeresis above and below, selection on diaeresis above',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo[&#x0308;]&#x0324;barbaz',
'expected': 'foo&#x0308;&#x0324;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-5_SI-2',
'desc': 'move 1 character forward over character with combining diaeresis above and below, selection on diaeresis below',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo&#x0308;[&#x0324;]barbaz',
'expected': 'foo&#x0308;&#x0324;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-5_SL',
'desc': 'move 1 character forward over character with combining diaeresis above and below, selection oblique on diaeresis and preceding text',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo[o&#x0308;]&#x0324;barbaz',
'expected': 'foo&#x0308;&#x0324;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-5_SR',
'desc': 'move 1 character forward over character with combining diaeresis above and below, selection oblique on diaeresis and following text',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo&#x0308;[&#x0324;bar]baz',
'expected': 'foo&#x0308;&#x0324;bar^baz' },
{ 'id': 'SM:m.f.c_CHAR-6_SC-1',
'desc': 'move 1 character forward over character with enclosing square',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^o&#x20DE;barbaz',
'expected': 'foo&#x20DE;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-7_SC-1',
'desc': 'move 1 character forward over character with combining long solidus overlay',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^o&#x0338;barbaz',
'expected': 'foo&#x0338;^barbaz' }
]
},
{ 'desc': 'sel.modify: move backward over combining diacritics, etc.',
'tests': [
{ 'id': 'SM:m.b.c_CHAR-2_SC-1',
'desc': 'move 1 character backward over combined o with diaeresis',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'fo&#xF6;^barbaz',
'expected': 'fo^&#xF6;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-3_SC-1',
'desc': 'move 1 character backward over character with combining diaeresis above',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0308;^barbaz',
'expected': 'fo^o&#x0308;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-4_SC-1',
'desc': 'move 1 character backward over character with combining diaeresis below',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0324;^barbaz',
'expected': 'fo^o&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-5_SC-1',
'desc': 'move 1 character backward over character with combining diaeresis above and below',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0308;&#x0324;^barbaz',
'expected': 'fo^o&#x0308;&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-5_SI-1',
'desc': 'move 1 character backward over character with combining diaeresis above and below, selection on diaeresis above',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo[&#x0308;]&#x0324;barbaz',
'expected': 'fo^o&#x0308;&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-5_SI-2',
'desc': 'move 1 character backward over character with combining diaeresis above and below, selection on diaeresis below',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0308;[&#x0324;]barbaz',
'expected': 'fo^o&#x0308;&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-5_SL',
'desc': 'move 1 character backward over character with combining diaeresis above and below, selection oblique on diaeresis and preceding text',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'fo[o&#x0308;]&#x0324;barbaz',
'expected': 'fo^o&#x0308;&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-5_SR',
'desc': 'move 1 character backward over character with combining diaeresis above and below, selection oblique on diaeresis and following text',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0308;[&#x0324;bar]baz',
'expected': 'fo^o&#x0308;&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-6_SC-1',
'desc': 'move 1 character backward over character with enclosing square',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x20DE;^barbaz',
'expected': 'fo^o&#x20DE;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-7_SC-1',
'desc': 'move 1 character backward over character with combining long solidus overlay',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0338;^barbaz',
'expected': 'fo^o&#x0338;barbaz' }
]
},
{ 'desc': 'sel.modify: move forward/backward/left/right in RTL text',
'tests': [
{ 'id': 'SM:m.f.c_Pdir:rtl-1_SC-1',
'desc': 'move caret forward 1 character in right-to-left text',
'function': 'sel.modify("move", "forward", "character");',
'pad': '<p dir="rtl">foo b^ar baz</p>',
'expected': '<p dir="rtl">foo ba^r baz</p>' },
{ 'id': 'SM:m.b.c_Pdir:rtl-1_SC-1',
'desc': 'move caret backward 1 character in right-to-left text',
'function': 'sel.modify("move", "backward", "character");',
'pad': '<p dir="rtl">foo ba^r baz</p>',
'expected': '<p dir="rtl">foo b^ar baz</p>' },
{ 'id': 'SM:m.r.c_Pdir:rtl-1_SC-1',
'desc': 'move caret 1 character to the right in LTR text within RTL context',
'function': 'sel.modify("move", "right", "character");',
'pad': '<p dir="rtl">foo b^ar baz</p>',
'expected': '<p dir="rtl">foo ba^r baz</p>' },
{ 'id': 'SM:m.l.c_Pdir:rtl-1_SC-1',
'desc': 'move caret 1 character to the left in LTR text within RTL context',
'function': 'sel.modify("move", "left", "character");',
'pad': '<p dir="rtl">foo ba^r baz</p>',
'expected': '<p dir="rtl">foo b^ar baz</p>' },
{ 'id': 'SM:m.f.c_TEXT:ar-1_SC-1',
'desc': 'move caret forward 1 character in Arabic text',
'function': 'sel.modify("move", "forward", "character");',
'pad': '&#1605;&#x0631;&#1581;^&#1576;&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;',
'expected': '&#1605;&#x0631;&#1581;&#1576;^&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;' },
{ 'id': 'SM:m.b.c_TEXT:ar-1_SC-1',
'desc': 'move caret backward 1 character in Arabic text',
'function': 'sel.modify("move", "backward", "character");',
'pad': '&#1605;&#x0631;&#1581;^&#1576;&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;',
'expected': '&#1605;&#x0631;^&#1581;&#1576;&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;' },
{ 'id': 'SM:m.f.c_TEXT:he-1_SC-1',
'desc': 'move caret forward 1 character in Hebrew text',
'function': 'sel.modify("move", "forward", "character");',
'pad': '&#x05E9;&#x05DC;^&#x05D5;&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;',
'expected': '&#x05E9;&#x05DC;&#x05D5;^&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;' },
{ 'id': 'SM:m.b.c_TEXT:he-1_SC-1',
'desc': 'move caret backward 1 character in Hebrew text',
'function': 'sel.modify("move", "backward", "character");',
'pad': '&#x05E9;&#x05DC;^&#x05D5;&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;',
'expected': '&#x05E9;^&#x05DC;&#x05D5;&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;' },
{ 'id': 'SM:m.f.c_BDOdir:rtl-1_SC-1',
'desc': 'move caret forward 1 character inside <bdo>',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo <bdo dir="rtl">b^ar</bdo> baz',
'expected': 'foo <bdo dir="rtl">ba^r</bdo> baz' },
{ 'id': 'SM:m.b.c_BDOdir:rtl-1_SC-1',
'desc': 'move caret backward 1 character inside <bdo>',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo <bdo dir="rtl">ba^r</bdo> baz',
'expected': 'foo <bdo dir="rtl">b^ar</bdo> baz' },
{ 'id': 'SM:m.r.c_BDOdir:rtl-1_SC-1',
'desc': 'move caret 1 character to the right inside <bdo>',
'function': 'sel.modify("move", "right", "character");',
'pad': 'foo <bdo dir="rtl">ba^r</bdo> baz',
'expected': 'foo <bdo dir="rtl">b^ar</bdo> baz' },
{ 'id': 'SM:m.l.c_BDOdir:rtl-1_SC-1',
'desc': 'move caret 1 character to the left inside <bdo>',
'function': 'sel.modify("move", "left", "character");',
'pad': 'foo <bdo dir="rtl">b^ar</bdo> baz',
'expected': 'foo <bdo dir="rtl">ba^r</bdo> baz' },
{ 'id': 'SM:m.f.c_TEXTrle-1_SC-rtl-1',
'desc': 'move caret forward in RTL text within RLE-PDF marks',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.b.c_TEXTrle-1_SC-rtl-1',
'desc': 'move caret backward in RTL text within RLE-PDF marks',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.r.c_TEXTrle-1_SC-rtl-1',
'desc': 'move caret 1 character to the right in RTL text within RLE-PDF marks',
'function': 'sel.modify("move", "right", "character");',
'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.l.c_TEXTrle-1_SC-rtl-1',
'desc': 'move caret 1 character to the left in RTL text within RLE-PDF marks',
'function': 'sel.modify("move", "left", "character");',
'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.f.c_TEXTrle-1_SC-ltr-1',
'desc': 'move caret forward in LTR text within RLE-PDF marks',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.b.c_TEXTrle-1_SC-ltr-1',
'desc': 'move caret backward in LTR text within RLE-PDF marks',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.r.c_TEXTrle-1_SC-ltr-1',
'desc': 'move caret 1 character to the right in LTR text within RLE-PDF marks',
'function': 'sel.modify("move", "right", "character");',
'pad': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.l.c_TEXTrle-1_SC-ltr-1',
'desc': 'move caret 1 character to the left in LTR text within RLE-PDF marks',
'function': 'sel.modify("move", "left", "character");',
'pad': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.f.c_TEXTrlo-1_SC-rtl-1',
'desc': 'move caret forward in RTL text within RLO-PDF marks',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.b.c_TEXTrlo-1_SC-rtl-1',
'desc': 'move caret backward in RTL text within RLO-PDF marks',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.r.c_TEXTrlo-1_SC-rtl-1',
'desc': 'move caret 1 character to the right in RTL text within RLO-PDF marks',
'function': 'sel.modify("move", "right", "character");',
'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.l.c_TEXTrlo-1_SC-rtl-1',
'desc': 'move caret 1 character to the left in RTL text within RLO-PDF marks',
'function': 'sel.modify("move", "left", "character");',
'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.f.c_TEXTrlo-1_SC-ltr-1',
'desc': 'move caret forward in Latin text within RLO-PDF marks',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.b.c_TEXTrlo-1_SC-ltr-1',
'desc': 'move caret backward in Latin text within RLO-PDF marks',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.r.c_TEXTrlo-1_SC-ltr-1',
'desc': 'move caret 1 character to the right in Latin text within RLO-PDF marks',
'function': 'sel.modify("move", "right", "character");',
'pad': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.l.c_TEXTrlo-1_SC-ltr-1',
'desc': 'move caret 1 character to the left in Latin text within RLO-PDF marks',
'function': 'sel.modify("move", "left", "character");',
'pad': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.f.c_TEXTrlm-1_SC-1',
'desc': 'move caret forward in RTL text within neutral characters followed by RLM',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".',
'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".' },
{ 'id': 'SM:m.b.c_TEXTrlm-1_SC-1',
'desc': 'move caret backward in RTL text within neutral characters followed by RLM',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".',
'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".' },
{ 'id': 'SM:m.r.c_TEXTrlm-1_SC-1',
'desc': 'move caret 1 character to the right in RTL text within neutral characters followed by RLM',
'function': 'sel.modify("move", "right", "character");',
'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".',
'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".' },
{ 'id': 'SM:m.l.c_TEXTrlm-1_SC-1',
'desc': 'move caret 1 character to the left in RTL text within neutral characters followed by RLM',
'function': 'sel.modify("move", "left", "character");',
'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".',
'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".' }
]
},
{ 'desc': 'sel.modify: move forward/backward over words in Japanese text',
'tests': [
{ 'id': 'SM:m.f.w_TEXT-jp_SC-1',
'desc': 'move caret forward 1 word in Japanese text (adjective)',
'function': 'sel.modify("move", "forward", "word");',
'pad': '^&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'expected': '&#x9762;&#x767D;&#x3044;^&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' },
{ 'id': 'SM:m.f.w_TEXT-jp_SC-2',
'desc': 'move caret forward 1 word in Japanese text (in the middle of a word)',
'function': 'sel.modify("move", "forward", "word");',
'pad': '&#x9762;^&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'expected': '&#x9762;&#x767D;&#x3044;^&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' },
{ 'id': 'SM:m.f.w_TEXT-jp_SC-3',
'desc': 'move caret forward 1 word in Japanese text (noun)',
'function': 'sel.modify("move", "forward", "word");',
'pad': '&#x9762;&#x767D;&#x3044;^&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'expected': [ '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;^&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;^&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' ] },
{ 'id': 'SM:m.f.w_TEXT-jp_SC-4',
'desc': 'move caret forward 1 word in Japanese text (Katakana)',
'function': 'sel.modify("move", "forward", "word");',
'pad': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;^&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'expected': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;^&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' },
{ 'id': 'SM:m.f.w_TEXT-jp_SC-5',
'desc': 'move caret forward 1 word in Japanese text (verb)',
'function': 'sel.modify("move", "forward", "word");',
'pad': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;^&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'expected': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;^&#x3002;' }
]
},
{ 'desc': 'sel.modify: extend selection forward',
'tests': [
{ 'id': 'SM:e.f.c_TEXT-1_SC-1',
'desc': 'extend selection 1 character forward',
'function': 'sel.modify("extend", "forward", "character");',
'pad': 'foo ^bar baz',
'expected': 'foo [b]ar baz' },
{ 'id': 'SM:e.f.c_TEXT-1_SI-1',
'desc': 'extend selection 1 character forward',
'function': 'sel.modify("extend", "forward", "character");',
'pad': 'foo [b]ar baz',
'expected': 'foo [ba]r baz' },
{ 'id': 'SM:e.f.w_TEXT-1_SC-1',
'desc': 'extend selection 1 word forward',
'function': 'sel.modify("extend", "forward", "word");',
'pad': 'foo ^bar baz',
'expected': 'foo [bar] baz' },
{ 'id': 'SM:e.f.w_TEXT-1_SI-1',
'desc': 'extend selection 1 word forward',
'function': 'sel.modify("extend", "forward", "word");',
'pad': 'foo [b]ar baz',
'expected': 'foo [bar] baz' },
{ 'id': 'SM:e.f.w_TEXT-1_SI-2',
'desc': 'extend selection 1 word forward',
'function': 'sel.modify("extend", "forward", "word");',
'pad': 'foo [bar] baz',
'expected': 'foo [bar baz]' }
]
},
{ 'desc': 'sel.modify: extend selection backward, shrinking it',
'tests': [
{ 'id': 'SM:e.b.c_TEXT-1_SI-2',
'desc': 'extend selection 1 character backward',
'function': 'sel.modify("extend", "backward", "character");',
'pad': 'foo [bar] baz',
'expected': 'foo [ba]r baz' },
{ 'id': 'SM:e.b.c_TEXT-1_SI-1',
'desc': 'extend selection 1 character backward',
'function': 'sel.modify("extend", "backward", "character");',
'pad': 'foo [b]ar baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SI-3',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo [bar baz]',
'expected': 'foo [bar] baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SI-2',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo [bar] baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SI-4',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo b[ar baz]',
'expected': 'foo b[ar] baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SI-5',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo b[ar] baz',
'expected': 'foo b^ar baz' }
]
},
{ 'desc': 'sel.modify: extend selection backward, creating or extending a reverse selections',
'tests': [
{ 'id': 'SM:e.b.c_TEXT-1_SC-1',
'desc': 'extend selection 1 character backward',
'function': 'sel.modify("extend", "backward", "character");',
'pad': 'foo b^ar baz',
'expected': 'foo ]b[ar baz' },
{ 'id': 'SM:e.b.c_TEXT-1_SIR-1',
'desc': 'extend selection 1 character backward',
'function': 'sel.modify("extend", "backward", "character");',
'pad': 'foo b]a[r baz',
'expected': 'foo ]ba[r baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SIR-1',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo b]a[r baz',
'expected': 'foo ]ba[r baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SIR-2',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo ]ba[r baz',
'expected': ']foo ba[r baz' }
]
},
{ 'desc': 'sel.modify: extend selection forward, shrinking a reverse selections',
'tests': [
{ 'id': 'SM:e.f.c_TEXT-1_SIR-1',
'desc': 'extend selection 1 character forward',
'function': 'sel.modify("extend", "forward", "character");',
'pad': 'foo b]a[r baz',
'expected': 'foo ba^r baz' },
{ 'id': 'SM:e.f.c_TEXT-1_SIR-2',
'desc': 'extend selection 1 character forward',
'function': 'sel.modify("extend", "forward", "character");',
'pad': 'foo ]ba[r baz',
'expected': 'foo b]a[r baz' },
{ 'id': 'SM:e.f.w_TEXT-1_SIR-1',
'desc': 'extend selection 1 word forward',
'function': 'sel.modify("extend", "forward", "word");',
'pad': 'foo ]ba[r baz',
'expected': 'foo ba^r baz' },
{ 'id': 'SM:e.f.w_TEXT-1_SIR-3',
'desc': 'extend selection 1 word forward',
'function': 'sel.modify("extend", "forward", "word");',
'pad': ']foo ba[r baz',
'expected': 'foo ]ba[r baz' }
]
},
{ 'desc': 'sel.modify: extend selection forward to line boundary',
'tests': [
{ 'id': 'SM:e.f.lb_BR.BR-1_SC-1',
'desc': 'extend selection forward to line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': 'fo^o<br>bar<br>baz',
'expected': 'fo[o]<br>bar<br>baz' },
{ 'id': 'SM:e.f.lb_BR.BR-1_SI-1',
'desc': 'extend selection forward to next line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': 'fo[o]<br>bar<br>baz',
'expected': 'fo[o<br>bar]<br>baz' },
{ 'id': 'SM:e.f.lb_BR.BR-1_SM-1',
'desc': 'extend selection forward to line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': 'fo[o<br>b]ar<br>baz',
'expected': 'fo[o<br>bar]<br>baz' },
{ 'id': 'SM:e.f.lb_P.P.P-1_SC-1',
'desc': 'extend selection forward to line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': '<p>fo^o</p><p>bar</p><p>baz</p>',
'expected': '<p>fo[o]</p><p>bar</p><p>baz</p>' },
{ 'id': 'SM:e.f.lb_P.P.P-1_SI-1',
'desc': 'extend selection forward to next line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': '<p>fo[o]</p><p>bar</p><p>baz</p>',
'expected': '<p>fo[o</p><p>bar]</p><p>baz</p>' },
{ 'id': 'SM:e.f.lb_P.P.P-1_SM-1',
'desc': 'extend selection forward to line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': '<p>fo[o</p><p>b]ar</p><p>baz</p>',
'expected': '<p>fo[o</p><p>bar]</p><p>baz</p>' },
{ 'id': 'SM:e.f.lb_P.P.P-1_SMR-1',
'desc': 'extend selection forward to line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': '<p>foo</p><p>b]a[r</p><p>baz</p>',
'expected': '<p>foo</p><p>ba[r]</p><p>baz</p>' }
]
},
{ 'desc': 'sel.modify: extend selection backward to line boundary',
'tests': [
{ 'id': 'SM:e.b.lb_BR.BR-1_SC-2',
'desc': 'extend selection backward to line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': 'foo<br>bar<br>b^az',
'expected': 'foo<br>bar<br>]b[az' },
{ 'id': 'SM:e.b.lb_BR.BR-1_SIR-2',
'desc': 'extend selection backward to previous line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': 'foo<br>bar<br>]b[az',
'expected': 'foo<br>]bar<br>b[az' },
{ 'id': 'SM:e.b.lb_BR.BR-1_SMR-2',
'desc': 'extend selection backward to line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': 'foo<br>ba]r<br>b[az',
'expected': 'foo<br>]bar<br>b[az' },
{ 'id': 'SM:e.b.lb_P.P.P-1_SC-2',
'desc': 'extend selection backward to line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': '<p>foo</p><p>bar</p><p>b^az</p>',
'expected': '<p>foo</p><p>bar</p><p>]b[az</p>' },
{ 'id': 'SM:e.b.lb_P.P.P-1_SIR-2',
'desc': 'extend selection backward to previous line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': '<p>foo</p><p>bar</p><p>]b[az</p>',
'expected': '<p>foo</p><p>]bar</p><p>b[az</p>' },
{ 'id': 'SM:e.b.lb_P.P.P-1_SMR-2',
'desc': 'extend selection backward to line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': '<p>foo</p><p>ba]r</p><p>b[az</p>',
'expected': '<p>foo</p><p>]bar</p><p>b[az</p>' },
{ 'id': 'SM:e.b.lb_P.P.P-1_SM-2',
'desc': 'extend selection backward to line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': '<p>foo</p><p>b[a]r</p><p>baz</p>',
'expected': '<p>foo</p><p>]b[ar</p><p>baz</p>' }
]
},
{ 'desc': 'sel.modify: extend selection forward to next line (NOTE: use identical text in every line!)',
'tests': [
{ 'id': 'SM:e.f.l_BR.BR-2_SC-1',
'desc': 'extend selection forward to next line',
'function': 'sel.modify("extend", "forward", "line");',
'pad': 'fo^o<br>foo<br>foo',
'expected': 'fo[o<br>fo]o<br>foo' },
{ 'id': 'SM:e.f.l_BR.BR-2_SI-1',
'desc': 'extend selection forward to next line',
'function': 'sel.modify("extend", "forward", "line");',
'pad': 'fo[o]<br>foo<br>foo',
'expected': 'fo[o<br>foo]<br>foo' },
{ 'id': 'SM:e.f.l_BR.BR-2_SM-1',
'desc': 'extend selection forward to next line',
'function': 'sel.modify("extend", "forward", "line");',
'pad': 'fo[o<br>f]oo<br>foo',
'expected': 'fo[o<br>foo<br>f]oo' },
{ 'id': 'SM:e.f.l_P.P-1_SC-1',
'desc': 'extend selection forward to next line over paragraph boundaries',
'function': 'sel.modify("extend", "forward", "line");',
'pad': '<p>foo^bar</p><p>foobar</p>',
'expected': '<p>foo[bar</p><p>foo]bar</p>' },
{ 'id': 'SM:e.f.l_P.P-1_SMR-1',
'desc': 'extend selection forward to next line over paragraph boundaries',
'function': 'sel.modify("extend", "forward", "line");',
'pad': '<p>fo]obar</p><p>foob[ar</p>',
'expected': '<p>foobar</p><p>fo]ob[ar</p>' }
]
},
{ 'desc': 'sel.modify: extend selection backward to previous line (NOTE: use identical text in every line!)',
'tests': [
{ 'id': 'SM:e.b.l_BR.BR-2_SC-2',
'desc': 'extend selection backward to previous line',
'function': 'sel.modify("extend", "backward", "line");',
'pad': 'foo<br>foo<br>f^oo',
'expected': 'foo<br>f]oo<br>f[oo' },
{ 'id': 'SM:e.b.l_BR.BR-2_SIR-2',
'desc': 'extend selection backward to previous line',
'function': 'sel.modify("extend", "backward", "line");',
'pad': 'foo<br>foo<br>]f[oo',
'expected': 'foo<br>]foo<br>f[oo' },
{ 'id': 'SM:e.b.l_BR.BR-2_SMR-2',
'desc': 'extend selection backward to previous line',
'function': 'sel.modify("extend", "backward", "line");',
'pad': 'foo<br>fo]o<br>f[oo',
'expected': 'fo]o<br>foo<br>f[oo' },
{ 'id': 'SM:e.b.l_P.P-1_SC-2',
'desc': 'extend selection backward to next line over paragraph boundaries',
'function': 'sel.modify("extend", "backward", "line");',
'pad': '<p>foobar</p><p>foo^bar</p>',
'expected': '<p>foo]bar</p><p>foo[bar</p>' },
{ 'id': 'SM:e.b.l_P.P-1_SM-1',
'desc': 'extend selection backward to next line over paragraph boundaries',
'function': 'sel.modify("extend", "backward", "line");',
'pad': '<p>fo[obar</p><p>foob]ar</p>',
'expected': '<p>fo[ob]ar</p><p>foobar</p>' }
]
},
{ 'desc': 'sel.selectAllChildren(<element>)',
'function': 'sel.selectAllChildren(doc.getElementById("div"));',
'tests': [
{ 'id': 'SAC:div_DIV-1_SC-1',
'desc': 'selectAllChildren(<div>)',
'pad': 'foo<div id="div">bar <span>ba^z</span></div>qoz',
'expected': [ 'foo<div id="div">[bar <span>baz</span>}</div>qoz',
'foo<div id="div">{bar <span>baz</span>}</div>qoz' ] },
]
}
]
}

View file

@ -0,0 +1,462 @@
UNAPPLY_TESTS = {
'id': 'U',
'caption': 'Unapply Existing Formatting Tests',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': False,
'expected': 'foo[bar]baz',
'RFC': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': 'remove link',
'command': 'unlink',
'tests': [
{ 'id': 'UNLINK_A-1_SO',
'desc': 'unlink wrapped <a> element',
'pad': 'foo[<a>bar</a>]baz' },
{ 'id': 'UNLINK_A-1_SW',
'desc': 'unlink <a> element where the selection wraps the full content',
'pad': 'foo<a>[bar]</a>baz' },
{ 'id': 'UNLINK_An:a.h:id-1_SO',
'desc': 'unlink wrapped <a> element that has a name and href attribute',
'pad': 'foo[<a name="A" href="#UNLINK:An:a.h:id-1_SO">bar</a>]baz' },
{ 'id': 'UNLINK_A-2_SO',
'desc': 'unlink contained <a> element',
'pad': 'foo[b<a>a</a>r]baz' },
{ 'id': 'UNLINK_A2-1_SO',
'desc': 'unlink 2 contained <a> elements',
'pad': 'foo[<a>b</a>a<a>r</a>]baz' }
]
}
],
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': 'remove bold',
'command': 'bold',
'tests': [
{ 'id': 'B_B-1_SW',
'rte1-id': 'u-bold-0',
'desc': 'Selection within tags; remove <b> tags',
'pad': 'foo<b>[bar]</b>baz' },
{ 'id': 'B_B-1_SO',
'desc': 'Selection outside of tags; remove <b> tags',
'pad': 'foo[<b>bar</b>]baz' },
{ 'id': 'B_B-1_SL',
'desc': 'Selection oblique left; remove <b> tags',
'pad': 'foo[<b>bar]</b>baz' },
{ 'id': 'B_B-1_SR',
'desc': 'Selection oblique right; remove <b> tags',
'pad': 'foo<b>[bar</b>]baz' },
{ 'id': 'B_STRONG-1_SW',
'rte1-id': 'u-bold-1',
'desc': 'Selection within tags; remove <strong> tags',
'pad': 'foo<strong>[bar]</strong>baz' },
{ 'id': 'B_STRONG-1_SO',
'desc': 'Selection outside of tags; remove <strong> tags',
'pad': 'foo[<strong>bar</strong>]baz' },
{ 'id': 'B_STRONG-1_SL',
'desc': 'Selection oblique left; remove <strong> tags',
'pad': 'foo[<strong>bar]</strong>baz' },
{ 'id': 'B_STRONG-1_SR',
'desc': 'Selection oblique right; remove <strong> tags',
'pad': 'foo<strong>[bar</strong>]baz' },
{ 'id': 'B_SPANs:fw:b-1_SW',
'rte1-id': 'u-bold-2',
'desc': 'Selection within tags; remove "font-weight: bold"',
'pad': 'foo<span style="font-weight: bold">[bar]</span>baz' },
{ 'id': 'B_SPANs:fw:b-1_SO',
'desc': 'Selection outside of tags; remove "font-weight: bold"',
'pad': 'foo[<span style="font-weight: bold">bar</span>]baz' },
{ 'id': 'B_SPANs:fw:b-1_SL',
'desc': 'Selection oblique left; remove "font-weight: bold"',
'pad': 'foo[<span style="font-weight: bold">bar]</span>baz' },
{ 'id': 'B_SPANs:fw:b-1_SR',
'desc': 'Selection oblique right; remove "font-weight: bold"',
'pad': 'foo<span style="font-weight: bold">[bar</span>]baz' },
{ 'id': 'B_B-P3-1_SO12',
'desc': 'Unbolding multiple paragraphs in inside bolded content with content-model violation',
'pad': '<b>{<p>foo</p><p>bar</p>}<p>baz</p></b>',
'expected': [ '<p>[foo</p><p>bar]</p><p><b>baz</b></p>',
'<p>[foo</p><p>bar]</p><b><p>baz</p></b>' ] },
{ 'id': 'B_B-P-I..P-1_SO-I',
'desc': 'Unbolding italicized content inside bolded content with content-model violation',
'pad': '<b><p>foo[<i>bar</i>]</p><p>baz</p></b>',
'expected': [ '<p><b>foo</b><i>[bar]</i></p><p><b>baz</b></p>',
'<b><p>foo</p></b><p><i>[bar]</i></p><b><p>baz</p></b>' ] },
{ 'id': 'B_B-2_SL',
'desc': 'Remove partially covered bold, selection extends left',
'pad': 'foo [bar <b>baz] qoz</b> quz sic',
'expected': 'foo [bar baz]<b> qoz</b> quz sic' },
{ 'id': 'B_B-2_SR',
'desc': 'Remove partially covered bold, selection extends right',
'pad': 'foo bar <b>baz [qoz</b> quz] sic',
'expected': 'foo bar <b>baz </b>[qoz quz] sic' }
]
},
{ 'desc': 'remove italic',
'command': 'italic',
'tests': [
{ 'id': 'I_I-1_SW',
'rte1-id': 'u-italic-0',
'desc': 'Selection within tags; remove <i> tags',
'pad': 'foo<i>[bar]</i>baz' },
{ 'id': 'I_I-1_SO',
'desc': 'Selection outside of tags; remove <i> tags',
'pad': 'foo[<i>bar</i>]baz' },
{ 'id': 'I_I-1_SL',
'desc': 'Selection oblique left; remove <i> tags',
'pad': 'foo[<i>bar]</i>baz' },
{ 'id': 'I_I-1_SR',
'desc': 'Selection oblique right; remove <i> tags',
'pad': 'foo<i>[bar</i>]baz' },
{ 'id': 'I_EM-1_SW',
'rte1-id': 'u-italic-1',
'desc': 'Selection within tags; remove <em> tags',
'pad': 'foo<em>[bar]</em>baz' },
{ 'id': 'I_EM-1_SO',
'desc': 'Selection outside of tags; remove <em> tags',
'pad': 'foo[<em>bar</em>]baz' },
{ 'id': 'I_EM-1_SL',
'desc': 'Selection oblique left; remove <em> tags',
'pad': 'foo[<em>bar]</em>baz' },
{ 'id': 'I_EM-1_SR',
'desc': 'Selection oblique right; remove <em> tags',
'pad': 'foo<em>[bar</em>]baz' },
{ 'id': 'I_SPANs:fs:i-1_SW',
'rte1-id': 'u-italic-2',
'desc': 'Selection within tags; remove "font-style: italic"',
'pad': 'foo<span style="font-style: italic">[bar]</span>baz' },
{ 'id': 'I_SPANs:fs:i-1_SO',
'desc': 'Selection outside of tags; Italicize "font-style: italic"',
'pad': 'foo[<span style="font-style: italic">bar</span>]baz' },
{ 'id': 'I_SPANs:fs:i-1_SL',
'desc': 'Selection oblique left; Italicize "font-style: italic"',
'pad': 'foo[<span style="font-style: italic">bar]</span>baz' },
{ 'id': 'I_SPANs:fs:i-1_SR',
'desc': 'Selection oblique right; Italicize "font-style: italic"',
'pad': 'foo<span style="font-style: italic">[bar</span>]baz' },
{ 'id': 'I_I-P3-1_SO2',
'desc': 'Unitalicize content with content-model violation',
'pad': '<i><p>foo</p>{<p>bar</p>}<p>baz</p></i>',
'expected': [ '<p><i>foo</i></p><p>[bar]</p><p><i>baz</i></p>',
'<i><p>foo</p></i><p>[bar]</p><i><p>baz</p></i>' ] }
]
},
{ 'desc': 'remove underline',
'command': 'underline',
'tests': [
{ 'id': 'U_U-1_SW',
'rte1-id': 'u-underline-0',
'desc': 'Selection within tags; remove <u> tags',
'pad': 'foo<u>[bar]</u>baz' },
{ 'id': 'U_U-1_SO',
'desc': 'Selection outside of tags; remove <u> tags',
'pad': 'foo[<u>bar</u>]baz' },
{ 'id': 'U_U-1_SL',
'desc': 'Selection oblique left; remove <u> tags',
'pad': 'foo[<u>bar]</u>baz' },
{ 'id': 'U_U-1_SR',
'desc': 'Selection oblique right; remove <u> tags',
'pad': 'foo<u>[bar</u>]baz' },
{ 'id': 'U_SPANs:td:u-1_SW',
'rte1-id': 'u-underline-1',
'desc': 'Selection within tags; remove "text-decoration: underline"',
'pad': 'foo<span style="text-decoration: underline">[bar]</span>baz' },
{ 'id': 'U_SPANs:td:u-1_SO',
'desc': 'Selection outside of tags; remove "text-decoration: underline"',
'pad': 'foo[<span style="text-decoration: underline">bar</span>]baz' },
{ 'id': 'U_SPANs:td:u-1_SL',
'desc': 'Selection oblique left; remove "text-decoration: underline"',
'pad': 'foo[<span style="text-decoration: underline">bar]</span>baz' },
{ 'id': 'U_SPANs:td:u-1_SR',
'desc': 'Selection oblique right; remove "text-decoration: underline"',
'pad': 'foo<span style="text-decoration: underline">[bar</span>]baz' },
{ 'id': 'U_U-S-1_SO',
'desc': 'Removing underline from underlined content with striked content',
'pad': '<u>foo[bar<s>baz</s>quoz]</u>',
'expected': '<u>foo</u>[bar<s>baz</s>quoz]' },
{ 'id': 'U_U-S-2_SI',
'desc': 'Removing underline from striked content inside underlined content',
'pad': '<u><s>foo[bar]baz</s>quoz</u>',
'expected': '<s><u>foo</u>[bar]<u>baz</u>quoz</s>' },
{ 'id': 'U_U-P3-1_SO',
'desc': 'Removing underline from underlined content with content-model violation',
'pad': '<u><p>foo</p>{<p>bar</p>}<p>baz</p></u>',
'expected': [ '<p><u>foo</u></p><p>[bar]</p><p><u>baz</u></p>',
'<u><p>foo</p></u><p>[bar]</p><u><p>baz</p></u>' ] }
]
},
{ 'desc': 'remove strike through',
'command': 'strikethrough',
'tests': [
{ 'id': 'S_S-1_SW',
'rte1-id': 'u-strikethrough-1',
'desc': 'Selection within tags; remove <s> tags',
'pad': 'foo<s>[bar]</s>baz' },
{ 'id': 'S_S-1_SO',
'desc': 'Selection outside of tags; remove <s> tags',
'pad': 'foo[<s>bar</s>]baz' },
{ 'id': 'S_S-1_SL',
'desc': 'Selection oblique left; remove <s> tags',
'pad': 'foo[<s>bar]</s>baz' },
{ 'id': 'S_S-1_SR',
'desc': 'Selection oblique right; remove <s> tags',
'pad': 'foo<s>[bar</s>]baz' },
{ 'id': 'S_STRIKE-1_SW',
'rte1-id': 'u-strikethrough-0',
'desc': 'Selection within tags; remove <strike> tags',
'pad': 'foo<strike>[bar]</strike>baz' },
{ 'id': 'S_STRIKE-1_SO',
'desc': 'Selection outside of tags; remove <strike> tags',
'pad': 'foo[<strike>bar</strike>]baz' },
{ 'id': 'S_STRIKE-1_SL',
'desc': 'Selection oblique left; remove <strike> tags',
'pad': 'foo[<strike>bar]</strike>baz' },
{ 'id': 'S_STRIKE-2_SR',
'desc': 'Selection oblique right; remove <strike> tags',
'pad': 'foo<strike>[bar</strike>]baz' },
{ 'id': 'S_DEL-1_SW',
'rte1-id': 'u-strikethrough-2',
'desc': 'Selection within tags; remove <del> tags',
'pad': 'foo<del>[bar]</del>baz' },
{ 'id': 'S_SPANs:td:lt-1_SW',
'rte1-id': 'u-strikethrough-3',
'desc': 'Selection within tags; remove "text-decoration:line-through"',
'pad': 'foo<span style="text-decoration:line-through">[bar]</span>baz' },
{ 'id': 'S_SPANs:td:lt-1_SO',
'desc': 'Selection outside of tags; Italicize "text-decoration:line-through"',
'pad': 'foo[<span style="text-decoration:line-through">bar</span>]baz' },
{ 'id': 'S_SPANs:td:lt-1_SL',
'desc': 'Selection oblique left; Italicize "text-decoration:line-through"',
'pad': 'foo[<span style="text-decoration:line-through">bar]</span>baz' },
{ 'id': 'S_SPANs:td:lt-1_SR',
'desc': 'Selection oblique right; Italicize "text-decoration:line-through"',
'pad': 'foo<span style="text-decoration:line-through">[bar</span>]baz' },
{ 'id': 'S_S-U-1_SI',
'desc': 'Removing underline from underlined content inside striked content',
'pad': '<s><u>foo[bar]baz</u>quoz</s>',
'expected': '<s><u>foo</u></s><u>[bar]</u><s><u>baz</u>quoz</s>' },
{ 'id': 'S_U-S-1_SI',
'desc': 'Removing underline from striked content inside underlined content',
'pad': '<u><s>foo[bar]baz</s>quoz</u>',
'expected': '<u><s>foo</s>[bar]<s>baz</s>quoz</u>' }
]
},
{ 'desc': 'remove subscript',
'command': 'subscript',
'tests': [
{ 'id': 'SUB_SUB-1_SW',
'rte1-id': 'u-subscript-0',
'desc': 'remove subscript',
'pad': 'foo<sub>[bar]</sub>baz' },
{ 'id': 'SUB_SPANs:va:sub-1_SW',
'rte1-id': 'u-subscript-1',
'desc': 'remove subscript',
'pad': 'foo<span style="vertical-align: sub">[bar]</span>baz' }
]
},
{ 'desc': 'remove superscript',
'command': 'superscript',
'tests': [
{ 'id': 'SUP_SUP-1_SW',
'rte1-id': 'u-superscript-0',
'desc': 'remove superscript',
'pad': 'foo<sup>[bar]</sup>baz' },
{ 'id': 'SUP_SPANs:va:super-1_SW',
'rte1-id': 'u-superscript-1',
'desc': 'remove superscript',
'pad': 'foo<span style="vertical-align: super">[bar]</span>baz' }
]
},
{ 'desc': 'remove links',
'command': 'unlink',
'tests': [
{ 'id': 'UNLINK_Ahref:url-1_SW',
'rte1-id': 'u-unlink-0',
'desc': 'unlink an <a> element with href attribute where all children are selected',
'pad': 'foo<a href="http://www.goo.gl">[bar]</a>baz' },
{ 'id': 'UNLINK_A-1_SC',
'desc': 'unlink an <a> element that contains the collapsed selection',
'pad': 'foo<a>ba^r</a>baz',
'expected': 'fooba^rbaz' },
{ 'id': 'UNLINK_A-1_SI',
'desc': 'unlink an <a> element that contains the whole selection',
'pad': 'foo<a>b[a]r</a>baz',
'expected': 'foob[a]rbaz' },
{ 'id': 'UNLINK_A-2_SL',
'desc': 'unlink a partially contained <a> element',
'pad': 'foo[ba<a>r]ba</a>z' },
{ 'id': 'UNLINK_A-3_SR',
'desc': 'unlink a partially contained <a> element',
'pad': 'fo<a>o[ba</a>r]baz' },
{ 'id': 'UNLINK_As:d:b.fw:b-1_SW',
'desc': 'unlink, preserving styles',
'pad': 'foo<a href="#" style="display: block; font-weight: bold">[bar]</a>baz',
'expected': 'foo<span style="display: block; font-weight: bold">[bar]</span>baz' },
{ 'id': 'UNLINK_A-IMG-1_SO',
'desc': 'unlink a linked image at the start of the content',
'pad': '{<a href="#"><img src="pic.jpg" align="right" height="140" width="200"></a>abc]',
'expected': '{<img src="pic.jpg" align="right" height="140" width="200">abc]' }
]
},
{ 'desc': 'outdent',
'command': 'outdent',
'tests': [
{ 'id': 'OUTDENT_BQ-1_SW',
'rte1-id': 'u-outdent-0',
'desc': 'outdent (remove) a <blockquote>',
'pad': 'foo<blockquote>[bar]</blockquote>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' },
{ 'id': 'OUTDENT_BQ.wibq.s:m:00040.b:n.p:0-1_SW',
'rte1-id': 'u-outdent-1',
'desc': 'outdent (remove) a styled <blockquote>',
'pad': 'foo<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px">[bar]</blockquote>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' },
{ 'id': 'OUTDENT_OL-LI-1_SW',
'rte1-id': 'u-outdent-3',
'desc': 'outdent (remove) an ordered list',
'pad': 'foo<ol><li>[bar]</li></ol>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' },
{ 'id': 'OUTDENT_UL-LI-1_SW',
'rte1-id': 'u-outdent-2',
'desc': 'outdent (remove) an unordered list',
'pad': 'foo<ul><li>[bar]</li></ul>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' },
{ 'id': 'OUTDENT_DIV-1_SW',
'rte1-id': 'u-outdent-4',
'desc': 'outdent (remove) a styled <div> with margin',
'pad': 'foo<div style="margin-left: 40px;">[bar]</div>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' }
]
},
{ 'desc': 'remove all formatting',
'command': 'removeformat',
'tests': [
{ 'id': 'REMOVEFORMAT_B-1_SW',
'rte1-id': 'u-removeformat-0',
'desc': 'remove a <b> tag using "removeformat"',
'pad': 'foo<b>[bar]</b>baz' },
{ 'id': 'REMOVEFORMAT_Ahref:url-1_SW',
'rte1-id': 'u-removeformat-0',
'desc': 'remove a link using "removeformat"',
'pad': 'foo<a href="http://www.goo.gl">[bar]</a>baz' },
{ 'id': 'REMOVEFORMAT_TABLE-TBODY-TR-TD-1_SW',
'rte1-id': 'u-removeformat-2',
'desc': 'remove a table using "removeformat"',
'pad': 'foo<table><tbody><tr><td>[bar]</td></tr></tbody></table>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' }
]
},
{ 'desc': 'remove bookmark',
'command': 'unbookmark',
'tests': [
{ 'id': 'UNBOOKMARK_An:name-1_SW',
'rte1-id': 'u-unbookmark-0',
'desc': 'unlink a bookmark (a named <a> element) where all children are selected',
'pad': 'foo<a name="bookmark">[bar]</a>baz' }
]
}
]
}

View file

@ -0,0 +1,226 @@
UNAPPLY_TESTS_CSS = {
'id': 'UC',
'caption': 'Unapply Existing Formatting Tests, using styleWithCSS',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': True,
'expected': 'foo[bar]baz',
'Proposed': [
{ 'desc': '',
'id': '',
'command': '',
'tests': [
]
},
{ 'desc': 'remove bold',
'command': 'bold',
'tests': [
{ 'id': 'B_B-1_SW',
'desc': 'Selection within tags; remove <b> tags',
'pad': 'foo<b>[bar]</b>baz' },
{ 'id': 'B_B-1_SO',
'desc': 'Selection outside of tags; remove <b> tags',
'pad': 'foo[<b>bar</b>]baz' },
{ 'id': 'B_B-1_SL',
'desc': 'Selection oblique left; remove <b> tags',
'pad': 'foo[<b>bar]</b>baz' },
{ 'id': 'B_B-1_SR',
'desc': 'Selection oblique right; remove <b> tags',
'pad': 'foo<b>[bar</b>]baz' },
{ 'id': 'B_STRONG-1_SW',
'desc': 'Selection within tags; remove <strong> tags',
'pad': 'foo<strong>[bar]</strong>baz' },
{ 'id': 'B_STRONG-1_SO',
'desc': 'Selection outside of tags; remove <strong> tags',
'pad': 'foo[<strong>bar</strong>]baz' },
{ 'id': 'B_STRONG-1_SL',
'desc': 'Selection oblique left; remove <strong> tags',
'pad': 'foo[<strong>bar]</strong>baz' },
{ 'id': 'B_STRONG-1_SR',
'desc': 'Selection oblique right; remove <strong> tags',
'pad': 'foo<strong>[bar</strong>]baz' },
{ 'id': 'B_SPANs:fw:b-1_SW',
'desc': 'Selection within tags; remove "font-weight: bold"',
'pad': 'foo<span style="font-weight: bold">[bar]</span>baz' },
{ 'id': 'B_SPANs:fw:b-1_SO',
'desc': 'Selection outside of tags; remove "font-weight: bold"',
'pad': 'foo[<span style="font-weight: bold">bar</span>]baz' },
{ 'id': 'B_SPANs:fw:b-1_SL',
'desc': 'Selection oblique left; remove "font-weight: bold"',
'pad': 'foo[<span style="font-weight: bold">bar]</span>baz' },
{ 'id': 'B_SPANs:fw:b-1_SR',
'desc': 'Selection oblique right; remove "font-weight: bold"',
'pad': 'foo<span style="font-weight: bold">[bar</span>]baz' }
]
},
{ 'desc': 'remove italic',
'command': 'italic',
'tests': [
{ 'id': 'I_I-1_SW',
'desc': 'Selection within tags; remove <i> tags',
'pad': 'foo<i>[bar]</i>baz' },
{ 'id': 'I_I-1_SO',
'desc': 'Selection outside of tags; remove <i> tags',
'pad': 'foo[<i>bar</i>]baz' },
{ 'id': 'I_I-1_SL',
'desc': 'Selection oblique left; remove <i> tags',
'pad': 'foo[<i>bar]</i>baz' },
{ 'id': 'I_I-1_SR',
'desc': 'Selection oblique right; remove <i> tags',
'pad': 'foo<i>[bar</i>]baz' },
{ 'id': 'I_EM-1_SW',
'desc': 'Selection within tags; remove <em> tags',
'pad': 'foo<em>[bar]</em>baz' },
{ 'id': 'I_EM-1_SO',
'desc': 'Selection outside of tags; remove <em> tags',
'pad': 'foo[<em>bar</em>]baz' },
{ 'id': 'I_EM-1_SL',
'desc': 'Selection oblique left; remove <em> tags',
'pad': 'foo[<em>bar]</em>baz' },
{ 'id': 'I_EM-1_SR',
'desc': 'Selection oblique right; remove <em> tags',
'pad': 'foo<em>[bar</em>]baz' },
{ 'id': 'I_SPANs:fs:i-1_SW',
'desc': 'Selection within tags; remove "font-style: italic"',
'pad': 'foo<span style="font-style: italic">[bar]</span>baz' },
{ 'id': 'I_SPANs:fs:i-1_SO',
'desc': 'Selection outside of tags; Italicize "font-style: italic"',
'pad': 'foo[<span style="font-style: italic">bar</span>]baz' },
{ 'id': 'I_SPANs:fs:i-1_SL',
'desc': 'Selection oblique left; Italicize "font-style: italic"',
'pad': 'foo[<span style="font-style: italic">bar]</span>baz' },
{ 'id': 'I_SPANs:fs:i-1_SR',
'desc': 'Selection oblique right; Italicize "font-style: italic"',
'pad': 'foo<span style="font-style: italic">[bar</span>]baz' }
]
},
{ 'desc': 'remove underline',
'command': 'underline',
'tests': [
{ 'id': 'U_U-1_SW',
'desc': 'Selection within tags; remove <u> tags',
'pad': 'foo<u>[bar]</u>baz' },
{ 'id': 'U_U-1_SO',
'desc': 'Selection outside of tags; remove <u> tags',
'pad': 'foo[<u>bar</u>]baz' },
{ 'id': 'U_U-1_SL',
'desc': 'Selection oblique left; remove <u> tags',
'pad': 'foo[<u>bar]</u>baz' },
{ 'id': 'U_U-1_SR',
'desc': 'Selection oblique right; remove <u> tags',
'pad': 'foo<u>[bar</u>]baz' },
{ 'id': 'U_SPANs:td:u-1_SW',
'desc': 'Selection within tags; remove "text-decoration: underline"',
'pad': 'foo<span style="text-decoration: underline">[bar]</span>baz' },
{ 'id': 'U_SPANs:td:u-1_SO',
'desc': 'Selection outside of tags; remove "text-decoration: underline"',
'pad': 'foo[<span style="text-decoration: underline">bar</span>]baz' },
{ 'id': 'U_SPANs:td:u-1_SL',
'desc': 'Selection oblique left; remove "text-decoration: underline"',
'pad': 'foo[<span style="text-decoration: underline">bar]</span>baz' },
{ 'id': 'U_SPANs:td:u-1_SR',
'desc': 'Selection oblique right; remove "text-decoration: underline"',
'pad': 'foo<span style="text-decoration: underline">[bar</span>]baz' }
]
},
{ 'desc': 'remove strike-through',
'command': 'strikethrough',
'tests': [
{ 'id': 'S_S-1_SW',
'desc': 'Selection within tags; remove <s> tags',
'pad': 'foo<s>[bar]</s>baz' },
{ 'id': 'S_S-1_SO',
'desc': 'Selection outside of tags; remove <s> tags',
'pad': 'foo[<s>bar</s>]baz' },
{ 'id': 'S_S-1_SL',
'desc': 'Selection oblique left; remove <s> tags',
'pad': 'foo[<s>bar]</s>baz' },
{ 'id': 'S_S-1_SR',
'desc': 'Selection oblique right; remove <s> tags',
'pad': 'foo<s>[bar</s>]baz' },
{ 'id': 'S_STRIKE-1_SW',
'desc': 'Selection within tags; remove <strike> tags',
'pad': 'foo<strike>[bar]</strike>baz' },
{ 'id': 'S_STRIKE-1_SO',
'desc': 'Selection outside of tags; remove <strike> tags',
'pad': 'foo[<strike>bar</strike>]baz' },
{ 'id': 'S_STRIKE-1_SL',
'desc': 'Selection oblique left; remove <strike> tags',
'pad': 'foo[<strike>bar]</strike>baz' },
{ 'id': 'S_STRIKE-1_SR',
'desc': 'Selection oblique right; remove <strike> tags',
'pad': 'foo<strike>[bar</strike>]baz' },
{ 'id': 'S_SPANs:td:lt-1_SW',
'desc': 'Selection within tags; remove "text-decoration:line-through"',
'pad': 'foo<span style="text-decoration:line-through">[bar]</span>baz' },
{ 'id': 'S_SPANs:td:lt-1_SO',
'desc': 'Selection outside of tags; Italicize "text-decoration:line-through"',
'pad': 'foo[<span style="text-decoration:line-through">bar</span>]baz' },
{ 'id': 'S_SPANs:td:lt-1_SL',
'desc': 'Selection oblique left; Italicize "text-decoration:line-through"',
'pad': 'foo[<span style="text-decoration:line-through">bar]</span>baz' },
{ 'id': 'S_SPANs:td:lt-1_SR',
'desc': 'Selection oblique right; Italicize "text-decoration:line-through"',
'pad': 'foo<span style="text-decoration:line-through">[bar</span>]baz' },
{ 'id': 'S_SPANc:s-1_SW',
'desc': 'Unapply "strike-through" on interited CSS style',
'checkClass': True,
'pad': 'foo<span class="s">[bar]</span>baz' },
{ 'id': 'S_SPANc:s-2_SI',
'desc': 'Unapply "strike-through" on interited CSS style',
'pad': '<span class="s">foo[bar]baz</span>',
'checkClass': True,
'expected': '<span class="s">foo</span>[bar]<span class="s">baz</span>' }
]
}
]
}

View file

@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Rich Text 2 Unit Test Example</title>
<!-- utility scripts -->
<script type="text/javascript" src="static/js/variables.js"></script>
<script type="text/javascript" src="static/js/canonicalize.js"></script>
<script type="text/javascript" src="static/js/compare.js"></script>
<script type="text/javascript" src="static/js/pad.js"></script>
<script type="text/javascript" src="static/js/range.js"></script>
<script type="text/javascript" src="static/js/units.js"></script>
<script type="text/javascript" src="static/js/run.js"></script>
<!-- you do not need static/js/output.js -->
<!--
Tests - note that those have the extensions .py,
but can be used as JS files directly.
-->
<script type="text/javascript" src="tests/selection.py"></script>
<script type="text/javascript" src="tests/apply.py"></script>
<script type="text/javascript" src="tests/applyCSS.py"></script>
<script type="text/javascript" src="tests/change.py"></script>
<script type="text/javascript" src="tests/changeCSS.py"></script>
<script type="text/javascript" src="tests/unapply.py"></script>
<script type="text/javascript" src="tests/unapplyCSS.py"></script>
<script type="text/javascript" src="tests/delete.py"></script>
<script type="text/javascript" src="tests/forwarddelete.py"></script>
<script type="text/javascript" src="tests/insert.py"></script>
<script type="text/javascript" src="tests/querySupported.py"></script>
<script type="text/javascript" src="tests/queryEnabled.py"></script>
<script type="text/javascript" src="tests/queryIndeterm.py"></script>
<script type="text/javascript" src="tests/queryState.py"></script>
<script type="text/javascript" src="tests/queryValue.py"></script>
<!-- Do something -->
<script type="text/javascript">
function runTest() {
initVariables();
initEditorDocs();
runTestSuite(UNAPPLY_TESTS);
// Below alert is just a simple demonstration on how to access the test results.
// Note that we only ran UNAPPLY tests above, so we have only results from that test set.
//
// The 'results' structure is as follows:
//
// results structure containing all results
// [<suite ID>] structure containing the results for the given suite *)
// .count number of tests in the given suite
// .valscore sum of all test value results (HTML or query value)
// .selscore sum of all selection results (HTML tests only)
// [<class ID>] structure containing the results for the given class **)
// .count number of tests in the given suite
// .valscore sum of all test value results (HTML or query value)
// .selscore sum of all selection results (HTML tests only)
// [<test ID>] structure containing the reults for a given test ***)
// .valscore value score (0 or 1), minimum over all containers
// .selscore selection score (0 or 1), minimum over all containers (HTML tests only)
// .valresult worst test value result (integer, see variables.js)
// .selresult worst selection result (integer, see variables.js)
// [<cont. ID>] structure containing the results of the test for a given container ****)
// .valscore value score (0 or 1)
// .selscore selection score (0 or 1)
// .valresult value result (integer, see variables.js)
// .selresult selection result (integer, see variables.js)
// .output output string (mainly for use by the online version)
// .innerHTML inner HTML of the testing container (<div> or <body>) after the test
// .outerHTML outer HTML of the testing container (<div> or <body>) after the test
// .bodyInnerHTML inner HTML of the <body> after the test
// .bodyOuterHTML outer HTML of the <body> after the test
//
// *) <suite ID>: a 1-3 character ID, e.g. UNAPPLY_TESTS.id, or 'U' (both referring the same suite)
// **) <class ID>: one of 'Proposed', 'RFC' or 'Finalized'
// ***) <test ID>: the ID of the test, without the leading 'RTE2-<suite ID>_' part
// ****) <container ID>: one of 'div' (test within a <div contenteditable="true">)
// 'dM' (test with designMode = 'on')
// 'body' (test within a <body contenteditable="true">)
alert("Result of 'Apply' tests:\nOut of " +
results[UNAPPLY_TESTS.id].count + " tests\n" +
results[UNAPPLY_TESTS.id].valscore + " had correct HTML, and\n" +
results[UNAPPLY_TESTS.id].selscore + " had a correct result selection\n(in all testing containers)." +
"\n\n" +
"Test RTE2-U_B_B-1_SW results with a contenteditable <body>:\n" +
results['U']['Proposed']['B_B-1_SW']['body'].valscore + " points for the value result, and\n" +
results['U']['Proposed']['B_B-1_SW']['body'].selscore + " points for the selection" +
""
);
}
</script>
</head>
<body onload="runTest()">
<iframe name="iframe-dM" id="iframe-dM" src="static/editable-dM.html"></iframe>
<iframe name="iframe-body" id="iframe-body" src="static/editable-body.html"></iframe>
<iframe name="iframe-div" id="iframe-div" src="static/editable-div.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,19 @@
#!/bin/sh
set -x
if test -d richtext2; then
rm -drf richtext2;
fi
svn checkout http://browserscope.googlecode.com/svn/trunk/categories/richtext2 richtext2 | tail -1 | sed 's/[^0-9]//g' > current_revision
find richtext2 -type d -name .svn -exec rm -drf \{\} \; 2> /dev/null
# Remove test_set.py and other similarly named files because they confuse our mochitest runner
find richtext2 =type f -name test_\* -exec rm -rf \{\} \; 2> /dev/null
hg add current_revision richtext2
hg stat .

View file

@ -0,0 +1,59 @@
[default]
support-files =
lib/richtext2/current_revision
lib/richtext2/richtext2/common.py
lib/richtext2/richtext2/unittestexample.html
lib/richtext2/richtext2/static/editable-dM.html
lib/richtext2/richtext2/static/editable.css
lib/richtext2/richtext2/static/editable-body.html
lib/richtext2/richtext2/static/editable-div.html
lib/richtext2/richtext2/static/js/variables.js
lib/richtext2/richtext2/static/js/range-bootstrap.js
lib/richtext2/richtext2/static/js/range.js
lib/richtext2/richtext2/static/js/output.js
lib/richtext2/richtext2/static/js/compare.js
lib/richtext2/richtext2/static/js/canonicalize.js
lib/richtext2/richtext2/static/js/pad.js
lib/richtext2/richtext2/static/js/run.js
lib/richtext2/richtext2/static/js/units.js
lib/richtext2/richtext2/static/common.css
lib/richtext2/richtext2/__init__.py
lib/richtext2/richtext2/handlers.py
lib/richtext2/richtext2/templates/output.html
lib/richtext2/richtext2/templates/richtext2.html
lib/richtext2/richtext2/tests/forwarddelete.py
lib/richtext2/richtext2/tests/selection.py
lib/richtext2/richtext2/tests/queryIndeterm.py
lib/richtext2/richtext2/tests/unapplyCSS.py
lib/richtext2/richtext2/tests/apply.py
lib/richtext2/richtext2/tests/unapply.py
lib/richtext2/richtext2/tests/change.py
lib/richtext2/richtext2/tests/queryState.py
lib/richtext2/richtext2/tests/queryValue.py
lib/richtext2/richtext2/tests/__init__.py
lib/richtext2/richtext2/tests/insert.py
lib/richtext2/richtext2/tests/queryEnabled.py
lib/richtext2/richtext2/tests/applyCSS.py
lib/richtext2/richtext2/tests/changeCSS.py
lib/richtext2/richtext2/tests/delete.py
lib/richtext2/richtext2/tests/querySupported.py
lib/richtext2/README
lib/richtext2/update_from_upstream
lib/richtext2/LICENSE
lib/richtext2/README.Mozilla
lib/richtext2/currentStatus.js
lib/richtext/current_revision
lib/richtext/README
lib/richtext/update_from_upstream
lib/richtext/LICENSE
lib/richtext/README.Mozilla
lib/richtext/richtext/editable.html
lib/richtext/richtext/richtext.html
lib/richtext/richtext/js/range.js
lib/richtext/currentStatus.js
[test_richtext2.html]
subsuite = clipboard
skip-if = os == 'android' && debug # Bug 1202045
[test_richtext.html]

View file

@ -0,0 +1,48 @@
<!DOCTYPE HTML>
<html>
<!--
BrowserScope richtext category tests
-->
<head>
<title>BrowserScope Richtext Tests</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="lib/richtext/currentStatus.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=550569">Mozilla Bug 550569</a>
<p id="display"></p>
<div id="content">
<iframe src="lib/richtext/richtext/richtext.html"></iframe>
</div>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
// Running all of the tests can take a long time, try to account for it
SimpleTest.requestLongerTimeout(5);
function sendScore(results, continueParams) {
ok(results.length > 1, "At least one test should have been run");
for (var i = 1; i < results.length; ++i) {
var result = results[i];
[type, command, param, success] = result.split(/[\-=]/);
var comp = is;
if (isKnownFailure(type, command, param)) {
comp = todo_is;
}
comp(success, "1", "Browserscope richtext category=" + type +
" test=" + command +
" param=" + param);
}
}
document.getElementsByTagName("iframe")[0].addEventListener("load", function() {
SimpleTest.finish();
}, false);
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,233 @@
<!DOCTYPE html>
<html lang="en">
<!--
BrowserScope richtext2 category tests
This test is originally based on the unit test example available as part of the
RichText2 suite:
http://code.google.com/p/browserscope/source/browse/trunk/categories/richtext2/unittestexample.html
-->
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>BrowserScope Richtext2 Tests</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<!-- utility scripts -->
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/variables.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/canonicalize.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/compare.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/pad.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/range.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/units.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/run.js"></script>
<!-- you do not need static/js/output.js -->
<!--
Tests - note that those have the extensions .py,
but can be used as JS files directly.
-->
<script type="text/javascript" src="lib/richtext2/richtext2/tests/selection.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/apply.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/applyCSS.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/change.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/changeCSS.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/unapply.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/unapplyCSS.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/delete.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/forwarddelete.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/insert.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/querySupported.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/queryEnabled.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/queryIndeterm.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/queryState.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/queryValue.py"></script>
<script type="text/javascript" src="lib/richtext2/currentStatus.js"></script>
<!-- Do something -->
<script type="text/javascript">
// Set this constant to true in order to get the current status of the test suite.
// This is useful for updating the currentStatus.js file when an editor bug is fixed.
const UPDATE_TEST_RESULTS = false;
// some tests (at least RTE2-QE_PASTE_TEXT-1) require clipboard data
function startTest() {
SimpleTest.waitForClipboard("foo",
function() {
SpecialPowers.clipboardCopyString("foo");
},
runTest,
function() {
ok(false, "Failed to copy a string to the clipboard");
SimpleTest.finish();
}
);
}
function runTest() {
initVariables();
initEditorDocs();
const tests = [
SELECTION_TESTS,
APPLY_TESTS,
APPLY_TESTS_CSS,
CHANGE_TESTS,
CHANGE_TESTS_CSS,
UNAPPLY_TESTS,
UNAPPLY_TESTS_CSS,
DELETE_TESTS,
FORWARDDELETE_TESTS,
INSERT_TESTS,
QUERYSUPPORTED_TESTS,
QUERYENABLED_TESTS,
QUERYINDETERM_TESTS,
QUERYSTATE_TESTS,
QUERYVALUE_TESTS,
];
for (var i = 0; i < tests.length; ++i) {
runTestSuite(tests[i]);
}
// Below alert is just a simple demonstration on how to access the test results.
// Note that we only ran UNAPPLY tests above, so we have only results from that test set.
//
// The 'results' structure is as follows:
//
// results structure containing all results
// [<suite ID>] structure containing the results for the given suite *)
// .count number of tests in the given suite
// .valscore sum of all test value results (HTML or query value)
// .selscore sum of all selection results (HTML tests only)
// [<class ID>] structure containing the results for the given class **)
// .count number of tests in the given suite
// .valscore sum of all test value results (HTML or query value)
// .selscore sum of all selection results (HTML tests only)
// [<test ID>] structure containing the reults for a given test ***)
// .valscore value score (0 or 1), minimum over all containers
// .selscore selection score (0 or 1), minimum over all containers (HTML tests only)
// .valresult worst test value result (integer, see variables.js)
// .selresult worst selection result (integer, see variables.js)
// [<cont. ID>] structure containing the results of the test for a given container ****)
// .valscore value score (0 or 1)
// .selscore selection score (0 or 1)
// .valresult value result (integer, see variables.js)
// .selresult selection result (integer, see variables.js)
// .output output string (mainly for use by the online version)
// .innerHTML inner HTML of the testing container (<div> or <body>) after the test
// .outerHTML outer HTML of the testing container (<div> or <body>) after the test
// .bodyInnerHTML inner HTML of the <body> after the test
// .bodyOuterHTML outer HTML of the <body> after the test
//
// *) <suite ID>: a 1-3 character ID, e.g. UNAPPLY_TESTS.id, or 'U' (both referring the same suite)
// **) <class ID>: one of 'Proposed', 'RFC' or 'Finalized'
// ***) <test ID>: the ID of the test, without the leading 'RTE2-<suite ID>_' part
// ****) <container ID>: one of 'div' (test within a <div contenteditable="true">)
// 'dM' (test with designMode = 'on')
// 'body' (test within a <body contenteditable="true">)
if (UPDATE_TEST_RESULTS) {
var newKnownFailures = {value: {}, select: {}};
for (var i = 0; i < tests.length; ++i) {
var category = tests[i];
for (var group in results[category.id]) {
switch (group) {
// Skip the known properties
case "count":
case "valscore":
case "selscore":
case "time":
break;
default:
for (var test_id in results[category.id][group]) {
switch (test_id) {
// Skip the known properties
case "count":
case "valscore":
case "selscore":
break;
default:
for (var structure in results[category.id][group][test_id]) {
switch (structure) {
// Only look at each test structure
case "dM":
case "body":
case "div":
if (!results[category.id][group][test_id][structure].valscore) {
newKnownFailures.value[category.id + "-" + group + "-" + test_id + "-" + structure] = true;
}
if (!results[category.id][group][test_id][structure].selscore) {
newKnownFailures.select[category.id + "-" + group + "-" + test_id + "-" + structure] = true;
}
}
}
}
}
}
}
}
var resultContainer = document.getElementById("results");
resultContainer.style.display = "";
resultContainer.textContent = JSON.stringify(newKnownFailures);
} else {
for (var i = 0; i < tests.length; ++i) {
var category = tests[i];
for (var group in results[category.id]) {
switch (group) {
// Skip the known properties
case "count":
case "valscore":
case "selscore":
case "time":
break;
default:
for (var test_id in results[category.id][group]) {
switch (test_id) {
// Skip the known properties
case "count":
case "valscore":
case "selscore":
break;
default:
for (var structure in results[category.id][group][test_id]) {
switch (structure) {
// Only look at each test structure
case "dM":
case "body":
case "div":
var row = results[category.id][group][test_id][structure];
var testName = [category.id, group, test_id, structure].join("-");
(testName in knownFailures.value ? todo_is : is)(
row.valscore, 1, "Browserscope richtext2 value: " + testName);
(testName in knownFailures.select ? todo_is : is)(
row.selscore, 1, "Browserscope richtext2 selection: " + testName);
}
}
}
}
}
}
}
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
// Running all of the tests can take a long time, try to account for it
SimpleTest.requestLongerTimeout(5);
</script>
</head>
<body onload="startTest()">
<iframe name="iframe-dM" id="iframe-dM" src="lib/richtext2/richtext2/static/editable-dM.html"></iframe>
<iframe name="iframe-body" id="iframe-body" src="lib/richtext2/richtext2/static/editable-body.html"></iframe>
<iframe name="iframe-div" id="iframe-div" src="lib/richtext2/richtext2/static/editable-div.html"></iframe>
<pre id="results" style="display: none"></pre>
</body>
</html>

View file

@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<html>
<body>
<div id="content">
<iframe id="formTarget" name="formTarget"></iframe>
<form action="data:text/html," target="formTarget">
<input name="test" id="initValue"><input type="submit">
</form>
</div>
</body>
</html

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
<style>
textarea { resize: none }
</style>
</head>
<body>
<div id="content">
<textarea id="ltr-ref" style="display: none">test.</textarea>
<textarea id="rtl-ref" style="display: none; direction: rtl">test.</textarea>
</div
</body>
</html>

View file

@ -0,0 +1,14 @@
[DEFAULT]
skip-if = os == 'android'
support-files = green.png
[test_bug489202.xul]
[test_bug599983.xul]
[test_bug607584.xul]
[test_bug616590.xul]
[test_bug780908.xul]
[test_contenteditable_text_input_handling.html]
[test_htmleditor_keyevent_handling.html]
[test_set_document_title_transaction.html]
[test_texteditor_keyevent_handling.html]
skip-if = (debug && os=='win') || (os == 'linux') # Bug 1116205, leaks on windows debug, fails delete key on linux

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,18 @@
Version:0.9
StartHTML:-1
EndHTML:-1
StartFragment:0000000111
EndFragment:0000000246
<!--StartFragment-->
<html>
<head>
<title>Test</title>
</head>
<body>
<p>
3.<b>1415926535897932</b>
</p>
</body>
</html>
<!--EndFragment-->

Binary file not shown.

View file

@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<body>
<a href="">test</a>
<div id="editor" contenteditable="true">abc</div>
<div style="height: 20000px;"></div>
</body>
</html>

View file

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<body>
<div style="height: 20000px;"></div>
<textarea id="editor"></textarea>
</body>
</html>

View file

@ -0,0 +1,5 @@
<!DOCTYPE>
<script>
localStorage["clicked"] = "true";
close();
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<body>
<button>Button</button>
<img src="green.png" usemap="#map">
<map name="map">
<!-- This URL ensures that the link doesn't get clicked, since
mochitests cannot access the outside network. -->
<area shape="rect" coords="0,0,10,10" href="https://youtube.com/">
</map>
<div style="height: 20000px;" tabindex="-1"><hr></div>
</body>
</html>

View file

@ -0,0 +1,41 @@
<html>
<head>
<script type="text/javascript">
function is(aLeft, aRight, aMessage)
{
window.opener.SimpleTest.is(aLeft, aRight, aMessage);
}
function unload()
{
window.opener.SimpleTest.finish();
}
function boom()
{
var root = document.documentElement;
while(root.firstChild) {
root.removeChild(root.firstChild);
}
root.appendChild(document.createTextNode("Mozilla"));
root.focus();
cespan = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
cespan.setAttributeNS(null, "contenteditable", "true");
root.appendChild(cespan);
try {
document.execCommand("selectAll", false, null);
} catch(e) { }
is(window.getSelection().toString(), "Mozilla",
"The nodes are not selected");
window.close();
}
window.opener.SimpleTest.waitForFocus(boom, window);
</script></head>
<body onunload="unload();"></body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

View file

@ -0,0 +1,245 @@
[DEFAULT]
support-files =
data/cfhtml-chromium.txt
data/cfhtml-firefox.txt
data/cfhtml-ie.txt
data/cfhtml-ooo.txt
data/cfhtml-nocontext.txt
file_bug549262.html
file_bug586662.html
file_bug674770-1.html
file_bug915962.html
file_select_all_without_body.html
green.png
spellcheck.js
[test_bug46555.html]
[test_bug200416.html]
[test_bug289384.html]
skip-if = os != "mac"
[test_bug290026.html]
[test_bug291780.html]
[test_bug309731.html]
[test_bug316447.html]
[test_bug318065.html]
[test_bug332636.html]
support-files = test_bug332636.html^headers^
[test_bug366682.html]
skip-if = os == 'android'
[test_bug372345.html]
skip-if = toolkit == 'android'
[test_bug404320.html]
[test_bug408231.html]
skip-if = toolkit == 'android'
[test_bug410986.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug414526.html]
[test_bug417418.html]
skip-if = android_version == '18' # bug 1147989
[test_bug432225.html]
skip-if = toolkit == 'android'
[test_bug439808.html]
[test_bug442186.html]
[test_bug449243.html]
[test_bug455992.html]
[test_bug456244.html]
[test_bug460740.html]
[test_bug468353.html]
[test_bug471319.html]
[test_bug471722.html]
[test_bug478725.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug480647.html]
[test_bug480972.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug483651.html]
[test_bug484181.html]
skip-if = toolkit == 'android'
[test_bug487524.html]
[test_bug490879.html]
subsuite = clipboard
skip-if = toolkit == 'android' # bug 1299578
[test_bug502673.html]
[test_bug514156.html]
[test_bug520189.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug525389.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug537046.html]
[test_bug549262.html]
skip-if = toolkit == 'android'
[test_bug550434.html]
skip-if = android_version == '18' # bug 1147989
[test_bug551704.html]
subsuite = clipboard
[test_bug552782.html]
[test_bug567213.html]
[test_bug569988.html]
skip-if = os == 'android'
[test_bug570144.html]
[test_bug578771.html]
skip-if = android_version == '18' # bug 1147989
[test_bug586662.html]
skip-if = toolkit == 'android'
[test_bug587461.html]
[test_bug590554.html]
[test_bug592592.html]
[test_bug596001.html]
subsuite = clipboard
[test_bug596333.html]
skip-if = toolkit == 'android'
[test_bug596506.html]
[test_bug597331.html]
skip-if = toolkit == 'android' || asan || (os == "win" && os_version != "5.1") # Bug 718316, Bug 1211213
[test_bug597784.html]
[test_bug599322.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug599983.html]
[test_bug600570.html]
subsuite = clipboard
skip-if = toolkit == 'android' || (os == "win" && os_version != "5.1") # Bug 718316
[test_bug602130.html]
[test_bug603556.html]
subsuite = clipboard
[test_bug604532.html]
skip-if = toolkit == 'android'
[test_bug607584.html]
[test_bug611182.html]
skip-if = toolkit == 'android'
[test_bug612128.html]
[test_bug612447.html]
[test_bug620906.html]
skip-if = toolkit == 'android' #TIMED_OUT
[test_bug622371.html]
skip-if = toolkit == 'android' #bug 957797
[test_bug625452.html]
[test_bug629845.html]
[test_bug635636.html]
skip-if = e10s || os == 'android'
[test_bug636465.html]
skip-if = os == 'android'
[test_bug638596.html]
[test_bug640321.html]
skip-if = android_version == '18' # bug 1147989
[test_bug641466.html]
[test_bug645914.html]
[test_bug646194.html]
[test_bug668599.html]
[test_bug674770-1.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug674770-2.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug674861.html]
[test_bug676401.html]
[test_bug677752.html]
[test_bug681229.html]
subsuite = clipboard
[test_bug686203.html]
[test_bug692520.html]
[test_bug697842.html]
[test_bug725069.html]
[test_bug735059.html]
[test_bug738366.html]
[test_bug740784.html]
[test_bug742261.html]
[test_bug757371.html]
[test_bug757771.html]
[test_bug767684.html]
[test_bug772796.html]
skip-if = toolkit == 'android' # bug 1309431
[test_bug773262.html]
[test_bug780035.html]
[test_bug787432.html]
[test_bug790475.html]
[test_bug795418.html]
[test_bug795418-2.html]
[test_bug795418-3.html]
[test_bug795418-4.html]
[test_bug795418-5.html]
[test_bug795418-6.html]
[test_bug795785.html]
[test_bug796839.html]
[test_bug830600.html]
subsuite = clipboard
skip-if = e10s
[test_bug832025.html]
[test_bug850043.html]
[test_bug857487.html]
[test_bug858918.html]
[test_bug915962.html]
[test_bug974309.html]
skip-if = toolkit == 'android'
[test_bug966155.html]
skip-if = os != "win"
[test_bug966552.html]
skip-if = os != "win"
[test_bug998188.html]
[test_bug1026397.html]
[test_bug1053048.html]
[test_bug1067255.html]
[test_bug1068979.html]
subsuite = clipboard
[test_bug1094000.html]
[test_bug1100966.html]
skip-if = os == 'android'
[test_bug1102906.html]
skip-if = os == 'android'
[test_bug1101392.html]
subsuite = clipboard
[test_bug1109465.html]
[test_bug1140105.html]
[test_bug1140617.html]
subsuite = clipboard
skip-if = toolkit == 'android' # bug 1299578
[test_bug1153237.html]
[test_bug1154791.html]
skip-if = os == 'android'
[test_bug1162952.html]
[test_bug1181130-1.html]
[test_bug1181130-2.html]
[test_bug1186799.html]
[test_bug1230473.html]
[test_bug1247483.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug1248128.html]
[test_bug1250010.html]
[test_bug1257363.html]
[test_bug1248185.html]
[test_bug1258085.html]
[test_bug1268736.html]
[test_bug1270235.html]
[test_bug1310912.html]
skip-if = toolkit == 'android' # bug 1315898
[test_bug1314790.html]
[test_bug1315065.html]
[test_bug1328023.html]
[test_bug1330796.html]
[test_bug1332876.html]
[test_CF_HTML_clipboard.html]
subsuite = clipboard
[test_composition_event_created_in_chrome.html]
[test_contenteditable_focus.html]
[test_dom_input_event_on_htmleditor.html]
skip-if = toolkit == 'android' # bug 1054087
[test_dom_input_event_on_texteditor.html]
[test_dragdrop.html]
skip-if = os == 'android'
[test_keypress_untrusted_event.html]
[test_root_element_replacement.html]
[test_select_all_without_body.html]
[test_spellcheck_pref.html]
skip-if = toolkit == 'android'
[test_backspace_vs.html]
[test_css_chrome_load_access.html]
skip-if = toolkit == 'android' # chrome urls not available due to packaging
[test_selection_move_commands.html]

View file

@ -0,0 +1,20 @@
function isSpellingCheckOk(aEditor, aMisspelledWords) {
var selcon = aEditor.selectionController;
var sel = selcon.getSelection(selcon.SELECTION_SPELLCHECK);
var numWords = sel.rangeCount;
is(numWords, aMisspelledWords.length, "Correct number of misspellings and words.");
if (numWords !== aMisspelledWords.length) {
return false;
}
for (var i = 0; i < numWords; ++i) {
var word = String(sel.getRangeAt(i));
is(word, aMisspelledWords[i], "Misspelling is what we think it is.");
if (word !== aMisspelledWords[i]) {
return false;
}
}
return true;
}

View file

@ -0,0 +1,159 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=572642
-->
<head>
<title>Test for Bug 572642</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=572642">Mozilla Bug 572642</a>
<p id="display"></p>
<div id="content">
<div id="editor1" contenteditable="true"></div>
<iframe id="editor2"></iframe>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 572642 **/
function copyCF_HTML(cfhtml, success, failure) {
const Cc = SpecialPowers.Cc;
const Ci = SpecialPowers.Ci;
const CF_HTML = "application/x-moz-nativehtml";
function getLoadContext() {
return SpecialPowers.wrap(window).QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext);
}
var cb = Cc["@mozilla.org/widget/clipboard;1"].
getService(Ci.nsIClipboard);
var counter = 0;
function copyCF_HTML_worker(success, failure) {
if (++counter > 50) {
ok(false, "Timed out while polling clipboard for pasted data");
failure();
return;
}
var flavors = [CF_HTML];
if (!cb.hasDataMatchingFlavors(flavors, flavors.length, cb.kGlobalClipboard)) {
setTimeout(function() { copyCF_HTML_worker(success, failure); }, 100);
return;
}
var trans = Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable);
trans.init(getLoadContext());
trans.addDataFlavor(CF_HTML);
cb.getData(trans, cb.kGlobalClipboard);
var data = SpecialPowers.createBlankObject();
try {
trans.getTransferData(CF_HTML, data, {});
data = SpecialPowers.wrap(data).value.QueryInterface(Ci.nsISupportsCString).data;
} catch (e) {
setTimeout(function() { copyCF_HTML_worker(success, failure); }, 100);
return;
}
success();
}
var trans = Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable);
trans.init(getLoadContext());
trans.addDataFlavor(CF_HTML);
var data = Cc["@mozilla.org/supports-cstring;1"].
createInstance(Ci.nsISupportsCString);
data.data = cfhtml;
trans.setTransferData(CF_HTML, data, cfhtml.length);
cb.setData(trans, null, cb.kGlobalClipboard);
copyCF_HTML_worker(success, failure);
}
function loadCF_HTMLdata(filename) {
var req = new XMLHttpRequest();
req.open("GET", filename, false);
req.overrideMimeType("text/plain; charset=x-user-defined");
req.send(null);
ok(req.status, 200, "Could not read the binary file " + filename);
return req.responseText;
}
var gTests = [
// Copied from Firefox
{fileName: "cfhtml-firefox.txt", expected: "Firefox"},
// Copied from OpenOffice.org
{fileName: "cfhtml-ooo.txt", expected: "hello"},
// Copied from IE
{fileName: "cfhtml-ie.txt", expected: "browser"},
// Copied from Chromium
{fileName: "cfhtml-chromium.txt", expected: "Pacific"},
// CF_HTML with no context specified (StartHTML and EndHTML set to -1)
{fileName: "cfhtml-nocontext.txt", expected: "3.1415926535897932"},
];
var gTestIndex = 0;
SimpleTest.waitForExplicitFinish();
for (var i = 0; i < gTests.length; ++i) {
gTests[i].data = loadCF_HTMLdata("data/" + gTests[i].fileName);
}
function runTest() {
var test = gTests[gTestIndex++];
copyCF_HTML(test.data, function() {
// contenteditable
var contentEditable = document.getElementById("editor1");
contentEditable.innerHTML = "";
contentEditable.focus();
synthesizeKey("v", {accelKey: true});
isnot(contentEditable.textContent.indexOf(test.expected), -1,
"Paste operation for " + test.fileName + " should be successful in contenteditable");
// designMode
var iframe = document.getElementById("editor2");
iframe.addEventListener("load", function() {
iframe.removeEventListener("load", arguments.callee, false);
var doc = iframe.contentDocument;
var win = doc.defaultView;
setTimeout(function() {
win.addEventListener("focus", function() {
win.removeEventListener("focus", arguments.callee, false);
doc.designMode = "on";
synthesizeKey("v", {accelKey: true}, win);
isnot(doc.body.textContent.indexOf(test.expected), -1,
"Paste operation for " + test.fileName + " should be successful in designMode");
if (gTestIndex == gTests.length)
SimpleTest.finish();
else
runTest();
}, false);
win.focus();
}, 0);
}, false);
iframe.src = "data:text/html,";
}, SimpleTest.finish);
}
var isMac = ("nsILocalFileMac" in SpecialPowers.Ci);
if (isMac)
SimpleTest.waitForFocus(runTest);
else {
// This test is not yet supported on non-Mac platforms, see bug 574005.
todo(false, "Test not supported on this platform");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,130 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1216427
-->
<head>
<title>Test for Bug 1216427</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1216427">Mozilla Bug 1216427</a>
<p id="display"></p>
<div id="content">
<div id="edit1" contenteditable="true">a&#x263a;&#xfe0f;b</div><!-- BMP symbol with VS16 -->
<div id="edit2" contenteditable="true">a&#x1f310;&#xfe0e;b</div><!-- plane 1 symbol with VS15 -->
<div id="edit3" contenteditable="true">a&#x3402;&#xE0100;b</div><!-- BMP ideograph with VS17 -->
<div id="edit4" contenteditable="true">a&#x20000;&#xE0101;b</div><!-- SMP ideograph with VS18 -->
<div id="edit5" contenteditable="true">a&#x263a;&#xfe01;&#xfe02;&#xfe03;b</div><!-- BMP symbol with extra VSes -->
<div id="edit6" contenteditable="true">a&#x20000;&#xE0100;&#xE0101;&#xE0102;b</div><!-- SMP symbol with extra VSes -->
<!-- The Regional Indicator combinations here were supported by Apple Color Emoji
even prior to the major extension of coverage in the 10.10.5 timeframe. -->
<div id="edit7" contenteditable="true">a&#x1F1E8;&#x1F1F3;b</div><!-- Regional Indicator flag: CN -->
<div id="edit8" contenteditable="true">a&#x1F1E8;&#x1F1F3;&#x1F1E9;&#x1F1EA;b</div><!-- two RI flags: CN, DE -->
<div id="edit9" contenteditable="true">a&#x1F1E8;&#x1F1F3;&#x1F1E9;&#x1F1EA;&#x1F1EA;&#x1F1F8;b</div><!-- three RI flags: CN, DE, ES -->
<div id="edit10" contenteditable="true">a&#x1F1E8;&#x1F1F3;&#x1F1E9;&#x1F1EA;&#x1F1EA;&#x1F1F8;&#x1F1EB;&#x1F1F7;b</div><!-- four RI flags: CN, DE, ES, FR -->
<div id="edit11" contenteditable="true">a&#x1F1E8;&#x1F1F3;&#x1F1E9;&#x1F1EA;&#x1F1EA;&#x1F1F8;&#x1F1EB;&#x1F1F7;&#x1F1EC;&#x1F1E7;b</div><!-- five RI flags: CN, DE, ES, FR, GB -->
<div id="edit1b" contenteditable="true">a&#x263a;&#xfe0f;b</div><!-- BMP symbol with VS16 -->
<div id="edit2b" contenteditable="true">a&#x1f310;&#xfe0e;b</div><!-- plane 1 symbol with VS15 -->
<div id="edit3b" contenteditable="true">a&#x3402;&#xE0100;b</div><!-- BMP ideograph with VS17 -->
<div id="edit4b" contenteditable="true">a&#x20000;&#xE0101;b</div><!-- SMP ideograph with VS18 -->
<div id="edit5b" contenteditable="true">a&#x263a;&#xfe01;&#xfe02;&#xfe03;b</div><!-- BMP symbol with extra VSes -->
<div id="edit6b" contenteditable="true">a&#x20000;&#xE0100;&#xE0101;&#xE0102;b</div><!-- SMP symbol with extra VSes -->
<div id="edit7b" contenteditable="true">a&#x1F1E8;&#x1F1F3;b</div><!-- Regional Indicator flag: CN -->
<div id="edit8b" contenteditable="true">a&#x1F1E8;&#x1F1F3;&#x1F1E9;&#x1F1EA;b</div><!-- two RI flags: CN, DE -->
<div id="edit9b" contenteditable="true">a&#x1F1E8;&#x1F1F3;&#x1F1E9;&#x1F1EA;&#x1F1EA;&#x1F1F8;b</div><!-- three RI flags: CN, DE, ES -->
<div id="edit10b" contenteditable="true">a&#x1F1E8;&#x1F1F3;&#x1F1E9;&#x1F1EA;&#x1F1EA;&#x1F1F8;&#x1F1EB;&#x1F1F7;b</div><!-- four RI flags: CN, DE, ES, FR -->
<div id="edit11b" contenteditable="true">a&#x1F1E8;&#x1F1F3;&#x1F1E9;&#x1F1EA;&#x1F1EA;&#x1F1F8;&#x1F1EB;&#x1F1F7;&#x1F1EC;&#x1F1E7;b</div><!-- five RI flags: CN, DE, ES, FR, GB -->
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 1216427 **/
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTest);
function test(edit, bsCount) {
edit.focus();
var sel = window.getSelection();
sel.collapse(edit.childNodes[0], edit.textContent.length - 1);
for (i = 0; i < bsCount; ++i) {
synthesizeKey("VK_BACK_SPACE", {});
}
is(edit.textContent, "ab", "The backspace key should delete the characters correctly");
}
function testWithMove(edit, offset, bsCount) {
edit.focus();
var sel = window.getSelection();
sel.collapse(edit.childNodes[0], 0);
var i;
for (i = 0; i < offset; ++i) {
synthesizeKey("VK_RIGHT", {});
synthesizeKey("VK_LEFT", {});
synthesizeKey("VK_RIGHT", {});
}
for (i = 0; i < bsCount; ++i) {
synthesizeKey("VK_BACK_SPACE", {});
}
is(edit.textContent, "ab", "The backspace key should delete the characters correctly");
}
function runTest() {
/* test backspace-deletion of the middle character(s) */
test(document.getElementById("edit1"), 1);
test(document.getElementById("edit2"), 1);
test(document.getElementById("edit3"), 1);
test(document.getElementById("edit4"), 1);
test(document.getElementById("edit5"), 1);
test(document.getElementById("edit6"), 1);
/*
* Tests with Regional Indicator flags: these behave differently depending
* whether an emoji font is present, as ligated flags are edited as single
* characters whereas non-ligated RI characters act individually.
*
* For now, only rely on such an emoji font on OS X 10.7+. (Note that the
* Segoe UI Emoji font on Win8.1 and Win10 does not implement Regional
* Indicator flags.)
*
* Once the Firefox Emoji font is ready, we can load that via @font-face
* and expect these tests to work across all platforms.
*/
hasEmojiFont =
(navigator.platform.indexOf("Mac") == 0 &&
/10\.([7-9]|[1-9][0-9])/.test(navigator.oscpu));
if (hasEmojiFont) {
test(document.getElementById("edit7"), 1);
test(document.getElementById("edit8"), 2);
test(document.getElementById("edit9"), 3);
test(document.getElementById("edit10"), 4);
test(document.getElementById("edit11"), 5);
}
/* extra tests with the use of RIGHT and LEFT to get to the right place */
testWithMove(document.getElementById("edit1b"), 2, 1);
testWithMove(document.getElementById("edit2b"), 2, 1);
testWithMove(document.getElementById("edit3b"), 2, 1);
testWithMove(document.getElementById("edit4b"), 2, 1);
testWithMove(document.getElementById("edit5b"), 2, 1);
testWithMove(document.getElementById("edit6b"), 2, 1);
if (hasEmojiFont) {
testWithMove(document.getElementById("edit7b"), 2, 1);
testWithMove(document.getElementById("edit8b"), 3, 2);
testWithMove(document.getElementById("edit9b"), 4, 3);
testWithMove(document.getElementById("edit10b"), 5, 4);
testWithMove(document.getElementById("edit11b"), 6, 5);
}
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,103 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1026397
-->
<head>
<title>Test for Bug 1026397</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1026397">Mozilla Bug 1026397</a>
<p id="display"></p>
<div id="content">
<input id="input">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 1026397 **/
SimpleTest.waitForExplicitFinish();
function runTests()
{
var input = document.getElementById("input");
input.focus();
function doTest(aMaxLength, aInitialValue, aCaretOffset,
aInsertString, aExpectedValueDuringComposition,
aExpectedValueAfterCompositionEnd, aAdditionalExplanation)
{
input.value = aInitialValue;
var maxLengthStr = "";
if (aMaxLength >= 0) {
input.maxLength = aMaxLength;
maxLengthStr = aMaxLength.toString();
} else {
input.removeAttribute("maxlength");
maxLengthStr = "not specified";
}
input.selectionStart = input.selectionEnd = aCaretOffset;
if (aAdditionalExplanation) {
aAdditionalExplanation = " " + aAdditionalExplanation;
} else {
aAdditionalExplanation = "";
}
synthesizeCompositionChange(
{ "composition":
{ "string": aInsertString,
"clauses":
[
{ "length": aInsertString.length, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": aInsertString.length, "length": 0 }
});
is(input.value, aExpectedValueDuringComposition,
"The value of input whose maxlength is " + maxLengthStr + " should be "
+ aExpectedValueDuringComposition + " during composition" + aAdditionalExplanation);
synthesizeComposition({ type: "compositioncommitasis" });
is(input.value, aExpectedValueAfterCompositionEnd,
"The value of input whose maxlength is " + maxLengthStr + " should be "
+ aExpectedValueAfterCompositionEnd + " after compositionend" + aAdditionalExplanation);
}
// maxlength hasn't been specified yet.
doTest(-1, "", 0, "\uD842\uDFB7\u91CE\u5BB6", "\uD842\uDFB7\u91CE\u5BB6", "\uD842\uDFB7\u91CE\u5BB6");
// maxlength="1"
doTest(1, "", 0, "\uD842\uDFB7\u91CE\u5BB6", "\uD842\uDFB7\u91CE\u5BB6", "");
// maxlength="2"
doTest(2, "", 0, "\uD842\uDFB7\u91CE\u5BB6", "\uD842\uDFB7\u91CE\u5BB6", "\uD842\uDFB7");
doTest(2, "X", 1, "\uD842\uDFB7\u91CE\u5BB6", "X\uD842\uDFB7\u91CE\u5BB6", "X");
doTest(2, "Y", 0, "\uD842\uDFB7\u91CE\u5BB6", "\uD842\uDFB7\u91CE\u5BB6Y", "Y");
// maxlength="3"
doTest(3, "", 0, "\uD842\uDFB7\u91CE\u5BB6", "\uD842\uDFB7\u91CE\u5BB6", "\uD842\uDFB7\u91CE");
doTest(3, "A", 1, "\uD842\uDFB7\u91CE\u5BB6", "A\uD842\uDFB7\u91CE\u5BB6", "A\uD842\uDFB7");
doTest(3, "B", 0, "\uD842\uDFB7\u91CE\u5BB6", "\uD842\uDFB7\u91CE\u5BB6B", "\uD842\uDFB7B");
doTest(3, "CD", 1, "\uD842\uDFB7\u91CE\u5BB6", "C\uD842\uDFB7\u91CE\u5BB6D", "CD");
// maxlength="4"
doTest(4, "EF", 1, "\uD842\uDFB7\u91CE\u5BB6", "E\uD842\uDFB7\u91CE\u5BB6F", "E\uD842\uDFB7F");
doTest(4, "GHI", 1, "\uD842\uDFB7\u91CE\u5BB6", "G\uD842\uDFB7\u91CE\u5BB6HI", "GHI");
// maxlength="1", inputting only high surrogate
doTest(1, "", 0, "\uD842", "\uD842", "\uD842", "even if input string is only a high surrogate");
// maxlength="1", inputting only low surrogate
doTest(1, "", 0, "\uDFB7", "\uDFB7", "\uDFB7", "even if input string is only a low surrogate");
SimpleTest.finish();
}
SimpleTest.waitForFocus(runTests);
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,73 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1053048
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1053048</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script type="application/javascript">
/** Test for Bug 1053048 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTests);
const nsISelectionPrivate = SpecialPowers.Ci.nsISelectionPrivate;
const nsISelectionListener = SpecialPowers.Ci.nsISelectionListener;
const nsIDOMNSEditableElement = SpecialPowers.Ci.nsIDOMNSEditableElement;
function runTests()
{
var textarea = SpecialPowers.wrap(document.getElementById("textarea"));
textarea.focus();
var editor = textarea.QueryInterface(nsIDOMNSEditableElement).editor;
var selectionPrivate = editor.selection.QueryInterface(nsISelectionPrivate);
var selectionListener = {
count: 0,
notifySelectionChanged: function (aDocument, aSelection, aReason)
{
ok(true, "selectionStart: " + textarea.selectionStart);
ok(true, "selectionEnd: " + textarea.selectionEnd);
this.count++;
}
};
// Move caret to the end of the textarea
synthesizeMouse(textarea, 290, 10, {});
is(textarea.selectionStart, 3, "selectionStart should be 3 (after \"foo\")");
is(textarea.selectionEnd, 3, "selectionEnd should be 3 (after \"foo\")");
selectionPrivate.addSelectionListener(selectionListener);
sendKey("RETURN");
is(selectionListener.count, 1, "nsISelectionListener.notifySelectionChanged() should be called");
is(textarea.selectionStart, 4, "selectionStart should be 4");
is(textarea.selectionEnd, 4, "selectionEnd should be 4");
is(textarea.value, "foo\n", "The line break should be appended");
selectionPrivate.removeSelectionListener(selectionListener);
SimpleTest.finish();
}
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1053048">Mozilla Bug 1053048</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<textarea id="textarea"
style="height: 100px; width: 300px; -moz-appearance: none"
spellcheck="false"
onkeydown="this.style.display='block'; this.style.height='200px';">foo</textarea>
<pre id="test">
</pre>
</body>
</html>

View file

@ -0,0 +1,57 @@
<!DOCTYPE HTML>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1067255
-->
<head>
<title>Test for Bug 1067255</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body onload="doTest();">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1067255">Mozilla Bug 1067255</a>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 1067255 **/
SimpleTest.waitForExplicitFinish();
function doTest() {
var text = $("text-field");
var password = $("password-field");
var editor1 = SpecialPowers.wrap(text).editor;
var editor2 = SpecialPowers.wrap(password).editor;
text.focus();
text.select();
ok(editor1.canCopy(), "can copy, text");
ok(editor1.canCut(), "can cut, text");
ok(editor1.canDelete(), "can delete, text");
password.focus();
password.select();
// Copy and cut commands don't do anything on passoword fields by default,
// but webpages can hook up event handlers to the event, and thus, we have to
// always keep the cut and copy event enabled in HTML/XHTML documents.
ok(editor2.canCopy(), "can copy, password");
ok(editor2.canCut(), "can cut, password");
ok(editor2.canDelete(), "can delete, password");
SimpleTest.finish();
}
</script>
</pre>
<input type="text" value="Gonzo says hi" id="text-field" />
<input type="password" value="Jan also" id="password-field" />
</body>
</html>

View file

@ -0,0 +1,72 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1068979
-->
<head>
<title>Test for Bug 1068979</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1068979">Mozilla Bug 1068979</a>
<p id="display"></p>
<div id="content">
<div id="editor1" contenteditable="true">&#x1d400;</div>
<div id="editor2" contenteditable="true">a<u>&#x1d401;</u>b</div>
<div id="editor3" contenteditable="true">a&#x1d402;<u>b</u></div>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 1068979 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
// Test backspacing over SMP characters pasted-in to a contentEditable
getSelection().selectAllChildren(document.getElementById("editor1"));
var ed1 = document.getElementById("editor1");
var ch1 = ed1.textContent;
ed1.focus();
synthesizeKey("C", {accelKey: true});
synthesizeKey("V", {accelKey: true});
synthesizeKey("V", {accelKey: true});
synthesizeKey("V", {accelKey: true});
synthesizeKey("V", {accelKey: true});
is(ed1.textContent, ch1 + ch1 + ch1 + ch1, "Should have four SMP characters");
sendKey("back_space");
is(ed1.textContent, ch1 + ch1 + ch1, "Three complete characters should remain");
sendKey("back_space");
is(ed1.textContent, ch1 + ch1, "Two complete characters should remain");
sendKey("back_space");
is(ed1.textContent, ch1, "Only one complete SMP character should remain");
ed1.blur();
// Test backspacing across an SMP character in a sub-element
getSelection().selectAllChildren(document.getElementById("editor2"));
var ed2 = document.getElementById("editor2");
ed2.focus();
sendKey("right");
sendKey("back_space");
sendKey("back_space");
is(ed2.textContent, "a", "Only the 'a' should remain");
ed2.blur();
// Test backspacing across an SMP character from a following sub-element
getSelection().selectAllChildren(document.getElementById("editor3"));
var ed3 = document.getElementById("editor3");
ed3.focus();
sendKey("right");
sendKey("left");
sendKey("back_space");
is(ed3.textContent, "ab", "The letters 'ab' should remain");
ed3.blur();
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,104 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1094000
-->
<head>
<title>Test for Bug 1094000</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1094000">Mozilla Bug 1094000</a>
<p id="display"></p>
<div id="content">
<div id="editor0" contenteditable></div>
<div id="editor1" contenteditable><br></div>
<div id="editor2" contenteditable>a</div>
<div id="editor3" contenteditable>b</div>
<div id="editor4" contenteditable>c</div>
<div id="editor5" contenteditable>d</div>
<div id="editor6" contenteditable>e</div>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 1094000 **/
SimpleTest.waitForExplicitFinish();
const kIsLinux = navigator.platform.indexOf("Linux") == 0;
function runTests()
{
var editor0 = document.getElementById("editor0");
var editor1 = document.getElementById("editor1");
var editor2 = document.getElementById("editor2");
var editor3 = document.getElementById("editor3");
var editor4 = document.getElementById("editor4");
var editor5 = document.getElementById("editor5");
var editor6 = document.getElementById("editor6");
ok(editor1.getBoundingClientRect().height - editor0.getBoundingClientRect().height > 1,
"an editor having a <br> element and an empty editor shouldn't be same height");
ok(Math.abs(editor1.getBoundingClientRect().height - editor2.getBoundingClientRect().height) <= 1,
"an editor having only a <br> element and an editor having \"a\" should be same height");
editor2.focus();
synthesizeKey("VK_RIGHT", {});
synthesizeKey("VK_BACK_SPACE", {});
is(editor2.innerHTML, "<br>",
"an editor which had \"a\" should have only <br> element after Backspace keypress");
ok(Math.abs(editor2.getBoundingClientRect().height - editor1.getBoundingClientRect().height) <= 1,
"an editor whose content was removed by Backspace key should have a place to put a caret");
editor3.focus();
synthesizeKey("VK_LEFT", {});
synthesizeKey("VK_DELETE", {});
is(editor3.innerHTML, "<br>",
"an editor which had \"b\" should have only <br> element after Delete keypress");
ok(Math.abs(editor3.getBoundingClientRect().height - editor1.getBoundingClientRect().height) <= 1,
"an editor whose content was removed by Delete key should have a place to put a caret");
editor4.focus();
window.getSelection().selectAllChildren(editor4);
synthesizeKey("VK_BACK_SPACE", {});
is(editor4.innerHTML, "<br>",
"an editor which had \"c\" should have only <br> element after removing selected text with Backspace key");
ok(Math.abs(editor4.getBoundingClientRect().height - editor1.getBoundingClientRect().height) <= 1,
"an editor whose content was selected and removed by Backspace key should have a place to put a caret");
editor5.focus();
window.getSelection().selectAllChildren(editor5);
synthesizeKey("VK_DELETE", {});
is(editor5.innerHTML, "<br>",
"an editor which had \"d\" should have only <br> element after removing selected text with Delete key");
ok(Math.abs(editor5.getBoundingClientRect().height - editor1.getBoundingClientRect().height) <= 1,
"an editor whose content was selected and removed by Delete key should have a place to put a caret");
editor6.focus();
window.getSelection().selectAllChildren(editor6);
synthesizeKey("x", { accelKey: true });
is(editor6.innerHTML, "<br>",
"an editor which had \"e\" should have only <br> element after removing selected text by \"Cut\"");
ok(Math.abs(editor6.getBoundingClientRect().height - editor1.getBoundingClientRect().height) <= 1,
"an editor whose content was selected and removed by \"Cut\" should have a place to put a caret");
editor0.focus();
synthesizeKey("VK_BACK_SPACE", {});
is(editor0.innerHTML, "",
"an empty editor should keep being empty even if Backspace key is pressed");
synthesizeKey("VK_DELETE", {});
is(editor0.innerHTML, "",
"an empty editor should keep being empty even if Delete key is pressed");
SimpleTest.finish();
}
SimpleTest.waitForFocus(runTests);
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,65 @@
<!DOCTYPE>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1100966
-->
<head>
<title>Test for Bug 1100966</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<div id="display">
</div>
<div id="content" contenteditable>
=====<br>
correct<br>
fivee sixx<br>
====
</div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
/** Test for Bug 1100966 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var div = document.getElementById("content");
div.focus();
synthesizeMouseAtCenter(div, {});
synthesizeKey(" ", {});
setTimeout(function() {
synthesizeKey("a", {});
setTimeout(function() {
synthesizeKey("VK_BACK_SPACE", {});
var sel = getSpellCheckSelection();
is(sel.rangeCount, 2, "We should have two misspelled words");
is(String(sel.getRangeAt(0)), "fivee", "Correct misspelled word");
is(String(sel.getRangeAt(1)), "sixx", "Correct misspelled word");
SimpleTest.finish();
},0);
},0);
});
function getSpellCheckSelection() {
var Ci = SpecialPowers.Ci;
var editingSession = SpecialPowers.wrap(window)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIEditingSession);
var editor = editingSession.getEditorForWindow(window);
var selcon = editor.selectionController;
return selcon.getSelection(selcon.SELECTION_SPELLCHECK);
}
</script>
</body>
</html>

View file

@ -0,0 +1,78 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1101392
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1101392</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1101392 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTests);
function runCopyCommand(element, compareText, nextTest)
{
element.focus();
var expectedEndpoint, sel;
if (element.localName == "textarea") {
element.select();
expectedEndpoint = element.selectionEnd;
} else {
sel = getSelection();
sel.selectAllChildren(element.parentNode);
expectedEndpoint = [sel.getRangeAt(0).endContainer,
sel.getRangeAt(0).endOffset];
}
function checkCollapse() {
var desc = " after cmd_copyAndCollapseToEnd for " +
element.localName;
if (element.localName == "textarea") {
is(element.selectionStart, expectedEndpoint, "start offset" + desc);
is(element.selectionEnd, expectedEndpoint, "end offset" + desc);
} else {
is(sel.isCollapsed, true, "collapsed" + desc);
is(sel.anchorNode, expectedEndpoint[0], "node" + desc);
is(sel.anchorOffset, expectedEndpoint[1], "offset" + desc);
}
nextTest();
}
SimpleTest.waitForClipboard(compareText,
() => SpecialPowers.doCommand(window, "cmd_copyAndCollapseToEnd"),
checkCollapse, checkCollapse);
}
function testDiv()
{
var content = document.getElementById("content");
runCopyCommand(content, 'abc', testTextarea);
}
function testTextarea()
{
var textarea = document.getElementById("textarea");
runCopyCommand(textarea, 'def', SimpleTest.finish);
}
function runTests()
{
testDiv();
}
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1101392">Mozilla Bug 1101392</a>
<div><div id="content">abc</div></div>
<textarea id="textarea">def</textarea>
<pre id="test">
</pre>
</body>
</html>

View file

@ -0,0 +1,51 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1102906
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1102906</title>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script>
"use strict";
/* Test for Bug 1102906 */
/* The caret should be movable by using keyboard after drag-and-drop. */
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus( () => {
let content = document.getElementById("content");
let drag = document.getElementById("drag")
let selection = window.getSelection();
/* Perform drag-and-drop for an arbitrary content. The caret should be at
the end of the contenteditable. */
selection.selectAllChildren(drag);
synthesizeDrop(drag, content, {}, "copy");
let textContentAfterDrop = content.textContent;
/* Move the caret to the front of the contenteditable by using keyboard. */
for (let i = 0; i < content.textContent.length; ++i) {
sendKey("LEFT");
}
sendChar("!");
is(content.textContent, "!" + textContentAfterDrop,
"The exclamation mark should be inserted at the front.");
SimpleTest.finish();
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1102906">Mozilla Bug 1102906</a>
<div id="content" contenteditable="true"><span id="drag">Drag</span></div>
</body>
</html>

View file

@ -0,0 +1,69 @@
<!DOCTYPE>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1109465
-->
<head>
<title>Test for Bug 1109465</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<div id="display">
<textarea></textarea>
</div>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
/** Test for Bug 1109465 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var t = document.querySelector("textarea");
t.focus();
// Type foo\nbar and place the caret at the end of the last line
synthesizeKey("f", {});
synthesizeKey("o", {});
synthesizeKey("o", {});
synthesizeKey("VK_RETURN", {});
synthesizeKey("b", {});
synthesizeKey("a", {});
synthesizeKey("r", {});
synthesizeKey("VK_UP", {});
is(t.selectionStart, 3, "Correct start of selection");
is(t.selectionEnd, 3, "Correct end of selection");
// Compose an IME string
synthesizeComposition({ type: "compositionstart" });
var composingString = "\u306B";
synthesizeCompositionChange(
{ "composition":
{ "string": composingString,
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
});
synthesizeComposition({ type: "compositioncommitasis" });
is(t.value, "foo\u306B\nbar", "Correct value after composition");
// Now undo to test that the transaction merger has correctly detected the
// IMETextTxn.
synthesizeKey("Z", {accelKey: true});
is(t.value, "foo\nbar", "Correct value after undo");
SimpleTest.finish();
});
</script>
</body>
</html>

View file

@ -0,0 +1,71 @@
<!DOCTYPE>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1140105
-->
<head>
<title>Test for Bug 1140105</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<div id="display">
</div>
<div id="content" contenteditable><font face="Arial">1234567890</font></div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
/** Test for Bug 1140105 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var div = document.getElementById("content");
div.focus();
synthesizeMouseAtCenter(div, {});
synthesizeKey("VK_LEFT", {});
var sel = window.getSelection();
var selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be in text node");
is(selRange.endOffset, 9, "offset should be 9");
var firstHas = {};
var anyHas = {};
var allHas = {};
var editor = getEditor();
var atomService = SpecialPowers.Cc["@mozilla.org/atom-service;1"].getService(SpecialPowers.Ci.nsIAtomService);
var propAtom = atomService.getAtom("font");
editor.getInlineProperty(propAtom, "face", "Arial", firstHas, anyHas, allHas);
is(firstHas.value, true, "Test for Arial: firstHas: true expected");
is(anyHas.value, true, "Test for Arial: anyHas: true expected");
is(allHas.value, true, "Test for Arial: allHas: true expected");
editor.getInlineProperty(propAtom, "face", "Courier", firstHas, anyHas, allHas);
is(firstHas.value, false, "Test for Courier: firstHas: false expected");
is(anyHas.value, false, "Test for Courier: anyHas: false expected");
is(allHas.value, false, "Test for Courier: allHas: false expected");
SimpleTest.finish();
});
function getEditor() {
var Ci = SpecialPowers.Ci;
var editingSession = SpecialPowers.wrap(window)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIEditingSession);
var editor = editingSession.getEditorForWindow(window);
editor.QueryInterface(Ci.nsIHTMLEditor);
return editor;
}
</script>
</body>
</html>

View file

@ -0,0 +1,37 @@
<!doctype html>
<title>Mozilla Bug 1140617</title>
<link rel=stylesheet href="/tests/SimpleTest/test.css">
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1140617"
target="_blank">Mozilla Bug 1140617</a>
<iframe id="i1" width="200" height="100" src="about:blank"></iframe>
<img id="i" src="green.png">
<script>
function runTest() {
SpecialPowers.setCommandNode(window, document.getElementById("i"));
SpecialPowers.doCommand(window, "cmd_copyImageContents");
var e = document.getElementById('i1');
var doc = e.contentDocument;
doc.designMode = "on";
doc.defaultView.focus();
var selection = doc.defaultView.getSelection();
selection.removeAllRanges();
selection.selectAllChildren(doc.body);
selection.collapseToEnd();
doc.execCommand("fontname", false, "Arial");
doc.execCommand("bold", false, null);
doc.execCommand("insertText", false, "12345");
doc.execCommand("paste", false, null);
doc.execCommand("insertText", false, "a");
is(doc.queryCommandValue("fontname"), "Arial", "Arial expected");
is(doc.queryCommandState("bold"), true, "Bold expected");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTest);
</script>

View file

@ -0,0 +1,49 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1153237
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1153237</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
// Avoid platform selection differences
SimpleTest.waitForFocus(function() {
SpecialPowers.pushPrefEnv({
"set": [["layout.word_select.eat_space_to_next_word", true]]
}, runTests);
});
function runTests()
{
var element = document.getElementById("editor");
var sel = window.getSelection();
element.focus();
is(sel.getRangeAt(0).startOffset, 0, "offset is zero");
SpecialPowers.doCommand(window, "cmd_selectRight2");
is(sel.toString(), "Some ",
"first word + space is selected: got '" + sel.toString() + "'");
SpecialPowers.doCommand(window, "cmd_selectRight2");
is(sel.toString(), "Some text",
"both words are selected: got '" + sel.toString() + "'");
SimpleTest.finish();
}
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1153237">Mozilla Bug 1153237</a>
<div id="editor" contenteditable>Some text</div><span></span>
<pre id="test">
</pre>
</body>
</html>

View file

@ -0,0 +1,67 @@
<!DOCTYPE>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1154791
-->
<head>
<title>Test for Bug 1154791</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<div id="display">
</div>
<div id="content" contenteditable>
<tt>thiss onee is stilll a</tt>
</div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
/** Test for Bug 1154791 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var div = document.getElementById("content");
div.focus();
synthesizeMouseAtCenter(div, {});
synthesizeKey("VK_LEFT", {});
synthesizeKey("VK_LEFT", {});
setTimeout(function() {
synthesizeKey("VK_BACK_SPACE", {});
setTimeout(function() {
synthesizeKey(" ", {});
setTimeout(function() {
var sel = getSpellCheckSelection();
is(sel.rangeCount, 2, "We should have two misspelled words");
is(String(sel.getRangeAt(0)), "thiss", "Correct misspelled word");
is(String(sel.getRangeAt(1)), "onee", "Correct misspelled word");
SimpleTest.finish();
},0);
},0);
},0);
});
function getSpellCheckSelection() {
var Ci = SpecialPowers.Ci;
var editingSession = SpecialPowers.wrap(window)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIEditingSession);
var editor = editingSession.getEditorForWindow(window);
var selcon = editor.selectionController;
return selcon.getSelection(selcon.SELECTION_SPELLCHECK);
}
</script>
</body>
</html>

View file

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1162952
-->
<head>
<title>Test for Bug 1162952</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1162952">Mozilla Bug 1162952</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 1162952 **/
var userCallbackRun = false;
document.addEventListener('keydown', function() {
// During a user callback, the commands should be enabled
userCallbackRun = true;
is(true, document.queryCommandEnabled('cut'));
is(true, document.queryCommandEnabled('copy'));
});
// Otherwise, they should be disabled
is(false, document.queryCommandEnabled('cut'));
is(false, document.queryCommandEnabled('copy'));
// Fire a user callback
synthesizeKey('A', {});
ok(userCallbackRun, "User callback should've been run");
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,50 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1181130
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1181130</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1181130">Mozilla Bug 1181130</a>
<p id="display"></p>
<div id="container" contenteditable="true">
editable div
<div id="noneditable" contenteditable="false">
non-editable div
<div id="editable" contenteditable="true">nested editable div</div>
</div>
</div>
<script type="application/javascript">
/** Test for Bug 1181130 **/
var container = document.getElementById("container");
var noneditable = document.getElementById("noneditable");
var editable = document.getElementById("editable");
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
synthesizeMouseAtCenter(noneditable, {});
ok(!document.getSelection().toString().includes("nested editable div"),
"Selection should not include non-editable content");
synthesizeMouseAtCenter(container, {});
ok(!document.getSelection().toString().includes("nested editable div"),
"Selection should not include non-editable content");
synthesizeMouseAtCenter(editable, {});
ok(!document.getSelection().toString().includes("nested editable div"),
"Selection should not include non-editable content");
SimpleTest.finish();
});
</script>
<pre id="test">
</pre>
</body>
</html>

View file

@ -0,0 +1,44 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1181130
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1181130</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1181130">Mozilla Bug 1181130</a>
<p id="display"></p>
<div id="container" contenteditable="true">
editable div
<div id="noneditable" contenteditable="false">
non-editable div
</div>
</div>
<script type="application/javascript">
/** Test for Bug 1181130 **/
var container = document.getElementById("container");
var noneditable = document.getElementById("noneditable");
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var nonHTMLElement = document.createElementNS("http://www.example.com", "element");
nonHTMLElement.innerHTML = '<div contenteditable="true">nested editable div</div>';
noneditable.appendChild(nonHTMLElement);
synthesizeMouseAtCenter(noneditable, {});
ok(!document.getSelection().toString().includes("nested editable div"),
"Selection should not include non-editable content");
SimpleTest.finish();
});
</script>
<pre id="test">
</pre>
</body>
</html>

View file

@ -0,0 +1,81 @@
<!DOCTYPE>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1186799
-->
<head>
<title>Test for Bug 1186799</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<div id="content">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1186799">Mozilla Bug 1186799</a>
<p id="display"></p>
<div id="content">
<span id="span">span</span>
<div id="editor" contenteditable></div>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 1186799 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var span = document.getElementById("span");
var editor = document.getElementById("editor");
editor.focus();
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
});
ok(isThereIMESelection(), "There should be IME selection");
var compositionEnd = false;
editor.addEventListener("compositionend", function () { compositionEnd = true; }, true);
synthesizeMouseAtCenter(span, {});
ok(compositionEnd, "composition end should be fired at clicking outside of the editor");
ok(!isThereIMESelection(), "There should be no IME selection");
SimpleTest.finish();
});
function isThereIMESelection()
{
var selCon = SpecialPowers.wrap(window).
QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
getInterface(SpecialPowers.Ci.nsIWebNavigation).
QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
getInterface(SpecialPowers.Ci.nsIEditingSession).
getEditorForWindow(window).
selectionController;
const kIMESelections = [
SpecialPowers.Ci.nsISelectionController.SELECTION_IME_RAWINPUT,
SpecialPowers.Ci.nsISelectionController.SELECTION_IME_SELECTEDRAWTEXT,
SpecialPowers.Ci.nsISelectionController.SELECTION_IME_CONVERTEDTEXT,
SpecialPowers.Ci.nsISelectionController.SELECTION_IME_SELECTEDCONVERTEDTEXT
];
for (var i = 0; i < kIMESelections.length; i++) {
var sel = selCon.getSelection(kIMESelections[i]);
if (sel && sel.rangeCount) {
return true;
}
}
return false;
}
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,124 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1230473
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1230473</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1230473">Mozilla Bug 1230473</a>
<input id="input">
<textarea id="textarea"></textarea>
<div id="div" contenteditable></div>
<script type="application/javascript">
/** Test for Bug 1230473 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(()=>{
function runTest(aEditor) {
function committer() {
aEditor.blur();
aEditor.focus();
}
function isNSEditableElement() {
return aEditor.tagName.toLowerCase() == "input" || aEditor.tagName.toLowerCase() == "textarea";
}
function value() {
return isNSEditableElement() ? aEditor.value : aEditor.textContent;
}
function isComposing() {
return isNSEditableElement() ? SpecialPowers.wrap(aEditor)
.QueryInterface(SpecialPowers.Ci.nsIDOMNSEditableElement)
.editor
.QueryInterface(SpecialPowers.Ci.nsIEditorIMESupport)
.composing :
SpecialPowers.wrap(window)
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
.getInterface(SpecialPowers.Ci.nsIWebNavigation)
.QueryInterface(SpecialPowers.Ci.nsIDocShell)
.editor
.QueryInterface(SpecialPowers.Ci.nsIEditorIMESupport)
.composing;
}
function clear() {
if (isNSEditableElement()) {
aEditor.value = "";
} else {
aEditor.textContent = "";
}
}
clear();
// Committing at compositionstart
aEditor.focus();
aEditor.addEventListener("compositionstart", committer, true);
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
aEditor.removeEventListener("compositionstart", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by compositionstart event handler");
is(value(), "", "composition in " + aEditor.id + " shouldn't insert any text since it's committed at compositionstart");
clear();
// Committing at first compositionupdate
aEditor.focus();
aEditor.addEventListener("compositionupdate", committer, true);
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
aEditor.removeEventListener("compositionupdate", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by compositionupdate event handler");
is(value(), "", "composition in " + aEditor.id + " shouldn't have inserted any text since it's committed at first compositionupdate");
clear();
// Committing at first text (eCompositionChange)
aEditor.focus();
aEditor.addEventListener("text", committer, true);
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
aEditor.removeEventListener("text", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by text event handler");
is(value(), "", "composition in " + aEditor.id + " should have inserted any text since it's committed at first text");
clear();
// Committing at second compositionupdate
aEditor.focus();
synthesizeComposition({ type: "compositionstart" });
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
ok(isComposing(), "composition should be in " + aEditor.id + " before dispatching second compositionupdate");
is(value(), "a", "composition in " + aEditor.id + " should be 'a' before dispatching second compositionupdate");
aEditor.addEventListener("compositionupdate", committer, true);
synthesizeCompositionChange({ composition: { string: "ab", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 2, length: 0 }});
aEditor.removeEventListener("compositionupdate", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by compositionupdate event handler");
todo_is(value(), "a", "composition in " + aEditor.id + " shouldn't have been modified since it's committed at second compositionupdate");
clear();
// Committing at second text (eCompositionChange)
aEditor.focus();
synthesizeComposition({ type: "compositionstart" });
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
ok(isComposing(), "composition should be in " + aEditor.id + " before dispatching second text");
is(value(), "a", "composition in " + aEditor.id + " should be 'a' before dispatching second text");
aEditor.addEventListener("text", committer, true);
synthesizeCompositionChange({ composition: { string: "ab", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 2, length: 0 }});
aEditor.removeEventListener("text", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by text event handler");
todo_is(value(), "a", "composition in " + aEditor.id + " shouldn't have been modified since it's committed at second text");
clear();
}
runTest(document.getElementById("input"));
runTest(document.getElementById("textarea"));
runTest(document.getElementById("div"));
SimpleTest.finish();
});
</script>
</body>
</html>

View file

@ -0,0 +1,61 @@
<!DOCTYPE HTML>
<html><head>
<title>Test for bug 1247483</title>
<style src="/tests/SimpleTest/test.css" type="text/css"></style>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script class="testbody" type="application/javascript">
function runTest() {
// Copy content from table.
var selection = getSelection();
var startRange = document.createRange();
startRange.setStart(document.getElementById("start"), 0);
startRange.setEnd(document.getElementById("end"), 2);
selection.removeAllRanges();
selection.addRange(startRange);
SpecialPowers.wrap(document).execCommand("copy", false, null);
// Paste content into "pastecontainer"
var pasteContainer = document.getElementById("pastecontainer");
var pasteRange = document.createRange();
pasteRange.selectNodeContents(pasteContainer);
pasteRange.collapse(false);
selection.removeAllRanges();
selection.addRange(pasteRange);
SpecialPowers.wrap(document).execCommand("paste", false, null);
is(pasteContainer.querySelectorAll("td").length, 4, "4 <td> should be pasted.");
document.execCommand("undo", false, null);
is(pasteContainer.querySelectorAll("td").length, 0, "Undo should have remove the 4 pasted <td>.");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTest);
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247483">Mozilla Bug 1247483</a>
<p id="display"></p>
<pre id="test">
</pre>
<div id="container" contenteditable="true">
<table>
<tr id="start"><td>1 1</td><td>1 2</td></tr>
<tr id="end"><td>2 1</td><td>2 2</td></tr>
</table>
</div>
<div id="pastecontainer" contenteditable="true">
</div>
</body>
</html>

View file

@ -0,0 +1,52 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1248128
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1248128</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var outer = document.querySelector("html");
ok(outer.scrollTop == 0, "scrollTop is zero: got " + outer.scrollTop);
var input = document.getElementById("testInput");
input.focus();
var scroll = outer.scrollTop;
ok(scroll > 0, "element has scrolled: new value " + scroll);
try {
SpecialPowers.doCommand(window, "cmd_moveLeft");
ok(false, "should not be able to do kMoveLeft");
} catch (e) {
ok(true, "unable to perform kMoveLeft");
}
ok(outer.scrollTop == scroll,
"scroll is unchanged: got " + outer.scrollTop + ", expected " + scroll);
// Make sure cmd_moveLeft isn't failing for some unrelated reason
synthesizeKey("a", {});
is(input.selectionStart, 1, "selectionStart after typing");
SpecialPowers.doCommand(window, "cmd_moveLeft");
is(input.selectionStart, 0, "selectionStart after move left");
SimpleTest.finish();
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1248128">Mozilla Bug 1248128</a>
<div style="height: 2000px;"></div>
<input type="text" id="testInput"></input>
<div style="height: 200px;"></div>
</body>
</html>

View file

@ -0,0 +1,57 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1248185
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1248185</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
// Avoid platform selection differences
SimpleTest.waitForFocus(function() {
SpecialPowers.pushPrefEnv({
"set": [["layout.word_select.eat_space_to_next_word", true]]
}, runTests);
});
function runTests()
{
var editor = document.querySelector("#test");
editor.focus();
var sel = window.getSelection();
SpecialPowers.doCommand(window, "cmd_moveRight2");
SpecialPowers.doCommand(window, "cmd_moveRight2");
SpecialPowers.doCommand(window, "cmd_moveRight2");
SpecialPowers.doCommand(window, "cmd_selectRight2");
ok(sel.toString() == "three ", "expected 'three ' to be selected");
SpecialPowers.doCommand(window, "cmd_moveRight2");
SpecialPowers.doCommand(window, "cmd_moveRight2");
SpecialPowers.doCommand(window, "cmd_moveRight2");
ok(sel.toString() == "", "expected empty selection");
SpecialPowers.doCommand(window, "cmd_selectLeft2");
ok(sel.toString() == "five", "expected 'five' to be selected");
SimpleTest.finish();
}
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1248185">Mozilla Bug 1248185</a>
<body>
<div style="font: 12px monospace; width: 45ch;">
<span contenteditable="" id="test">blablablablablablablablablablablablablabla one two three four five</span>
<div>
<span>foo</span>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,93 @@
<!DOCTYPE>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1250010
-->
<head>
<title>Test for Bug 1250010</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<div id="display">
</div>
<div id="test1" contenteditable><p><b><font color="red">1234567890</font></b></p></div>
<div id="test2" contenteditable><p><tt>xyz</tt></p><p><tt><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAIAAABvrngfAAAAFklEQVQImWMwjWhCQwxECoW3oCHihAB0LyYv5/oAHwAAAABJRU5ErkJggg=="></tt></p></div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
function getImageDataURI()
{
return document.getElementsByTagName("img")[0].getAttribute("src");
}
/** Test for Bug 1250010 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
// First test: Empty paragraph is split correctly.
var div = document.getElementById("test1");
div.focus();
synthesizeMouseAtCenter(div, {});
var sel = window.getSelection();
var selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the end of text node");
is(selRange.endOffset, 10, "offset should be 10");
synthesizeKey("VK_RETURN", {});
synthesizeKey("VK_RETURN", {});
synthesizeKey("b", {});
synthesizeKey("VK_UP", {});
synthesizeKey("a", {});
is(div.innerHTML, "<p><b><font color=\"red\">1234567890</font></b></p>" +
"<p><b><font color=\"red\">a<br></font></b></p>" +
"<p><b><font color=\"red\">b<br></font></b></p>",
"unexpected HTML");
// Second test: Since we modified the code path that splits non-text nodes,
// test that this works, if the split node is not empty.
div = document.getElementById("test2");
div.focus();
synthesizeMouseAtCenter(div, {});
selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the end of text node");
is(selRange.endOffset, 3, "offset should be 3");
// Move behind the image and press enter, insert an "A".
// That should insert a new empty paragraph with the "A" after what we have.
synthesizeKey("VK_RIGHT", {});
synthesizeKey("VK_RIGHT", {});
synthesizeKey("VK_RETURN", {});
synthesizeKey("A", {});
// The resulting HTML is sadly less than optimal:
// A <br> gets inserted after the image and the "A" is followed by an empty <tt></tt>.
var newHTML = div.innerHTML;
var expectedHTML;
// Existing part with additional <br> inserted.
expectedHTML = "<p><tt>xyz</tt></p><p><tt><img src=\"" + getImageDataURI() + "\"><br></tt></p>" +
// New part caused by pressing enter after the image and typing an "A".
"<p><tt>A</tt><br><tt></tt></p>";
is(newHTML, expectedHTML, "unexpected HTML");
// In case the empty tag gets deleted some day, let them know that something improved.
expectedHTML = "<p><tt>xyz</tt></p><p><tt><img src=\"" + getImageDataURI() + "\"><br></tt></p>" +
"<p><tt>A</tt><br></p>";
todo_is(newHTML, expectedHTML, "unexpected HTML");
SimpleTest.finish();
});
</script>
</body>
</html>

View file

@ -0,0 +1,182 @@
<!DOCTYPE>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1257363
-->
<head>
<title>Test for Bug 1257363</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<div id="display">
</div>
<div id="backspaceCSS" contenteditable><p style="color:red;">12345</p>67</div>
<div id="backspace" contenteditable><p><font color="red">12345</font></p>67</div>
<div id="deleteCSS" contenteditable><p style="color:red;">x</p></div>
<div id="delete" contenteditable><p><font color="red">y</font></p></div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
/** Test for Bug 1257363 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
// ***** Backspace test *****
var div = document.getElementById("backspaceCSS");
div.focus();
synthesizeMouse(div, 100, 2, {}); /* click behind and down */
var sel = window.getSelection();
var selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the end of text node");
is(selRange.endOffset, 5, "offset should be 5");
// Return and backspace should take us to where we started.
synthesizeKey("VK_RETURN", {});
synthesizeKey("VK_BACK_SPACE", {});
selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the end of text node");
is(selRange.endOffset, 5, "offset should be 5");
// Add an "a" to the end of the paragraph.
synthesizeKey("a", {});
// Return and forward delete should take us to the following line.
synthesizeKey("VK_RETURN", {});
synthesizeKey("VK_DELETE", {});
// Add a "b" to the start.
synthesizeKey("b", {});
is(div.innerHTML, "<p style=\"color:red;\">12345a</p>b67",
"unexpected HTML");
// Let's repeat the whole thing, but a font tag instead of CSS.
// The behaviour is different since the font is carried over.
div = document.getElementById("backspace");
div.focus();
synthesizeMouse(div, 100, 2, {}); /* click behind and down */
sel = window.getSelection();
selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the end of text node");
is(selRange.endOffset, 5, "offset should be 5");
// Return and backspace should take us to where we started.
synthesizeKey("VK_RETURN", {});
synthesizeKey("VK_BACK_SPACE", {});
selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the end of text node");
is(selRange.endOffset, 5, "offset should be 5");
// Add an "a" to the end of the paragraph.
synthesizeKey("a", {});
// Return and forward delete should take us to the following line.
synthesizeKey("VK_RETURN", {});
synthesizeKey("VK_DELETE", {});
// Add a "b" to the start.
synthesizeKey("b", {});
// Here we get a somewhat ugly result since the red sticks.
is(div.innerHTML, "<p><font color=\"red\">12345a</font></p><font color=\"red\">b</font>67",
"unexpected HTML");
// ***** Delete test *****
div = document.getElementById("deleteCSS");
div.focus();
synthesizeMouse(div, 100, 2, {}); /* click behind and down */
var sel = window.getSelection();
var selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the end of text node");
is(selRange.endOffset, 1, "offset should be 1");
// left, enter should create a new empty paragraph before
// but leave the selection at the start of the existing paragraph.
synthesizeKey("VK_LEFT", {});
synthesizeKey("VK_RETURN", {});
selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the start of text node");
is(selRange.endOffset, 0, "offset should be 0");
is(selRange.endContainer.nodeValue, "x", "we should be in the text node with the x");
// Now moving up into the new empty paragraph.
synthesizeKey("VK_UP", {});
selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "P", "selection should be the new empty paragraph");
is(selRange.endOffset, 0, "offset should be 0");
// Forward delete should now take us to where we started.
synthesizeKey("VK_DELETE", {});
selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the start of text node");
is(selRange.endOffset, 0, "offset should be 0");
// Add an "a" to the start of the paragraph.
synthesizeKey("a", {});
is(div.innerHTML, "<p style=\"color:red;\">ax</p>",
"unexpected HTML");
// Let's repeat the whole thing, but a font tag instead of CSS.
div = document.getElementById("delete");
div.focus();
synthesizeMouse(div, 100, 2, {}); /* click behind and down */
var sel = window.getSelection();
var selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the end of text node");
is(selRange.endOffset, 1, "offset should be 1");
// left, enter should create a new empty paragraph before
// but leave the selection at the start of the existing paragraph.
synthesizeKey("VK_LEFT", {});
synthesizeKey("VK_RETURN", {});
selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the start of text node");
is(selRange.endOffset, 0, "offset should be 0");
is(selRange.endContainer.nodeValue, "y", "we should be in the text node with the y");
// Now moving up into the new empty paragraph.
synthesizeKey("VK_UP", {});
selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "FONT", "selection should be the font tag");
is(selRange.endOffset, 0, "offset should be 0");
is(selRange.endContainer.parentNode.nodeName, "P", "the parent of the font should be a paragraph");
// Forward delete should now take us to where we started.
synthesizeKey("VK_DELETE", {});
selRange = sel.getRangeAt(0);
is(selRange.endContainer.nodeName, "#text", "selection should be at the start of text node");
is(selRange.endOffset, 0, "offset should be 0");
// Add an "a" to the start of the paragraph.
synthesizeKey("a", {});
is(div.innerHTML, "<p><font color=\"red\">ay</font></p>",
"unexpected HTML");
SimpleTest.finish();
});
</script>
</body>
</html>

View file

@ -0,0 +1,66 @@
<!DOCTYPE html>
<title>Test for Bug 1186799</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<div contenteditable></div>
<script>
var div = document.querySelector("div");
function reset() {
div.innerHTML = "x<br> y";
div.focus();
synthesizeKey("VK_DOWN", {});
}
function checks(msg) {
is(div.innerHTML, "x<br><br>",
msg + ": Should add a second <br> to prevent collapse of first");
is(div.childNodes.length, 3, msg + ": No empty text nodes allowed");
ok(getSelection().isCollapsed, msg + ": Selection must be collapsed");
is(getSelection().focusNode, div, msg + ": Focus must be in div");
is(getSelection().focusOffset, 2,
msg + ": Focus must be between the two <br>s");
}
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
// Put selection after the "y" and backspace
reset();
synthesizeKey("VK_RIGHT", {});
synthesizeKey("VK_BACK_SPACE", {});
checks("Collapsed backspace");
// Now do the same with delete
reset();
synthesizeKey("VK_DELETE", {});
checks("Collapsed delete");
// Forward selection
reset();
synthesizeKey("VK_RIGHT", {shiftKey: true});
synthesizeKey("VK_BACK_SPACE", {});
checks("Forward-selected backspace");
// Backward selection
reset();
synthesizeKey("VK_RIGHT", {});
synthesizeKey("VK_LEFT", {shiftKey: true});
synthesizeKey("VK_BACK_SPACE", {});
checks("Backward-selected backspace");
// Make sure we're not deleting if the whitespace isn't actually collapsed
div.style.whiteSpace = "pre-wrap";
reset();
synthesizeKey("VK_RIGHT", {});
synthesizeKey("VK_RIGHT", {});
synthesizeKey("VK_BACK_SPACE", {});
if (div.innerHTML, "x<br> ", "pre-wrap: Don't delete uncollapsed space");
ok(getSelection().isCollapsed, "pre-wrap: Selection must be collapsed");
is(getSelection().focusNode, div.lastChild,
"pre-wrap: Focus must be in final text node");
is(getSelection().focusOffset, 1, "pre-wrap: Focus must be at end of node");
SimpleTest.finish();
});
</script>

View file

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1268736
-->
<head>
<title>Test for Bug 1268736</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268736">Mozilla Bug 1268736</a>
<table id="table" border="1" width="100%">
<tbody>
<tr>
<td>a</td>
<td>b</td>
<td>c</td>
</tr>
<tr>
<td>d</td>
<td id="cell_readonly">e</td>
<td contenteditable="true" id="cell_writable">f</td>
</tr>
</tbody>
</table>
<script type="application/javascript">
/**
* Test for Bug 1268736
*
* Tests for editing a table cell's contents when the table cell is or isn't a child of a contenteditable node.
*
*/
function getEditor() {
const Ci = SpecialPowers.Ci;
var editingSession = SpecialPowers.wrap(window)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIEditingSession);
return editingSession.getEditorForWindow(window).QueryInterface(Ci.nsITableEditor);
}
var table = document.getElementById("table");
var tableHTML = table.innerHTML;
var editor = getEditor();
var cell = document.getElementById("cell_readonly");
cell.focus();
editor.deleteTableCellContents();
is(table.innerHTML == tableHTML, true, "editor should not modify non-editable table cell" );
cell = document.getElementById("cell_writable");
cell.focus();
editor.deleteTableCellContents();
is(cell.innerHTML == "<br>", true, "editor can modify editable table cells" );
</script>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show more