tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_nsIEditor_deleteNode.html (8269B)


      1 <!doctype html>
      2 <html>
      3 <head>
      4 <meta charset="utf-8">
      5 <title>nsIEditor.insertNode</title>
      6 <script src="/tests/SimpleTest/SimpleTest.js"></script>
      7 <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
      8 <script>
      9 "use strict";
     10 
     11 function stringifyInputEvent(aEvent) {
     12  if (!aEvent) {
     13    return "null";
     14  }
     15  return `${aEvent.type}: { inputType=${aEvent.inputType} }`;
     16 }
     17 
     18 function getRangeDescription(range) {
     19  function getNodeDescription(node) {
     20    if (!node) {
     21      return "null";
     22    }
     23    switch (node.nodeType) {
     24      case Node.TEXT_NODE:
     25        return `${node.nodeName} "${node.data}"`;
     26      case Node.ELEMENT_NODE:
     27        return `<${node.nodeName.toLowerCase()}>`;
     28      default:
     29        return `${node.nodeName}`;
     30    }
     31  }
     32  if (range === null) {
     33    return "null";
     34  }
     35  if (range === undefined) {
     36    return "undefined";
     37  }
     38  return range.startContainer == range.endContainer &&
     39    range.startOffset == range.endOffset
     40    ? `(${getNodeDescription(range.startContainer)}, ${range.startOffset})`
     41    : `(${getNodeDescription(range.startContainer)}, ${
     42        range.startOffset
     43      }) - (${getNodeDescription(range.endContainer)}, ${range.endOffset})`;
     44 }
     45 
     46 SimpleTest.waitForExplicitFinish();
     47 SimpleTest.waitForFocus(() => {
     48  const editingHost = document.querySelector("div[contenteditable]");
     49  const editor =
     50    SpecialPowers.wrap(window).docShell.editingSession.getEditorForWindow(window);
     51 
     52  editingHost.focus();
     53 
     54  let events = [];
     55  editingHost.addEventListener("input", event => events.push(event));
     56 
     57  (function test_delete_node_before_selection() {
     58    editingHost.innerHTML = "<span>abc</span><span>def</span>";
     59    getSelection().collapse(editingHost.querySelector("span + span").firstChild, 0);
     60    editor.deleteNode(editingHost.querySelector("span"));
     61    is(
     62      editingHost.innerHTML,
     63      "<span>def</span>",
     64      "test_delete_node_before_selection: deleteNode() should delete the node"
     65    );
     66    is(
     67      events.length,
     68      1,
     69      "test_delete_node_before_selection: Only one input event should be fired when deleteNode() deletes a node"
     70    );
     71    is(
     72      stringifyInputEvent(events[0]),
     73      stringifyInputEvent({ type: "input", inputType: "" }),
     74      "test_delete_node_before_selection: input event should be fired when deleting a node"
     75    );
     76    is(
     77      getRangeDescription(getSelection().getRangeAt(0)),
     78      getRangeDescription({
     79        startContainer: editingHost.firstChild.firstChild,
     80        startOffset: 0,
     81        endContainer: editingHost.firstChild.firstChild,
     82        endOffset: 0,
     83      }),
     84      "test_delete_node_before_selection: selection shouldn't be updated"
     85    );
     86  })();
     87 
     88  (function test_delete_node_after_selection() {
     89    events = [];
     90    editingHost.innerHTML = "<span>abc</span><span>def</span>";
     91    getSelection().collapse(editingHost.querySelector("span").firstChild, 0);
     92    editor.deleteNode(editingHost.querySelector("span + span"));
     93    is(
     94      editingHost.innerHTML,
     95      "<span>abc</span>",
     96      "test_delete_node_after_selection: deleteNode() should delete the node"
     97    );
     98    is(
     99      events.length,
    100      1,
    101      "test_delete_node_after_selection: Only one input event should be fired when deleteNode() deletes a node"
    102    );
    103    is(
    104      stringifyInputEvent(events[0]),
    105      stringifyInputEvent({ type: "input", inputType: "" }),
    106      "test_delete_node_after_selection: input event should be fired when deleting a node"
    107    );
    108    is(
    109      getRangeDescription(getSelection().getRangeAt(0)),
    110      getRangeDescription({
    111        startContainer: editingHost.firstChild.firstChild,
    112        startOffset: 0,
    113        endContainer: editingHost.firstChild.firstChild,
    114        endOffset: 0,
    115      }),
    116      "test_delete_node_after_selection: selection shouldn't be updated"
    117    );
    118  })();
    119 
    120  (function test_delete_node_containing_selection() {
    121    events = [];
    122    editingHost.innerHTML = "<span>abc</span><span>def</span>";
    123    getSelection().collapse(editingHost.querySelector("span").firstChild, 0);
    124    editor.deleteNode(editingHost.querySelector("span"));
    125    is(
    126      editingHost.innerHTML,
    127      "<span>def</span>",
    128      "test_delete_node_containing_selection: deleteNode() should delete the node"
    129    );
    130    is(
    131      events.length,
    132      1,
    133      "test_delete_node_containing_selection: Only one input event should be fired when deleteNode() deletes a node"
    134    );
    135    is(
    136      stringifyInputEvent(events[0]),
    137      stringifyInputEvent({ type: "input", inputType: "" }),
    138      "test_delete_node_containing_selection: input event should be fired when deleting a node"
    139    );
    140    is(
    141      getRangeDescription(getSelection().getRangeAt(0)),
    142      getRangeDescription({
    143        startContainer: editingHost,
    144        startOffset: 0,
    145        endContainer: editingHost,
    146        endOffset: 0,
    147      }),
    148      "test_delete_node_containing_selection: selection should be updated whether node was"
    149    );
    150  })();
    151 
    152  (function test_delete_node_containing_selection_with_preserving_selection() {
    153    events = [];
    154    editingHost.innerHTML = "<span>abc</span><span>def</span>";
    155    getSelection().collapse(editingHost.querySelector("span").firstChild, 0);
    156    editor.deleteNode(editingHost.querySelector("span"), true);
    157    is(
    158      editingHost.innerHTML,
    159      "<span>def</span>",
    160      "test_delete_node_containing_selection_with_preserving_selection: deleteNode() should delete the node"
    161    );
    162    is(
    163      events.length,
    164      1,
    165      "test_delete_node_containing_selection_with_preserving_selection: Only one input event should be fired when deleteNode() deletes a node"
    166    );
    167    is(
    168      stringifyInputEvent(events[0]),
    169      stringifyInputEvent({ type: "input", inputType: "" }),
    170      "test_delete_node_containing_selection_with_preserving_selection: input event should be fired when deleting a node"
    171    );
    172    is(
    173      getRangeDescription(getSelection().getRangeAt(0)),
    174      getRangeDescription({
    175        startContainer: editingHost,
    176        startOffset: 0,
    177        endContainer: editingHost,
    178        endOffset: 0,
    179      }),
    180      "test_delete_node_containing_selection_with_preserving_selection: selection should be updated whether node was"
    181    );
    182  })();
    183 
    184  (function test_not_preserve_selection_nested_by_beforeinput() {
    185    editingHost.innerHTML = "<span>abc</span><span>ghi</span>";
    186    const span = document.createElement("span");
    187    span.textContent = "def";
    188    getSelection().collapse(editingHost, 0);
    189    editingHost.addEventListener("beforeinput", () => {
    190      editor.insertNode(span, editingHost, 1);
    191    }, {once: true});
    192    editor.deleteNode(editingHost.querySelector("span + span"), true);
    193    is(
    194      editingHost.innerHTML,
    195      "<span>abc</span><span>def</span>",
    196      "test_not_preserve_selection_nested_by_beforeinput: both insertNode() and deleteNode() should work"
    197    );
    198    is(
    199      getRangeDescription(getSelection().getRangeAt(0)),
    200      getRangeDescription({
    201        startContainer: editingHost,
    202        startOffset: 2,
    203        endContainer: editingHost,
    204        endOffset: 2,
    205      }),
    206      "test_not_preserve_selection_nested_by_beforeinput: only insertNode() called in beforeinput listener should update selection"
    207    );
    208  })();
    209 
    210  (function test_not_preserve_selection_nested_by_input() {
    211    editingHost.innerHTML = "<span>abc</span><span>ghi</span>";
    212    const span = document.createElement("span");
    213    span.textContent = "def";
    214    getSelection().collapse(editingHost, 0);
    215    editingHost.addEventListener("input", () => {
    216      editor.insertNode(span, editingHost, 1);
    217    }, {once: true});
    218    editor.deleteNode(editingHost.querySelector("span + span"), true);
    219    is(
    220      editingHost.innerHTML,
    221      "<span>abc</span><span>def</span>",
    222      "test_not_preserve_selection_nested_by_input: both insertNode() and deleteNode() should work"
    223    );
    224    is(
    225      getRangeDescription(getSelection().getRangeAt(0)),
    226      getRangeDescription({
    227        startContainer: editingHost,
    228        startOffset: 2,
    229        endContainer: editingHost,
    230        endOffset: 2,
    231      }),
    232      "test_not_preserve_selection_nested_by_input: only insertNode() called in input listener should update selection"
    233    );
    234  })();
    235 
    236  SimpleTest.finish();
    237 });
    238 
    239 </script>
    240 </head>
    241 <body><div contenteditable><br></div></body>
    242 </html>