| <!doctype html> | |
| <meta charset=utf-8> | |
| <title>Deletion tests</title> | |
| <script src=/resources/testharness.js></script> | |
| <script src=/resources/testharnessreport.js></script> | |
| <div contenteditable></div> | |
| <script> | |
| var div = document.querySelector("div"); | |
| // Format: [start html, start pos, expected html, expected pos, command] | |
| // Positions are a sequence of offsets starting from div, e.g., "1,2,0" | |
| // translates to node = div.childNodes[1].childNodes[2], offset = 0. For a | |
| // non-collapsed selection, use a hyphen, like "0,0-1,0". The selections are | |
| // created with collapse() followed by extend() to allow reverse selections, so | |
| // order is significant. | |
| // | |
| // Expected values can be arrays, in which case any is acceptable. | |
| var tests = [ | |
| ["<p><br></p><p><br></p>", "1,0", "<p><br></p>", "0,0", "delete"], | |
| ["<p><br></p><p><br></p>", "0,0", "<p><br></p>", "0,0", "forwarddelete"], | |
| // Range | |
| ["<p><br></p><p><br></p>", "0,0-1,0", "<p><br></p>", "0,0", "delete"], | |
| ["<p><br></p><p><br></p>", "0,0-1,0", "<p><br></p>", "0,0", "forwarddelete"], | |
| ["<p><br></p><p><br></p>", "1,0-0,0", "<p><br></p>", "0,0", "delete"], | |
| ["<p><br></p><p><br></p>", "1,0-0,0", "<p><br></p>", "0,0", "forwarddelete"], | |
| // Different start values | |
| ["<p>x<br></p><p><br></p>", "1,0", | |
| // WebKit/Blink like to get rid of the extra <br> | |
| ["<p>x<br></p>", "<p>x</p>"], | |
| // The selection should really be collapsed inside the text node, but in the | |
| // parent is close enough. | |
| ["0,0,1", "0,1"], "delete"], | |
| ["<p><br><br></p><p><br></p>", "1,0", "<p><br><br></p>", "0,1", "delete"], | |
| ["<p><br></p><p><br><br></p>", "1,1", | |
| "<p><br></p><p><br></p>", "1,0", "delete"], | |
| ["<p><br><br><br></p>", "0,2", "<p><br><br></p>", "0,1", "delete"], | |
| ["<p><br></p><p><br><br><br></p>", "1,2", | |
| "<p><br></p><p><br><br></p>", "1,1", "delete"], | |
| ["<p><br><br></p><p><br><br></p>", "1,1", | |
| "<p><br><br></p><p><br></p>", "1,0", "delete"], | |
| ["<p><br></p><br>", "1", "<p><br></p>", "0,0", "delete"], | |
| // The trailing \n in these cases is actually significant, because it was | |
| // necessary to trigger an actual Gecko bug (somehow!). | |
| ["<p><br></p><p><br></p>\n", "1,0", "<p><br></p>\n", "0,0", "delete"], | |
| ["<p><br></p><p><br></p>\n", "0,0", "<p><br></p>\n", "0,0", "forwarddelete"], | |
| ["\n<p><tt>x</tt></p><p><tt><br></tt></p><p><tt><br></tt></p>\n", "3,0,0", | |
| "\n<p><tt>x</tt></p><p><tt><br></tt></p>\n", "2,0,0", "delete"], | |
| ]; | |
| div.focus(); | |
| for (var i = 0; i < tests.length; i++) { | |
| test(function() { | |
| var test = tests[i]; | |
| div.innerHTML = test[0]; | |
| setSelection(test[1]); | |
| document.execCommand(test[4], false, ""); | |
| if (typeof test[2] == "string") { | |
| assert_equals(div.innerHTML, test[2], "innerHTML"); | |
| } else { | |
| assert_in_array(div.innerHTML, test[2], "innerHTML"); | |
| } | |
| var actualSel = recordSelection(); | |
| var expectedSel = []; | |
| if (typeof test[3] == "string") { | |
| test[3] = [test[3]]; | |
| } | |
| for (var j = 0; j < test[3].length; j++) { | |
| setSelection(test[3][j]); | |
| expectedSel.push(recordSelection()); | |
| } | |
| assertSelectionEquals(actualSel, expectedSel, test[2]); | |
| }, i + ": " + format_value(tests[i][0]) + " " + tests[i][1] + | |
| " " + tests[i][4]); | |
| } | |
| function setSelection(selstr) { | |
| var parts = selstr.split("-"); | |
| var collapsePoint = getPointFromArray(parts[0].split(",")); | |
| getSelection().collapse(collapsePoint[0], collapsePoint[1]); | |
| if (parts[1]) { | |
| var extendPoint = getPointFromArray(parts[1].split(",")); | |
| getSelection().extend(extendPoint[0], extendPoint[1]); | |
| } | |
| } | |
| function getPointFromArray(offsets) { | |
| var retNode = div, retOffset; | |
| var offset; | |
| while (offset = offsets.shift()) { | |
| if (!offsets.length) { | |
| retOffset = offset; | |
| } else { | |
| retNode = retNode.childNodes[offset]; | |
| } | |
| } | |
| return [retNode, retOffset]; | |
| } | |
| function recordSelection() { | |
| return [getSelection().anchorNode, getSelection().anchorOffset, | |
| getSelection().focusNode, getSelection().focusOffset]; | |
| } | |
| function assertSelectionEquals(actual, expected, html) { | |
| if (typeof expected == "string") { | |
| expected = [expected]; | |
| } | |
| var pass = false; | |
| for (var i = 0; i < expected.length; i++) { | |
| if (expected[i][0] === actual[0] && | |
| expected[i][1] === actual[1] && | |
| expected[i][2] === actual[2] && | |
| expected[i][3] === actual[3]) { | |
| pass = true; | |
| break; | |
| } | |
| } | |
| assert_true(pass, "Wrong selection, expected " + formatSel(expected) + | |
| ", got " + formatSel(actual) + | |
| " (in HTML " + format_value(html) + ")"); | |
| } | |
| function formatSel(arr) { | |
| if (arr.length == 1) { | |
| arr = arr[0]; | |
| } | |
| if (Array.isArray(arr[0])) { | |
| var ret = []; | |
| for (var i = 0; i < arr.length; i++) { | |
| ret.push(formatSel(arr[i])); | |
| } | |
| return ret.join(" or "); | |
| } | |
| if (arr[0] == arr[2] && arr[1] == arr[3]) { | |
| return "collapsed (" + format_value(arr[0]) + ", " + arr[1] + ")"; | |
| } | |
| return "(" + format_value(arr[0]) + ", " + arr[1] + | |
| ")-(" + format_value(arr[2]) + ", " + arr[3] + ")"; | |
| } | |
| </script> |